summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/3rdparty/assimp/LICENSE47
-rw-r--r--src/3rdparty/assimp/README79
-rw-r--r--src/3rdparty/assimp/assimp.pri349
-rw-r--r--src/3rdparty/assimp/code/3DSConverter.cpp844
-rw-r--r--src/3rdparty/assimp/code/3DSHelper.h581
-rw-r--r--src/3rdparty/assimp/code/3DSLoader.cpp1373
-rw-r--r--src/3rdparty/assimp/code/3DSLoader.h280
-rw-r--r--src/3rdparty/assimp/code/ACLoader.cpp860
-rw-r--r--src/3rdparty/assimp/code/ACLoader.h272
-rw-r--r--src/3rdparty/assimp/code/ASELoader.cpp1302
-rw-r--r--src/3rdparty/assimp/code/ASELoader.h208
-rw-r--r--src/3rdparty/assimp/code/ASEParser.cpp2150
-rw-r--r--src/3rdparty/assimp/code/ASEParser.h669
-rw-r--r--src/3rdparty/assimp/code/Assimp.cpp731
-rw-r--r--src/3rdparty/assimp/code/AssimpPCH.cpp70
-rw-r--r--src/3rdparty/assimp/code/AssimpPCH.h149
-rw-r--r--src/3rdparty/assimp/code/B3DImporter.cpp672
-rw-r--r--src/3rdparty/assimp/code/B3DImporter.h126
-rw-r--r--src/3rdparty/assimp/code/BVHLoader.cpp505
-rw-r--r--src/3rdparty/assimp/code/BVHLoader.h173
-rw-r--r--src/3rdparty/assimp/code/BaseImporter.cpp529
-rw-r--r--src/3rdparty/assimp/code/BaseImporter.h499
-rw-r--r--src/3rdparty/assimp/code/BaseProcess.cpp96
-rw-r--r--src/3rdparty/assimp/code/BaseProcess.h289
-rw-r--r--src/3rdparty/assimp/code/BlenderDNA.cpp372
-rw-r--r--src/3rdparty/assimp/code/BlenderDNA.h867
-rw-r--r--src/3rdparty/assimp/code/BlenderDNA.inl638
-rw-r--r--src/3rdparty/assimp/code/BlenderIntermediate.h183
-rw-r--r--src/3rdparty/assimp/code/BlenderLoader.cpp1012
-rw-r--r--src/3rdparty/assimp/code/BlenderLoader.h261
-rw-r--r--src/3rdparty/assimp/code/BlenderModifier.cpp313
-rw-r--r--src/3rdparty/assimp/code/BlenderModifier.h155
-rw-r--r--src/3rdparty/assimp/code/BlenderScene.cpp596
-rw-r--r--src/3rdparty/assimp/code/BlenderScene.h683
-rw-r--r--src/3rdparty/assimp/code/BlenderSceneGen.h223
-rw-r--r--src/3rdparty/assimp/code/BoostWorkaround/boost/LICENSE_1_0.txt23
-rw-r--r--src/3rdparty/assimp/code/BoostWorkaround/boost/foreach.hpp93
-rw-r--r--src/3rdparty/assimp/code/BoostWorkaround/boost/format.hpp85
-rw-r--r--src/3rdparty/assimp/code/BoostWorkaround/boost/lexical_cast.hpp23
-rw-r--r--src/3rdparty/assimp/code/BoostWorkaround/boost/math/common_factor_rt.hpp37
-rw-r--r--src/3rdparty/assimp/code/BoostWorkaround/boost/pointer_cast.hpp45
-rw-r--r--src/3rdparty/assimp/code/BoostWorkaround/boost/scoped_array.hpp79
-rw-r--r--src/3rdparty/assimp/code/BoostWorkaround/boost/scoped_ptr.hpp79
-rw-r--r--src/3rdparty/assimp/code/BoostWorkaround/boost/shared_array.hpp228
-rw-r--r--src/3rdparty/assimp/code/BoostWorkaround/boost/shared_ptr.hpp257
-rw-r--r--src/3rdparty/assimp/code/BoostWorkaround/boost/static_assert.hpp20
-rw-r--r--src/3rdparty/assimp/code/BoostWorkaround/boost/timer.hpp72
-rw-r--r--src/3rdparty/assimp/code/BoostWorkaround/boost/tuple/tuple.hpp285
-rw-r--r--src/3rdparty/assimp/code/ByteSwap.h245
-rw-r--r--src/3rdparty/assimp/code/COBLoader.cpp1278
-rw-r--r--src/3rdparty/assimp/code/COBLoader.h175
-rw-r--r--src/3rdparty/assimp/code/COBScene.h272
-rw-r--r--src/3rdparty/assimp/code/CSMLoader.cpp281
-rw-r--r--src/3rdparty/assimp/code/CSMLoader.h88
-rw-r--r--src/3rdparty/assimp/code/CalcTangentsProcess.cpp283
-rw-r--r--src/3rdparty/assimp/code/CalcTangentsProcess.h118
-rw-r--r--src/3rdparty/assimp/code/ColladaHelper.h603
-rw-r--r--src/3rdparty/assimp/code/ColladaLoader.cpp1492
-rw-r--r--src/3rdparty/assimp/code/ColladaLoader.h241
-rw-r--r--src/3rdparty/assimp/code/ColladaParser.cpp2797
-rw-r--r--src/3rdparty/assimp/code/ColladaParser.h341
-rw-r--r--src/3rdparty/assimp/code/ComputeUVMappingProcess.cpp508
-rw-r--r--src/3rdparty/assimp/code/ComputeUVMappingProcess.h149
-rw-r--r--src/3rdparty/assimp/code/ConvertToLHProcess.cpp318
-rw-r--r--src/3rdparty/assimp/code/ConvertToLHProcess.h169
-rw-r--r--src/3rdparty/assimp/code/DXFLoader.cpp611
-rw-r--r--src/3rdparty/assimp/code/DXFLoader.h184
-rw-r--r--src/3rdparty/assimp/code/DefaultIOStream.cpp141
-rw-r--r--src/3rdparty/assimp/code/DefaultIOStream.h133
-rw-r--r--src/3rdparty/assimp/code/DefaultIOSystem.cpp167
-rw-r--r--src/3rdparty/assimp/code/DefaultIOSystem.h83
-rw-r--r--src/3rdparty/assimp/code/DefaultLogger.cpp423
-rw-r--r--src/3rdparty/assimp/code/DefaultProgressHandler.h64
-rw-r--r--src/3rdparty/assimp/code/Exceptional.h122
-rw-r--r--src/3rdparty/assimp/code/FileLogStream.h64
-rw-r--r--src/3rdparty/assimp/code/FileSystemFilter.h244
-rw-r--r--src/3rdparty/assimp/code/FindDegenerates.cpp216
-rw-r--r--src/3rdparty/assimp/code/FindDegenerates.h110
-rw-r--r--src/3rdparty/assimp/code/FindInstancesProcess.cpp294
-rw-r--r--src/3rdparty/assimp/code/FindInstancesProcess.h140
-rw-r--r--src/3rdparty/assimp/code/FindInvalidDataProcess.cpp419
-rw-r--r--src/3rdparty/assimp/code/FindInvalidDataProcess.h111
-rw-r--r--src/3rdparty/assimp/code/FixNormalsStep.cpp180
-rw-r--r--src/3rdparty/assimp/code/FixNormalsStep.h96
-rw-r--r--src/3rdparty/assimp/code/GenFaceNormalsProcess.cpp138
-rw-r--r--src/3rdparty/assimp/code/GenFaceNormalsProcess.h88
-rw-r--r--src/3rdparty/assimp/code/GenVertexNormalsProcess.cpp228
-rw-r--r--src/3rdparty/assimp/code/GenVertexNormalsProcess.h118
-rw-r--r--src/3rdparty/assimp/code/GenericProperty.h112
-rw-r--r--src/3rdparty/assimp/code/HMPFileData.h134
-rw-r--r--src/3rdparty/assimp/code/HMPLoader.cpp497
-rw-r--r--src/3rdparty/assimp/code/HMPLoader.h159
-rw-r--r--src/3rdparty/assimp/code/HalfLifeFileData.h150
-rw-r--r--src/3rdparty/assimp/code/Hash.h108
-rw-r--r--src/3rdparty/assimp/code/IFF.h102
-rw-r--r--src/3rdparty/assimp/code/IRRLoader.cpp1462
-rw-r--r--src/3rdparty/assimp/code/IRRLoader.h314
-rw-r--r--src/3rdparty/assimp/code/IRRMeshLoader.cpp504
-rw-r--r--src/3rdparty/assimp/code/IRRMeshLoader.h99
-rw-r--r--src/3rdparty/assimp/code/IRRShared.cpp496
-rw-r--r--src/3rdparty/assimp/code/IRRShared.h115
-rw-r--r--src/3rdparty/assimp/code/Importer.cpp1423
-rw-r--r--src/3rdparty/assimp/code/ImproveCacheLocality.cpp382
-rw-r--r--src/3rdparty/assimp/code/ImproveCacheLocality.h102
-rw-r--r--src/3rdparty/assimp/code/JoinVerticesProcess.cpp398
-rw-r--r--src/3rdparty/assimp/code/JoinVerticesProcess.h105
-rw-r--r--src/3rdparty/assimp/code/LWOAnimation.cpp589
-rw-r--r--src/3rdparty/assimp/code/LWOAnimation.h345
-rw-r--r--src/3rdparty/assimp/code/LWOBLoader.cpp396
-rw-r--r--src/3rdparty/assimp/code/LWOFileData.h701
-rw-r--r--src/3rdparty/assimp/code/LWOLoader.cpp1420
-rw-r--r--src/3rdparty/assimp/code/LWOLoader.h490
-rw-r--r--src/3rdparty/assimp/code/LWOMaterial.cpp900
-rw-r--r--src/3rdparty/assimp/code/LWSLoader.cpp889
-rw-r--r--src/3rdparty/assimp/code/LWSLoader.h245
-rw-r--r--src/3rdparty/assimp/code/LimitBoneWeightsProcess.cpp208
-rw-r--r--src/3rdparty/assimp/code/LimitBoneWeightsProcess.h146
-rw-r--r--src/3rdparty/assimp/code/LineSplitter.h217
-rw-r--r--src/3rdparty/assimp/code/MD2FileData.h163
-rw-r--r--src/3rdparty/assimp/code/MD2Loader.cpp414
-rw-r--r--src/3rdparty/assimp/code/MD2Loader.h126
-rw-r--r--src/3rdparty/assimp/code/MD2NormalTable.h217
-rw-r--r--src/3rdparty/assimp/code/MD3FileData.h315
-rw-r--r--src/3rdparty/assimp/code/MD3Loader.cpp1050
-rw-r--r--src/3rdparty/assimp/code/MD3Loader.h331
-rw-r--r--src/3rdparty/assimp/code/MD4FileData.h218
-rw-r--r--src/3rdparty/assimp/code/MD5Loader.cpp739
-rw-r--r--src/3rdparty/assimp/code/MD5Loader.h194
-rw-r--r--src/3rdparty/assimp/code/MD5Parser.cpp475
-rw-r--r--src/3rdparty/assimp/code/MD5Parser.h460
-rw-r--r--src/3rdparty/assimp/code/MDCFileData.h199
-rw-r--r--src/3rdparty/assimp/code/MDCLoader.cpp472
-rw-r--r--src/3rdparty/assimp/code/MDCLoader.h132
-rw-r--r--src/3rdparty/assimp/code/MDCNormalTable.h299
-rw-r--r--src/3rdparty/assimp/code/MDLDefaultColorMap.h118
-rw-r--r--src/3rdparty/assimp/code/MDLFileData.h962
-rw-r--r--src/3rdparty/assimp/code/MDLLoader.cpp1933
-rw-r--r--src/3rdparty/assimp/code/MDLLoader.h460
-rw-r--r--src/3rdparty/assimp/code/MDLMaterialLoader.cpp827
-rw-r--r--src/3rdparty/assimp/code/MS3DLoader.cpp643
-rw-r--r--src/3rdparty/assimp/code/MS3DLoader.h158
-rw-r--r--src/3rdparty/assimp/code/MakeVerboseFormat.cpp218
-rw-r--r--src/3rdparty/assimp/code/MakeVerboseFormat.h106
-rw-r--r--src/3rdparty/assimp/code/MaterialSystem.cpp612
-rw-r--r--src/3rdparty/assimp/code/MaterialSystem.h246
-rw-r--r--src/3rdparty/assimp/code/MemoryIOWrapper.h181
-rw-r--r--src/3rdparty/assimp/code/NDOLoader.cpp289
-rw-r--r--src/3rdparty/assimp/code/NDOLoader.h116
-rw-r--r--src/3rdparty/assimp/code/NFFLoader.cpp1276
-rw-r--r--src/3rdparty/assimp/code/NFFLoader.h216
-rw-r--r--src/3rdparty/assimp/code/OFFLoader.cpp211
-rw-r--r--src/3rdparty/assimp/code/OFFLoader.h96
-rw-r--r--src/3rdparty/assimp/code/ObjFileData.h325
-rwxr-xr-xsrc/3rdparty/assimp/code/ObjFileImporter.cpp529
-rw-r--r--src/3rdparty/assimp/code/ObjFileImporter.h136
-rw-r--r--src/3rdparty/assimp/code/ObjFileMtlImporter.cpp308
-rw-r--r--src/3rdparty/assimp/code/ObjFileMtlImporter.h115
-rwxr-xr-xsrc/3rdparty/assimp/code/ObjFileParser.cpp681
-rw-r--r--src/3rdparty/assimp/code/ObjFileParser.h137
-rw-r--r--src/3rdparty/assimp/code/ObjTools.h263
-rw-r--r--src/3rdparty/assimp/code/OgreImporter.cpp867
-rw-r--r--src/3rdparty/assimp/code/OgreImporter.h142
-rw-r--r--src/3rdparty/assimp/code/OgreImporterMaterial.cpp253
-rw-r--r--src/3rdparty/assimp/code/OgreXmlHelper.h83
-rw-r--r--src/3rdparty/assimp/code/OptimizeGraph.cpp352
-rw-r--r--src/3rdparty/assimp/code/OptimizeGraph.h147
-rw-r--r--src/3rdparty/assimp/code/OptimizeMeshes.cpp249
-rw-r--r--src/3rdparty/assimp/code/OptimizeMeshes.h187
-rw-r--r--src/3rdparty/assimp/code/ParsingUtils.h181
-rw-r--r--src/3rdparty/assimp/code/PlyLoader.cpp1050
-rw-r--r--src/3rdparty/assimp/code/PlyLoader.h174
-rw-r--r--src/3rdparty/assimp/code/PlyParser.cpp919
-rw-r--r--src/3rdparty/assimp/code/PlyParser.h501
-rw-r--r--src/3rdparty/assimp/code/PretransformVertices.cpp715
-rw-r--r--src/3rdparty/assimp/code/PretransformVertices.h166
-rw-r--r--src/3rdparty/assimp/code/ProcessHelper.h559
-rw-r--r--src/3rdparty/assimp/code/Profiler.h97
-rw-r--r--src/3rdparty/assimp/code/Q3BSPFileData.h228
-rw-r--r--src/3rdparty/assimp/code/Q3BSPFileImporter.cpp738
-rw-r--r--src/3rdparty/assimp/code/Q3BSPFileImporter.h110
-rw-r--r--src/3rdparty/assimp/code/Q3BSPFileParser.cpp272
-rw-r--r--src/3rdparty/assimp/code/Q3BSPFileParser.h89
-rw-r--r--src/3rdparty/assimp/code/Q3BSPZipArchive.cpp196
-rw-r--r--src/3rdparty/assimp/code/Q3BSPZipArchive.h181
-rw-r--r--src/3rdparty/assimp/code/Q3DLoader.cpp600
-rw-r--r--src/3rdparty/assimp/code/Q3DLoader.h135
-rw-r--r--src/3rdparty/assimp/code/RawLoader.cpp312
-rw-r--r--src/3rdparty/assimp/code/RawLoader.h123
-rw-r--r--src/3rdparty/assimp/code/RemoveComments.cpp108
-rw-r--r--src/3rdparty/assimp/code/RemoveComments.h88
-rw-r--r--src/3rdparty/assimp/code/RemoveRedundantMaterials.cpp208
-rw-r--r--src/3rdparty/assimp/code/RemoveRedundantMaterials.h107
-rw-r--r--src/3rdparty/assimp/code/RemoveVCProcess.cpp328
-rw-r--r--src/3rdparty/assimp/code/RemoveVCProcess.h123
-rw-r--r--src/3rdparty/assimp/code/SGSpatialSort.cpp169
-rw-r--r--src/3rdparty/assimp/code/SGSpatialSort.h139
-rw-r--r--src/3rdparty/assimp/code/SMDLoader.cpp1135
-rw-r--r--src/3rdparty/assimp/code/SMDLoader.h420
-rw-r--r--src/3rdparty/assimp/code/STLLoader.cpp398
-rw-r--r--src/3rdparty/assimp/code/STLLoader.h119
-rw-r--r--src/3rdparty/assimp/code/SceneCombiner.cpp1140
-rw-r--r--src/3rdparty/assimp/code/SceneCombiner.h365
-rw-r--r--src/3rdparty/assimp/code/ScenePreprocessor.cpp281
-rw-r--r--src/3rdparty/assimp/code/ScenePreprocessor.h116
-rw-r--r--src/3rdparty/assimp/code/SkeletonMeshBuilder.cpp265
-rw-r--r--src/3rdparty/assimp/code/SkeletonMeshBuilder.h122
-rw-r--r--src/3rdparty/assimp/code/SmoothingGroups.h103
-rw-r--r--src/3rdparty/assimp/code/SmoothingGroups.inl138
-rw-r--r--src/3rdparty/assimp/code/SortByPTypeProcess.cpp409
-rw-r--r--src/3rdparty/assimp/code/SortByPTypeProcess.h88
-rw-r--r--src/3rdparty/assimp/code/SpatialSort.cpp350
-rw-r--r--src/3rdparty/assimp/code/SpatialSort.h170
-rw-r--r--src/3rdparty/assimp/code/SplitLargeMeshes.cpp677
-rw-r--r--src/3rdparty/assimp/code/SplitLargeMeshes.h216
-rw-r--r--src/3rdparty/assimp/code/StandardShapes.cpp502
-rw-r--r--src/3rdparty/assimp/code/StandardShapes.h196
-rw-r--r--src/3rdparty/assimp/code/StdOStreamLogStream.h52
-rw-r--r--src/3rdparty/assimp/code/StreamReader.h387
-rw-r--r--src/3rdparty/assimp/code/StringComparison.h217
-rw-r--r--src/3rdparty/assimp/code/Subdivision.cpp591
-rw-r--r--src/3rdparty/assimp/code/Subdivision.h124
-rw-r--r--src/3rdparty/assimp/code/TargetAnimation.cpp246
-rw-r--r--src/3rdparty/assimp/code/TargetAnimation.h179
-rw-r--r--src/3rdparty/assimp/code/TerragenLoader.cpp248
-rw-r--r--src/3rdparty/assimp/code/TerragenLoader.h107
-rw-r--r--src/3rdparty/assimp/code/TextureTransform.cpp567
-rw-r--r--src/3rdparty/assimp/code/TextureTransform.h225
-rw-r--r--src/3rdparty/assimp/code/TinyFormatter.h141
-rw-r--r--src/3rdparty/assimp/code/TriangulateProcess.cpp401
-rw-r--r--src/3rdparty/assimp/code/TriangulateProcess.h98
-rw-r--r--src/3rdparty/assimp/code/UnrealLoader.cpp430
-rw-r--r--src/3rdparty/assimp/code/UnrealLoader.h204
-rw-r--r--src/3rdparty/assimp/code/ValidateDataStructure.cpp972
-rw-r--r--src/3rdparty/assimp/code/ValidateDataStructure.h187
-rw-r--r--src/3rdparty/assimp/code/Vertex.h304
-rw-r--r--src/3rdparty/assimp/code/VertexTriangleAdjacency.cpp132
-rw-r--r--src/3rdparty/assimp/code/VertexTriangleAdjacency.h120
-rw-r--r--src/3rdparty/assimp/code/Win32DebugLogStream.h50
-rw-r--r--src/3rdparty/assimp/code/XFileHelper.h200
-rw-r--r--src/3rdparty/assimp/code/XFileImporter.cpp672
-rw-r--r--src/3rdparty/assimp/code/XFileImporter.h159
-rw-r--r--src/3rdparty/assimp/code/XFileParser.cpp1434
-rw-r--r--src/3rdparty/assimp/code/XFileParser.h162
-rw-r--r--src/3rdparty/assimp/code/aiAssert.cpp72
-rw-r--r--src/3rdparty/assimp/code/assbin_chunks.h196
-rw-r--r--src/3rdparty/assimp/code/fast_atof.h306
-rw-r--r--src/3rdparty/assimp/code/irrXMLWrapper.h131
-rw-r--r--src/3rdparty/assimp/code/makefile111
-rw-r--r--src/3rdparty/assimp/code/makefile.mingw105
-rw-r--r--src/3rdparty/assimp/code/pstdint.h729
-rw-r--r--src/3rdparty/assimp/code/qnan.h110
-rw-r--r--src/3rdparty/assimp/code/res/assimp.rc80
-rw-r--r--src/3rdparty/assimp/code/res/resource.h14
-rw-r--r--src/3rdparty/assimp/contrib/ConvertUTF/ConvertUTF.c539
-rw-r--r--src/3rdparty/assimp/contrib/ConvertUTF/ConvertUTF.h151
-rw-r--r--src/3rdparty/assimp/contrib/ConvertUTF/readme.txt43
-rw-r--r--src/3rdparty/assimp/contrib/irrXML/CXMLReaderImpl.h809
-rw-r--r--src/3rdparty/assimp/contrib/irrXML/heapsort.h73
-rw-r--r--src/3rdparty/assimp/contrib/irrXML/irrArray.h444
-rw-r--r--src/3rdparty/assimp/contrib/irrXML/irrString.h664
-rw-r--r--src/3rdparty/assimp/contrib/irrXML/irrTypes.h108
-rw-r--r--src/3rdparty/assimp/contrib/irrXML/irrXML.cpp152
-rw-r--r--src/3rdparty/assimp/contrib/irrXML/irrXML.h540
-rw-r--r--src/3rdparty/assimp/contrib/unzip/crypt.h137
-rw-r--r--src/3rdparty/assimp/contrib/unzip/ioapi.c169
-rw-r--r--src/3rdparty/assimp/contrib/unzip/ioapi.h75
-rw-r--r--src/3rdparty/assimp/contrib/unzip/unzip.c1604
-rw-r--r--src/3rdparty/assimp/contrib/unzip/unzip.h358
-rw-r--r--src/3rdparty/assimp/contrib/zlib/README115
-rw-r--r--src/3rdparty/assimp/contrib/zlib/adler32.c169
-rw-r--r--src/3rdparty/assimp/contrib/zlib/compress.c80
-rw-r--r--src/3rdparty/assimp/contrib/zlib/crc32.c442
-rw-r--r--src/3rdparty/assimp/contrib/zlib/crc32.h441
-rw-r--r--src/3rdparty/assimp/contrib/zlib/deflate.c1834
-rw-r--r--src/3rdparty/assimp/contrib/zlib/deflate.h342
-rw-r--r--src/3rdparty/assimp/contrib/zlib/inffast.c340
-rw-r--r--src/3rdparty/assimp/contrib/zlib/inffast.h11
-rw-r--r--src/3rdparty/assimp/contrib/zlib/inffixed.h94
-rw-r--r--src/3rdparty/assimp/contrib/zlib/inflate.c1480
-rw-r--r--src/3rdparty/assimp/contrib/zlib/inflate.h122
-rw-r--r--src/3rdparty/assimp/contrib/zlib/inftrees.c330
-rw-r--r--src/3rdparty/assimp/contrib/zlib/inftrees.h62
-rw-r--r--src/3rdparty/assimp/contrib/zlib/trees.c1244
-rw-r--r--src/3rdparty/assimp/contrib/zlib/trees.h128
-rw-r--r--src/3rdparty/assimp/contrib/zlib/zconf.h428
-rw-r--r--src/3rdparty/assimp/contrib/zlib/zconf.in.h332
-rw-r--r--src/3rdparty/assimp/contrib/zlib/zlib.h1613
-rw-r--r--src/3rdparty/assimp/contrib/zlib/zutil.c318
-rw-r--r--src/3rdparty/assimp/contrib/zlib/zutil.h274
-rw-r--r--src/3rdparty/assimp/include/Compiler/poppack1.h22
-rw-r--r--src/3rdparty/assimp/include/Compiler/pushpack1.h42
-rw-r--r--src/3rdparty/assimp/include/DefaultLogger.h190
-rw-r--r--src/3rdparty/assimp/include/IOStream.h135
-rw-r--r--src/3rdparty/assimp/include/IOSystem.h222
-rw-r--r--src/3rdparty/assimp/include/LogStream.h93
-rw-r--r--src/3rdparty/assimp/include/Logger.h234
-rw-r--r--src/3rdparty/assimp/include/NullLogger.h95
-rw-r--r--src/3rdparty/assimp/include/ProgressHandler.h93
-rw-r--r--src/3rdparty/assimp/include/aiAnim.h484
-rw-r--r--src/3rdparty/assimp/include/aiAssert.h30
-rw-r--r--src/3rdparty/assimp/include/aiCamera.h223
-rw-r--r--src/3rdparty/assimp/include/aiColor4D.h94
-rw-r--r--src/3rdparty/assimp/include/aiColor4D.inl140
-rw-r--r--src/3rdparty/assimp/include/aiConfig.h634
-rw-r--r--src/3rdparty/assimp/include/aiDefines.h255
-rw-r--r--src/3rdparty/assimp/include/aiFileIO.h135
-rw-r--r--src/3rdparty/assimp/include/aiLight.h233
-rw-r--r--src/3rdparty/assimp/include/aiMaterial.h1423
-rw-r--r--src/3rdparty/assimp/include/aiMaterial.inl191
-rw-r--r--src/3rdparty/assimp/include/aiMatrix3x3.h166
-rw-r--r--src/3rdparty/assimp/include/aiMatrix3x3.inl250
-rw-r--r--src/3rdparty/assimp/include/aiMatrix4x4.h240
-rw-r--r--src/3rdparty/assimp/include/aiMatrix4x4.inl419
-rw-r--r--src/3rdparty/assimp/include/aiMesh.h733
-rw-r--r--src/3rdparty/assimp/include/aiPostProcess.h604
-rw-r--r--src/3rdparty/assimp/include/aiQuaternion.h311
-rw-r--r--src/3rdparty/assimp/include/aiScene.h417
-rw-r--r--src/3rdparty/assimp/include/aiTexture.h198
-rw-r--r--src/3rdparty/assimp/include/aiTypes.h493
-rw-r--r--src/3rdparty/assimp/include/aiVector2D.h187
-rw-r--r--src/3rdparty/assimp/include/aiVector3D.h129
-rw-r--r--src/3rdparty/assimp/include/aiVector3D.inl180
-rw-r--r--src/3rdparty/assimp/include/aiVersion.h104
-rw-r--r--src/3rdparty/assimp/include/assimp.h434
-rw-r--r--src/3rdparty/assimp/include/assimp.hpp609
-rw-r--r--src/3rdparty/assimp/revision.h1
-rw-r--r--src/core/core.pro1
327 files changed, 118466 insertions, 0 deletions
diff --git a/src/3rdparty/assimp/LICENSE b/src/3rdparty/assimp/LICENSE
new file mode 100644
index 000000000..ade560478
--- /dev/null
+++ b/src/3rdparty/assimp/LICENSE
@@ -0,0 +1,47 @@
+Open Asset Import Library (Assimp)
+
+
+Copyright (c) 2006-2010, Assimp Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+AN EXCEPTION applies to all files in the ./test/models-nonbsd subfolder.
+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.
+
diff --git a/src/3rdparty/assimp/README b/src/3rdparty/assimp/README
new file mode 100644
index 000000000..e5592ffbb
--- /dev/null
+++ b/src/3rdparty/assimp/README
@@ -0,0 +1,79 @@
+ Open Asset Import Library (Assimp) Readme
+ -----------------------------------------
+
+Here you can find information regarding Open Asset Import Library:
+
+ 1. Overview
+ 1.1 Supported file formats
+ 1.2 File structure
+ 2. Build the Asset Import Library
+ 3. Help
+ 4. License
+
+
+
+1. Overview
+1.1 Supported file formats
+
+The Asset Import Library provides a lot of model formats:
+ - 3DS
+ - ASE
+ - DXF
+ - HMP
+ - MD2
+ - MD3
+ - MD5
+ - MDC
+ - MDL
+ - NFF
+ - PLY
+ - STL
+ - X
+ - LWO
+ - OBJ
+ - SMD
+
+ - Collada
+
+ - LWO
+
+ - Ogre XML
+
+ - partly LWS
+
+ - .. + many more, see http://assimp.sourceforge.net/main_features_formats.html for a full list.
+
+
+1.2 Repository structure
+
+Open Asset Import Library is implemented in C++ and provides a C-interface. The directory structure is:
+
+ /bin Binaries of the Asset Import Library.
+ /code Code of the Asset Import Library.
+ /contrib Third-party-libraries used by the Asset Import Library.
+ /doc Documentation (doxygen generated and data structure as a xml file)
+ /include Public headers.
+ /lib Static library location for Windows.
+ /obj Object file location for Windows.
+ /port Ports to other languages.
+
+ /test Unit- and regression tests, model test suite.
+ /tools Tools (viewer, command line `assimp`).
+ /workspaces Build enviroments for vc,xcode,...
+
+
+
+2. Build the Asset Import Library
+
+Take a look into the INSTALL file.
+
+
+
+3. Help
+
+For more help go to http://assimp.sourceforge.net/ (or, for germans, http://www.zfx.info). Or take a look into the doc-folder, which contains the doxygen-generated documentation in HTMl format (CHMs for Windows are as well contained in some distributions and located right here in the root folder).
+
+
+4. License
+
+The license of the Asset Import Library is based on the BSD-License. It is contained in the LICENSE file.
diff --git a/src/3rdparty/assimp/assimp.pri b/src/3rdparty/assimp/assimp.pri
new file mode 100644
index 000000000..a3810b39a
--- /dev/null
+++ b/src/3rdparty/assimp/assimp.pri
@@ -0,0 +1,349 @@
+# This file mostly g3nerated by "qmake -project" - use that to regenerate if large scale
+# changes occur to this directory
+
+# AssImp expects this to be defined on debug builds
+CONFIG(debug, debug|release) : DEFINES+=_DEBUG
+CONFIG += exceptions
+
+win32:DEFINES+=_CRT_SECURE_NO_WARNINGS
+
+VPATH += \
+ $$PWD \
+ $$PWD/code \
+ $$PWD/contrib/ConvertUTF \
+ $$PWD/contrib/zlib \
+ $$PWD/contrib/irrXML \
+ $$PWD/contrib/unzip
+
+INCLUDEPATH += \
+ $$PWD \
+ $$PWD/code \
+ $$PWD/code/BoostWorkaround \
+ $$PWD/include \
+ $$PWD/include/Compiler \
+ $$PWD/contrib/ConvertUTF \
+ $$PWD/contrib/zlib \
+ $$PWD/contrib/irrXML \
+ $$PWD/contrib/unzip
+
+# Input
+HEADERS += \
+ revision.h \
+ code/3DSHelper.h \
+ code/3DSLoader.h \
+ code/ACLoader.h \
+ code/ASELoader.h \
+ code/ASEParser.h \
+ code/assbin_chunks.h \
+ code/AssimpPCH.h \
+ code/B3DImporter.h \
+ code/BaseImporter.h \
+ code/BaseProcess.h \
+ code/BlenderDNA.h \
+ code/BlenderIntermediate.h \
+ code/BlenderLoader.h \
+ code/BlenderModifier.h \
+ code/BlenderScene.h \
+ code/BlenderSceneGen.h \
+ code/BVHLoader.h \
+ code/ByteSwap.h \
+ code/CalcTangentsProcess.h \
+ code/COBLoader.h \
+ code/COBScene.h \
+ code/ColladaHelper.h \
+ code/ColladaLoader.h \
+ code/ColladaParser.h \
+ code/ComputeUVMappingProcess.h \
+ code/ConvertToLHProcess.h \
+ code/CSMLoader.h \
+ code/DefaultIOStream.h \
+ code/DefaultIOSystem.h \
+ code/DefaultProgressHandler.h \
+ code/DXFLoader.h \
+ code/Exceptional.h \
+ code/fast_atof.h \
+ code/FileLogStream.h \
+ code/FileSystemFilter.h \
+ code/FindDegenerates.h \
+ code/FindInstancesProcess.h \
+ code/FindInvalidDataProcess.h \
+ code/FixNormalsStep.h \
+ code/GenericProperty.h \
+ code/GenFaceNormalsProcess.h \
+ code/GenVertexNormalsProcess.h \
+ code/HalfLifeFileData.h \
+ code/Hash.h \
+ code/HMPFileData.h \
+ code/HMPLoader.h \
+ code/IFF.h \
+ code/ImproveCacheLocality.h \
+ code/IRRLoader.h \
+ code/IRRMeshLoader.h \
+ code/IRRShared.h \
+ code/irrXMLWrapper.h \
+ code/JoinVerticesProcess.h \
+ code/LimitBoneWeightsProcess.h \
+ code/LineSplitter.h \
+ code/LWOAnimation.h \
+ code/LWOFileData.h \
+ code/LWOLoader.h \
+ code/LWSLoader.h \
+ code/MakeVerboseFormat.h \
+ code/MaterialSystem.h \
+ code/MD2FileData.h \
+ code/MD2Loader.h \
+ code/MD2NormalTable.h \
+ code/MD3FileData.h \
+ code/MD3Loader.h \
+ code/MD4FileData.h \
+ code/MD5Loader.h \
+ code/MD5Parser.h \
+ code/MDCFileData.h \
+ code/MDCLoader.h \
+ code/MDCNormalTable.h \
+ code/MDLDefaultColorMap.h \
+ code/MDLFileData.h \
+ code/MDLLoader.h \
+ code/MemoryIOWrapper.h \
+ code/MS3DLoader.h \
+ code/NDOLoader.h \
+ code/NFFLoader.h \
+ code/ObjFileData.h \
+ code/ObjFileImporter.h \
+ code/ObjFileMtlImporter.h \
+ code/ObjFileParser.h \
+ code/ObjTools.h \
+ code/OFFLoader.h \
+ code/OgreImporter.h \
+ code/OgreXmlHelper.h \
+ code/OptimizeGraph.h \
+ code/OptimizeMeshes.h \
+ code/ParsingUtils.h \
+ code/PlyLoader.h \
+ code/PlyParser.h \
+ code/PretransformVertices.h \
+ code/ProcessHelper.h \
+ code/Profiler.h \
+ code/pstdint.h \
+ code/Q3BSPFileData.h \
+ code/Q3BSPFileImporter.h \
+ code/Q3BSPFileParser.h \
+ code/Q3BSPZipArchive.h \
+ code/Q3DLoader.h \
+ code/qnan.h \
+ code/RawLoader.h \
+ code/RemoveComments.h \
+ code/RemoveRedundantMaterials.h \
+ code/RemoveVCProcess.h \
+ code/SceneCombiner.h \
+ code/ScenePreprocessor.h \
+ code/SGSpatialSort.h \
+ code/SkeletonMeshBuilder.h \
+ code/SMDLoader.h \
+ code/SmoothingGroups.h \
+ code/SortByPTypeProcess.h \
+ code/SpatialSort.h \
+ code/SplitLargeMeshes.h \
+ code/StandardShapes.h \
+ code/StdOStreamLogStream.h \
+ code/STLLoader.h \
+ code/StreamReader.h \
+ code/StringComparison.h \
+ code/Subdivision.h \
+ code/TargetAnimation.h \
+ code/TerragenLoader.h \
+ code/TextureTransform.h \
+ code/TinyFormatter.h \
+ code/TriangulateProcess.h \
+ code/UnrealLoader.h \
+ code/ValidateDataStructure.h \
+ code/Vertex.h \
+ code/VertexTriangleAdjacency.h \
+ code/Win32DebugLogStream.h \
+ code/XFileHelper.h \
+ code/XFileImporter.h \
+ code/XFileParser.h \
+ include/aiAnim.h \
+ include/aiAssert.h \
+ include/aiCamera.h \
+ include/aiColor4D.h \
+ include/aiConfig.h \
+ include/aiDefines.h \
+ include/aiFileIO.h \
+ include/aiLight.h \
+ include/aiMaterial.h \
+ include/aiMatrix3x3.h \
+ include/aiMatrix4x4.h \
+ include/aiMesh.h \
+ include/aiPostProcess.h \
+ include/aiQuaternion.h \
+ include/aiScene.h \
+ include/aiTexture.h \
+ include/aiTypes.h \
+ include/aiVector2D.h \
+ include/aiVector3D.h \
+ include/aiVersion.h \
+ include/assimp.h \
+ include/assimp.hpp \
+ include/DefaultLogger.h \
+ include/IOStream.h \
+ include/IOSystem.h \
+ include/Logger.h \
+ include/LogStream.h \
+ include/NullLogger.h \
+ include/ProgressHandler.h \
+ code/res/resource.h \
+ contrib/ConvertUTF/ConvertUTF.h \
+ contrib/irrXML/CXMLReaderImpl.h \
+ contrib/irrXML/heapsort.h \
+ contrib/irrXML/irrArray.h \
+ contrib/irrXML/irrString.h \
+ contrib/irrXML/irrTypes.h \
+ contrib/irrXML/irrXML.h \
+ contrib/unzip/crypt.h \
+ contrib/unzip/ioapi.h \
+ contrib/unzip/unzip.h \
+ contrib/zlib/crc32.h \
+ contrib/zlib/deflate.h \
+ contrib/zlib/inffast.h \
+ contrib/zlib/inffixed.h \
+ contrib/zlib/inflate.h \
+ contrib/zlib/inftrees.h \
+ contrib/zlib/trees.h \
+ contrib/zlib/zconf.h \
+ contrib/zlib/zconf.in.h \
+ contrib/zlib/zlib.h \
+ contrib/zlib/zutil.h \
+ include/Compiler/poppack1.h \
+ include/Compiler/pushpack1.h \
+ code/BoostWorkaround/boost/foreach.hpp \
+ code/BoostWorkaround/boost/format.hpp \
+ code/BoostWorkaround/boost/lexical_cast.hpp \
+ code/BoostWorkaround/boost/pointer_cast.hpp \
+ code/BoostWorkaround/boost/scoped_array.hpp \
+ code/BoostWorkaround/boost/scoped_ptr.hpp \
+ code/BoostWorkaround/boost/shared_array.hpp \
+ code/BoostWorkaround/boost/shared_ptr.hpp \
+ code/BoostWorkaround/boost/static_assert.hpp \
+ code/BoostWorkaround/boost/timer.hpp \
+ code/BoostWorkaround/boost/math/common_factor_rt.hpp \
+ code/BoostWorkaround/boost/tuple/tuple.hpp \
+ include/aiVector3D.inl \
+ include/aiColor4D.inl \
+ include/aiMatrix3x3.inl \
+ include/aiMatrix4x4.inl \
+ include/aiMaterial.inl \
+ code/SmoothingGroups.inl \
+ code/BlenderDNA.inl
+
+SOURCES += \
+ code/3DSConverter.cpp \
+ code/3DSLoader.cpp \
+ code/ACLoader.cpp \
+ code/aiAssert.cpp \
+ code/ASELoader.cpp \
+ code/ASEParser.cpp \
+ code/Assimp.cpp \
+ code/AssimpPCH.cpp \
+ code/B3DImporter.cpp \
+ code/BaseImporter.cpp \
+ code/BaseProcess.cpp \
+ code/BlenderDNA.cpp \
+ code/BlenderLoader.cpp \
+ code/BlenderModifier.cpp \
+ code/BlenderScene.cpp \
+ code/BVHLoader.cpp \
+ code/CalcTangentsProcess.cpp \
+ code/COBLoader.cpp \
+ code/ColladaLoader.cpp \
+ code/ColladaParser.cpp \
+ code/ComputeUVMappingProcess.cpp \
+ code/ConvertToLHProcess.cpp \
+ code/CSMLoader.cpp \
+ code/DefaultIOStream.cpp \
+ code/DefaultIOSystem.cpp \
+ code/DefaultLogger.cpp \
+ code/DXFLoader.cpp \
+ code/FindDegenerates.cpp \
+ code/FindInstancesProcess.cpp \
+ code/FindInvalidDataProcess.cpp \
+ code/FixNormalsStep.cpp \
+ code/GenFaceNormalsProcess.cpp \
+ code/GenVertexNormalsProcess.cpp \
+ code/HMPLoader.cpp \
+ code/Importer.cpp \
+ code/ImproveCacheLocality.cpp \
+ code/IRRLoader.cpp \
+ code/IRRMeshLoader.cpp \
+ code/IRRShared.cpp \
+ code/JoinVerticesProcess.cpp \
+ code/LimitBoneWeightsProcess.cpp \
+ code/LWOAnimation.cpp \
+ code/LWOBLoader.cpp \
+ code/LWOLoader.cpp \
+ code/LWOMaterial.cpp \
+ code/LWSLoader.cpp \
+ code/MakeVerboseFormat.cpp \
+ code/MaterialSystem.cpp \
+ code/MD2Loader.cpp \
+ code/MD3Loader.cpp \
+ code/MD5Loader.cpp \
+ code/MD5Parser.cpp \
+ code/MDCLoader.cpp \
+ code/MDLLoader.cpp \
+ code/MDLMaterialLoader.cpp \
+ code/MS3DLoader.cpp \
+ code/NDOLoader.cpp \
+ code/NFFLoader.cpp \
+ code/ObjFileImporter.cpp \
+ code/ObjFileMtlImporter.cpp \
+ code/ObjFileParser.cpp \
+ code/OFFLoader.cpp \
+ code/OgreImporter.cpp \
+ code/OgreImporterMaterial.cpp \
+ code/OptimizeGraph.cpp \
+ code/OptimizeMeshes.cpp \
+ code/PlyLoader.cpp \
+ code/PlyParser.cpp \
+ code/PretransformVertices.cpp \
+ code/Q3BSPFileImporter.cpp \
+ code/Q3BSPFileParser.cpp \
+ code/Q3BSPZipArchive.cpp \
+ code/Q3DLoader.cpp \
+ code/RawLoader.cpp \
+ code/RemoveComments.cpp \
+ code/RemoveRedundantMaterials.cpp \
+ code/RemoveVCProcess.cpp \
+ code/SceneCombiner.cpp \
+ code/ScenePreprocessor.cpp \
+ code/SGSpatialSort.cpp \
+ code/SkeletonMeshBuilder.cpp \
+ code/SMDLoader.cpp \
+ code/SortByPTypeProcess.cpp \
+ code/SpatialSort.cpp \
+ code/SplitLargeMeshes.cpp \
+ code/StandardShapes.cpp \
+ code/STLLoader.cpp \
+ code/Subdivision.cpp \
+ code/TargetAnimation.cpp \
+ code/TerragenLoader.cpp \
+ code/TextureTransform.cpp \
+ code/TriangulateProcess.cpp \
+ code/UnrealLoader.cpp \
+ code/ValidateDataStructure.cpp \
+ code/VertexTriangleAdjacency.cpp \
+ code/XFileImporter.cpp \
+ code/XFileParser.cpp \
+ contrib/ConvertUTF/ConvertUTF.c \
+ contrib/irrXML/irrXML.cpp \
+ contrib/unzip/ioapi.c \
+ contrib/unzip/unzip.c \
+ contrib/zlib/adler32.c \
+ contrib/zlib/compress.c \
+ contrib/zlib/crc32.c \
+ contrib/zlib/deflate.c \
+ contrib/zlib/inffast.c \
+ contrib/zlib/inflate.c \
+ contrib/zlib/inftrees.c \
+ contrib/zlib/trees.c \
+ contrib/zlib/zutil.c
diff --git a/src/3rdparty/assimp/code/3DSConverter.cpp b/src/3rdparty/assimp/code/3DSConverter.cpp
new file mode 100644
index 000000000..4ede10f60
--- /dev/null
+++ b/src/3rdparty/assimp/code/3DSConverter.cpp
@@ -0,0 +1,844 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the 3ds importer class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
+
+// internal headers
+#include "3DSLoader.h"
+#include "TargetAnimation.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Setup final material indices, generae a default material if necessary
+void Discreet3DSImporter::ReplaceDefaultMaterial()
+{
+
+ // Try to find an existing material that matches the
+ // typical default material setting:
+ // - no textures
+ // - diffuse color (in grey!)
+ // NOTE: This is here to workaround the fact that some
+ // exporters are writing a default material, too.
+ unsigned int idx = 0xcdcdcdcd;
+ for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
+ {
+ std::string s = mScene->mMaterials[i].mName;
+ for (std::string::iterator it = s.begin(); it != s.end(); ++it)
+ *it = ::tolower(*it);
+
+ if (std::string::npos == s.find("default"))continue;
+
+ if (mScene->mMaterials[i].mDiffuse.r !=
+ mScene->mMaterials[i].mDiffuse.g ||
+ mScene->mMaterials[i].mDiffuse.r !=
+ mScene->mMaterials[i].mDiffuse.b)continue;
+
+ if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 ||
+ mScene->mMaterials[i].sTexBump.mMapName.length() != 0 ||
+ mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 ||
+ mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 ||
+ mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 ||
+ mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 )
+ {
+ continue;
+ }
+ idx = i;
+ }
+ if (0xcdcdcdcd == idx)idx = (unsigned int)mScene->mMaterials.size();
+
+ // now iterate through all meshes and through all faces and
+ // find all faces that are using the default material
+ unsigned int cnt = 0;
+ for (std::vector<D3DS::Mesh>::iterator
+ i = mScene->mMeshes.begin();
+ i != mScene->mMeshes.end();++i)
+ {
+ for (std::vector<unsigned int>::iterator
+ a = (*i).mFaceMaterials.begin();
+ a != (*i).mFaceMaterials.end();++a)
+ {
+ // NOTE: The additional check seems to be necessary,
+ // some exporters seem to generate invalid data here
+ if (0xcdcdcdcd == (*a))
+ {
+ (*a) = idx;
+ ++cnt;
+ }
+ else if ( (*a) >= mScene->mMaterials.size())
+ {
+ (*a) = idx;
+ DefaultLogger::get()->warn("Material index overflow in 3DS file. Using default material");
+ ++cnt;
+ }
+ }
+ }
+ if (cnt && idx == mScene->mMaterials.size())
+ {
+ // We need to create our own default material
+ D3DS::Material sMat;
+ sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f);
+ sMat.mName = "%%%DEFAULT";
+ mScene->mMaterials.push_back(sMat);
+
+ DefaultLogger::get()->info("3DS: Generating default material");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Check whether all indices are valid. Otherwise we'd crash before the validation step is reached
+void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh)
+{
+ for (std::vector< D3DS::Face >::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end();++i)
+ {
+ // check whether all indices are in range
+ for (unsigned int a = 0; a < 3;++a)
+ {
+ if ((*i).mIndices[a] >= sMesh.mPositions.size())
+ {
+ DefaultLogger::get()->warn("3DS: Vertex index overflow)");
+ (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1;
+ }
+ if ( !sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size())
+ {
+ DefaultLogger::get()->warn("3DS: Texture coordinate index overflow)");
+ (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size()-1;
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Generate out unique verbose format representation
+void Discreet3DSImporter::MakeUnique(D3DS::Mesh& sMesh)
+{
+ // TODO: really necessary? I don't think. Just a waste of memory and time
+ // to do it now in a separate buffer.
+
+ // Allocate output storage
+ std::vector<aiVector3D> vNew (sMesh.mFaces.size() * 3);
+ std::vector<aiVector3D> vNew2;
+ if (sMesh.mTexCoords.size())
+ vNew2.resize(sMesh.mFaces.size() * 3);
+
+ for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size();++i)
+ {
+ D3DS::Face& face = sMesh.mFaces[i];
+
+ // Positions
+ for (unsigned int a = 0; a < 3;++a,++base)
+ {
+ vNew[base] = sMesh.mPositions[face.mIndices[a]];
+ if (sMesh.mTexCoords.size())
+ vNew2[base] = sMesh.mTexCoords[face.mIndices[a]];
+
+ face.mIndices[a] = base;
+ }
+ }
+ sMesh.mPositions = vNew;
+ sMesh.mTexCoords = vNew2;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a 3DS texture to texture keys in an aiMaterial
+void CopyTexture(MaterialHelper& mat, D3DS::Texture& texture, aiTextureType type)
+{
+ // Setup the texture name
+ aiString tex;
+ tex.Set( texture.mMapName);
+ mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0));
+
+ // Setup the texture blend factor
+ if (is_not_qnan(texture.mTextureBlend))
+ mat.AddProperty<float>( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0));
+
+ // Setup the texture mapping mode
+ mat.AddProperty<int>((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0));
+ mat.AddProperty<int>((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0));
+
+ // Mirroring - double the scaling values
+ // FIXME: this is not really correct ...
+ if (texture.mMapMode == aiTextureMapMode_Mirror)
+ {
+ texture.mScaleU *= 2.f;
+ texture.mScaleV *= 2.f;
+ texture.mOffsetU /= 2.f;
+ texture.mOffsetV /= 2.f;
+ }
+
+ // Setup texture UV transformations
+ mat.AddProperty<float>(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a 3DS material to an aiMaterial
+void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat,
+ MaterialHelper& mat)
+{
+ // NOTE: Pass the background image to the viewer by bypassing the
+ // material system. This is an evil hack, never do it again!
+ if (0 != mBackgroundImage.length() && bHasBG)
+ {
+ aiString tex;
+ tex.Set( mBackgroundImage);
+ mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
+
+ // Be sure this is only done for the first material
+ mBackgroundImage = std::string("");
+ }
+
+ // At first add the base ambient color of the scene to the material
+ oldMat.mAmbient.r += mClrAmbient.r;
+ oldMat.mAmbient.g += mClrAmbient.g;
+ oldMat.mAmbient.b += mClrAmbient.b;
+
+ aiString name;
+ name.Set( oldMat.mName);
+ mat.AddProperty( &name, AI_MATKEY_NAME);
+
+ // Material colors
+ mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
+ mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
+ mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
+
+ // Phong shininess and shininess strength
+ if (D3DS::Discreet3DS::Phong == oldMat.mShading ||
+ D3DS::Discreet3DS::Metal == oldMat.mShading)
+ {
+ if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength)
+ {
+ oldMat.mShading = D3DS::Discreet3DS::Gouraud;
+ }
+ else
+ {
+ mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
+ mat.AddProperty( &oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
+ }
+ }
+
+ // Opacity
+ mat.AddProperty<float>( &oldMat.mTransparency,1,AI_MATKEY_OPACITY);
+
+ // Bump height scaling
+ mat.AddProperty<float>( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING);
+
+ // Two sided rendering?
+ if (oldMat.mTwoSided)
+ {
+ int i = 1;
+ mat.AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
+ }
+
+ // Shading mode
+ aiShadingMode eShading = aiShadingMode_NoShading;
+ switch (oldMat.mShading)
+ {
+ case D3DS::Discreet3DS::Flat:
+ eShading = aiShadingMode_Flat; break;
+
+ // I don't know what "Wire" shading should be,
+ // assume it is simple lambertian diffuse shading
+ case D3DS::Discreet3DS::Wire:
+ {
+ // Set the wireframe flag
+ unsigned int iWire = 1;
+ mat.AddProperty<int>( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME);
+ }
+
+ case D3DS::Discreet3DS::Gouraud:
+ eShading = aiShadingMode_Gouraud; break;
+
+ // assume cook-torrance shading for metals.
+ case D3DS::Discreet3DS::Phong :
+ eShading = aiShadingMode_Phong; break;
+
+ case D3DS::Discreet3DS::Metal :
+ eShading = aiShadingMode_CookTorrance; break;
+
+ // FIX to workaround a warning with GCC 4 who complained
+ // about a missing case Blinn: here - Blinn isn't a valid
+ // value in the 3DS Loader, it is just needed for ASE
+ case D3DS::Discreet3DS::Blinn :
+ eShading = aiShadingMode_Blinn; break;
+ }
+ mat.AddProperty<int>( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL);
+
+ // DIFFUSE texture
+ if ( oldMat.sTexDiffuse.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexDiffuse, aiTextureType_DIFFUSE);
+
+ // SPECULAR texture
+ if ( oldMat.sTexSpecular.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexSpecular, aiTextureType_SPECULAR);
+
+ // OPACITY texture
+ if ( oldMat.sTexOpacity.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexOpacity, aiTextureType_OPACITY);
+
+ // EMISSIVE texture
+ if ( oldMat.sTexEmissive.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexEmissive, aiTextureType_EMISSIVE);
+
+ // BUMP texture
+ if ( oldMat.sTexBump.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexBump, aiTextureType_HEIGHT);
+
+ // SHININESS texture
+ if ( oldMat.sTexShininess.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexShininess, aiTextureType_SHININESS);
+
+ // REFLECTION texture
+ if ( oldMat.sTexReflective.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexReflective, aiTextureType_REFLECTION);
+
+ // Store the name of the material itself, too
+ if ( oldMat.mName.length()) {
+ aiString tex;
+ tex.Set( oldMat.mName);
+ mat.AddProperty( &tex, AI_MATKEY_NAME);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Split meshes by their materials and generate output aiMesh'es
+void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
+{
+ std::vector<aiMesh*> avOutMeshes;
+ avOutMeshes.reserve(mScene->mMeshes.size() * 2);
+
+ unsigned int iFaceCnt = 0,num = 0;
+ aiString name;
+
+ // we need to split all meshes by their materials
+ for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end();++i) {
+ boost::scoped_array< std::vector<unsigned int> > aiSplit(new std::vector<unsigned int>[mScene->mMaterials.size()]);
+
+ name.length = ASSIMP_itoa10(name.data,num++);
+
+ unsigned int iNum = 0;
+ for (std::vector<unsigned int>::const_iterator a = (*i).mFaceMaterials.begin();
+ a != (*i).mFaceMaterials.end();++a,++iNum)
+ {
+ aiSplit[*a].push_back(iNum);
+ }
+ // now generate submeshes
+ for (unsigned int p = 0; p < mScene->mMaterials.size();++p)
+ {
+ if (aiSplit[p].empty()) {
+ continue;
+ }
+ aiMesh* meshOut = new aiMesh();
+
+ std::string name_parts((*i).mName);
+ name_parts.append("::");
+ name_parts.append(mScene->mMaterials.at(p).mName);
+ meshOut->mName.Set(name_parts);
+
+ meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // be sure to setup the correct material index
+ meshOut->mMaterialIndex = p;
+
+ // use the color data as temporary storage
+ meshOut->mColors[0] = (aiColor4D*)(&*i);
+ avOutMeshes.push_back(meshOut);
+
+ // convert vertices
+ meshOut->mNumFaces = (unsigned int)aiSplit[p].size();
+ meshOut->mNumVertices = meshOut->mNumFaces*3;
+
+ // allocate enough storage for faces
+ meshOut->mFaces = new aiFace[meshOut->mNumFaces];
+ iFaceCnt += meshOut->mNumFaces;
+
+ meshOut->mVertices = new aiVector3D[meshOut->mNumVertices];
+ meshOut->mNormals = new aiVector3D[meshOut->mNumVertices];
+ if ((*i).mTexCoords.size())
+ {
+ meshOut->mTextureCoords[0] = new aiVector3D[meshOut->mNumVertices];
+ }
+ for (unsigned int q = 0, base = 0; q < aiSplit[p].size();++q)
+ {
+ register unsigned int index = aiSplit[p][q];
+ aiFace& face = meshOut->mFaces[q];
+
+ face.mIndices = new unsigned int[3];
+ face.mNumIndices = 3;
+
+ for (unsigned int a = 0; a < 3;++a,++base)
+ {
+ unsigned int idx = (*i).mFaces[index].mIndices[a];
+ meshOut->mVertices[base] = (*i).mPositions[idx];
+ meshOut->mNormals [base] = (*i).mNormals[idx];
+
+ if ((*i).mTexCoords.size())
+ meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx];
+
+ face.mIndices[a] = base;
+ }
+ }
+ }
+ }
+
+ // Copy them to the output array
+ pcOut->mNumMeshes = (unsigned int)avOutMeshes.size();
+ pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes]();
+ for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) {
+ pcOut->mMeshes[a] = avOutMeshes[a];
+ }
+
+ // We should have at least one face here
+ if (!iFaceCnt) {
+ throw DeadlyImportError("No faces loaded. The mesh is empty");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Add a node to the scenegraph and setup its final transformation
+void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
+ D3DS::Node* pcIn, aiMatrix4x4& /* absTrafo */)
+{
+ std::vector<unsigned int> iArray;
+ iArray.reserve(3);
+
+ aiMatrix4x4 abs;
+
+ // Find all meshes with the same name as the node
+ for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
+ {
+ const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0];
+ ai_assert(NULL != pcMesh);
+
+ if (pcIn->mName == pcMesh->mName)
+ iArray.push_back(a);
+ }
+ if (!iArray.empty())
+ {
+ // The matrix should be identical for all meshes with the
+ // same name. It HAS to be identical for all meshes .....
+ D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]);
+
+ // Compute the inverse of the transformation matrix to move the
+ // vertices back to their relative and local space
+ aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat;
+ mInv.Inverse();mInvTransposed.Transpose();
+ aiVector3D pivot = pcIn->vPivot;
+
+ pcOut->mNumMeshes = (unsigned int)iArray.size();
+ pcOut->mMeshes = new unsigned int[iArray.size()];
+ for (unsigned int i = 0;i < iArray.size();++i) {
+ const unsigned int iIndex = iArray[i];
+ aiMesh* const mesh = pcSOut->mMeshes[iIndex];
+
+ // Transform the vertices back into their local space
+ // fixme: consider computing normals after this, so we don't need to transform them
+ const aiVector3D* const pvEnd = mesh->mVertices+mesh->mNumVertices;
+ aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
+
+ for (;pvCurrent != pvEnd;++pvCurrent,++t2) {
+ *pvCurrent = mInv * (*pvCurrent);
+ *t2 = mInvTransposed * (*t2);
+ }
+
+ // Handle negative transformation matrix determinant -> invert vertex x
+ if (imesh->mMat.Determinant() < 0.0f)
+ {
+ /* we *must* have normals */
+ for (pvCurrent = mesh->mVertices,t2 = mesh->mNormals;pvCurrent != pvEnd;++pvCurrent,++t2) {
+ pvCurrent->x *= -1.f;
+ t2->x *= -1.f;
+ }
+ DefaultLogger::get()->info("3DS: Flipping mesh X-Axis");
+ }
+
+ // Handle pivot point
+ if (pivot.x || pivot.y || pivot.z)
+ {
+ for (pvCurrent = mesh->mVertices;pvCurrent != pvEnd;++pvCurrent) {
+ *pvCurrent -= pivot;
+ }
+ }
+
+ // Setup the mesh index
+ pcOut->mMeshes[i] = iIndex;
+ }
+ }
+
+ // Setup the name of the node
+ pcOut->mName.Set(pcIn->mName);
+
+ // Now build the transformation matrix of the node
+ // ROTATION
+ if (pcIn->aRotationKeys.size()){
+
+ // FIX to get to Assimp's quaternion conventions
+ for (std::vector<aiQuatKey>::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) {
+ (*it).mValue.w *= -1.f;
+ }
+
+ pcOut->mTransformation = aiMatrix4x4( pcIn->aRotationKeys[0].mValue.GetMatrix() );
+ }
+ else if (pcIn->aCameraRollKeys.size())
+ {
+ aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(- pcIn->aCameraRollKeys[0].mValue),
+ pcOut->mTransformation);
+ }
+
+ // SCALING
+ aiMatrix4x4& m = pcOut->mTransformation;
+ if (pcIn->aScalingKeys.size())
+ {
+ const aiVector3D& v = pcIn->aScalingKeys[0].mValue;
+ m.a1 *= v.x; m.b1 *= v.x; m.c1 *= v.x;
+ m.a2 *= v.y; m.b2 *= v.y; m.c2 *= v.y;
+ m.a3 *= v.z; m.b3 *= v.z; m.c3 *= v.z;
+ }
+
+ // TRANSLATION
+ if (pcIn->aPositionKeys.size())
+ {
+ const aiVector3D& v = pcIn->aPositionKeys[0].mValue;
+ m.a4 += v.x;
+ m.b4 += v.y;
+ m.c4 += v.z;
+ }
+
+ // Generate animation channels for the node
+ if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 ||
+ pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 ||
+ pcIn->aTargetPositionKeys.size() > 1)
+ {
+ aiAnimation* anim = pcSOut->mAnimations[0];
+ ai_assert(NULL != anim);
+
+ if (pcIn->aCameraRollKeys.size() > 1)
+ {
+ DefaultLogger::get()->debug("3DS: Converting camera roll track ...");
+
+ // Camera roll keys - in fact they're just rotations
+ // around the camera's z axis. The angles are given
+ // in degrees (and they're clockwise).
+ pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size());
+ for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size();++i)
+ {
+ aiQuatKey& q = pcIn->aRotationKeys[i];
+ aiFloatKey& f = pcIn->aCameraRollKeys[i];
+
+ q.mTime = f.mTime;
+
+ // FIX to get to Assimp quaternion conventions
+ q.mValue = aiQuaternion(0.f,0.f,AI_DEG_TO_RAD( /*-*/ f.mValue));
+ }
+ }
+#if 0
+ if (pcIn->aTargetPositionKeys.size() > 1)
+ {
+ DefaultLogger::get()->debug("3DS: Converting target track ...");
+
+ // Camera or spot light - need to convert the separate
+ // target position channel to our representation
+ TargetAnimationHelper helper;
+
+ if (pcIn->aPositionKeys.empty())
+ {
+ // We can just pass zero here ...
+ helper.SetFixedMainAnimationChannel(aiVector3D());
+ }
+ else helper.SetMainAnimationChannel(&pcIn->aPositionKeys);
+ helper.SetTargetAnimationChannel(&pcIn->aTargetPositionKeys);
+
+ // Do the conversion
+ std::vector<aiVectorKey> distanceTrack;
+ helper.Process(&distanceTrack);
+
+ // Now add a new node as child, name it <ourName>.Target
+ // and assign the distance track to it. This is that the
+ // information where the target is and how it moves is
+ // not lost
+ D3DS::Node* nd = new D3DS::Node();
+ pcIn->push_back(nd);
+
+ nd->mName = pcIn->mName + ".Target";
+
+ aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
+ nda->mNodeName.Set(nd->mName);
+
+ nda->mNumPositionKeys = (unsigned int)distanceTrack.size();
+ nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
+ ::memcpy(nda->mPositionKeys,&distanceTrack[0],
+ sizeof(aiVectorKey)*nda->mNumPositionKeys);
+ }
+#endif
+
+ // Cameras or lights define their transformation in their parent node and in the
+ // corresponding light or camera chunks. However, we read and process the latter
+ // to to be able to return valid cameras/lights even if no scenegraph is given.
+ for (unsigned int n = 0; n < pcSOut->mNumCameras;++n) {
+ if (pcSOut->mCameras[n]->mName == pcOut->mName) {
+ pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f,0.f,1.f);
+ }
+ }
+ for (unsigned int n = 0; n < pcSOut->mNumLights;++n) {
+ if (pcSOut->mLights[n]->mName == pcOut->mName) {
+ pcSOut->mLights[n]->mDirection = aiVector3D(0.f,0.f,1.f);
+ }
+ }
+
+ // Allocate a new node anim and setup its name
+ aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
+ nda->mNodeName.Set(pcIn->mName);
+
+ // POSITION keys
+ if (pcIn->aPositionKeys.size() > 0)
+ {
+ nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size();
+ nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
+ ::memcpy(nda->mPositionKeys,&pcIn->aPositionKeys[0],
+ sizeof(aiVectorKey)*nda->mNumPositionKeys);
+ }
+
+ // ROTATION keys
+ if (pcIn->aRotationKeys.size() > 0)
+ {
+ nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size();
+ nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys];
+
+ // Rotations are quaternion offsets
+ aiQuaternion abs;
+ for (unsigned int n = 0; n < nda->mNumRotationKeys;++n)
+ {
+ const aiQuatKey& q = pcIn->aRotationKeys[n];
+
+ abs = (n ? abs * q.mValue : q.mValue);
+ nda->mRotationKeys[n].mTime = q.mTime;
+ nda->mRotationKeys[n].mValue = abs.Normalize();
+ }
+ }
+
+ // SCALING keys
+ if (pcIn->aScalingKeys.size() > 0)
+ {
+ nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size();
+ nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys];
+ ::memcpy(nda->mScalingKeys,&pcIn->aScalingKeys[0],
+ sizeof(aiVectorKey)*nda->mNumScalingKeys);
+ }
+ }
+
+ // Allocate storage for children
+ pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size();
+ pcOut->mChildren = new aiNode*[pcIn->mChildren.size()];
+
+ // Recursively process all children
+ const unsigned int size = pcIn->mChildren.size();
+ for (unsigned int i = 0; i < size;++i)
+ {
+ pcOut->mChildren[i] = new aiNode();
+ pcOut->mChildren[i]->mParent = pcOut;
+ AddNodeToGraph(pcSOut,pcOut->mChildren[i],pcIn->mChildren[i],abs);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Find out how many node animation channels we'll have finally
+void CountTracks(D3DS::Node* node, unsigned int& cnt)
+{
+ //////////////////////////////////////////////////////////////////////////////
+ // We will never generate more than one channel for a node, so
+ // this is rather easy here.
+
+ if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 ||
+ node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 ||
+ node->aTargetPositionKeys.size() > 1)
+ {
+ ++cnt;
+
+ // account for the additional channel for the camera/spotlight target position
+ if (node->aTargetPositionKeys.size() > 1)++cnt;
+ }
+
+ // Recursively process all children
+ for (unsigned int i = 0; i < node->mChildren.size();++i)
+ CountTracks(node->mChildren[i],cnt);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Generate the output node graph
+void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
+{
+ pcOut->mRootNode = new aiNode();
+ if (0 == mRootNode->mChildren.size())
+ {
+ //////////////////////////////////////////////////////////////////////////////
+ // It seems the file is so messed up that it has not even a hierarchy.
+ // generate a flat hiearachy which looks like this:
+ //
+ // ROOT_NODE
+ // |
+ // ----------------------------------------
+ // | | | | |
+ // MESH_0 MESH_1 MESH_2 ... MESH_N CAMERA_0 ....
+ //
+ DefaultLogger::get()->warn("No hierarchy information has been found in the file. ");
+
+ pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes +
+ mScene->mCameras.size() + mScene->mLights.size();
+
+ pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mRootNode->mNumChildren ];
+ pcOut->mRootNode->mName.Set("<3DSDummyRoot>");
+
+ // Build dummy nodes for all meshes
+ unsigned int a = 0;
+ for (unsigned int i = 0; i < pcOut->mNumMeshes;++i,++a)
+ {
+ aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
+ pcNode->mParent = pcOut->mRootNode;
+ pcNode->mMeshes = new unsigned int[1];
+ pcNode->mMeshes[0] = i;
+ pcNode->mNumMeshes = 1;
+
+ // Build a name for the node
+ pcNode->mName.length = sprintf(pcNode->mName.data,"3DSMesh_%i",i);
+ }
+
+ // Build dummy nodes for all cameras
+ for (unsigned int i = 0; i < (unsigned int )mScene->mCameras.size();++i,++a)
+ {
+ aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
+ pcNode->mParent = pcOut->mRootNode;
+
+ // Build a name for the node
+ pcNode->mName = mScene->mCameras[i]->mName;
+ }
+
+ // Build dummy nodes for all lights
+ for (unsigned int i = 0; i < (unsigned int )mScene->mLights.size();++i,++a)
+ {
+ aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
+ pcNode->mParent = pcOut->mRootNode;
+
+ // Build a name for the node
+ pcNode->mName = mScene->mLights[i]->mName;
+ }
+ }
+ else
+ {
+ // First of all: find out how many scaling, rotation and translation
+ // animation tracks we'll have afterwards
+ unsigned int numChannel = 0;
+ CountTracks(mRootNode,numChannel);
+
+ if (numChannel)
+ {
+ // Allocate a primary animation channel
+ pcOut->mNumAnimations = 1;
+ pcOut->mAnimations = new aiAnimation*[1];
+ aiAnimation* anim = pcOut->mAnimations[0] = new aiAnimation();
+
+ anim->mName.Set("3DSMasterAnim");
+
+ // Allocate enough storage for all node animation channels,
+ // but don't set the mNumChannels member - we'll use it to
+ // index into the array
+ anim->mChannels = new aiNodeAnim*[numChannel];
+ }
+
+ aiMatrix4x4 m;
+ AddNodeToGraph(pcOut, pcOut->mRootNode, mRootNode,m);
+ }
+
+ // We used the first vertex color set to store some emporary values so we need to cleanup here
+ for (unsigned int a = 0; a < pcOut->mNumMeshes;++a)
+ pcOut->mMeshes[a]->mColors[0] = NULL;
+
+ // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
+ pcOut->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
+ 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f) * pcOut->mRootNode->mTransformation;
+
+ // If the root node is unnamed name it "<3DSRoot>"
+ if (::strstr( pcOut->mRootNode->mName.data, "UNNAMED" ) ||
+ (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') )
+ {
+ pcOut->mRootNode->mName.Set("<3DSRoot>");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert all meshes in the scene and generate the final output scene.
+void Discreet3DSImporter::ConvertScene(aiScene* pcOut)
+{
+ // Allocate enough storage for all output materials
+ pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size();
+ pcOut->mMaterials = new aiMaterial*[pcOut->mNumMaterials];
+
+ // ... and convert the 3DS materials to aiMaterial's
+ for (unsigned int i = 0; i < pcOut->mNumMaterials;++i)
+ {
+ MaterialHelper* pcNew = new MaterialHelper();
+ ConvertMaterial(mScene->mMaterials[i],*pcNew);
+ pcOut->mMaterials[i] = pcNew;
+ }
+
+ // Generate the output mesh list
+ ConvertMeshes(pcOut);
+
+ // Now copy all light sources to the output scene
+ pcOut->mNumLights = (unsigned int)mScene->mLights.size();
+ if (pcOut->mNumLights)
+ {
+ pcOut->mLights = new aiLight*[pcOut->mNumLights];
+ ::memcpy(pcOut->mLights,&mScene->mLights[0],sizeof(void*)*pcOut->mNumLights);
+ }
+
+ // Now copy all cameras to the output scene
+ pcOut->mNumCameras = (unsigned int)mScene->mCameras.size();
+ if (pcOut->mNumCameras)
+ {
+ pcOut->mCameras = new aiCamera*[pcOut->mNumCameras];
+ ::memcpy(pcOut->mCameras,&mScene->mCameras[0],sizeof(void*)*pcOut->mNumCameras);
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
diff --git a/src/3rdparty/assimp/code/3DSHelper.h b/src/3rdparty/assimp/code/3DSHelper.h
new file mode 100644
index 000000000..2e823c67a
--- /dev/null
+++ b/src/3rdparty/assimp/code/3DSHelper.h
@@ -0,0 +1,581 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Defines helper data structures for the import of 3DS files */
+
+#ifndef AI_3DSFILEHELPER_H_INC
+#define AI_3DSFILEHELPER_H_INC
+
+
+#include "SpatialSort.h"
+#include "SmoothingGroups.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+namespace Assimp {
+namespace D3DS {
+
+#include "./../include/Compiler/pushpack1.h"
+
+// ---------------------------------------------------------------------------
+/** Discreet3DS class: Helper class for loading 3ds files. Defines chunks
+* and data structures.
+*/
+class Discreet3DS
+{
+private:
+ inline Discreet3DS() {}
+
+public:
+
+ //! data structure for a single chunk in a .3ds file
+ struct Chunk
+ {
+ uint16_t Flag;
+ uint32_t Size;
+ } PACK_STRUCT;
+
+
+ //! Used for shading field in material3ds structure
+ //! From AutoDesk 3ds SDK
+ typedef enum
+ {
+ // translated to gouraud shading with wireframe active
+ Wire = 0x0,
+
+ // if this material is set, no vertex normals will
+ // be calculated for the model. Face normals + gouraud
+ Flat = 0x1,
+
+ // standard gouraud shading
+ Gouraud = 0x2,
+
+ // phong shading
+ Phong = 0x3,
+
+ // cooktorrance or anistropic phong shading ...
+ // the exact meaning is unknown, if you know it
+ // feel free to tell me ;-)
+ Metal = 0x4,
+
+ // required by the ASE loader
+ Blinn = 0x5
+ } shadetype3ds;
+
+ // Flags for animated keys
+ enum
+ {
+ KEY_USE_TENS = 0x1,
+ KEY_USE_CONT = 0x2,
+ KEY_USE_BIAS = 0x4,
+ KEY_USE_EASE_TO = 0x8,
+ KEY_USE_EASE_FROM = 0x10
+ } ;
+
+ enum
+ {
+
+ // ********************************************************************
+ // Basic chunks which can be found everywhere in the file
+ CHUNK_VERSION = 0x0002,
+ CHUNK_RGBF = 0x0010, // float4 R; float4 G; float4 B
+ CHUNK_RGBB = 0x0011, // int1 R; int1 G; int B
+
+ // Linear color values (gamma = 2.2?)
+ CHUNK_LINRGBF = 0x0013, // float4 R; float4 G; float4 B
+ CHUNK_LINRGBB = 0x0012, // int1 R; int1 G; int B
+
+ CHUNK_PERCENTW = 0x0030, // int2 percentage
+ CHUNK_PERCENTF = 0x0031, // float4 percentage
+ // ********************************************************************
+
+ // Prj master chunk
+ CHUNK_PRJ = 0xC23D,
+
+ // MDLI master chunk
+ CHUNK_MLI = 0x3DAA,
+
+ // Primary main chunk of the .3ds file
+ CHUNK_MAIN = 0x4D4D,
+
+ // Mesh main chunk
+ CHUNK_OBJMESH = 0x3D3D,
+
+ // Specifies the background color of the .3ds file
+ // This is passed through the material system for
+ // viewing purposes.
+ CHUNK_BKGCOLOR = 0x1200,
+
+ // Specifies the ambient base color of the scene.
+ // This is added to all materials in the file
+ CHUNK_AMBCOLOR = 0x2100,
+
+ // Specifies the background image for the whole scene
+ // This value is passed through the material system
+ // to the viewer
+ CHUNK_BIT_MAP = 0x1100,
+ CHUNK_BIT_MAP_EXISTS = 0x1101,
+
+ // ********************************************************************
+ // Viewport related stuff. Ignored
+ CHUNK_DEFAULT_VIEW = 0x3000,
+ CHUNK_VIEW_TOP = 0x3010,
+ CHUNK_VIEW_BOTTOM = 0x3020,
+ CHUNK_VIEW_LEFT = 0x3030,
+ CHUNK_VIEW_RIGHT = 0x3040,
+ CHUNK_VIEW_FRONT = 0x3050,
+ CHUNK_VIEW_BACK = 0x3060,
+ CHUNK_VIEW_USER = 0x3070,
+ CHUNK_VIEW_CAMERA = 0x3080,
+ // ********************************************************************
+
+ // Mesh chunks
+ CHUNK_OBJBLOCK = 0x4000,
+ CHUNK_TRIMESH = 0x4100,
+ CHUNK_VERTLIST = 0x4110,
+ CHUNK_VERTFLAGS = 0x4111,
+ CHUNK_FACELIST = 0x4120,
+ CHUNK_FACEMAT = 0x4130,
+ CHUNK_MAPLIST = 0x4140,
+ CHUNK_SMOOLIST = 0x4150,
+ CHUNK_TRMATRIX = 0x4160,
+ CHUNK_MESHCOLOR = 0x4165,
+ CHUNK_TXTINFO = 0x4170,
+ CHUNK_LIGHT = 0x4600,
+ CHUNK_CAMERA = 0x4700,
+ CHUNK_HIERARCHY = 0x4F00,
+
+ // Specifies the global scaling factor. This is applied
+ // to the root node's transformation matrix
+ CHUNK_MASTER_SCALE = 0x0100,
+
+ // ********************************************************************
+ // Material chunks
+ CHUNK_MAT_MATERIAL = 0xAFFF,
+
+ // asciiz containing the name of the material
+ CHUNK_MAT_MATNAME = 0xA000,
+ CHUNK_MAT_AMBIENT = 0xA010, // followed by color chunk
+ CHUNK_MAT_DIFFUSE = 0xA020, // followed by color chunk
+ CHUNK_MAT_SPECULAR = 0xA030, // followed by color chunk
+
+ // Specifies the shininess of the material
+ // followed by percentage chunk
+ CHUNK_MAT_SHININESS = 0xA040,
+ CHUNK_MAT_SHININESS_PERCENT = 0xA041 ,
+
+ // Specifies the shading mode to be used
+ // followed by a short
+ CHUNK_MAT_SHADING = 0xA100,
+
+ // NOTE: Emissive color (self illumination) seems not
+ // to be a color but a single value, type is unknown.
+ // Make the parser accept both of them.
+ // followed by percentage chunk (?)
+ CHUNK_MAT_SELF_ILLUM = 0xA080,
+
+ // Always followed by percentage chunk (?)
+ CHUNK_MAT_SELF_ILPCT = 0xA084,
+
+ // Always followed by percentage chunk
+ CHUNK_MAT_TRANSPARENCY = 0xA050,
+
+ // Diffuse texture channel 0
+ CHUNK_MAT_TEXTURE = 0xA200,
+
+ // Contains opacity information for each texel
+ CHUNK_MAT_OPACMAP = 0xA210,
+
+ // Contains a reflection map to be used to reflect
+ // the environment. This is partially supported.
+ CHUNK_MAT_REFLMAP = 0xA220,
+
+ // Self Illumination map (emissive colors)
+ CHUNK_MAT_SELFIMAP = 0xA33d,
+
+ // Bumpmap. Not specified whether it is a heightmap
+ // or a normal map. Assme it is a heightmap since
+ // artist normally prefer this format.
+ CHUNK_MAT_BUMPMAP = 0xA230,
+
+ // Specular map. Seems to influence the specular color
+ CHUNK_MAT_SPECMAP = 0xA204,
+
+ // Holds shininess data.
+ CHUNK_MAT_MAT_SHINMAP = 0xA33C,
+
+ // Scaling in U/V direction.
+ // (need to gen separate UV coordinate set
+ // and do this by hand)
+ CHUNK_MAT_MAP_USCALE = 0xA354,
+ CHUNK_MAT_MAP_VSCALE = 0xA356,
+
+ // Translation in U/V direction.
+ // (need to gen separate UV coordinate set
+ // and do this by hand)
+ CHUNK_MAT_MAP_UOFFSET = 0xA358,
+ CHUNK_MAT_MAP_VOFFSET = 0xA35a,
+
+ // UV-coordinates rotation around the z-axis
+ // Assumed to be in radians.
+ CHUNK_MAT_MAP_ANG = 0xA35C,
+
+ // Tiling flags for 3DS files
+ CHUNK_MAT_MAP_TILING = 0xa351,
+
+ // Specifies the file name of a texture
+ CHUNK_MAPFILE = 0xA300,
+
+ // Specifies whether a materail requires two-sided rendering
+ CHUNK_MAT_TWO_SIDE = 0xA081,
+ // ********************************************************************
+
+ // Main keyframer chunk. Contains translation/rotation/scaling data
+ CHUNK_KEYFRAMER = 0xB000,
+
+ // Supported sub chunks
+ CHUNK_TRACKINFO = 0xB002,
+ CHUNK_TRACKOBJNAME = 0xB010,
+ CHUNK_TRACKDUMMYOBJNAME = 0xB011,
+ CHUNK_TRACKPIVOT = 0xB013,
+ CHUNK_TRACKPOS = 0xB020,
+ CHUNK_TRACKROTATE = 0xB021,
+ CHUNK_TRACKSCALE = 0xB022,
+
+ // ********************************************************************
+ // Keyframes for various other stuff in the file
+ // Partially ignored
+ CHUNK_AMBIENTKEY = 0xB001,
+ CHUNK_TRACKMORPH = 0xB026,
+ CHUNK_TRACKHIDE = 0xB029,
+ CHUNK_OBJNUMBER = 0xB030,
+ CHUNK_TRACKCAMERA = 0xB003,
+ CHUNK_TRACKFOV = 0xB023,
+ CHUNK_TRACKROLL = 0xB024,
+ CHUNK_TRACKCAMTGT = 0xB004,
+ CHUNK_TRACKLIGHT = 0xB005,
+ CHUNK_TRACKLIGTGT = 0xB006,
+ CHUNK_TRACKSPOTL = 0xB007,
+ CHUNK_FRAMES = 0xB008,
+ // ********************************************************************
+
+ // light sub-chunks
+ CHUNK_DL_OFF = 0x4620,
+ CHUNK_DL_OUTER_RANGE = 0x465A,
+ CHUNK_DL_INNER_RANGE = 0x4659,
+ CHUNK_DL_MULTIPLIER = 0x465B,
+ CHUNK_DL_EXCLUDE = 0x4654,
+ CHUNK_DL_ATTENUATE = 0x4625,
+ CHUNK_DL_SPOTLIGHT = 0x4610,
+
+ // camera sub-chunks
+ CHUNK_CAM_RANGES = 0x4720
+ };
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a 3ds mesh face */
+struct Face : public FaceWithSmoothingGroup
+{
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a texture */
+struct Texture
+{
+ //! Default constructor
+ Texture()
+ : mOffsetU (0.0f)
+ , mOffsetV (0.0f)
+ , mScaleU (1.0f)
+ , mScaleV (1.0f)
+ , mRotation (0.0f)
+ , mMapMode (aiTextureMapMode_Wrap)
+ , iUVSrc (0)
+ {
+ mTextureBlend = get_qnan();
+ }
+
+ //! Specifies the blend factor for the texture
+ float mTextureBlend;
+
+ //! Specifies the filename of the texture
+ std::string mMapName;
+
+ //! Specifies texture coordinate offsets/scaling/rotations
+ float mOffsetU;
+ float mOffsetV;
+ float mScaleU;
+ float mScaleV;
+ float mRotation;
+
+ //! Specifies the mapping mode to be used for the texture
+ aiTextureMapMode mMapMode;
+
+ //! Used internally
+ bool bPrivate;
+ int iUVSrc;
+};
+
+#include "./../include/Compiler/poppack1.h"
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a 3ds material */
+struct Material
+{
+ //! Default constructor. Builds a default name for the material
+ Material()
+ :
+ mDiffuse (0.6f,0.6f,0.6f), // FIX ... we won't want object to be black
+ mSpecularExponent (0.0f),
+ mShininessStrength (1.0f),
+ mShading(Discreet3DS::Gouraud),
+ mTransparency (1.0f),
+ mBumpHeight (1.0f),
+ mTwoSided (false)
+ {
+ static int iCnt = 0;
+
+ char szTemp[128];
+ sprintf(szTemp,"UNNAMED_%i",iCnt++);
+ mName = szTemp;
+ }
+
+ //! Name of the material
+ std::string mName;
+ //! Diffuse color of the material
+ aiColor3D mDiffuse;
+ //! Specular exponent
+ float mSpecularExponent;
+ //! Shininess strength, in percent
+ float mShininessStrength;
+ //! Specular color of the material
+ aiColor3D mSpecular;
+ //! Ambient color of the material
+ aiColor3D mAmbient;
+ //! Shading type to be used
+ Discreet3DS::shadetype3ds mShading;
+ //! Opacity of the material
+ float mTransparency;
+ //! Diffuse texture channel
+ Texture sTexDiffuse;
+ //! Opacity texture channel
+ Texture sTexOpacity;
+ //! Specular texture channel
+ Texture sTexSpecular;
+ //! Reflective texture channel
+ Texture sTexReflective;
+ //! Bump texture channel
+ Texture sTexBump;
+ //! Emissive texture channel
+ Texture sTexEmissive;
+ //! Shininess texture channel
+ Texture sTexShininess;
+ //! Scaling factor for the bump values
+ float mBumpHeight;
+ //! Emissive color
+ aiColor3D mEmissive;
+ //! Ambient texture channel
+ //! (used by the ASE format)
+ Texture sTexAmbient;
+ //! True if the material must be rendered from two sides
+ bool mTwoSided;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent a 3ds file mesh */
+struct Mesh : public MeshWithSmoothingGroups<D3DS::Face>
+{
+ //! Default constructor
+ Mesh()
+ {
+ static int iCnt = 0;
+
+ // Generate a default name for the mesh
+ char szTemp[128];
+ ::sprintf(szTemp,"UNNAMED_%i",iCnt++);
+ mName = szTemp;
+ }
+
+ //! Name of the mesh
+ std::string mName;
+
+ //! Texture coordinates
+ std::vector<aiVector3D> mTexCoords;
+
+ //! Face materials
+ std::vector<unsigned int> mFaceMaterials;
+
+ //! Local transformation matrix
+ aiMatrix4x4 mMat;
+};
+
+// ---------------------------------------------------------------------------
+/** Float key - quite similar to aiVectorKey and aiQuatKey. Both are in the
+ C-API, so it would be difficult to make them a template. */
+struct aiFloatKey
+{
+ double mTime; ///< The time of this key
+ float mValue; ///< The value of this key
+
+#ifdef __cplusplus
+
+ // time is not compared
+ bool operator == (const aiFloatKey& o) const
+ {return o.mValue == this->mValue;}
+
+ bool operator != (const aiFloatKey& o) const
+ {return o.mValue != this->mValue;}
+
+ // Only time is compared. This operator is defined
+ // for use with std::sort
+ bool operator < (const aiFloatKey& o) const
+ {return mTime < o.mTime;}
+
+ bool operator > (const aiFloatKey& o) const
+ {return mTime < o.mTime;}
+
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent a 3ds file node */
+struct Node
+{
+ Node()
+
+ : mHierarchyPos (0)
+ , mHierarchyIndex (0)
+
+ {
+ static int iCnt = 0;
+
+ // Generate a default name for the node
+ char szTemp[128];
+ ::sprintf(szTemp,"UNNAMED_%i",iCnt++);
+ mName = szTemp;
+
+ aRotationKeys.reserve (20);
+ aPositionKeys.reserve (20);
+ aScalingKeys.reserve (20);
+ }
+
+ ~Node()
+ {
+ for (unsigned int i = 0; i < mChildren.size();++i)
+ delete mChildren[i];
+ }
+
+ //! Pointer to the parent node
+ Node* mParent;
+
+ //! Holds all child nodes
+ std::vector<Node*> mChildren;
+
+ //! Name of the node
+ std::string mName;
+
+ //! Dummy nodes: real name to be combined with the $$$DUMMY
+ std::string mDummyName;
+
+ //! Position of the node in the hierarchy (tree depth)
+ int16_t mHierarchyPos;
+
+ //! Index of the node
+ int16_t mHierarchyIndex;
+
+ //! Rotation keys loaded from the file
+ std::vector<aiQuatKey> aRotationKeys;
+
+ //! Position keys loaded from the file
+ std::vector<aiVectorKey> aPositionKeys;
+
+ //! Scaling keys loaded from the file
+ std::vector<aiVectorKey> aScalingKeys;
+
+
+ // For target lights (spot lights and directional lights):
+ // The position of the target
+ std::vector< aiVectorKey > aTargetPositionKeys;
+
+ // For cameras: the camera roll angle
+ std::vector< aiFloatKey > aCameraRollKeys;
+
+ //! Pivot position loaded from the file
+ aiVector3D vPivot;
+
+ //! Add a child node, setup the right parent node for it
+ //! \param pc Node to be 'adopted'
+ inline Node& push_back(Node* pc)
+ {
+ mChildren.push_back(pc);
+ pc->mParent = this;
+ return *this;
+ }
+};
+// ---------------------------------------------------------------------------
+/** Helper structure analogue to aiScene */
+struct Scene
+{
+ //! List of all materials loaded
+ //! NOTE: 3ds references materials globally
+ std::vector<Material> mMaterials;
+
+ //! List of all meshes loaded
+ std::vector<Mesh> mMeshes;
+
+ //! List of all cameras loaded
+ std::vector<aiCamera*> mCameras;
+
+ //! List of all lights loaded
+ std::vector<aiLight*> mLights;
+
+ //! Pointer to the root node of the scene
+ // --- moved to main class
+ // Node* pcRootNode;
+};
+
+
+} // end of namespace D3DS
+} // end of namespace Assimp
+
+#endif // AI_XFILEHELPER_H_INC
diff --git a/src/3rdparty/assimp/code/3DSLoader.cpp b/src/3rdparty/assimp/code/3DSLoader.cpp
new file mode 100644
index 000000000..c2f1bebae
--- /dev/null
+++ b/src/3rdparty/assimp/code/3DSLoader.cpp
@@ -0,0 +1,1373 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file 3DSLoader.cpp
+ * @brief Implementation of the 3ds importer class
+ *
+ * http://www.the-labs.com/Blender/3DS-details.html
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
+
+// internal headers
+#include "3DSLoader.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Begins a new parsing block
+// - Reads the current chunk and validates it
+// - computes its length
+#define ASSIMP_3DS_BEGIN_CHUNK() \
+ while (true) { \
+ if (stream->GetRemainingSizeToLimit() < sizeof(Discreet3DS::Chunk)){ \
+ return; \
+ } \
+ Discreet3DS::Chunk chunk; \
+ ReadChunk(&chunk); \
+ int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \
+ const int oldReadLimit = stream->GetReadLimit(); \
+ stream->SetReadLimit(stream->GetCurrentPos() + chunkSize); \
+
+
+// ------------------------------------------------------------------------------------------------
+// End a parsing block
+// Must follow at the end of each parsing block, reset chunk end marker to previous value
+#define ASSIMP_3DS_END_CHUNK() \
+ stream->SkipToReadLimit(); \
+ stream->SetReadLimit(oldReadLimit); \
+ if (stream->GetRemainingSizeToLimit() == 0) \
+ return; \
+ }
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+Discreet3DSImporter::Discreet3DSImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+Discreet3DSImporter::~Discreet3DSImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ std::string extension = GetExtension(pFile);
+ if (extension == "3ds" || extension == "prj" ) {
+ return true;
+ }
+ if (!extension.length() || checkSig) {
+ uint16_t token[3];
+ token[0] = 0x4d4d;
+ token[1] = 0x3dc2;
+ //token[2] = 0x3daa;
+ return CheckMagicToken(pIOHandler,pFile,token,2,0,2);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get list of all extension supported by this loader
+void Discreet3DSImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("3ds");
+ extensions.insert("prj");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void Discreet3DSImporter::SetupProperties(const Importer*)
+{
+ // nothing to be done for the moment
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void Discreet3DSImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ StreamReaderLE stream(pIOHandler->Open(pFile,"rb"));
+ this->stream = &stream;
+
+ // We should have at least one chunk
+ if (stream.GetRemainingSize() < 16) {
+ throw DeadlyImportError("3DS file is either empty or corrupt: " + pFile);
+ }
+
+ // Allocate our temporary 3DS representation
+ mScene = new D3DS::Scene();
+
+ // Initialize members
+ mLastNodeIndex = -1;
+ mCurrentNode = new D3DS::Node();
+ mRootNode = mCurrentNode;
+ mRootNode->mHierarchyPos = -1;
+ mRootNode->mHierarchyIndex = -1;
+ mRootNode->mParent = NULL;
+ mMasterScale = 1.0f;
+ mBackgroundImage = "";
+ bHasBG = false;
+ bIsPrj = false;
+
+ // Parse the file
+ ParseMainChunk();
+
+ // Process all meshes in the file. First check whether all
+ // face indices haev valid values. The generate our
+ // internal verbose representation. Finally compute normal
+ // vectors from the smoothing groups we read from the
+ // file.
+ for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(),
+ end = mScene->mMeshes.end(); i != end;++i) {
+ CheckIndices(*i);
+ MakeUnique (*i);
+ ComputeNormalsWithSmoothingsGroups<D3DS::Face>(*i);
+ }
+
+ // Replace all occurences of the default material with a
+ // valid material. Generate it if no material containing
+ // DEFAULT in its name has been found in the file
+ ReplaceDefaultMaterial();
+
+ // Convert the scene from our internal representation to an
+ // aiScene object. This involves copying all meshes, lights
+ // and cameras to the scene
+ ConvertScene(pScene);
+
+ // Generate the node graph for the scene. This is a little bit
+ // tricky since we'll need to split some meshes into submeshes
+ GenerateNodeGraph(pScene);
+
+ // Now apply the master scaling factor to the scene
+ ApplyMasterScale(pScene);
+
+ // Delete our internal scene representation and the root
+ // node, so the whole hierarchy will follow
+ delete mRootNode;
+ delete mScene;
+
+ AI_DEBUG_INVALIDATE_PTR(mRootNode);
+ AI_DEBUG_INVALIDATE_PTR(mScene);
+ AI_DEBUG_INVALIDATE_PTR(this->stream);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Applies a master-scaling factor to the imported scene
+void Discreet3DSImporter::ApplyMasterScale(aiScene* pScene)
+{
+ // There are some 3DS files with a zero scaling factor
+ if (!mMasterScale)mMasterScale = 1.0f;
+ else mMasterScale = 1.0f / mMasterScale;
+
+ // Construct an uniform scaling matrix and multiply with it
+ pScene->mRootNode->mTransformation *= aiMatrix4x4(
+ mMasterScale,0.0f, 0.0f, 0.0f,
+ 0.0f, mMasterScale,0.0f, 0.0f,
+ 0.0f, 0.0f, mMasterScale,0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+
+ // Check whether a scaling track is assigned to the root node.
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a new chunk from the file
+void Discreet3DSImporter::ReadChunk(Discreet3DS::Chunk* pcOut)
+{
+ ai_assert(pcOut != NULL);
+
+ pcOut->Flag = stream->GetI2();
+ pcOut->Size = stream->GetI4();
+
+ if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize())
+ throw DeadlyImportError("Chunk is too large");
+
+ if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit())
+ DefaultLogger::get()->error("3DS: Chunk overflow");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skip a chunk
+void Discreet3DSImporter::SkipChunk()
+{
+ Discreet3DS::Chunk psChunk;
+ ReadChunk(&psChunk);
+
+ stream->IncPtr(psChunk.Size-sizeof(Discreet3DS::Chunk));
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Process the primary chunk of the file
+void Discreet3DSImporter::ParseMainChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+
+ case Discreet3DS::CHUNK_PRJ:
+ bIsPrj = true;
+ case Discreet3DS::CHUNK_MAIN:
+ ParseEditorChunk();
+ break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+ // recursively continue processing this hierarchy level
+ return ParseMainChunk();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseEditorChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_OBJMESH:
+
+ ParseObjectChunk();
+ break;
+
+ // NOTE: In several documentations in the internet this
+ // chunk appears at different locations
+ case Discreet3DS::CHUNK_KEYFRAMER:
+
+ ParseKeyframeChunk();
+ break;
+
+ case Discreet3DS::CHUNK_VERSION:
+ {
+ // print the version number
+ char buff[10];
+ ASSIMP_itoa10(buff,stream->GetI2());
+ DefaultLogger::get()->info(std::string("3DS file format version: ") + buff);
+ }
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseObjectChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_OBJBLOCK:
+ {
+ unsigned int cnt = 0;
+ const char* sz = (const char*)stream->GetPtr();
+
+ // Get the name of the geometry object
+ while (stream->GetI1())++cnt;
+ ParseChunk(sz,cnt);
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MATERIAL:
+
+ // Add a new material to the list
+ mScene->mMaterials.push_back(D3DS::Material());
+ ParseMaterialChunk();
+ break;
+
+ case Discreet3DS::CHUNK_AMBCOLOR:
+
+ // This is the ambient base color of the scene.
+ // We add it to the ambient color of all materials
+ ParseColorChunk(&mClrAmbient,true);
+ if (is_qnan(mClrAmbient.r))
+ {
+ // We failed to read the ambient base color.
+ DefaultLogger::get()->error("3DS: Failed to read ambient base color");
+ mClrAmbient.r = mClrAmbient.g = mClrAmbient.b = 0.0f;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_BIT_MAP:
+ {
+ // Specifies the background image. The string should already be
+ // properly 0 terminated but we need to be sure
+ unsigned int cnt = 0;
+ const char* sz = (const char*)stream->GetPtr();
+ while (stream->GetI1())++cnt;
+ mBackgroundImage = std::string(sz,cnt);
+ }
+ break;
+
+ case Discreet3DS::CHUNK_BIT_MAP_EXISTS:
+ bHasBG = true;
+ break;
+
+ case Discreet3DS::CHUNK_MASTER_SCALE:
+ // Scene master scaling factor
+ mMasterScale = stream->GetF4();
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num)
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // IMPLEMENTATION NOTE;
+ // Cameras or lights define their transformation in their parent node and in the
+ // corresponding light or camera chunks. However, we read and process the latter
+ // to to be able to return valid cameras/lights even if no scenegraph is given.
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_TRIMESH:
+ {
+ // this starts a new triangle mesh
+ mScene->mMeshes.push_back(D3DS::Mesh());
+ D3DS::Mesh& m = mScene->mMeshes.back();
+
+ // Setup the name of the mesh
+ m.mName = std::string(name, num);
+
+ // Read mesh chunks
+ ParseMeshChunk();
+ }
+ break;
+
+ case Discreet3DS::CHUNK_LIGHT:
+ {
+ // This starts a new light
+ aiLight* light = new aiLight();
+ mScene->mLights.push_back(light);
+
+ light->mName.Set(std::string(name, num));
+
+ // First read the position of the light
+ light->mPosition.x = stream->GetF4();
+ light->mPosition.y = stream->GetF4();
+ light->mPosition.z = stream->GetF4();
+
+ light->mColorDiffuse = aiColor3D(1.f,1.f,1.f);
+
+ // Now check for further subchunks
+ if (!bIsPrj) /* fixme */
+ ParseLightChunk();
+
+ // The specular light color is identical the the diffuse light color. The ambient light color
+ // is equal to the ambient base color of the whole scene.
+ light->mColorSpecular = light->mColorDiffuse;
+ light->mColorAmbient = mClrAmbient;
+
+ if (light->mType == aiLightSource_UNDEFINED)
+ {
+ // It must be a point light
+ light->mType = aiLightSource_POINT;
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_CAMERA:
+ {
+ // This starts a new camera
+ aiCamera* camera = new aiCamera();
+ mScene->mCameras.push_back(camera);
+ camera->mName.Set(std::string(name, num));
+
+ // First read the position of the camera
+ camera->mPosition.x = stream->GetF4();
+ camera->mPosition.y = stream->GetF4();
+ camera->mPosition.z = stream->GetF4();
+
+ // Then the camera target
+ camera->mLookAt.x = stream->GetF4() - camera->mPosition.x;
+ camera->mLookAt.y = stream->GetF4() - camera->mPosition.y;
+ camera->mLookAt.z = stream->GetF4() - camera->mPosition.z;
+ float len = camera->mLookAt.Length();
+ if (len < 1e-5f) {
+
+ // There are some files with lookat == position. Don't know why or whether it's ok or not.
+ DefaultLogger::get()->error("3DS: Unable to read proper camera look-at vector");
+ camera->mLookAt = aiVector3D(0.f,1.f,0.f);
+
+ }
+ else camera->mLookAt /= len;
+
+ // And finally - the camera rotation angle, in counter clockwise direction
+ const float angle = AI_DEG_TO_RAD( stream->GetF4() );
+ aiQuaternion quat(camera->mLookAt,angle);
+ camera->mUp = quat.GetMatrix() * aiVector3D(0.f,1.f,0.f);
+
+ // Read the lense angle
+ camera->mHorizontalFOV = AI_DEG_TO_RAD ( stream->GetF4() );
+ if (camera->mHorizontalFOV < 0.001f) {
+ camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f);
+ }
+
+ // Now check for further subchunks
+ if (!bIsPrj) /* fixme */ {
+ ParseCameraChunk();
+ }}
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseLightChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+ aiLight* light = mScene->mLights.back();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_DL_SPOTLIGHT:
+ // Now we can be sure that the light is a spot light
+ light->mType = aiLightSource_SPOT;
+
+ // We wouldn't need to normalize here, but we do it
+ light->mDirection.x = stream->GetF4() - light->mPosition.x;
+ light->mDirection.y = stream->GetF4() - light->mPosition.y;
+ light->mDirection.z = stream->GetF4() - light->mPosition.z;
+ light->mDirection.Normalize();
+
+ // Now the hotspot and falloff angles - in degrees
+ light->mAngleInnerCone = AI_DEG_TO_RAD( stream->GetF4() );
+
+ // FIX: the falloff angle is just an offset
+ light->mAngleOuterCone = light->mAngleInnerCone+AI_DEG_TO_RAD( stream->GetF4() );
+ break;
+
+ // intensity multiplier
+ case Discreet3DS::CHUNK_DL_MULTIPLIER:
+ light->mColorDiffuse = light->mColorDiffuse * stream->GetF4();
+ break;
+
+ // light color
+ case Discreet3DS::CHUNK_RGBF:
+ case Discreet3DS::CHUNK_LINRGBF:
+ light->mColorDiffuse.r *= stream->GetF4();
+ light->mColorDiffuse.g *= stream->GetF4();
+ light->mColorDiffuse.b *= stream->GetF4();
+ break;
+
+ // light attenuation
+ case Discreet3DS::CHUNK_DL_ATTENUATE:
+ light->mAttenuationLinear = stream->GetF4();
+ break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseCameraChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+ aiCamera* camera = mScene->mCameras.back();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ // near and far clip plane
+ case Discreet3DS::CHUNK_CAM_RANGES:
+ camera->mClipPlaneNear = stream->GetF4();
+ camera->mClipPlaneFar = stream->GetF4();
+ break;
+ }
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseKeyframeChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_TRACKCAMTGT:
+ case Discreet3DS::CHUNK_TRACKSPOTL:
+ case Discreet3DS::CHUNK_TRACKCAMERA:
+ case Discreet3DS::CHUNK_TRACKINFO:
+ case Discreet3DS::CHUNK_TRACKLIGHT:
+ case Discreet3DS::CHUNK_TRACKLIGTGT:
+
+ // this starts a new mesh hierarchy chunk
+ ParseHierarchyChunk(chunk.Flag);
+ break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Little helper function for ParseHierarchyChunk
+void Discreet3DSImporter::InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent)
+{
+ if (!pcCurrent) {
+ mRootNode->push_back(pcNode);
+ return;
+ }
+
+ if (pcCurrent->mHierarchyPos == pcNode->mHierarchyPos) {
+ if (pcCurrent->mParent) {
+ pcCurrent->mParent->push_back(pcNode);
+ }
+ else pcCurrent->push_back(pcNode);
+ return;
+ }
+ return InverseNodeSearch(pcNode,pcCurrent->mParent);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Find a node with a specific name in the import hierarchy
+D3DS::Node* FindNode(D3DS::Node* root, const std::string& name)
+{
+ if (root->mName == name)
+ return root;
+ for (std::vector<D3DS::Node*>::iterator it = root->mChildren.begin();it != root->mChildren.end(); ++it) {
+ D3DS::Node* nd;
+ if (( nd = FindNode(*it,name)))
+ return nd;
+ }
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Binary predicate for std::unique()
+template <class T>
+bool KeyUniqueCompare(const T& first, const T& second)
+{
+ return first.mTime == second.mTime;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skip some additional import data.
+void Discreet3DSImporter::SkipTCBInfo()
+{
+ unsigned int flags = stream->GetI2();
+
+ if (!flags) {
+ // Currently we can't do anything with these values. They occur
+ // quite rare, so it wouldn't be worth the effort implementing
+ // them. 3DS ist not really suitable for complex animations,
+ // so full support is not required.
+ DefaultLogger::get()->warn("3DS: Skipping TCB animation info");
+ }
+
+ if (flags & Discreet3DS::KEY_USE_TENS) {
+ stream->IncPtr(4);
+ }
+ if (flags & Discreet3DS::KEY_USE_BIAS) {
+ stream->IncPtr(4);
+ }
+ if (flags & Discreet3DS::KEY_USE_CONT) {
+ stream->IncPtr(4);
+ }
+ if (flags & Discreet3DS::KEY_USE_EASE_FROM) {
+ stream->IncPtr(4);
+ }
+ if (flags & Discreet3DS::KEY_USE_EASE_TO) {
+ stream->IncPtr(4);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read hierarchy and keyframe info
+void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent)
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_TRACKOBJNAME:
+
+ // This is the name of the object to which the track applies. The chunk also
+ // defines the position of this object in the hierarchy.
+ {
+
+ // First of all: get the name of the object
+ unsigned int cnt = 0;
+ const char* sz = (const char*)stream->GetPtr();
+
+ while (stream->GetI1())++cnt;
+ std::string name = std::string(sz,cnt);
+
+ // Now find out whether we have this node already (target animation channels
+ // are stored with a separate object ID)
+ D3DS::Node* pcNode = FindNode(mRootNode,name);
+ if (pcNode)
+ {
+ // Make this node the current node
+ mCurrentNode = pcNode;
+ break;
+ }
+ pcNode = new D3DS::Node();
+ pcNode->mName = name;
+
+ // There are two unknown values which we can safely ignore
+ stream->IncPtr(4);
+
+ // Now read the hierarchy position of the object
+ uint16_t hierarchy = stream->GetI2() + 1;
+ pcNode->mHierarchyPos = hierarchy;
+ pcNode->mHierarchyIndex = mLastNodeIndex;
+
+ // And find a proper position in the graph for it
+ if (mCurrentNode && mCurrentNode->mHierarchyPos == hierarchy) {
+
+ // add to the parent of the last touched node
+ mCurrentNode->mParent->push_back(pcNode);
+ mLastNodeIndex++;
+ }
+ else if (hierarchy >= mLastNodeIndex) {
+
+ // place it at the current position in the hierarchy
+ mCurrentNode->push_back(pcNode);
+ mLastNodeIndex = hierarchy;
+ }
+ else {
+ // need to go back to the specified position in the hierarchy.
+ InverseNodeSearch(pcNode,mCurrentNode);
+ mLastNodeIndex++;
+ }
+ // Make this node the current node
+ mCurrentNode = pcNode;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_TRACKDUMMYOBJNAME:
+
+ // This is the "real" name of a $$$DUMMY object
+ {
+ const char* sz = (const char*) stream->GetPtr();
+ while (stream->GetI1()) {}
+
+ // If object name is DUMMY, take this one instead
+ if (mCurrentNode->mName == "$$$DUMMY") {
+ //DefaultLogger::get()->warn("3DS: Skipping dummy object name for non-dummy object");
+ mCurrentNode->mName = std::string(sz);
+ break;
+ }
+ }
+ break;
+
+ case Discreet3DS::CHUNK_TRACKPIVOT:
+
+ if ( Discreet3DS::CHUNK_TRACKINFO != parent)
+ {
+ DefaultLogger::get()->warn("3DS: Skipping pivot subchunk for non usual object");
+ break;
+ }
+
+ // Pivot = origin of rotation and scaling
+ mCurrentNode->vPivot.x = stream->GetF4();
+ mCurrentNode->vPivot.y = stream->GetF4();
+ mCurrentNode->vPivot.z = stream->GetF4();
+ break;
+
+
+ // ////////////////////////////////////////////////////////////////////
+ // POSITION KEYFRAME
+ case Discreet3DS::CHUNK_TRACKPOS:
+ {
+ stream->IncPtr(10);
+ const unsigned int numFrames = stream->GetI4();
+ bool sortKeys = false;
+
+ // This could also be meant as the target position for
+ // (targeted) lights and cameras
+ std::vector<aiVectorKey>* l;
+ if ( Discreet3DS::CHUNK_TRACKCAMTGT == parent || Discreet3DS::CHUNK_TRACKLIGTGT == parent) {
+ l = & mCurrentNode->aTargetPositionKeys;
+ }
+ else l = & mCurrentNode->aPositionKeys;
+
+ l->reserve(numFrames);
+ for (unsigned int i = 0; i < numFrames;++i) {
+ const unsigned int fidx = stream->GetI4();
+
+ // Setup a new position key
+ aiVectorKey v;
+ v.mTime = (double)fidx;
+
+ SkipTCBInfo();
+ v.mValue.x = stream->GetF4();
+ v.mValue.y = stream->GetF4();
+ v.mValue.z = stream->GetF4();
+
+ // check whether we'll need to sort the keys
+ if (!l->empty() && v.mTime <= l->back().mTime)
+ sortKeys = true;
+
+ // Add the new keyframe to the list
+ l->push_back(v);
+ }
+
+ // Sort all keys with ascending time values and remove duplicates?
+ if (sortKeys) {
+ std::stable_sort(l->begin(),l->end());
+ l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare<aiVectorKey>), l->end() );
+ }}
+
+ break;
+
+ // ////////////////////////////////////////////////////////////////////
+ // CAMERA ROLL KEYFRAME
+ case Discreet3DS::CHUNK_TRACKROLL:
+ {
+ // roll keys are accepted for cameras only
+ if (parent != Discreet3DS::CHUNK_TRACKCAMERA) {
+ DefaultLogger::get()->warn("3DS: Ignoring roll track for non-camera object");
+ break;
+ }
+ bool sortKeys = false;
+ std::vector<aiFloatKey>* l = &mCurrentNode->aCameraRollKeys;
+
+ stream->IncPtr(10);
+ const unsigned int numFrames = stream->GetI4();
+ l->reserve(numFrames);
+ for (unsigned int i = 0; i < numFrames;++i) {
+ const unsigned int fidx = stream->GetI4();
+
+ // Setup a new position key
+ aiFloatKey v;
+ v.mTime = (double)fidx;
+
+ // This is just a single float
+ SkipTCBInfo();
+ v.mValue = stream->GetF4();
+
+ // Check whether we'll need to sort the keys
+ if (!l->empty() && v.mTime <= l->back().mTime)
+ sortKeys = true;
+
+ // Add the new keyframe to the list
+ l->push_back(v);
+ }
+
+ // Sort all keys with ascending time values and remove duplicates?
+ if (sortKeys) {
+ std::stable_sort(l->begin(),l->end());
+ l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare<aiFloatKey>), l->end() );
+ }}
+ break;
+
+
+ // ////////////////////////////////////////////////////////////////////
+ // CAMERA FOV KEYFRAME
+ case Discreet3DS::CHUNK_TRACKFOV:
+ {
+ DefaultLogger::get()->error("3DS: Skipping FOV animation track. "
+ "This is not supported");
+ }
+ break;
+
+
+ // ////////////////////////////////////////////////////////////////////
+ // ROTATION KEYFRAME
+ case Discreet3DS::CHUNK_TRACKROTATE:
+ {
+ stream->IncPtr(10);
+ const unsigned int numFrames = stream->GetI4();
+
+ bool sortKeys = false;
+ std::vector<aiQuatKey>* l = &mCurrentNode->aRotationKeys;
+ l->reserve(numFrames);
+
+ for (unsigned int i = 0; i < numFrames;++i) {
+ const unsigned int fidx = stream->GetI4();
+ SkipTCBInfo();
+
+ aiQuatKey v;
+ v.mTime = (double)fidx;
+
+ // The rotation keyframe is given as an axis-angle pair
+ const float rad = stream->GetF4();
+ aiVector3D axis;
+ axis.x = stream->GetF4();
+ axis.y = stream->GetF4();
+ axis.z = stream->GetF4();
+
+ if (!axis.x && !axis.y && !axis.z)
+ axis.y = 1.f;
+
+ // Construct a rotation quaternion from the axis-angle pair
+ v.mValue = aiQuaternion(axis,rad);
+
+ // Check whether we'll need to sort the keys
+ if (!l->empty() && v.mTime <= l->back().mTime)
+ sortKeys = true;
+
+ // add the new keyframe to the list
+ l->push_back(v);
+ }
+ // Sort all keys with ascending time values and remove duplicates?
+ if (sortKeys) {
+ std::stable_sort(l->begin(),l->end());
+ l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare<aiQuatKey>), l->end() );
+ }}
+ break;
+
+ // ////////////////////////////////////////////////////////////////////
+ // SCALING KEYFRAME
+ case Discreet3DS::CHUNK_TRACKSCALE:
+ {
+ stream->IncPtr(10);
+ const unsigned int numFrames = stream->GetI2();
+ stream->IncPtr(2);
+
+ bool sortKeys = false;
+ std::vector<aiVectorKey>* l = &mCurrentNode->aScalingKeys;
+ l->reserve(numFrames);
+
+ for (unsigned int i = 0; i < numFrames;++i) {
+ const unsigned int fidx = stream->GetI4();
+ SkipTCBInfo();
+
+ // Setup a new key
+ aiVectorKey v;
+ v.mTime = (double)fidx;
+
+ // ... and read its value
+ v.mValue.x = stream->GetF4();
+ v.mValue.y = stream->GetF4();
+ v.mValue.z = stream->GetF4();
+
+ // check whether we'll need to sort the keys
+ if (!l->empty() && v.mTime <= l->back().mTime)
+ sortKeys = true;
+
+ // Remove zero-scalings on singular axes - they've been reported to be there erroneously in some strange files
+ if (!v.mValue.x) v.mValue.x = 1.f;
+ if (!v.mValue.y) v.mValue.y = 1.f;
+ if (!v.mValue.z) v.mValue.z = 1.f;
+
+ l->push_back(v);
+ }
+ // Sort all keys with ascending time values and remove duplicates?
+ if (sortKeys) {
+ std::stable_sort(l->begin(),l->end());
+ l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare<aiVectorKey>), l->end() );
+ }}
+ break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a face chunk - it contains smoothing groups and material assignments
+void Discreet3DSImporter::ParseFaceChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // Get the mesh we're currently working on
+ D3DS::Mesh& mMesh = mScene->mMeshes.back();
+
+ // Get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_SMOOLIST:
+ {
+ // This is the list of smoothing groups - a bitfield for every face.
+ // Up to 32 smoothing groups assigned to a single face.
+ unsigned int num = chunkSize/4, m = 0;
+ for (std::vector<D3DS::Face>::iterator i = mMesh.mFaces.begin(); m != num;++i, ++m) {
+ // nth bit is set for nth smoothing group
+ (*i).iSmoothGroup = stream->GetI4();
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_FACEMAT:
+ {
+ // at fist an asciiz with the material name
+ const char* sz = (const char*)stream->GetPtr();
+ while (stream->GetI1()) {}
+
+ // find the index of the material
+ unsigned int idx = 0xcdcdcdcd, cnt = 0;
+ for (std::vector<D3DS::Material>::const_iterator i = mScene->mMaterials.begin();i != mScene->mMaterials.end();++i,++cnt) {
+ // use case independent comparisons. hopefully it will work.
+ if ((*i).mName.length() && !ASSIMP_stricmp(sz, (*i).mName.c_str())) {
+ idx = cnt;
+ break;
+ }
+ }
+ if (0xcdcdcdcd == idx) {
+ DefaultLogger::get()->error(std::string("3DS: Unknown material: ") + sz);
+ }
+
+ // Now continue and read all material indices
+ cnt = (uint16_t)stream->GetI2();
+ for (unsigned int i = 0; i < cnt;++i) {
+ unsigned int fidx = (uint16_t)stream->GetI2();
+
+ // check range
+ if (fidx >= mMesh.mFaceMaterials.size()) {
+ DefaultLogger::get()->error("3DS: Invalid face index in face material list");
+ }
+ else mMesh.mFaceMaterials[fidx] = idx;
+ }}
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a mesh chunk. Here's the actual mesh data
+void Discreet3DSImporter::ParseMeshChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // Get the mesh we're currently working on
+ D3DS::Mesh& mMesh = mScene->mMeshes.back();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_VERTLIST:
+ {
+ // This is the list of all vertices in the current mesh
+ int num = (int)(uint16_t)stream->GetI2();
+ mMesh.mPositions.reserve(num);
+ while (num-- > 0) {
+ aiVector3D v;
+ v.x = stream->GetF4();
+ v.y = stream->GetF4();
+ v.z = stream->GetF4();
+ mMesh.mPositions.push_back(v);
+ }}
+ break;
+ case Discreet3DS::CHUNK_TRMATRIX:
+ {
+ // This is the RLEATIVE transformation matrix of the current mesh. Vertices are
+ // pretransformed by this matrix wonder.
+ mMesh.mMat.a1 = stream->GetF4();
+ mMesh.mMat.b1 = stream->GetF4();
+ mMesh.mMat.c1 = stream->GetF4();
+ mMesh.mMat.a2 = stream->GetF4();
+ mMesh.mMat.b2 = stream->GetF4();
+ mMesh.mMat.c2 = stream->GetF4();
+ mMesh.mMat.a3 = stream->GetF4();
+ mMesh.mMat.b3 = stream->GetF4();
+ mMesh.mMat.c3 = stream->GetF4();
+ mMesh.mMat.a4 = stream->GetF4();
+ mMesh.mMat.b4 = stream->GetF4();
+ mMesh.mMat.c4 = stream->GetF4();
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAPLIST:
+ {
+ // This is the list of all UV coords in the current mesh
+ int num = (int)(uint16_t)stream->GetI2();
+ mMesh.mTexCoords.reserve(num);
+ while (num-- > 0) {
+ aiVector3D v;
+ v.x = stream->GetF4();
+ v.y = stream->GetF4();
+ mMesh.mTexCoords.push_back(v);
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_FACELIST:
+ {
+ // This is the list of all faces in the current mesh
+ int num = (int)(uint16_t)stream->GetI2();
+ mMesh.mFaces.reserve(num);
+ while (num-- > 0) {
+ // 3DS faces are ALWAYS triangles
+ mMesh.mFaces.push_back(D3DS::Face());
+ D3DS::Face& sFace = mMesh.mFaces.back();
+
+ sFace.mIndices[0] = (uint16_t)stream->GetI2();
+ sFace.mIndices[1] = (uint16_t)stream->GetI2();
+ sFace.mIndices[2] = (uint16_t)stream->GetI2();
+
+ stream->IncPtr(2); // skip edge visibility flag
+ }
+
+ // Resize the material array (0xcdcdcdcd marks the default material; so if a face is
+ // not referenced by a material, $$DEFAULT will be assigned to it)
+ mMesh.mFaceMaterials.resize(mMesh.mFaces.size(),0xcdcdcdcd);
+
+ // Larger 3DS files could have multiple FACE chunks here
+ chunkSize = stream->GetRemainingSizeToLimit();
+ if ( chunkSize > (int) sizeof(Discreet3DS::Chunk ) )
+ ParseFaceChunk();
+ }
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a 3DS material chunk
+void Discreet3DSImporter::ParseMaterialChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_MAT_MATNAME:
+
+ {
+ // The material name string is already zero-terminated, but we need to be sure ...
+ const char* sz = (const char*)stream->GetPtr();
+ unsigned int cnt = 0;
+ while (stream->GetI1())
+ ++cnt;
+
+ if (!cnt) {
+ // This may not be, we use the default name instead
+ DefaultLogger::get()->error("3DS: Empty material name");
+ }
+ else mScene->mMaterials.back().mName = std::string(sz,cnt);
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAT_DIFFUSE:
+ {
+ // This is the diffuse material color
+ aiColor3D* pc = &mScene->mMaterials.back().mDiffuse;
+ ParseColorChunk(pc);
+ if (is_qnan(pc->r)) {
+ // color chunk is invalid. Simply ignore it
+ DefaultLogger::get()->error("3DS: Unable to read DIFFUSE chunk");
+ pc->r = pc->g = pc->b = 1.0f;
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_MAT_SPECULAR:
+ {
+ // This is the specular material color
+ aiColor3D* pc = &mScene->mMaterials.back().mSpecular;
+ ParseColorChunk(pc);
+ if (is_qnan(pc->r)) {
+ // color chunk is invalid. Simply ignore it
+ DefaultLogger::get()->error("3DS: Unable to read SPECULAR chunk");
+ pc->r = pc->g = pc->b = 1.0f;
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_MAT_AMBIENT:
+ {
+ // This is the ambient material color
+ aiColor3D* pc = &mScene->mMaterials.back().mAmbient;
+ ParseColorChunk(pc);
+ if (is_qnan(pc->r)) {
+ // color chunk is invalid. Simply ignore it
+ DefaultLogger::get()->error("3DS: Unable to read AMBIENT chunk");
+ pc->r = pc->g = pc->b = 0.0f;
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_MAT_SELF_ILLUM:
+ {
+ // This is the emissive material color
+ aiColor3D* pc = &mScene->mMaterials.back().mEmissive;
+ ParseColorChunk(pc);
+ if (is_qnan(pc->r)) {
+ // color chunk is invalid. Simply ignore it
+ DefaultLogger::get()->error("3DS: Unable to read EMISSIVE chunk");
+ pc->r = pc->g = pc->b = 0.0f;
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_MAT_TRANSPARENCY:
+ {
+ // This is the material's transparency
+ float* pcf = &mScene->mMaterials.back().mTransparency;
+ *pcf = ParsePercentageChunk();
+
+ // NOTE: transparency, not opacity
+ if (is_qnan(*pcf))
+ *pcf = 1.0f;
+ else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAT_SHADING:
+ // This is the material shading mode
+ mScene->mMaterials.back().mShading = (D3DS::Discreet3DS::shadetype3ds)stream->GetI2();
+ break;
+
+ case Discreet3DS::CHUNK_MAT_TWO_SIDE:
+ // This is the two-sided flag
+ mScene->mMaterials.back().mTwoSided = true;
+ break;
+
+ case Discreet3DS::CHUNK_MAT_SHININESS:
+ { // This is the shininess of the material
+ float* pcf = &mScene->mMaterials.back().mSpecularExponent;
+ *pcf = ParsePercentageChunk();
+ if (is_qnan(*pcf))
+ *pcf = 0.0f;
+ else *pcf *= (float)0xFFFF;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAT_SHININESS_PERCENT:
+ { // This is the shininess strength of the material
+ float* pcf = &mScene->mMaterials.back().mShininessStrength;
+ *pcf = ParsePercentageChunk();
+ if (is_qnan(*pcf))
+ *pcf = 0.0f;
+ else *pcf *= (float)0xffff / 100.0f;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAT_SELF_ILPCT:
+ { // This is the self illumination strength of the material
+ float f = ParsePercentageChunk();
+ if (is_qnan(f))
+ f = 0.0f;
+ else f *= (float)0xFFFF / 100.0f;
+ mScene->mMaterials.back().mEmissive = aiColor3D(f,f,f);
+ }
+ break;
+
+ // Parse texture chunks
+ case Discreet3DS::CHUNK_MAT_TEXTURE:
+ // Diffuse texture
+ ParseTextureChunk(&mScene->mMaterials.back().sTexDiffuse);
+ break;
+ case Discreet3DS::CHUNK_MAT_BUMPMAP:
+ // Height map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexBump);
+ break;
+ case Discreet3DS::CHUNK_MAT_OPACMAP:
+ // Opacity texture
+ ParseTextureChunk(&mScene->mMaterials.back().sTexOpacity);
+ break;
+ case Discreet3DS::CHUNK_MAT_MAT_SHINMAP:
+ // Shininess map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexShininess);
+ break;
+ case Discreet3DS::CHUNK_MAT_SPECMAP:
+ // Specular map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexSpecular);
+ break;
+ case Discreet3DS::CHUNK_MAT_SELFIMAP:
+ // Self-illumination (emissive) map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexEmissive);
+ break;
+ case Discreet3DS::CHUNK_MAT_REFLMAP:
+ // Reflection map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexReflective);
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut)
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_MAPFILE:
+ {
+ // The material name string is already zero-terminated, but we need to be sure ...
+ const char* sz = (const char*)stream->GetPtr();
+ unsigned int cnt = 0;
+ while (stream->GetI1())
+ ++cnt;
+ pcOut->mMapName = std::string(sz,cnt);
+ }
+ break;
+
+
+ case Discreet3DS::CHUNK_PERCENTF:
+ // Manually parse the blend factor
+ pcOut->mTextureBlend = stream->GetF4();
+ break;
+
+ case Discreet3DS::CHUNK_PERCENTW:
+ // Manually parse the blend factor
+ pcOut->mTextureBlend = (float)((uint16_t)stream->GetI2()) / 100.0f;
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_USCALE:
+ // Texture coordinate scaling in the U direction
+ pcOut->mScaleU = stream->GetF4();
+ if (0.0f == pcOut->mScaleU)
+ {
+ DefaultLogger::get()->warn("Texture coordinate scaling in the x direction is zero. Assuming 1.");
+ pcOut->mScaleU = 1.0f;
+ }
+ break;
+ case Discreet3DS::CHUNK_MAT_MAP_VSCALE:
+ // Texture coordinate scaling in the V direction
+ pcOut->mScaleV = stream->GetF4();
+ if (0.0f == pcOut->mScaleV)
+ {
+ DefaultLogger::get()->warn("Texture coordinate scaling in the y direction is zero. Assuming 1.");
+ pcOut->mScaleV = 1.0f;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_UOFFSET:
+ // Texture coordinate offset in the U direction
+ pcOut->mOffsetU = -stream->GetF4();
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_VOFFSET:
+ // Texture coordinate offset in the V direction
+ pcOut->mOffsetV = stream->GetF4();
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_ANG:
+ // Texture coordinate rotation, CCW in DEGREES
+ pcOut->mRotation = -AI_DEG_TO_RAD( stream->GetF4() );
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_TILING:
+ {
+ const uint16_t iFlags = stream->GetI2();
+
+ // Get the mapping mode (for both axes)
+ if (iFlags & 0x2u)
+ pcOut->mMapMode = aiTextureMapMode_Mirror;
+
+ else if (iFlags & 0x10u)
+ pcOut->mMapMode = aiTextureMapMode_Decal;
+
+ // wrapping in all remaining cases
+ else pcOut->mMapMode = aiTextureMapMode_Wrap;
+ }
+ break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a percentage chunk
+float Discreet3DSImporter::ParsePercentageChunk()
+{
+ Discreet3DS::Chunk chunk;
+ ReadChunk(&chunk);
+
+ if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag)
+ return stream->GetF4();
+ else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag)
+ return (float)((uint16_t)stream->GetI2()) / (float)0xFFFF;
+ return get_qnan();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a color chunk. If a percentage chunk is found instead it is read as a grayscale color
+void Discreet3DSImporter::ParseColorChunk(aiColor3D* out,
+ bool acceptPercent)
+{
+ ai_assert(out != NULL);
+
+ // error return value
+ const float qnan = get_qnan();
+ static const aiColor3D clrError = aiColor3D(qnan,qnan,qnan);
+
+ Discreet3DS::Chunk chunk;
+ ReadChunk(&chunk);
+ const unsigned int diff = chunk.Size - sizeof(Discreet3DS::Chunk);
+
+ // Get the type of the chunk
+ switch(chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_LINRGBF:
+ case Discreet3DS::CHUNK_RGBF:
+ if (sizeof(float) * 3 > diff) {
+ *out = clrError;
+ return;
+ }
+ out->r = stream->GetF4();
+ out->g = stream->GetF4();
+ out->b = stream->GetF4();
+ break;
+
+ case Discreet3DS::CHUNK_LINRGBB:
+ case Discreet3DS::CHUNK_RGBB:
+ if (sizeof(char) * 3 > diff) {
+ *out = clrError;
+ return;
+ }
+ out->r = (float)(uint8_t)stream->GetI1() / 255.0f;
+ out->g = (float)(uint8_t)stream->GetI1() / 255.0f;
+ out->b = (float)(uint8_t)stream->GetI1() / 255.0f;
+ break;
+
+ // Percentage chunks are accepted, too.
+ case Discreet3DS::CHUNK_PERCENTF:
+ if (acceptPercent && 4 <= diff) {
+ out->g = out->b = out->r = stream->GetF4();
+ break;
+ }
+ *out = clrError;
+ return;
+
+ case Discreet3DS::CHUNK_PERCENTW:
+ if (acceptPercent && 1 <= diff) {
+ out->g = out->b = out->r = (float)(uint8_t)stream->GetI1() / 255.0f;
+ break;
+ }
+ *out = clrError;
+ return;
+
+ default:
+ stream->IncPtr(diff);
+ // Skip unknown chunks, hope this won't cause any problems.
+ return ParseColorChunk(out,acceptPercent);
+ };
+}
+
+#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
diff --git a/src/3rdparty/assimp/code/3DSLoader.h b/src/3rdparty/assimp/code/3DSLoader.h
new file mode 100644
index 000000000..133a7c8ef
--- /dev/null
+++ b/src/3rdparty/assimp/code/3DSLoader.h
@@ -0,0 +1,280 @@
+
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 3DSLoader.h
+ * @brief 3DS File format loader
+ */
+#ifndef AI_3DSIMPORTER_H_INC
+#define AI_3DSIMPORTER_H_INC
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+struct aiNode;
+#include "3DSHelper.h"
+
+namespace Assimp {
+class MaterialHelper;
+
+using namespace D3DS;
+
+// ---------------------------------------------------------------------------------
+/** Importer class for 3D Studio r3 and r4 3DS files
+ */
+class Discreet3DSImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ Discreet3DSImporter();
+
+ /** Destructor, private as well */
+ ~Discreet3DSImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /** Converts a temporary material to the outer representation
+ */
+ void ConvertMaterial(D3DS::Material& p_cMat,
+ MaterialHelper& p_pcOut);
+
+ // -------------------------------------------------------------------
+ /** Read a chunk
+ *
+ * @param pcOut Receives the current chunk
+ */
+ void ReadChunk(Discreet3DS::Chunk* pcOut);
+
+ // -------------------------------------------------------------------
+ /** Parse a percentage chunk. mCurrent will point to the next
+ * chunk behind afterwards. If no percentage chunk is found
+ * QNAN is returned.
+ */
+ float ParsePercentageChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a color chunk. mCurrent will point to the next
+ * chunk behind afterwards. If no color chunk is found
+ * QNAN is returned in all members.
+ */
+ void ParseColorChunk(aiColor3D* p_pcOut,
+ bool p_bAcceptPercent = true);
+
+
+ // -------------------------------------------------------------------
+ /** Skip a chunk in the file
+ */
+ void SkipChunk();
+
+ // -------------------------------------------------------------------
+ /** Generate the nodegraph
+ */
+ void GenerateNodeGraph(aiScene* pcOut);
+
+ // -------------------------------------------------------------------
+ /** Parse a main top-level chunk in the file
+ */
+ void ParseMainChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a top-level chunk in the file
+ */
+ void ParseChunk(const char* name, unsigned int num);
+
+ // -------------------------------------------------------------------
+ /** Parse a top-level editor chunk in the file
+ */
+ void ParseEditorChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a top-level object chunk in the file
+ */
+ void ParseObjectChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a material chunk in the file
+ */
+ void ParseMaterialChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a mesh chunk in the file
+ */
+ void ParseMeshChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a light chunk in the file
+ */
+ void ParseLightChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a camera chunk in the file
+ */
+ void ParseCameraChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a face list chunk in the file
+ */
+ void ParseFaceChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a keyframe chunk in the file
+ */
+ void ParseKeyframeChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a hierarchy chunk in the file
+ */
+ void ParseHierarchyChunk(uint16_t parent);
+
+ // -------------------------------------------------------------------
+ /** Parse a texture chunk in the file
+ */
+ void ParseTextureChunk(D3DS::Texture* pcOut);
+
+ // -------------------------------------------------------------------
+ /** Convert the meshes in the file
+ */
+ void ConvertMeshes(aiScene* pcOut);
+
+ // -------------------------------------------------------------------
+ /** Replace the default material in the scene
+ */
+ void ReplaceDefaultMaterial();
+
+ // -------------------------------------------------------------------
+ /** Convert the whole scene
+ */
+ void ConvertScene(aiScene* pcOut);
+
+ // -------------------------------------------------------------------
+ /** generate unique vertices for a mesh
+ */
+ void MakeUnique(D3DS::Mesh& sMesh);
+
+ // -------------------------------------------------------------------
+ /** Add a node to the node graph
+ */
+ void AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,D3DS::Node* pcIn,
+ aiMatrix4x4& absTrafo);
+
+ // -------------------------------------------------------------------
+ /** Search for a node in the graph.
+ * Called recursively
+ */
+ void InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent);
+
+ // -------------------------------------------------------------------
+ /** Apply the master scaling factor to the mesh
+ */
+ void ApplyMasterScale(aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ /** Clamp all indices in the file to a valid range
+ */
+ void CheckIndices(D3DS::Mesh& sMesh);
+
+ // -------------------------------------------------------------------
+ /** Skip the TCB info in a track key
+ */
+ void SkipTCBInfo();
+
+protected:
+
+ /** Stream to read from */
+ StreamReaderLE* stream;
+
+ /** Last touched node index */
+ short mLastNodeIndex;
+
+ /** Current node, root node */
+ D3DS::Node* mCurrentNode, *mRootNode;
+
+ /** Scene under construction */
+ D3DS::Scene* mScene;
+
+ /** Ambient base color of the scene */
+ aiColor3D mClrAmbient;
+
+ /** Master scaling factor of the scene */
+ float mMasterScale;
+
+ /** Path to the background image of the scene */
+ std::string mBackgroundImage;
+ bool bHasBG;
+
+ /** true if PRJ file */
+ bool bIsPrj;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/ACLoader.cpp b/src/3rdparty/assimp/code/ACLoader.cpp
new file mode 100644
index 000000000..b4aef8686
--- /dev/null
+++ b/src/3rdparty/assimp/code/ACLoader.cpp
@@ -0,0 +1,860 @@
+
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the AC3D importer class */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_AC_IMPORTER
+
+// internal headers
+#include "ACLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+#include "Subdivision.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+// skip to the next token
+#define AI_AC_SKIP_TO_NEXT_TOKEN() \
+ if (!SkipSpaces(&buffer)) \
+ { \
+ DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL"); \
+ continue; \
+ }
+
+// ------------------------------------------------------------------------------------------------
+// read a string (may be enclosed in double quotation marks). buffer must point to "
+#define AI_AC_GET_STRING(out) \
+ ++buffer; \
+ const char* sz = buffer; \
+ while ('\"' != *buffer) \
+ { \
+ if (IsLineEnd( *buffer )) \
+ { \
+ DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL in string"); \
+ out = "ERROR"; \
+ break; \
+ } \
+ ++buffer; \
+ } \
+ if (IsLineEnd( *buffer ))continue; \
+ out = std::string(sz,(unsigned int)(buffer-sz)); \
+ ++buffer;
+
+
+// ------------------------------------------------------------------------------------------------
+// read 1 to n floats prefixed with an optional predefined identifier
+#define AI_AC_CHECKED_LOAD_FLOAT_ARRAY(name,name_length,num,out) \
+ AI_AC_SKIP_TO_NEXT_TOKEN(); \
+ if (name_length) \
+ { \
+ if (strncmp(buffer,name,name_length) || !IsSpace(buffer[name_length])) \
+ { \
+ DefaultLogger::get()->error("AC3D: Unexpexted token. " name " was expected."); \
+ continue; \
+ } \
+ buffer += name_length+1; \
+ } \
+ for (unsigned int i = 0; i < num;++i) \
+ { \
+ AI_AC_SKIP_TO_NEXT_TOKEN(); \
+ buffer = fast_atof_move(buffer,((float*)out)[i]); \
+ }
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+AC3DImporter::AC3DImporter() : buffer(0), configSplitBFCull(false), configEvalSubdivision(false), mNumMeshes(0), lights(0), groups(0), polys(0), worlds(0)
+{
+ // nothing to be done here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+AC3DImporter::~AC3DImporter()
+{
+ // nothing to be done here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool AC3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ std::string extension = GetExtension(pFile);
+
+ // fixme: are acc and ac3d *really* used? Some sources say they are
+ if (extension == "ac" || extension == "ac3d" || extension == "acc") {
+ return true;
+ }
+ if (!extension.length() || checkSig) {
+ uint32_t token = AI_MAKE_MAGIC("AC3D");
+ return CheckMagicToken(pIOHandler,pFile,&token,1,0);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get list of file extensions handled by this loader
+void AC3DImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("ac");
+ extensions.insert("acc");
+ extensions.insert("ac3d");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a pointer to the next line from the file
+bool AC3DImporter::GetNextLine( )
+{
+ SkipLine(&buffer);
+ return SkipSpaces(&buffer);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse an object section in an AC file
+void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
+{
+ if (!TokenMatch(buffer,"OBJECT",6))
+ return;
+
+ SkipSpaces(&buffer);
+
+ ++mNumMeshes;
+
+ objects.push_back(Object());
+ Object& obj = objects.back();
+
+ aiLight* light = NULL;
+ if (!ASSIMP_strincmp(buffer,"light",5))
+ {
+ // This is a light source. Add it to the list
+ mLights->push_back(light = new aiLight());
+
+ // Return a point light with no attenuation
+ light->mType = aiLightSource_POINT;
+ light->mColorDiffuse = light->mColorSpecular = aiColor3D(1.f,1.f,1.f);
+ light->mAttenuationConstant = 1.f;
+
+ // Generate a default name for both the light source and the node
+ // FIXME - what's the right way to print a size_t? Is 'zu' universally available? stick with the safe version.
+ light->mName.length = ::sprintf(light->mName.data,"ACLight_%i",static_cast<unsigned int>(mLights->size())-1);
+ obj.name = std::string( light->mName.data );
+
+ DefaultLogger::get()->debug("AC3D: Light source encountered");
+ obj.type = Object::Light;
+ }
+ else if (!ASSIMP_strincmp(buffer,"group",5))
+ {
+ obj.type = Object::Group;
+ }
+ else if (!ASSIMP_strincmp(buffer,"world",5))
+ {
+ obj.type = Object::World;
+ }
+ else obj.type = Object::Poly;
+ while (GetNextLine())
+ {
+ if (TokenMatch(buffer,"kids",4))
+ {
+ SkipSpaces(&buffer);
+ unsigned int num = strtol10(buffer,&buffer);
+ GetNextLine();
+ if (num)
+ {
+ // load the children of this object recursively
+ obj.children.reserve(num);
+ for (unsigned int i = 0; i < num; ++i)
+ LoadObjectSection(obj.children);
+ }
+ return;
+ }
+ else if (TokenMatch(buffer,"name",4))
+ {
+ SkipSpaces(&buffer);
+ AI_AC_GET_STRING(obj.name);
+
+ // If this is a light source, we'll also need to store
+ // the name of the node in it.
+ if (light)
+ {
+ light->mName.Set(obj.name);
+ }
+ }
+ else if (TokenMatch(buffer,"texture",7))
+ {
+ SkipSpaces(&buffer);
+ AI_AC_GET_STRING(obj.texture);
+ }
+ else if (TokenMatch(buffer,"texrep",6))
+ {
+ SkipSpaces(&buffer);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texRepeat);
+ if (!obj.texRepeat.x || !obj.texRepeat.y)
+ obj.texRepeat = aiVector2D (1.f,1.f);
+ }
+ else if (TokenMatch(buffer,"texoff",6))
+ {
+ SkipSpaces(&buffer);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texOffset);
+ }
+ else if (TokenMatch(buffer,"rot",3))
+ {
+ SkipSpaces(&buffer);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,9,&obj.rotation);
+ }
+ else if (TokenMatch(buffer,"loc",3))
+ {
+ SkipSpaces(&buffer);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&obj.translation);
+ }
+ else if (TokenMatch(buffer,"subdiv",6))
+ {
+ SkipSpaces(&buffer);
+ obj.subDiv = strtol10(buffer,&buffer);
+ }
+ else if (TokenMatch(buffer,"crease",6))
+ {
+ SkipSpaces(&buffer);
+ obj.crease = fast_atof(buffer);
+ }
+ else if (TokenMatch(buffer,"numvert",7))
+ {
+ SkipSpaces(&buffer);
+
+ unsigned int t = strtol10(buffer,&buffer);
+ obj.vertices.reserve(t);
+ for (unsigned int i = 0; i < t;++i)
+ {
+ if (!GetNextLine())
+ {
+ DefaultLogger::get()->error("AC3D: Unexpected EOF: not all vertices have been parsed yet");
+ break;
+ }
+ else if (!IsNumeric(*buffer))
+ {
+ DefaultLogger::get()->error("AC3D: Unexpected token: not all vertices have been parsed yet");
+ --buffer; // make sure the line is processed a second time
+ break;
+ }
+ obj.vertices.push_back(aiVector3D());
+ aiVector3D& v = obj.vertices.back();
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&v.x);
+ }
+ }
+ else if (TokenMatch(buffer,"numsurf",7))
+ {
+ SkipSpaces(&buffer);
+
+ bool Q3DWorkAround = false;
+
+ const unsigned int t = strtol10(buffer,&buffer);
+ obj.surfaces.reserve(t);
+ for (unsigned int i = 0; i < t;++i)
+ {
+ GetNextLine();
+ if (!TokenMatch(buffer,"SURF",4))
+ {
+ // FIX: this can occur for some files - Quick 3D for
+ // example writes no surf chunks
+ if (!Q3DWorkAround)
+ {
+ DefaultLogger::get()->warn("AC3D: SURF token was expected");
+ DefaultLogger::get()->debug("Continuing with Quick3D Workaround enabled");
+ }
+ --buffer; // make sure the line is processed a second time
+ // break; --- see fix notes above
+
+ Q3DWorkAround = true;
+ }
+ SkipSpaces(&buffer);
+ obj.surfaces.push_back(Surface());
+ Surface& surf = obj.surfaces.back();
+ surf.flags = strtol_cppstyle(buffer);
+
+ while (1)
+ {
+ if (!GetNextLine())
+ {
+ DefaultLogger::get()->error("AC3D: Unexpected EOF: surface is incomplete");
+ break;
+ }
+ if (TokenMatch(buffer,"mat",3))
+ {
+ SkipSpaces(&buffer);
+ surf.mat = strtol10(buffer);
+ }
+ else if (TokenMatch(buffer,"refs",4))
+ {
+ // --- see fix notes above
+ if (Q3DWorkAround)
+ {
+ if (!surf.entries.empty())
+ {
+ buffer -= 6;
+ break;
+ }
+ }
+
+ SkipSpaces(&buffer);
+ const unsigned int m = strtol10(buffer);
+ surf.entries.reserve(m);
+
+ obj.numRefs += m;
+
+ for (unsigned int k = 0; k < m; ++k)
+ {
+ if (!GetNextLine())
+ {
+ DefaultLogger::get()->error("AC3D: Unexpected EOF: surface references are incomplete");
+ break;
+ }
+ surf.entries.push_back(Surface::SurfaceEntry());
+ Surface::SurfaceEntry& entry = surf.entries.back();
+
+ entry.first = strtol10(buffer,&buffer);
+ SkipSpaces(&buffer);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&entry.second);
+ }
+ }
+ else
+ {
+
+ --buffer; // make sure the line is processed a second time
+ break;
+ }
+ }
+ }
+ }
+ }
+ DefaultLogger::get()->error("AC3D: Unexpected EOF: \'kids\' line was expected");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a material from AC3DImporter::Material to aiMaterial
+void AC3DImporter::ConvertMaterial(const Object& object,
+ const Material& matSrc,
+ MaterialHelper& matDest)
+{
+ aiString s;
+
+ if (matSrc.name.length())
+ {
+ s.Set(matSrc.name);
+ matDest.AddProperty(&s,AI_MATKEY_NAME);
+ }
+ if (object.texture.length())
+ {
+ s.Set(object.texture);
+ matDest.AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ // UV transformation
+ if (1.f != object.texRepeat.x || 1.f != object.texRepeat.y ||
+ object.texOffset.x || object.texOffset.y)
+ {
+ aiUVTransform transform;
+ transform.mScaling = object.texRepeat;
+ transform.mTranslation = object.texOffset;
+ matDest.AddProperty(&transform,1,AI_MATKEY_UVTRANSFORM_DIFFUSE(0));
+ }
+ }
+
+ matDest.AddProperty<aiColor3D>(&matSrc.rgb,1, AI_MATKEY_COLOR_DIFFUSE);
+ matDest.AddProperty<aiColor3D>(&matSrc.amb,1, AI_MATKEY_COLOR_AMBIENT);
+ matDest.AddProperty<aiColor3D>(&matSrc.emis,1,AI_MATKEY_COLOR_EMISSIVE);
+ matDest.AddProperty<aiColor3D>(&matSrc.spec,1,AI_MATKEY_COLOR_SPECULAR);
+
+ int n;
+ if (matSrc.shin)
+ {
+ n = aiShadingMode_Phong;
+ matDest.AddProperty<float>(&matSrc.shin,1,AI_MATKEY_SHININESS);
+ }
+ else n = aiShadingMode_Gouraud;
+ matDest.AddProperty<int>(&n,1,AI_MATKEY_SHADING_MODEL);
+
+ float f = 1.f - matSrc.trans;
+ matDest.AddProperty<float>(&f,1,AI_MATKEY_OPACITY);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts the loaded data to the internal verbose representation
+aiNode* AC3DImporter::ConvertObjectSection(Object& object,
+ std::vector<aiMesh*>& meshes,
+ std::vector<MaterialHelper*>& outMaterials,
+ const std::vector<Material>& materials,
+ aiNode* parent)
+{
+ aiNode* node = new aiNode();
+ node->mParent = parent;
+ if (object.vertices.size())
+ {
+ if (!object.surfaces.size() || !object.numRefs)
+ {
+ /* " An object with 7 vertices (no surfaces, no materials defined).
+ This is a good way of getting point data into AC3D.
+ The Vertex->create convex-surface/object can be used on these
+ vertices to 'wrap' a 3d shape around them "
+ (http://www.opencity.info/html/ac3dfileformat.html)
+
+ therefore: if no surfaces are defined return point data only
+ */
+
+ DefaultLogger::get()->info("AC3D: No surfaces defined in object definition, "
+ "a point list is returned");
+
+ meshes.push_back(new aiMesh());
+ aiMesh* mesh = meshes.back();
+
+ mesh->mNumFaces = mesh->mNumVertices = (unsigned int)object.vertices.size();
+ aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
+ aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+
+ for (unsigned int i = 0; i < mesh->mNumVertices;++i,++faces,++verts)
+ {
+ *verts = object.vertices[i];
+ faces->mNumIndices = 1;
+ faces->mIndices = new unsigned int[1];
+ faces->mIndices[0] = i;
+ }
+
+ // use the primary material in this case. this should be the
+ // default material if all objects of the file contain points
+ // and no faces.
+ mesh->mMaterialIndex = 0;
+ outMaterials.push_back(new MaterialHelper());
+ ConvertMaterial(object, materials[0], *outMaterials.back());
+ }
+ else
+ {
+ // need to generate one or more meshes for this object.
+ // find out how many different materials we have
+ typedef std::pair< unsigned int, unsigned int > IntPair;
+ typedef std::vector< IntPair > MatTable;
+ MatTable needMat(materials.size(),IntPair(0,0));
+
+ std::vector<Surface>::iterator it,end = object.surfaces.end();
+ std::vector<Surface::SurfaceEntry>::iterator it2,end2;
+
+ for (it = object.surfaces.begin(); it != end; ++it)
+ {
+ register unsigned int idx = (*it).mat;
+ if (idx >= needMat.size())
+ {
+ DefaultLogger::get()->error("AC3D: material index is out of range");
+ idx = 0;
+ }
+ if ((*it).entries.empty())
+ {
+ DefaultLogger::get()->warn("AC3D: surface her zero vertex references");
+ }
+
+ // validate all vertex indices to make sure we won't crash here
+ for (it2 = (*it).entries.begin(),
+ end2 = (*it).entries.end(); it2 != end2; ++it2)
+ {
+ if ((*it2).first >= object.vertices.size())
+ {
+ DefaultLogger::get()->warn("AC3D: Invalid vertex reference");
+ (*it2).first = 0;
+ }
+ }
+
+ if (!needMat[idx].first)++node->mNumMeshes;
+
+ switch ((*it).flags & 0xf)
+ {
+ // closed line
+ case 0x1:
+
+ needMat[idx].first += (unsigned int)(*it).entries.size();
+ needMat[idx].second += (unsigned int)(*it).entries.size()<<1u;
+ break;
+
+ // unclosed line
+ case 0x2:
+
+ needMat[idx].first += (unsigned int)(*it).entries.size()-1;
+ needMat[idx].second += ((unsigned int)(*it).entries.size()-1)<<1u;
+ break;
+
+ // 0 == polygon, else unknown
+ default:
+
+ if ((*it).flags & 0xf)
+ {
+ DefaultLogger::get()->warn("AC3D: The type flag of a surface is unknown");
+ (*it).flags &= ~(0xf);
+ }
+
+ // the number of faces increments by one, the number
+ // of vertices by surface.numref.
+ needMat[idx].first++;
+ needMat[idx].second += (unsigned int)(*it).entries.size();
+ };
+ }
+ unsigned int* pip = node->mMeshes = new unsigned int[node->mNumMeshes];
+ unsigned int mat = 0;
+ const size_t oldm = meshes.size();
+ for (MatTable::const_iterator cit = needMat.begin(), cend = needMat.end();
+ cit != cend; ++cit, ++mat)
+ {
+ if (!(*cit).first)continue;
+
+ // allocate a new aiMesh object
+ *pip++ = (unsigned int)meshes.size();
+ aiMesh* mesh = new aiMesh();
+ meshes.push_back(mesh);
+
+ mesh->mMaterialIndex = (unsigned int)outMaterials.size();
+ outMaterials.push_back(new MaterialHelper());
+ ConvertMaterial(object, materials[mat], *outMaterials.back());
+
+ // allocate storage for vertices and normals
+ mesh->mNumFaces = (*cit).first;
+ aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
+
+ mesh->mNumVertices = (*cit).second;
+ aiVector3D* vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+ unsigned int cur = 0;
+
+ // allocate UV coordinates, but only if the texture name for the
+ // surface is not empty
+ aiVector3D* uv = NULL;
+ if (object.texture.length())
+ {
+ uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
+ mesh->mNumUVComponents[0] = 2;
+ }
+
+ for (it = object.surfaces.begin(); it != end; ++it)
+ {
+ if (mat == (*it).mat)
+ {
+ const Surface& src = *it;
+
+ // closed polygon
+ unsigned int type = (*it).flags & 0xf;
+ if (!type)
+ {
+ aiFace& face = *faces++;
+ if ((face.mNumIndices = (unsigned int)src.entries.size()))
+ {
+ face.mIndices = new unsigned int[face.mNumIndices];
+ for (unsigned int i = 0; i < face.mNumIndices;++i,++vertices)
+ {
+ const Surface::SurfaceEntry& entry = src.entries[i];
+ face.mIndices[i] = cur++;
+
+ // copy vertex positions
+ *vertices = object.vertices[entry.first] + object.translation;
+
+
+ // copy texture coordinates
+ if (uv)
+ {
+ uv->x = entry.second.x;
+ uv->y = entry.second.y;
+ ++uv;
+ }
+ }
+ }
+ }
+ else
+ {
+
+ it2 = (*it).entries.begin();
+
+ // either a closed or an unclosed line
+ register unsigned int tmp = (unsigned int)(*it).entries.size();
+ if (0x2 == type)--tmp;
+ for (unsigned int m = 0; m < tmp;++m)
+ {
+ aiFace& face = *faces++;
+
+ face.mNumIndices = 2;
+ face.mIndices = new unsigned int[2];
+ face.mIndices[0] = cur++;
+ face.mIndices[1] = cur++;
+
+ // copy vertex positions
+ *vertices++ = object.vertices[(*it2).first];
+
+ // copy texture coordinates
+ if (uv)
+ {
+ uv->x = (*it2).second.x;
+ uv->y = (*it2).second.y;
+ ++uv;
+ }
+
+
+ if (0x1 == type && tmp-1 == m)
+ {
+ // if this is a closed line repeat its beginning now
+ it2 = (*it).entries.begin();
+ }
+ else ++it2;
+
+ // second point
+ *vertices++ = object.vertices[(*it2).first];
+
+ if (uv)
+ {
+ uv->x = (*it2).second.x;
+ uv->y = (*it2).second.y;
+ ++uv;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Now apply catmull clark subdivision if necessary. We split meshes into
+ // materials which is not done by AC3D during smoothing, so we need to
+ // collect all meshes using the same material group.
+ if (object.subDiv) {
+ if (configEvalSubdivision) {
+ boost::scoped_ptr<Subdivider> div(Subdivider::Create(Subdivider::CATMULL_CLARKE));
+ DefaultLogger::get()->info("AC3D: Evaluating subdivision surface: "+object.name);
+
+ std::vector<aiMesh*> cpy(meshes.size()-oldm,NULL);
+ div->Subdivide(&meshes[oldm],cpy.size(),&cpy.front(),object.subDiv,true);
+ std::copy(cpy.begin(),cpy.end(),meshes.begin()+oldm);
+
+ // previous meshes are deleted vy Subdivide().
+ }
+ else {
+ DefaultLogger::get()->info("AC3D: Letting the subdivision surface untouched due to my configuration: "
+ +object.name);
+ }
+ }
+ }
+ }
+
+ if (object.name.length())
+ node->mName.Set(object.name);
+ else
+ {
+ // generate a name depending on the type of the node
+ switch (object.type)
+ {
+ case Object::Group:
+ node->mName.length = ::sprintf(node->mName.data,"ACGroup_%i",groups++);
+ break;
+ case Object::Poly:
+ node->mName.length = ::sprintf(node->mName.data,"ACPoly_%i",polys++);
+ break;
+ case Object::Light:
+ node->mName.length = ::sprintf(node->mName.data,"ACLight_%i",lights++);
+ break;
+
+ // there shouldn't be more than one world, but we don't care
+ case Object::World:
+ node->mName.length = ::sprintf(node->mName.data,"ACWorld_%i",worlds++);
+ break;
+ }
+ }
+
+
+ // setup the local transformation matrix of the object
+ // compute the transformation offset to the parent node
+ node->mTransformation = aiMatrix4x4 ( object.rotation );
+
+ if (object.type == Object::Group || !object.numRefs)
+ {
+ node->mTransformation.a4 = object.translation.x;
+ node->mTransformation.b4 = object.translation.y;
+ node->mTransformation.c4 = object.translation.z;
+ }
+
+ // add children to the object
+ if (object.children.size())
+ {
+ node->mNumChildren = (unsigned int)object.children.size();
+ node->mChildren = new aiNode*[node->mNumChildren];
+ for (unsigned int i = 0; i < node->mNumChildren;++i)
+ {
+ node->mChildren[i] = ConvertObjectSection(object.children[i],meshes,outMaterials,materials,node);
+ }
+ }
+
+ return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+void AC3DImporter::SetupProperties(const Importer* pImp)
+{
+ configSplitBFCull = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL,1) ? true : false;
+ configEvalSubdivision = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION,1) ? true : false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void AC3DImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open AC3D file " + pFile + ".");
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+
+ buffer = &mBuffer2[0];
+ mNumMeshes = 0;
+
+ lights = polys = worlds = groups = 0;
+
+ if (::strncmp(buffer,"AC3D",4)) {
+ throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found");
+ }
+
+ // print the file format version to the console
+ unsigned int version = HexDigitToDecimal( buffer[4] );
+ char msg[3];
+ ASSIMP_itoa10(msg,3,version);
+ DefaultLogger::get()->info(std::string("AC3D file format version: ") + msg);
+
+ std::vector<Material> materials;
+ materials.reserve(5);
+
+ std::vector<Object> rootObjects;
+ rootObjects.reserve(5);
+
+ std::vector<aiLight*> lights;
+ mLights = & lights;
+
+ while (GetNextLine())
+ {
+ if (TokenMatch(buffer,"MATERIAL",8))
+ {
+ materials.push_back(Material());
+ Material& mat = materials.back();
+
+ // manually parse the material ... sscanf would use the buldin atof ...
+ // Format: (name) rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f
+
+ AI_AC_SKIP_TO_NEXT_TOKEN();
+ if ('\"' == *buffer)
+ {
+ AI_AC_GET_STRING(mat.name);
+ AI_AC_SKIP_TO_NEXT_TOKEN();
+ }
+
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("rgb",3,3,&mat.rgb);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("amb",3,3,&mat.amb);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("emis",4,3,&mat.emis);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("spec",4,3,&mat.spec);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("shi",3,1,&mat.shin);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("trans",5,1,&mat.trans);
+ }
+ LoadObjectSection(rootObjects);
+ }
+
+ if (rootObjects.empty() || !mNumMeshes)
+ {
+ throw DeadlyImportError("AC3D: No meshes have been loaded");
+ }
+ if (materials.empty())
+ {
+ DefaultLogger::get()->warn("AC3D: No material has been found");
+ materials.push_back(Material());
+ }
+
+ mNumMeshes += (mNumMeshes>>2u) + 1;
+ std::vector<aiMesh*> meshes;
+ meshes.reserve(mNumMeshes);
+
+ std::vector<MaterialHelper*> omaterials;
+ materials.reserve(mNumMeshes);
+
+ // generate a dummy root if there are multiple objects on the top layer
+ Object* root;
+ if (1 == rootObjects.size())
+ root = &rootObjects[0];
+ else
+ {
+ root = new Object();
+ }
+
+ // now convert the imported stuff to our output data structure
+ pScene->mRootNode = ConvertObjectSection(*root,meshes,omaterials,materials);
+ if (1 != rootObjects.size())delete root;
+
+ if (!::strncmp( pScene->mRootNode->mName.data, "Node", 4))
+ pScene->mRootNode->mName.Set("<AC3DWorld>");
+
+ // copy meshes
+ if (meshes.empty())
+ {
+ throw DeadlyImportError("An unknown error occured during converting");
+ }
+ pScene->mNumMeshes = (unsigned int)meshes.size();
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ ::memcpy(pScene->mMeshes,&meshes[0],pScene->mNumMeshes*sizeof(void*));
+
+ // copy materials
+ pScene->mNumMaterials = (unsigned int)omaterials.size();
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+ ::memcpy(pScene->mMaterials,&omaterials[0],pScene->mNumMaterials*sizeof(void*));
+
+ // copy lights
+ pScene->mNumLights = (unsigned int)lights.size();
+ if (lights.size())
+ {
+ pScene->mLights = new aiLight*[lights.size()];
+ ::memcpy(pScene->mLights,&lights[0],lights.size()*sizeof(void*));
+ }
+}
+
+#endif //!defined ASSIMP_BUILD_NO_AC_IMPORTER
diff --git a/src/3rdparty/assimp/code/ACLoader.h b/src/3rdparty/assimp/code/ACLoader.h
new file mode 100644
index 000000000..d6c31b2d0
--- /dev/null
+++ b/src/3rdparty/assimp/code/ACLoader.h
@@ -0,0 +1,272 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file ACLoader.h
+ * @brief Declaration of the .ac importer class.
+ */
+#ifndef AI_AC3DLOADER_H_INCLUDED
+#define AI_AC3DLOADER_H_INCLUDED
+
+#include <vector>
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** AC3D (*.ac) importer class
+*/
+class AC3DImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ AC3DImporter();
+
+ /** Destructor, private as well */
+ ~AC3DImporter();
+
+
+ // Represents an AC3D material
+ struct Material
+ {
+ Material()
+ : rgb (0.6f,0.6f,0.6f)
+ , spec (1.f,1.f,1.f)
+ , shin (0.f)
+ , trans (0.f)
+ {}
+
+ // base color of the material
+ aiColor3D rgb;
+
+ // ambient color of the material
+ aiColor3D amb;
+
+ // emissive color of the material
+ aiColor3D emis;
+
+ // specular color of the material
+ aiColor3D spec;
+
+ // shininess exponent
+ float shin;
+
+ // transparency. 0 == opaque
+ float trans;
+
+ // name of the material. optional.
+ std::string name;
+ };
+
+ // Represents an AC3D surface
+ struct Surface
+ {
+ Surface()
+ : mat (0)
+ , flags (0)
+ {}
+
+ unsigned int mat,flags;
+
+ typedef std::pair<unsigned int, aiVector2D > SurfaceEntry;
+ std::vector< SurfaceEntry > entries;
+ };
+
+ // Represents an AC3D object
+ struct Object
+ {
+ Object()
+ : type (World)
+ , name( "" )
+ , children()
+ , texture( "" )
+ , texRepeat( 1.f, 1.f )
+ , texOffset( 0.0f, 0.0f )
+ , rotation()
+ , translation()
+ , vertices()
+ , surfaces()
+ , numRefs (0)
+ , subDiv (0)
+ , crease(0.0f)
+ {}
+
+ // Type description
+ enum Type
+ {
+ World = 0x0,
+ Poly = 0x1,
+ Group = 0x2,
+ Light = 0x4
+ } type;
+
+ // name of the object
+ std::string name;
+
+ // object children
+ std::vector<Object> children;
+
+ // texture to be assigned to all surfaces of the object
+ std::string texture;
+
+ // texture repat factors (scaling for all coordinates)
+ aiVector2D texRepeat, texOffset;
+
+ // rotation matrix
+ aiMatrix3x3 rotation;
+
+ // translation vector
+ aiVector3D translation;
+
+ // vertices
+ std::vector<aiVector3D> vertices;
+
+ // surfaces
+ std::vector<Surface> surfaces;
+
+ // number of indices (= num verts in verbose format)
+ unsigned int numRefs;
+
+ // number of subdivisions to be performed on the
+ // imported data
+ unsigned int subDiv;
+
+ // max angle limit for smoothing
+ float crease;
+ };
+
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details*/
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.*/
+ void SetupProperties(const Importer* pImp);
+
+private:
+
+ // -------------------------------------------------------------------
+ /** Get the next line from the file.
+ * @return false if the end of the file was reached*/
+ bool GetNextLine();
+
+ // -------------------------------------------------------------------
+ /** Load the object section. This method is called recursively to
+ * load subobjects, the method returns after a 'kids 0' was
+ * encountered.
+ * @objects List of output objects*/
+ void LoadObjectSection(std::vector<Object>& objects);
+
+ // -------------------------------------------------------------------
+ /** Convert all objects into meshes and nodes.
+ * @param object Current object to work on
+ * @param meshes Pointer to the list of output meshes
+ * @param outMaterials List of output materials
+ * @param materials Material list
+ * @param Scenegraph node for the object */
+ aiNode* ConvertObjectSection(Object& object,
+ std::vector<aiMesh*>& meshes,
+ std::vector<MaterialHelper*>& outMaterials,
+ const std::vector<Material>& materials,
+ aiNode* parent = NULL);
+
+ // -------------------------------------------------------------------
+ /** Convert a material
+ * @param object Current object
+ * @param matSrc Source material description
+ * @param matDest Destination material to be filled */
+ void ConvertMaterial(const Object& object,
+ const Material& matSrc,
+ MaterialHelper& matDest);
+
+private:
+
+
+ // points to the next data line
+ const char* buffer;
+
+ // Configuration option: if enabled, up to two meshes
+ // are generated per material: those faces who have
+ // their bf cull flags set are separated.
+ bool configSplitBFCull;
+
+ // Configuration switch: subdivision surfaces are only
+ // evaluated if the value is true.
+ bool configEvalSubdivision;
+
+ // counts how many objects we have in the tree.
+ // basing on this information we can find a
+ // good estimate how many meshes we'll have in the final scene.
+ unsigned int mNumMeshes;
+
+ // current list of light sources
+ std::vector<aiLight*>* mLights;
+
+ // name counters
+ unsigned int lights, groups, polys, worlds;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_AC3DIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/ASELoader.cpp b/src/3rdparty/assimp/code/ASELoader.cpp
new file mode 100644
index 000000000..82cdbf411
--- /dev/null
+++ b/src/3rdparty/assimp/code/ASELoader.cpp
@@ -0,0 +1,1302 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file ASELoader.cpp
+ * @brief Implementation of the ASE importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
+
+// internal headers
+#include "ASELoader.h"
+#include "MaterialSystem.h"
+#include "StringComparison.h"
+#include "SkeletonMeshBuilder.h"
+#include "TargetAnimation.h"
+
+// utilities
+#include "fast_atof.h"
+
+using namespace Assimp;
+using namespace Assimp::ASE;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ASEImporter::ASEImporter() : mParser(0), mBuffer(0), pcScene(0), configRecomputeNormals(false)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ASEImporter::~ASEImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const
+{
+ // check file extension
+ const std::string extension = GetExtension(pFile);
+
+ if ( extension == "ase" || extension == "ask")
+ return true;
+
+ if ((!extension.length() || cs) && pIOHandler) {
+ const char* tokens[] = {"*3dsmax_asciiexport"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ASEImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("ase");
+ extensions.insert("ask");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration options
+void ASEImporter::SetupProperties(const Importer* pImp)
+{
+ configRecomputeNormals = (pImp->GetPropertyInteger(
+ AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS,1) ? true : false);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void ASEImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open ASE file " + pFile + ".");
+ }
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+
+ this->mBuffer = &mBuffer2[0];
+ this->pcScene = pScene;
+
+ // ------------------------------------------------------------------
+ // Guess the file format by looking at the extension
+ // ASC is considered to be the older format 110,
+ // ASE is the actual version 200 (that is currently written by max)
+ // ------------------------------------------------------------------
+ unsigned int defaultFormat;
+ std::string::size_type s = pFile.length()-1;
+ switch (pFile.c_str()[s]) {
+
+ case 'C':
+ case 'c':
+ defaultFormat = AI_ASE_OLD_FILE_FORMAT;
+ break;
+ default:
+ defaultFormat = AI_ASE_NEW_FILE_FORMAT;
+ };
+
+ // Construct an ASE parser and parse the file
+ ASE::Parser parser(mBuffer,defaultFormat);
+ mParser = &parser;
+ mParser->Parse();
+
+ //------------------------------------------------------------------
+ // Check whether we god at least one mesh. If we did - generate
+ // materials and copy meshes.
+ // ------------------------------------------------------------------
+ if ( !mParser->m_vMeshes.empty()) {
+
+ // If absolutely no material has been loaded from the file
+ // we need to generate a default material
+ GenerateDefaultMaterial();
+
+ // process all meshes
+ bool tookNormals = false;
+ std::vector<aiMesh*> avOutMeshes;
+ avOutMeshes.reserve(mParser->m_vMeshes.size()*2);
+ for (std::vector<ASE::Mesh>::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) {
+ if ((*i).bSkip) {
+ continue;
+ }
+ BuildUniqueRepresentation(*i);
+
+ // Need to generate proper vertex normals if necessary
+ if (GenerateNormals(*i)) {
+ tookNormals = true;
+ }
+
+ // Convert all meshes to aiMesh objects
+ ConvertMeshes(*i,avOutMeshes);
+ }
+ if (tookNormals) {
+ DefaultLogger::get()->debug("ASE: Taking normals from the file. Use "
+ "the AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS setting if you "
+ "experience problems");
+ }
+
+ // Now build the output mesh list. Remove dummies
+ pScene->mNumMeshes = (unsigned int)avOutMeshes.size();
+ aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ for (std::vector<aiMesh*>::const_iterator i = avOutMeshes.begin();i != avOutMeshes.end();++i) {
+ if (!(*i)->mNumFaces) {
+ continue;
+ }
+ *pp++ = *i;
+ }
+ pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes);
+
+ // Build final material indices (remove submaterials and setup
+ // the final list)
+ BuildMaterialIndices();
+ }
+
+ // ------------------------------------------------------------------
+ // Copy all scene graph nodes - lights, cameras, dummies and meshes
+ // into one huge list.
+ //------------------------------------------------------------------
+ std::vector<BaseNode*> nodes;
+ nodes.reserve(mParser->m_vMeshes.size() +mParser->m_vLights.size()
+ + mParser->m_vCameras.size() + mParser->m_vDummies.size());
+
+ // Lights
+ for (std::vector<ASE::Light>::iterator it = mParser->m_vLights.begin(),
+ end = mParser->m_vLights.end();it != end; ++it)nodes.push_back(&(*it));
+ // Cameras
+ for (std::vector<ASE::Camera>::iterator it = mParser->m_vCameras.begin(),
+ end = mParser->m_vCameras.end();it != end; ++it)nodes.push_back(&(*it));
+ // Meshes
+ for (std::vector<ASE::Mesh>::iterator it = mParser->m_vMeshes.begin(),
+ end = mParser->m_vMeshes.end();it != end; ++it)nodes.push_back(&(*it));
+ // Dummies
+ for (std::vector<ASE::Dummy>::iterator it = mParser->m_vDummies.begin(),
+ end = mParser->m_vDummies.end();it != end; ++it)nodes.push_back(&(*it));
+
+ // build the final node graph
+ BuildNodes(nodes);
+
+ // build output animations
+ BuildAnimations(nodes);
+
+ // build output cameras
+ BuildCameras();
+
+ // build output lights
+ BuildLights();
+
+ // ------------------------------------------------------------------
+ // If we have no meshes use the SkeletonMeshBuilder helper class
+ // to build a mesh for the animation skeleton
+ // FIXME: very strange results
+ // ------------------------------------------------------------------
+ if (!pScene->mNumMeshes) {
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ SkeletonMeshBuilder skeleton(pScene);
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void ASEImporter::GenerateDefaultMaterial()
+{
+ ai_assert(NULL != mParser);
+
+ bool bHas = false;
+ for (std::vector<ASE::Mesh>::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) {
+ if ((*i).bSkip)continue;
+ if (ASE::Face::DEFAULT_MATINDEX == (*i).iMaterialIndex) {
+ (*i).iMaterialIndex = (unsigned int)mParser->m_vMaterials.size();
+ bHas = true;
+ }
+ }
+ if (bHas || mParser->m_vMaterials.empty()) {
+ // add a simple material without submaterials to the parser's list
+ mParser->m_vMaterials.push_back ( ASE::Material() );
+ ASE::Material& mat = mParser->m_vMaterials.back();
+
+ mat.mDiffuse = aiColor3D(0.6f,0.6f,0.6f);
+ mat.mSpecular = aiColor3D(1.0f,1.0f,1.0f);
+ mat.mAmbient = aiColor3D(0.05f,0.05f,0.05f);
+ mat.mShading = Discreet3DS::Gouraud;
+ mat.mName = AI_DEFAULT_MATERIAL_NAME;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ASEImporter::BuildAnimations(const std::vector<BaseNode*>& nodes)
+{
+ // check whether we have at least one mesh which has animations
+ std::vector<ASE::BaseNode*>::const_iterator i = nodes.begin();
+ unsigned int iNum = 0;
+ for (;i != nodes.end();++i) {
+
+ // TODO: Implement Bezier & TCB support
+ if ((*i)->mAnim.mPositionType != ASE::Animation::TRACK) {
+ DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. "
+ "This is not supported.");
+ }
+ if ((*i)->mAnim.mRotationType != ASE::Animation::TRACK) {
+ DefaultLogger::get()->warn("ASE: Rotation controller uses Bezier/TCB keys. "
+ "This is not supported.");
+ }
+ if ((*i)->mAnim.mScalingType != ASE::Animation::TRACK) {
+ DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. "
+ "This is not supported.");
+ }
+
+ // We compare against 1 here - firstly one key is not
+ // really an animation and secondly MAX writes dummies
+ // that represent the node transformation.
+ if ((*i)->mAnim.akeyPositions.size()>1 || (*i)->mAnim.akeyRotations.size()>1 || (*i)->mAnim.akeyScaling.size()>1){
+ ++iNum;
+ }
+ if ((*i)->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( (*i)->mTargetPosition.x )) {
+ ++iNum;
+ }
+ }
+ if (iNum) {
+ // Generate a new animation channel and setup everything for it
+ pcScene->mNumAnimations = 1;
+ pcScene->mAnimations = new aiAnimation*[1];
+ aiAnimation* pcAnim = pcScene->mAnimations[0] = new aiAnimation();
+ pcAnim->mNumChannels = iNum;
+ pcAnim->mChannels = new aiNodeAnim*[iNum];
+ pcAnim->mTicksPerSecond = mParser->iFrameSpeed * mParser->iTicksPerFrame;
+
+ iNum = 0;
+
+ // Now iterate through all meshes and collect all data we can find
+ for (i = nodes.begin();i != nodes.end();++i) {
+
+ ASE::BaseNode* me = *i;
+ if ( me->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( me->mTargetPosition.x )) {
+ // Generate an extra channel for the camera/light target.
+ // BuildNodes() does also generate an extra node, named
+ // <baseName>.Target.
+ aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim();
+ nd->mNodeName.Set(me->mName + ".Target");
+
+ // If there is no input position channel we will need
+ // to supply the default position from the node's
+ // local transformation matrix.
+ /*TargetAnimationHelper helper;
+ if (me->mAnim.akeyPositions.empty())
+ {
+ aiMatrix4x4& mat = (*i)->mTransform;
+ helper.SetFixedMainAnimationChannel(aiVector3D(
+ mat.a4, mat.b4, mat.c4));
+ }
+ else helper.SetMainAnimationChannel (&me->mAnim.akeyPositions);
+ helper.SetTargetAnimationChannel (&me->mTargetAnim.akeyPositions);
+
+ helper.Process(&me->mTargetAnim.akeyPositions);*/
+
+ // Allocate the key array and fill it
+ nd->mNumPositionKeys = (unsigned int) me->mTargetAnim.akeyPositions.size();
+ nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
+
+ ::memcpy(nd->mPositionKeys,&me->mTargetAnim.akeyPositions[0],
+ nd->mNumPositionKeys * sizeof(aiVectorKey));
+ }
+
+ if (me->mAnim.akeyPositions.size() > 1 || me->mAnim.akeyRotations.size() > 1 || me->mAnim.akeyScaling.size() > 1) {
+ // Begin a new node animation channel for this node
+ aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim();
+ nd->mNodeName.Set(me->mName);
+
+ // copy position keys
+ if (me->mAnim.akeyPositions.size() > 1 )
+ {
+ // Allocate the key array and fill it
+ nd->mNumPositionKeys = (unsigned int) me->mAnim.akeyPositions.size();
+ nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
+
+ ::memcpy(nd->mPositionKeys,&me->mAnim.akeyPositions[0],
+ nd->mNumPositionKeys * sizeof(aiVectorKey));
+ }
+ // copy rotation keys
+ if (me->mAnim.akeyRotations.size() > 1 ) {
+ // Allocate the key array and fill it
+ nd->mNumRotationKeys = (unsigned int) me->mAnim.akeyRotations.size();
+ nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys];
+
+ // --------------------------------------------------------------------
+ // Rotation keys are offsets to the previous keys.
+ // We have the quaternion representations of all
+ // of them, so we just need to concatenate all
+ // (unit-length) quaternions to get the absolute
+ // rotations.
+ // Rotation keys are ABSOLUTE for older files
+ // --------------------------------------------------------------------
+
+ aiQuaternion cur;
+ for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) {
+ aiQuatKey q = me->mAnim.akeyRotations[a];
+
+ if (mParser->iFileFormat > 110) {
+ cur = (a ? cur*q.mValue : q.mValue);
+ q.mValue = cur.Normalize();
+ }
+ nd->mRotationKeys[a] = q;
+
+ // need this to get to Assimp quaternion conventions
+ nd->mRotationKeys[a].mValue.w *= -1.f;
+ }
+ }
+ // copy scaling keys
+ if (me->mAnim.akeyScaling.size() > 1 ) {
+ // Allocate the key array and fill it
+ nd->mNumScalingKeys = (unsigned int) me->mAnim.akeyScaling.size();
+ nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys];
+
+ ::memcpy(nd->mScalingKeys,&me->mAnim.akeyScaling[0],
+ nd->mNumScalingKeys * sizeof(aiVectorKey));
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build output cameras
+void ASEImporter::BuildCameras()
+{
+ if (!mParser->m_vCameras.empty()) {
+ pcScene->mNumCameras = (unsigned int)mParser->m_vCameras.size();
+ pcScene->mCameras = new aiCamera*[pcScene->mNumCameras];
+
+ for (unsigned int i = 0; i < pcScene->mNumCameras;++i) {
+ aiCamera* out = pcScene->mCameras[i] = new aiCamera();
+ ASE::Camera& in = mParser->m_vCameras[i];
+
+ // copy members
+ out->mClipPlaneFar = in.mFar;
+ out->mClipPlaneNear = (in.mNear ? in.mNear : 0.1f);
+ out->mHorizontalFOV = in.mFOV;
+
+ out->mName.Set(in.mName);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build output lights
+void ASEImporter::BuildLights()
+{
+ if (!mParser->m_vLights.empty()) {
+ pcScene->mNumLights = (unsigned int)mParser->m_vLights.size();
+ pcScene->mLights = new aiLight*[pcScene->mNumLights];
+
+ for (unsigned int i = 0; i < pcScene->mNumLights;++i) {
+ aiLight* out = pcScene->mLights[i] = new aiLight();
+ ASE::Light& in = mParser->m_vLights[i];
+
+ // The direction is encoded in the transformation matrix of the node.
+ // In 3DS MAX the light source points into negative Z direction if
+ // the node transformation is the identity.
+ out->mDirection = aiVector3D(0.f,0.f,-1.f);
+
+ out->mName.Set(in.mName);
+ switch (in.mLightType)
+ {
+ case ASE::Light::TARGET:
+ out->mType = aiLightSource_SPOT;
+ out->mAngleInnerCone = AI_DEG_TO_RAD(in.mAngle);
+ out->mAngleOuterCone = (in.mFalloff ? AI_DEG_TO_RAD(in.mFalloff) : out->mAngleInnerCone);
+ break;
+
+ case ASE::Light::DIRECTIONAL:
+ out->mType = aiLightSource_DIRECTIONAL;
+ break;
+
+ default:
+ //case ASE::Light::OMNI:
+ out->mType = aiLightSource_POINT;
+ break;
+ };
+ out->mColorDiffuse = out->mColorSpecular = in.mColor * in.mIntensity;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ASEImporter::AddNodes(const std::vector<BaseNode*>& nodes,
+ aiNode* pcParent,const char* szName)
+{
+ aiMatrix4x4 m;
+ AddNodes(nodes,pcParent,szName,m);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Add meshes to a given node
+void ASEImporter::AddMeshes(const ASE::BaseNode* snode,aiNode* node)
+{
+ for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) {
+ // Get the name of the mesh (the mesh instance has been temporarily stored in the third vertex color)
+ const aiMesh* pcMesh = pcScene->mMeshes[i];
+ const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2];
+
+ if (mesh == snode) {
+ ++node->mNumMeshes;
+ }
+ }
+
+ if (node->mNumMeshes) {
+ node->mMeshes = new unsigned int[node->mNumMeshes];
+ for (unsigned int i = 0, p = 0; i < pcScene->mNumMeshes;++i) {
+
+ const aiMesh* pcMesh = pcScene->mMeshes[i];
+ const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2];
+ if (mesh == snode) {
+ node->mMeshes[p++] = i;
+
+ // Transform all vertices of the mesh back into their local space ->
+ // at the moment they are pretransformed
+ aiMatrix4x4 m = mesh->mTransform;
+ m.Inverse();
+
+ aiVector3D* pvCurPtr = pcMesh->mVertices;
+ const aiVector3D* pvEndPtr = pvCurPtr + pcMesh->mNumVertices;
+ while (pvCurPtr != pvEndPtr) {
+ *pvCurPtr = m * (*pvCurPtr);
+ pvCurPtr++;
+ }
+
+ // Do the same for the normal vectors, if we have them.
+ // As always, inverse transpose.
+ if (pcMesh->mNormals) {
+ aiMatrix3x3 m3 = aiMatrix3x3( mesh->mTransform );
+ m3.Transpose();
+
+ pvCurPtr = pcMesh->mNormals;
+ pvEndPtr = pvCurPtr + pcMesh->mNumVertices;
+ while (pvCurPtr != pvEndPtr) {
+ *pvCurPtr = m3 * (*pvCurPtr);
+ pvCurPtr++;
+ }
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Add child nodes to a given parent node
+void ASEImporter::AddNodes (const std::vector<BaseNode*>& nodes,
+ aiNode* pcParent, const char* szName,
+ const aiMatrix4x4& mat)
+{
+ const size_t len = szName ? ::strlen(szName) : 0;
+ ai_assert(4 <= AI_MAX_NUMBER_OF_COLOR_SETS);
+
+ // Receives child nodes for the pcParent node
+ std::vector<aiNode*> apcNodes;
+
+ // Now iterate through all nodes in the scene and search for one
+ // which has *us* as parent.
+ for (std::vector<BaseNode*>::const_iterator it = nodes.begin(), end = nodes.end(); it != end; ++it) {
+ const BaseNode* snode = *it;
+ if (szName) {
+ if (len != snode->mParent.length() || ::strcmp(szName,snode->mParent.c_str()))
+ continue;
+ }
+ else if (snode->mParent.length())
+ continue;
+
+ (*it)->mProcessed = true;
+
+ // Allocate a new node and add it to the output data structure
+ apcNodes.push_back(new aiNode());
+ aiNode* node = apcNodes.back();
+
+ node->mName.Set((snode->mName.length() ? snode->mName.c_str() : "Unnamed_Node"));
+ node->mParent = pcParent;
+
+ // Setup the transformation matrix of the node
+ aiMatrix4x4 mParentAdjust = mat;
+ mParentAdjust.Inverse();
+ node->mTransformation = mParentAdjust*snode->mTransform;
+
+ // Add sub nodes - prevent stack overflow due to recursive parenting
+ if (node->mName != node->mParent->mName) {
+ AddNodes(nodes,node,node->mName.data,snode->mTransform);
+ }
+
+ // Further processing depends on the type of the node
+ if (snode->mType == ASE::BaseNode::Mesh) {
+ // If the type of this node is "Mesh" we need to search
+ // the list of output meshes in the data structure for
+ // all those that belonged to this node once. This is
+ // slightly inconvinient here and a better solution should
+ // be used when this code is refactored next.
+ AddMeshes(snode,node);
+ }
+ else if (is_not_qnan( snode->mTargetPosition.x )) {
+ // If this is a target camera or light we generate a small
+ // child node which marks the position of the camera
+ // target (the direction information is contained in *this*
+ // node's animation track but the exact target position
+ // would be lost otherwise)
+ if (!node->mNumChildren) {
+ node->mChildren = new aiNode*[1];
+ }
+
+ aiNode* nd = new aiNode();
+
+ nd->mName.Set ( snode->mName + ".Target" );
+
+ nd->mTransformation.a4 = snode->mTargetPosition.x - snode->mTransform.a4;
+ nd->mTransformation.b4 = snode->mTargetPosition.y - snode->mTransform.b4;
+ nd->mTransformation.c4 = snode->mTargetPosition.z - snode->mTransform.c4;
+
+ nd->mParent = node;
+
+ // The .Target node is always the first child node
+ for (unsigned int m = 0; m < node->mNumChildren;++m)
+ node->mChildren[m+1] = node->mChildren[m];
+
+ node->mChildren[0] = nd;
+ node->mNumChildren++;
+
+ // What we did is so great, it is at least worth a debug message
+ DefaultLogger::get()->debug("ASE: Generating separate target node ("+snode->mName+")");
+ }
+ }
+
+ // Allocate enough space for the child nodes
+ // We allocate one slot more in case this is a target camera/light
+ pcParent->mNumChildren = (unsigned int)apcNodes.size();
+ if (pcParent->mNumChildren) {
+ pcParent->mChildren = new aiNode*[apcNodes.size()+1 /* PLUS ONE !!! */];
+
+ // now build all nodes for our nice new children
+ for (unsigned int p = 0; p < apcNodes.size();++p)
+ pcParent->mChildren[p] = apcNodes[p];
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build the output node graph
+void ASEImporter::BuildNodes(std::vector<BaseNode*>& nodes) {
+ ai_assert(NULL != pcScene);
+
+ // allocate the one and only root node
+ aiNode* root = pcScene->mRootNode = new aiNode();
+ root->mName.Set("<ASERoot>");
+
+ // Setup the coordinate system transformation
+ pcScene->mRootNode->mNumChildren = 1;
+ pcScene->mRootNode->mChildren = new aiNode*[1];
+ aiNode* ch = pcScene->mRootNode->mChildren[0] = new aiNode();
+ ch->mParent = root;
+
+ // Change the transformation matrix of all nodes
+ for (std::vector<BaseNode*>::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) {
+ aiMatrix4x4& m = (*it)->mTransform;
+ m.Transpose(); // row-order vs column-order
+ }
+
+ // add all nodes
+ AddNodes(nodes,ch,NULL);
+
+ // now iterate through al nodes and find those that have not yet
+ // been added to the nodegraph (= their parent could not be recognized)
+ std::vector<const BaseNode*> aiList;
+ for (std::vector<BaseNode*>::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) {
+ if ((*it)->mProcessed) {
+ continue;
+ }
+
+ // check whether our parent is known
+ bool bKnowParent = false;
+
+ // search the list another time, starting *here* and try to find out whether
+ // there is a node that references *us* as a parent
+ for (std::vector<BaseNode*>::const_iterator it2 = nodes.begin();it2 != end; ++it2) {
+ if (it2 == it) {
+ continue;
+ }
+
+ if ((*it2)->mName == (*it)->mParent) {
+ bKnowParent = true;
+ break;
+ }
+ }
+ if (!bKnowParent) {
+ aiList.push_back(*it);
+ }
+ }
+
+ // Are there ane orphaned nodes?
+ if (!aiList.empty()) {
+ std::vector<aiNode*> apcNodes;
+ apcNodes.reserve(aiList.size() + pcScene->mRootNode->mNumChildren);
+
+ for (unsigned int i = 0; i < pcScene->mRootNode->mNumChildren;++i)
+ apcNodes.push_back(pcScene->mRootNode->mChildren[i]);
+
+ delete[] pcScene->mRootNode->mChildren;
+ for (std::vector<const BaseNode*>::/*const_*/iterator i = aiList.begin();i != aiList.end();++i) {
+ const ASE::BaseNode* src = *i;
+
+ // The parent is not known, so we can assume that we must add
+ // this node to the root node of the whole scene
+ aiNode* pcNode = new aiNode();
+ pcNode->mParent = pcScene->mRootNode;
+ pcNode->mName.Set(src->mName);
+ AddMeshes(src,pcNode);
+ AddNodes(nodes,pcNode,pcNode->mName.data);
+ apcNodes.push_back(pcNode);
+ }
+
+ // Regenerate our output array
+ pcScene->mRootNode->mChildren = new aiNode*[apcNodes.size()];
+ for (unsigned int i = 0; i < apcNodes.size();++i)
+ pcScene->mRootNode->mChildren[i] = apcNodes[i];
+
+ pcScene->mRootNode->mNumChildren = (unsigned int)apcNodes.size();
+ }
+
+ // Reset the third color set to NULL - we used this field to store a temporary pointer
+ for (unsigned int i = 0; i < pcScene->mNumMeshes;++i)
+ pcScene->mMeshes[i]->mColors[2] = NULL;
+
+ // The root node should not have at least one child or the file is valid
+ if (!pcScene->mRootNode->mNumChildren) {
+ throw DeadlyImportError("ASE: No nodes loaded. The file is either empty or corrupt");
+ }
+
+ // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
+ pcScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
+ 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert the imported data to the internal verbose representation
+void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh) {
+ // allocate output storage
+ std::vector<aiVector3D> mPositions;
+ std::vector<aiVector3D> amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ std::vector<aiColor4D> mVertexColors;
+ std::vector<aiVector3D> mNormals;
+ std::vector<BoneVertex> mBoneVertices;
+
+ unsigned int iSize = (unsigned int)mesh.mFaces.size() * 3;
+ mPositions.resize(iSize);
+
+ // optional texture coordinates
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) {
+ if (!mesh.amTexCoords[i].empty()) {
+ amTexCoords[i].resize(iSize);
+ }
+ }
+ // optional vertex colors
+ if (!mesh.mVertexColors.empty()) {
+ mVertexColors.resize(iSize);
+ }
+
+ // optional vertex normals (vertex normals can simply be copied)
+ if (!mesh.mNormals.empty()) {
+ mNormals.resize(iSize);
+ }
+ // bone vertices. There is no need to change the bone list
+ if (!mesh.mBoneVertices.empty()) {
+ mBoneVertices.resize(iSize);
+ }
+
+ // iterate through all faces in the mesh
+ unsigned int iCurrent = 0, fi = 0;
+ for (std::vector<ASE::Face>::iterator i = mesh.mFaces.begin();i != mesh.mFaces.end();++i,++fi) {
+ for (unsigned int n = 0; n < 3;++n,++iCurrent)
+ {
+ mPositions[iCurrent] = mesh.mPositions[(*i).mIndices[n]];
+
+ // add texture coordinates
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
+ if (mesh.amTexCoords[c].empty())break;
+ amTexCoords[c][iCurrent] = mesh.amTexCoords[c][(*i).amUVIndices[c][n]];
+ }
+ // add vertex colors
+ if (!mesh.mVertexColors.empty()) {
+ mVertexColors[iCurrent] = mesh.mVertexColors[(*i).mColorIndices[n]];
+ }
+ // add normal vectors
+ if (!mesh.mNormals.empty()) {
+ mNormals[iCurrent] = mesh.mNormals[fi*3+n];
+ mNormals[iCurrent].Normalize();
+ }
+
+ // handle bone vertices
+ if ((*i).mIndices[n] < mesh.mBoneVertices.size()) {
+ // (sometimes this will cause bone verts to be duplicated
+ // however, I' quite sure Schrompf' JoinVerticesStep
+ // will fix that again ...)
+ mBoneVertices[iCurrent] = mesh.mBoneVertices[(*i).mIndices[n]];
+ }
+ (*i).mIndices[n] = iCurrent;
+ }
+ }
+
+ // replace the old arrays
+ mesh.mNormals = mNormals;
+ mesh.mPositions = mPositions;
+ mesh.mVertexColors = mVertexColors;
+
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
+ mesh.amTexCoords[c] = amTexCoords[c];
+}
+
+// ------------------------------------------------------------------------------------------------
+// Copy a texture from the ASE structs to the output material
+void CopyASETexture(MaterialHelper& mat, ASE::Texture& texture, aiTextureType type)
+{
+ // Setup the texture name
+ aiString tex;
+ tex.Set( texture.mMapName);
+ mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0));
+
+ // Setup the texture blend factor
+ if (is_not_qnan(texture.mTextureBlend))
+ mat.AddProperty<float>( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0));
+
+ // Setup texture UV transformations
+ mat.AddProperty<float>(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert from ASE material to output material
+void ASEImporter::ConvertMaterial(ASE::Material& mat)
+{
+ // LARGE TODO: Much code her is copied from 3DS ... join them maybe?
+
+ // Allocate the output material
+ mat.pcInstance = new MaterialHelper();
+
+ // At first add the base ambient color of the
+ // scene to the material
+ mat.mAmbient.r += mParser->m_clrAmbient.r;
+ mat.mAmbient.g += mParser->m_clrAmbient.g;
+ mat.mAmbient.b += mParser->m_clrAmbient.b;
+
+ aiString name;
+ name.Set( mat.mName);
+ mat.pcInstance->AddProperty( &name, AI_MATKEY_NAME);
+
+ // material colors
+ mat.pcInstance->AddProperty( &mat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
+ mat.pcInstance->AddProperty( &mat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mat.pcInstance->AddProperty( &mat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
+ mat.pcInstance->AddProperty( &mat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
+
+ // shininess
+ if (0.0f != mat.mSpecularExponent && 0.0f != mat.mShininessStrength)
+ {
+ mat.pcInstance->AddProperty( &mat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
+ mat.pcInstance->AddProperty( &mat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
+ }
+ // If there is no shininess, we can disable phong lighting
+ else if (D3DS::Discreet3DS::Metal == mat.mShading ||
+ D3DS::Discreet3DS::Phong == mat.mShading ||
+ D3DS::Discreet3DS::Blinn == mat.mShading)
+ {
+ mat.mShading = D3DS::Discreet3DS::Gouraud;
+ }
+
+ // opacity
+ mat.pcInstance->AddProperty<float>( &mat.mTransparency,1,AI_MATKEY_OPACITY);
+
+ // Two sided rendering?
+ if (mat.mTwoSided)
+ {
+ int i = 1;
+ mat.pcInstance->AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
+ }
+
+ // shading mode
+ aiShadingMode eShading = aiShadingMode_NoShading;
+ switch (mat.mShading)
+ {
+ case D3DS::Discreet3DS::Flat:
+ eShading = aiShadingMode_Flat; break;
+ case D3DS::Discreet3DS::Phong :
+ eShading = aiShadingMode_Phong; break;
+ case D3DS::Discreet3DS::Blinn :
+ eShading = aiShadingMode_Blinn; break;
+
+ // I don't know what "Wire" shading should be,
+ // assume it is simple lambertian diffuse (L dot N) shading
+ case D3DS::Discreet3DS::Wire:
+ {
+ // set the wireframe flag
+ unsigned int iWire = 1;
+ mat.pcInstance->AddProperty<int>( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME);
+ }
+ case D3DS::Discreet3DS::Gouraud:
+ eShading = aiShadingMode_Gouraud; break;
+ case D3DS::Discreet3DS::Metal :
+ eShading = aiShadingMode_CookTorrance; break;
+ }
+ mat.pcInstance->AddProperty<int>( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL);
+
+ // DIFFUSE texture
+ if ( mat.sTexDiffuse.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexDiffuse, aiTextureType_DIFFUSE);
+
+ // SPECULAR texture
+ if ( mat.sTexSpecular.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexSpecular, aiTextureType_SPECULAR);
+
+ // AMBIENT texture
+ if ( mat.sTexAmbient.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexAmbient, aiTextureType_AMBIENT);
+
+ // OPACITY texture
+ if ( mat.sTexOpacity.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexOpacity, aiTextureType_OPACITY);
+
+ // EMISSIVE texture
+ if ( mat.sTexEmissive.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexEmissive, aiTextureType_EMISSIVE);
+
+ // BUMP texture
+ if ( mat.sTexBump.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexBump, aiTextureType_HEIGHT);
+
+ // SHININESS texture
+ if ( mat.sTexShininess.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexShininess, aiTextureType_SHININESS);
+
+ // store the name of the material itself, too
+ if ( mat.mName.length() > 0) {
+ aiString tex;tex.Set( mat.mName);
+ mat.pcInstance->AddProperty( &tex, AI_MATKEY_NAME);
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build output meshes
+void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOutMeshes)
+{
+ // validate the material index of the mesh
+ if (mesh.iMaterialIndex >= mParser->m_vMaterials.size()) {
+ mesh.iMaterialIndex = (unsigned int)mParser->m_vMaterials.size()-1;
+ DefaultLogger::get()->warn("Material index is out of range");
+ }
+
+ // If the material the mesh is assigned to is consisting of submeshes, split it
+ if (!mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials.empty()) {
+ std::vector<ASE::Material> vSubMaterials = mParser->
+ m_vMaterials[mesh.iMaterialIndex].avSubMaterials;
+
+ std::vector<unsigned int>* aiSplit = new std::vector<unsigned int>[vSubMaterials.size()];
+
+ // build a list of all faces per submaterial
+ for (unsigned int i = 0; i < mesh.mFaces.size();++i) {
+ // check range
+ if (mesh.mFaces[i].iMaterial >= vSubMaterials.size()) {
+ DefaultLogger::get()->warn("Submaterial index is out of range");
+
+ // use the last material instead
+ aiSplit[vSubMaterials.size()-1].push_back(i);
+ }
+ else aiSplit[mesh.mFaces[i].iMaterial].push_back(i);
+ }
+
+ // now generate submeshes
+ for (unsigned int p = 0; p < vSubMaterials.size();++p) {
+ if (!aiSplit[p].empty()) {
+
+ aiMesh* p_pcOut = new aiMesh();
+ p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // let the sub material index
+ p_pcOut->mMaterialIndex = p;
+
+ // we will need this material
+ mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials[p].bNeed = true;
+
+ // store the real index here ... color channel 3
+ p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex;
+
+ // store a pointer to the mesh in color channel 2
+ p_pcOut->mColors[2] = (aiColor4D*) &mesh;
+ avOutMeshes.push_back(p_pcOut);
+
+ // convert vertices
+ p_pcOut->mNumVertices = (unsigned int)aiSplit[p].size()*3;
+ p_pcOut->mNumFaces = (unsigned int)aiSplit[p].size();
+
+ // receive output vertex weights
+ std::vector<std::pair<unsigned int, float> > *avOutputBones = NULL;
+ if (!mesh.mBones.empty()) {
+ avOutputBones = new std::vector<std::pair<unsigned int, float> >[mesh.mBones.size()];
+ }
+
+ // allocate enough storage for faces
+ p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces];
+
+ unsigned int iBase = 0,iIndex;
+ if (p_pcOut->mNumVertices) {
+ p_pcOut->mVertices = new aiVector3D[p_pcOut->mNumVertices];
+ p_pcOut->mNormals = new aiVector3D[p_pcOut->mNumVertices];
+ for (unsigned int q = 0; q < aiSplit[p].size();++q) {
+
+ iIndex = aiSplit[p][q];
+
+ p_pcOut->mFaces[q].mIndices = new unsigned int[3];
+ p_pcOut->mFaces[q].mNumIndices = 3;
+
+ for (unsigned int t = 0; t < 3;++t, ++iBase) {
+ const uint32_t iIndex2 = mesh.mFaces[iIndex].mIndices[t];
+
+ p_pcOut->mVertices[iBase] = mesh.mPositions [iIndex2];
+ p_pcOut->mNormals [iBase] = mesh.mNormals [iIndex2];
+
+ // convert bones, if existing
+ if (!mesh.mBones.empty()) {
+ // check whether there is a vertex weight for this vertex index
+ if (iIndex2 < mesh.mBoneVertices.size()) {
+
+ for (std::vector<std::pair<int,float> >::const_iterator
+ blubb = mesh.mBoneVertices[iIndex2].mBoneWeights.begin();
+ blubb != mesh.mBoneVertices[iIndex2].mBoneWeights.end();++blubb) {
+
+ // NOTE: illegal cases have already been filtered out
+ avOutputBones[(*blubb).first].push_back(std::pair<unsigned int, float>(
+ iBase,(*blubb).second));
+ }
+ }
+ }
+ p_pcOut->mFaces[q].mIndices[t] = iBase;
+ }
+ }
+ }
+ // convert texture coordinates (up to AI_MAX_NUMBER_OF_TEXTURECOORDS sets supported)
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
+ if (!mesh.amTexCoords[c].empty())
+ {
+ p_pcOut->mTextureCoords[c] = new aiVector3D[p_pcOut->mNumVertices];
+ iBase = 0;
+ for (unsigned int q = 0; q < aiSplit[p].size();++q) {
+ iIndex = aiSplit[p][q];
+ for (unsigned int t = 0; t < 3;++t) {
+ p_pcOut->mTextureCoords[c][iBase++] = mesh.amTexCoords[c][mesh.mFaces[iIndex].mIndices[t]];
+ }
+ }
+ // Setup the number of valid vertex components
+ p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c];
+ }
+ }
+
+ // Convert vertex colors (only one set supported)
+ if (!mesh.mVertexColors.empty()){
+ p_pcOut->mColors[0] = new aiColor4D[p_pcOut->mNumVertices];
+ iBase = 0;
+ for (unsigned int q = 0; q < aiSplit[p].size();++q) {
+ iIndex = aiSplit[p][q];
+ for (unsigned int t = 0; t < 3;++t) {
+ p_pcOut->mColors[0][iBase++] = mesh.mVertexColors[mesh.mFaces[iIndex].mIndices[t]];
+ }
+ }
+ }
+ // Copy bones
+ if (!mesh.mBones.empty()) {
+ p_pcOut->mNumBones = 0;
+ for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock)
+ if (!avOutputBones[mrspock].empty())p_pcOut->mNumBones++;
+
+ p_pcOut->mBones = new aiBone* [ p_pcOut->mNumBones ];
+ aiBone** pcBone = p_pcOut->mBones;
+ for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock)
+ {
+ if (!avOutputBones[mrspock].empty()) {
+ // we will need this bone. add it to the output mesh and
+ // add all per-vertex weights
+ aiBone* pc = *pcBone = new aiBone();
+ pc->mName.Set(mesh.mBones[mrspock].mName);
+
+ pc->mNumWeights = (unsigned int)avOutputBones[mrspock].size();
+ pc->mWeights = new aiVertexWeight[pc->mNumWeights];
+
+ for (unsigned int captainkirk = 0; captainkirk < pc->mNumWeights;++captainkirk)
+ {
+ const std::pair<unsigned int,float>& ref = avOutputBones[mrspock][captainkirk];
+ pc->mWeights[captainkirk].mVertexId = ref.first;
+ pc->mWeights[captainkirk].mWeight = ref.second;
+ }
+ ++pcBone;
+ }
+ }
+ // delete allocated storage
+ delete[] avOutputBones;
+ }
+ }
+ }
+ // delete storage
+ delete[] aiSplit;
+ }
+ else
+ {
+ // Otherwise we can simply copy the data to one output mesh
+ // This codepath needs less memory and uses fast memcpy()s
+ // to do the actual copying. So I think it is worth the
+ // effort here.
+
+ aiMesh* p_pcOut = new aiMesh();
+ p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // set an empty sub material index
+ p_pcOut->mMaterialIndex = ASE::Face::DEFAULT_MATINDEX;
+ mParser->m_vMaterials[mesh.iMaterialIndex].bNeed = true;
+
+ // store the real index here ... in color channel 3
+ p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex;
+
+ // store a pointer to the mesh in color channel 2
+ p_pcOut->mColors[2] = (aiColor4D*) &mesh;
+ avOutMeshes.push_back(p_pcOut);
+
+ // If the mesh hasn't faces or vertices, there are two cases
+ // possible: 1. the model is invalid. 2. This is a dummy
+ // helper object which we are going to remove later ...
+ if (mesh.mFaces.empty() || mesh.mPositions.empty()) {
+ return;
+ }
+
+ // convert vertices
+ p_pcOut->mNumVertices = (unsigned int)mesh.mPositions.size();
+ p_pcOut->mNumFaces = (unsigned int)mesh.mFaces.size();
+
+ // allocate enough storage for faces
+ p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces];
+
+ // copy vertices
+ p_pcOut->mVertices = new aiVector3D[mesh.mPositions.size()];
+ memcpy(p_pcOut->mVertices,&mesh.mPositions[0],
+ mesh.mPositions.size() * sizeof(aiVector3D));
+
+ // copy normals
+ p_pcOut->mNormals = new aiVector3D[mesh.mNormals.size()];
+ memcpy(p_pcOut->mNormals,&mesh.mNormals[0],
+ mesh.mNormals.size() * sizeof(aiVector3D));
+
+ // copy texture coordinates
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
+ if (!mesh.amTexCoords[c].empty()) {
+ p_pcOut->mTextureCoords[c] = new aiVector3D[mesh.amTexCoords[c].size()];
+ memcpy(p_pcOut->mTextureCoords[c],&mesh.amTexCoords[c][0],
+ mesh.amTexCoords[c].size() * sizeof(aiVector3D));
+
+ // setup the number of valid vertex components
+ p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c];
+ }
+ }
+
+ // copy vertex colors
+ if (!mesh.mVertexColors.empty()) {
+ p_pcOut->mColors[0] = new aiColor4D[mesh.mVertexColors.size()];
+ memcpy(p_pcOut->mColors[0],&mesh.mVertexColors[0],
+ mesh.mVertexColors.size() * sizeof(aiColor4D));
+ }
+
+ // copy faces
+ for (unsigned int iFace = 0; iFace < p_pcOut->mNumFaces;++iFace) {
+ p_pcOut->mFaces[iFace].mNumIndices = 3;
+ p_pcOut->mFaces[iFace].mIndices = new unsigned int[3];
+
+ // copy indices
+ p_pcOut->mFaces[iFace].mIndices[0] = mesh.mFaces[iFace].mIndices[0];
+ p_pcOut->mFaces[iFace].mIndices[1] = mesh.mFaces[iFace].mIndices[1];
+ p_pcOut->mFaces[iFace].mIndices[2] = mesh.mFaces[iFace].mIndices[2];
+ }
+
+ // copy vertex bones
+ if (!mesh.mBones.empty() && !mesh.mBoneVertices.empty()) {
+ std::vector<aiVertexWeight>* avBonesOut = new std::vector<aiVertexWeight>[mesh.mBones.size()];
+
+ // find all vertex weights for this bone
+ unsigned int quak = 0;
+ for (std::vector<BoneVertex>::const_iterator harrypotter = mesh.mBoneVertices.begin();
+ harrypotter != mesh.mBoneVertices.end();++harrypotter,++quak) {
+
+ for (std::vector<std::pair<int,float> >::const_iterator
+ ronaldweasley = (*harrypotter).mBoneWeights.begin();
+ ronaldweasley != (*harrypotter).mBoneWeights.end();++ronaldweasley)
+ {
+ aiVertexWeight weight;
+ weight.mVertexId = quak;
+ weight.mWeight = (*ronaldweasley).second;
+ avBonesOut[(*ronaldweasley).first].push_back(weight);
+ }
+ }
+
+ // now build a final bone list
+ p_pcOut->mNumBones = 0;
+ for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy)
+ if (!avBonesOut[jfkennedy].empty())p_pcOut->mNumBones++;
+
+ p_pcOut->mBones = new aiBone*[p_pcOut->mNumBones];
+ aiBone** pcBone = p_pcOut->mBones;
+ for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) {
+ if (!avBonesOut[jfkennedy].empty()) {
+ aiBone* pc = *pcBone = new aiBone();
+ pc->mName.Set(mesh.mBones[jfkennedy].mName);
+ pc->mNumWeights = (unsigned int)avBonesOut[jfkennedy].size();
+ pc->mWeights = new aiVertexWeight[pc->mNumWeights];
+ ::memcpy(pc->mWeights,&avBonesOut[jfkennedy][0],
+ sizeof(aiVertexWeight) * pc->mNumWeights);
+ ++pcBone;
+ }
+ }
+
+ // delete allocated storage
+ delete[] avBonesOut;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup proper material indices and build output materials
+void ASEImporter::BuildMaterialIndices()
+{
+ ai_assert(NULL != pcScene);
+
+ // iterate through all materials and check whether we need them
+ for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat)
+ {
+ ASE::Material& mat = mParser->m_vMaterials[iMat];
+ if (mat.bNeed) {
+ // Convert it to the aiMaterial layout
+ ConvertMaterial(mat);
+ ++pcScene->mNumMaterials;
+ }
+ for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat)
+ {
+ ASE::Material& submat = mat.avSubMaterials[iSubMat];
+ if (submat.bNeed) {
+ // Convert it to the aiMaterial layout
+ ConvertMaterial(submat);
+ ++pcScene->mNumMaterials;
+ }
+ }
+ }
+
+ // allocate the output material array
+ pcScene->mMaterials = new aiMaterial*[pcScene->mNumMaterials];
+ D3DS::Material** pcIntMaterials = new D3DS::Material*[pcScene->mNumMaterials];
+
+ unsigned int iNum = 0;
+ for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) {
+ ASE::Material& mat = mParser->m_vMaterials[iMat];
+ if (mat.bNeed)
+ {
+ ai_assert(NULL != mat.pcInstance);
+ pcScene->mMaterials[iNum] = mat.pcInstance;
+
+ // Store the internal material, too
+ pcIntMaterials[iNum] = &mat;
+
+ // Iterate through all meshes and search for one which is using
+ // this top-level material index
+ for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh)
+ {
+ aiMesh* mesh = pcScene->mMeshes[iMesh];
+ if (ASE::Face::DEFAULT_MATINDEX == mesh->mMaterialIndex &&
+ iMat == (uintptr_t)mesh->mColors[3])
+ {
+ mesh->mMaterialIndex = iNum;
+ mesh->mColors[3] = NULL;
+ }
+ }
+ iNum++;
+ }
+ for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) {
+ ASE::Material& submat = mat.avSubMaterials[iSubMat];
+ if (submat.bNeed) {
+ ai_assert(NULL != submat.pcInstance);
+ pcScene->mMaterials[iNum] = submat.pcInstance;
+
+ // Store the internal material, too
+ pcIntMaterials[iNum] = &submat;
+
+ // Iterate through all meshes and search for one which is using
+ // this sub-level material index
+ for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) {
+ aiMesh* mesh = pcScene->mMeshes[iMesh];
+
+ if (iSubMat == mesh->mMaterialIndex && iMat == (uintptr_t)mesh->mColors[3]) {
+ mesh->mMaterialIndex = iNum;
+ mesh->mColors[3] = NULL;
+ }
+ }
+ iNum++;
+ }
+ }
+ }
+
+ // Dekete our temporary array
+ delete[] pcIntMaterials;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Generate normal vectors basing on smoothing groups
+bool ASEImporter::GenerateNormals(ASE::Mesh& mesh) {
+
+ if (!mesh.mNormals.empty() && !configRecomputeNormals)
+ {
+ // Check whether there are only uninitialized normals. If there are
+ // some, skip all normals from the file and compute them on our own
+ for (std::vector<aiVector3D>::const_iterator qq = mesh.mNormals.begin();qq != mesh.mNormals.end();++qq) {
+ if ((*qq).x || (*qq).y || (*qq).z)
+ {
+ return true;
+ }
+ }
+ }
+ // The array is reused.
+ ComputeNormalsWithSmoothingsGroups<ASE::Face>(mesh);
+ return false;
+}
+
+#endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER
diff --git a/src/3rdparty/assimp/code/ASELoader.h b/src/3rdparty/assimp/code/ASELoader.h
new file mode 100644
index 000000000..5a117ba2f
--- /dev/null
+++ b/src/3rdparty/assimp/code/ASELoader.h
@@ -0,0 +1,208 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file ASELoader.h
+ * @brief Definition of the .ASE importer class.
+ */
+#ifndef AI_ASELOADER_H_INCLUDED
+#define AI_ASELOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+struct aiNode;
+#include "ASEParser.h"
+
+namespace Assimp {
+class MaterialHelper;
+
+// --------------------------------------------------------------------------------
+/** Importer class for the 3DS ASE ASCII format.
+ *
+ */
+class ASEImporter : public BaseImporter {
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ ASEImporter();
+
+ /** Destructor, private as well */
+ ~ASEImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+
+private:
+
+ // -------------------------------------------------------------------
+ /** Generate normal vectors basing on smoothing groups
+ * (in some cases the normal are already contained in the file)
+ * \param mesh Mesh to work on
+ * \return false if the normals have been recomputed
+ */
+ bool GenerateNormals(ASE::Mesh& mesh);
+
+
+ // -------------------------------------------------------------------
+ /** Create valid vertex/normal/UV/color/face lists.
+ * All elements are unique, faces have only one set of indices
+ * after this step occurs.
+ * \param mesh Mesh to work on
+ */
+ void BuildUniqueRepresentation(ASE::Mesh& mesh);
+
+
+ /** Create one-material-per-mesh meshes ;-)
+ * \param mesh Mesh to work with
+ * \param Receives the list of all created meshes
+ */
+ void ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOut);
+
+
+ // -------------------------------------------------------------------
+ /** Convert a material to a MaterialHelper object
+ * \param mat Input material
+ */
+ void ConvertMaterial(ASE::Material& mat);
+
+
+ // -------------------------------------------------------------------
+ /** Setup the final material indices for each mesh
+ */
+ void BuildMaterialIndices();
+
+
+ // -------------------------------------------------------------------
+ /** Build the node graph
+ */
+ void BuildNodes(std::vector<ASE::BaseNode*>& nodes);
+
+
+ // -------------------------------------------------------------------
+ /** Build output cameras
+ */
+ void BuildCameras();
+
+
+ // -------------------------------------------------------------------
+ /** Build output lights
+ */
+ void BuildLights();
+
+
+ // -------------------------------------------------------------------
+ /** Build output animations
+ */
+ void BuildAnimations(const std::vector<ASE::BaseNode*>& nodes);
+
+
+ // -------------------------------------------------------------------
+ /** Add sub nodes to a node
+ * \param pcParent parent node to be filled
+ * \param szName Name of the parent node
+ * \param matrix Current transform
+ */
+ void AddNodes(const std::vector<ASE::BaseNode*>& nodes,
+ aiNode* pcParent,const char* szName);
+
+ void AddNodes(const std::vector<ASE::BaseNode*>& nodes,
+ aiNode* pcParent,const char* szName,
+ const aiMatrix4x4& matrix);
+
+ void AddMeshes(const ASE::BaseNode* snode,aiNode* node);
+
+ // -------------------------------------------------------------------
+ /** Generate a default material and add it to the parser's list
+ * Called if no material has been found in the file (rare for ASE,
+ * but not impossible)
+ */
+ void GenerateDefaultMaterial();
+
+protected:
+
+ /** Parser instance */
+ ASE::Parser* mParser;
+
+ /** Buffer to hold the loaded file */
+ char* mBuffer;
+
+ /** Scene to be filled */
+ aiScene* pcScene;
+
+ /** Config options: Recompute the normals in every case - WA
+ for 3DS Max broken ASE normal export */
+ bool configRecomputeNormals;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/ASEParser.cpp b/src/3rdparty/assimp/code/ASEParser.cpp
new file mode 100644
index 000000000..c62785f30
--- /dev/null
+++ b/src/3rdparty/assimp/code/ASEParser.cpp
@@ -0,0 +1,2150 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file ASEParser.cpp
+ * @brief Implementation of the ASE parser class
+ */
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "TextureTransform.h"
+#include "ASELoader.h"
+#include "MaterialSystem.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+using namespace Assimp::ASE;
+
+
+// ------------------------------------------------------------------------------------------------
+// Begin an ASE parsing function
+
+#define AI_ASE_PARSER_INIT() \
+ int iDepth = 0;
+
+// ------------------------------------------------------------------------------------------------
+// Handle a "top-level" section in the file. EOF is no error in this case.
+
+#define AI_ASE_HANDLE_TOP_LEVEL_SECTION() \
+ else if ('{' == *filePtr)iDepth++; \
+ else if ('}' == *filePtr) \
+ { \
+ if (0 == --iDepth) \
+ { \
+ ++filePtr; \
+ SkipToNextToken(); \
+ return; \
+ } \
+ } \
+ else if ('\0' == *filePtr) \
+ { \
+ return; \
+ } \
+ if (IsLineEnd(*filePtr) && !bLastWasEndLine) \
+ { \
+ ++iLineNumber; \
+ bLastWasEndLine = true; \
+ } else bLastWasEndLine = false; \
+ ++filePtr;
+
+// ------------------------------------------------------------------------------------------------
+// Handle a nested section in the file. EOF is an error in this case
+// @param level "Depth" of the section
+// @param msg Full name of the section (including the asterisk)
+
+#define AI_ASE_HANDLE_SECTION(level, msg) \
+ if ('{' == *filePtr)iDepth++; \
+ else if ('}' == *filePtr) \
+ { \
+ if (0 == --iDepth) \
+ { \
+ ++filePtr; \
+ SkipToNextToken(); \
+ return; \
+ } \
+ } \
+ else if ('\0' == *filePtr) \
+ { \
+ LogError("Encountered unexpected EOL while parsing a " msg \
+ " chunk (Level " level ")"); \
+ } \
+ if (IsLineEnd(*filePtr) && !bLastWasEndLine) \
+ { \
+ ++iLineNumber; \
+ bLastWasEndLine = true; \
+ } else bLastWasEndLine = false; \
+ ++filePtr;
+
+// ------------------------------------------------------------------------------------------------
+Parser::Parser (const char* szFile, unsigned int fileFormatDefault)
+{
+ ai_assert(NULL != szFile);
+ filePtr = szFile;
+ iFileFormat = fileFormatDefault;
+
+ // make sure that the color values are invalid
+ m_clrBackground.r = get_qnan();
+ m_clrAmbient.r = get_qnan();
+
+ // setup some default values
+ iLineNumber = 0;
+ iFirstFrame = 0;
+ iLastFrame = 0;
+ iFrameSpeed = 30; // use 30 as default value for this property
+ iTicksPerFrame = 1; // use 1 as default value for this property
+ bLastWasEndLine = false; // need to handle \r\n seqs due to binary file mapping
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::LogWarning(const char* szWarn)
+{
+ ai_assert(NULL != szWarn);
+
+ char szTemp[1024];
+#if _MSC_VER >= 1400
+ sprintf_s(szTemp,"Line %i: %s",iLineNumber,szWarn);
+#else
+ snprintf(szTemp,1024,"Line %i: %s",iLineNumber,szWarn);
+#endif
+
+ // output the warning to the logger ...
+ DefaultLogger::get()->warn(szTemp);
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::LogInfo(const char* szWarn)
+{
+ ai_assert(NULL != szWarn);
+
+ char szTemp[1024];
+#if _MSC_VER >= 1400
+ sprintf_s(szTemp,"Line %i: %s",iLineNumber,szWarn);
+#else
+ snprintf(szTemp,1024,"Line %i: %s",iLineNumber,szWarn);
+#endif
+
+ // output the information to the logger ...
+ DefaultLogger::get()->info(szTemp);
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::LogError(const char* szWarn)
+{
+ ai_assert(NULL != szWarn);
+
+ char szTemp[1024];
+#if _MSC_VER >= 1400
+ sprintf_s(szTemp,"Line %i: %s",iLineNumber,szWarn);
+#else
+ snprintf(szTemp,1024,"Line %i: %s",iLineNumber,szWarn);
+#endif
+
+ // throw an exception
+ throw DeadlyImportError(szTemp);
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Parser::SkipToNextToken()
+{
+ while (true)
+ {
+ char me = *filePtr;
+
+ // increase the line number counter if necessary
+ if (IsLineEnd(me) && !bLastWasEndLine)
+ {
+ ++iLineNumber;
+ bLastWasEndLine = true;
+ }
+ else bLastWasEndLine = false;
+ if ('*' == me || '}' == me || '{' == me)return true;
+ if ('\0' == me)return false;
+
+ ++filePtr;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Parser::SkipSection()
+{
+ // must handle subsections ...
+ int iCnt = 0;
+ while (true)
+ {
+ if ('}' == *filePtr)
+ {
+ --iCnt;
+ if (0 == iCnt)
+ {
+ // go to the next valid token ...
+ ++filePtr;
+ SkipToNextToken();
+ return true;
+ }
+ }
+ else if ('{' == *filePtr)
+ {
+ ++iCnt;
+ }
+ else if ('\0' == *filePtr)
+ {
+ LogWarning("Unable to parse block: Unexpected EOF, closing bracket \'}\' was expected [#1]");
+ return false;
+ }
+ else if (IsLineEnd(*filePtr))++iLineNumber;
+ ++filePtr;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::Parse()
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Version should be 200. Validate this ...
+ if (TokenMatch(filePtr,"3DSMAX_ASCIIEXPORT",18))
+ {
+ unsigned int fmt;
+ ParseLV4MeshLong(fmt);
+
+ if (fmt > 200)
+ {
+ LogWarning("Unknown file format version: *3DSMAX_ASCIIEXPORT should \
+ be <= 200");
+ }
+ // *************************************************************
+ // - fmt will be 0 if we're unable to read the version number
+ // there are some faulty files without a version number ...
+ // in this case we'll guess the exact file format by looking
+ // at the file extension (ASE, ASK, ASC)
+ // *************************************************************
+
+ if (fmt)iFileFormat = fmt;
+ continue;
+ }
+ // main scene information
+ if (TokenMatch(filePtr,"SCENE",5))
+ {
+ ParseLV1SceneBlock();
+ continue;
+ }
+ // "group" - no implementation yet, in facte
+ // we're just ignoring them for the moment
+ if (TokenMatch(filePtr,"GROUP",5))
+ {
+ Parse();
+ continue;
+ }
+ // material list
+ if (TokenMatch(filePtr,"MATERIAL_LIST",13))
+ {
+ ParseLV1MaterialListBlock();
+ continue;
+ }
+ // geometric object (mesh)
+ if (TokenMatch(filePtr,"GEOMOBJECT",10))
+
+ {
+ m_vMeshes.push_back(Mesh());
+ ParseLV1ObjectBlock(m_vMeshes.back());
+ continue;
+ }
+ // helper object = dummy in the hierarchy
+ if (TokenMatch(filePtr,"HELPEROBJECT",12))
+
+ {
+ m_vDummies.push_back(Dummy());
+ ParseLV1ObjectBlock(m_vDummies.back());
+ continue;
+ }
+ // light object
+ if (TokenMatch(filePtr,"LIGHTOBJECT",11))
+
+ {
+ m_vLights.push_back(Light());
+ ParseLV1ObjectBlock(m_vLights.back());
+ continue;
+ }
+ // camera object
+ if (TokenMatch(filePtr,"CAMERAOBJECT",12))
+ {
+ m_vCameras.push_back(Camera());
+ ParseLV1ObjectBlock(m_vCameras.back());
+ continue;
+ }
+ // comment - print it on the console
+ if (TokenMatch(filePtr,"COMMENT",7))
+ {
+ std::string out = "<unknown>";
+ ParseString(out,"*COMMENT");
+ LogInfo(("Comment: " + out).c_str());
+ continue;
+ }
+ // ASC bone weights
+ if (AI_ASE_IS_OLD_FILE_FORMAT() && TokenMatch(filePtr,"MESH_SOFTSKINVERTS",18))
+ {
+ ParseLV1SoftSkinBlock();
+ }
+ }
+ AI_ASE_HANDLE_TOP_LEVEL_SECTION();
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV1SoftSkinBlock()
+{
+ // TODO: fix line counting here
+
+ // **************************************************************
+ // The soft skin block is formatted differently. There are no
+ // nested sections supported and the single elements aren't
+ // marked by keywords starting with an asterisk.
+
+ /**
+ FORMAT BEGIN
+
+ *MESH_SOFTSKINVERTS {
+ <nodename>
+ <number of vertices>
+
+ [for <number of vertices> times:]
+ <number of weights> [for <number of weights> times:] <bone name> <weight>
+ }
+
+ FORMAT END
+ */
+ // **************************************************************
+ while (true)
+ {
+ if (*filePtr == '}' ) {++filePtr;return;}
+ else if (*filePtr == '\0') return;
+ else if (*filePtr == '{' ) ++filePtr;
+
+ else // if (!IsSpace(*filePtr) && !IsLineEnd(*filePtr))
+ {
+ ASE::Mesh* curMesh = NULL;
+ unsigned int numVerts = 0;
+
+ const char* sz = filePtr;
+ while (!IsSpaceOrNewLine(*filePtr))++filePtr;
+
+ const unsigned int diff = (unsigned int)(filePtr-sz);
+ if (diff)
+ {
+ std::string name = std::string(sz,diff);
+ for (std::vector<ASE::Mesh>::iterator it = m_vMeshes.begin();
+ it != m_vMeshes.end(); ++it)
+ {
+ if ((*it).mName == name)
+ {
+ curMesh = & (*it);
+ break;
+ }
+ }
+ if (!curMesh)
+ {
+ LogWarning("Encountered unknown mesh in *MESH_SOFTSKINVERTS section");
+
+ // Skip the mesh data - until we find a new mesh
+ // or the end of the *MESH_SOFTSKINVERTS section
+ while (true)
+ {
+ SkipSpacesAndLineEnd(&filePtr);
+ if (*filePtr == '}')
+ {++filePtr;return;}
+ else if (!IsNumeric(*filePtr))
+ break;
+
+ SkipLine(&filePtr);
+ }
+ }
+ else
+ {
+ SkipSpacesAndLineEnd(&filePtr);
+ ParseLV4MeshLong(numVerts);
+
+ // Reserve enough storage
+ curMesh->mBoneVertices.reserve(numVerts);
+
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ SkipSpacesAndLineEnd(&filePtr);
+ unsigned int numWeights;
+ ParseLV4MeshLong(numWeights);
+
+ curMesh->mBoneVertices.push_back(ASE::BoneVertex());
+ ASE::BoneVertex& vert = curMesh->mBoneVertices.back();
+
+ // Reserve enough storage
+ vert.mBoneWeights.reserve(numWeights);
+
+ for (unsigned int w = 0; w < numWeights;++w)
+ {
+ std::string bone;
+ ParseString(bone,"*MESH_SOFTSKINVERTS.Bone");
+
+ // Find the bone in the mesh's list
+ std::pair<int,float> me;
+ me.first = -1;
+
+ for (unsigned int n = 0; n < curMesh->mBones.size();++n)
+ {
+ if (curMesh->mBones[n].mName == bone)
+ {
+ me.first = n;
+ break;
+ }
+ }
+ if (-1 == me.first)
+ {
+ // We don't have this bone yet, so add it to the list
+ me.first = (int)curMesh->mBones.size();
+ curMesh->mBones.push_back(ASE::Bone(bone));
+ }
+ ParseLV4MeshFloat( me.second );
+
+ // Add the new bone weight to list
+ vert.mBoneWeights.push_back(me);
+ }
+ }
+ }
+ }
+ }
+ ++filePtr;
+ SkipSpacesAndLineEnd(&filePtr);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV1SceneBlock()
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ if (TokenMatch(filePtr,"SCENE_BACKGROUND_STATIC",23))
+
+ {
+ // parse a color triple and assume it is really the bg color
+ ParseLV4MeshFloatTriple( &m_clrBackground.r );
+ continue;
+ }
+ if (TokenMatch(filePtr,"SCENE_AMBIENT_STATIC",20))
+
+ {
+ // parse a color triple and assume it is really the bg color
+ ParseLV4MeshFloatTriple( &m_clrAmbient.r );
+ continue;
+ }
+ if (TokenMatch(filePtr,"SCENE_FIRSTFRAME",16))
+ {
+ ParseLV4MeshLong(iFirstFrame);
+ continue;
+ }
+ if (TokenMatch(filePtr,"SCENE_LASTFRAME",15))
+ {
+ ParseLV4MeshLong(iLastFrame);
+ continue;
+ }
+ if (TokenMatch(filePtr,"SCENE_FRAMESPEED",16))
+ {
+ ParseLV4MeshLong(iFrameSpeed);
+ continue;
+ }
+ if (TokenMatch(filePtr,"SCENE_TICKSPERFRAME",19))
+ {
+ ParseLV4MeshLong(iTicksPerFrame);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_TOP_LEVEL_SECTION();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV1MaterialListBlock()
+{
+ AI_ASE_PARSER_INIT();
+
+ unsigned int iMaterialCount = 0;
+ unsigned int iOldMaterialCount = (unsigned int)m_vMaterials.size();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ if (TokenMatch(filePtr,"MATERIAL_COUNT",14))
+ {
+ ParseLV4MeshLong(iMaterialCount);
+
+ // now allocate enough storage to hold all materials
+ m_vMaterials.resize(iOldMaterialCount+iMaterialCount);
+ continue;
+ }
+ if (TokenMatch(filePtr,"MATERIAL",8))
+ {
+ unsigned int iIndex = 0;
+ ParseLV4MeshLong(iIndex);
+
+ if (iIndex >= iMaterialCount)
+ {
+ LogWarning("Out of range: material index is too large");
+ iIndex = iMaterialCount-1;
+ }
+
+ // get a reference to the material
+ Material& sMat = m_vMaterials[iIndex+iOldMaterialCount];
+ // parse the material block
+ ParseLV2MaterialBlock(sMat);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_TOP_LEVEL_SECTION();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV2MaterialBlock(ASE::Material& mat)
+{
+ AI_ASE_PARSER_INIT();
+
+ unsigned int iNumSubMaterials = 0;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ if (TokenMatch(filePtr,"MATERIAL_NAME",13))
+ {
+ if (!ParseString(mat.mName,"*MATERIAL_NAME"))
+ SkipToNextToken();
+ continue;
+ }
+ // ambient material color
+ if (TokenMatch(filePtr,"MATERIAL_AMBIENT",16))
+ {
+ ParseLV4MeshFloatTriple(&mat.mAmbient.r);
+ continue;
+ }
+ // diffuse material color
+ if (TokenMatch(filePtr,"MATERIAL_DIFFUSE",16) )
+ {
+ ParseLV4MeshFloatTriple(&mat.mDiffuse.r);
+ continue;
+ }
+ // specular material color
+ if (TokenMatch(filePtr,"MATERIAL_SPECULAR",17))
+ {
+ ParseLV4MeshFloatTriple(&mat.mSpecular.r);
+ continue;
+ }
+ // material shading type
+ if (TokenMatch(filePtr,"MATERIAL_SHADING",16))
+ {
+ if (TokenMatch(filePtr,"Blinn",5))
+ {
+ mat.mShading = Discreet3DS::Blinn;
+ }
+ else if (TokenMatch(filePtr,"Phong",5))
+ {
+ mat.mShading = Discreet3DS::Phong;
+ }
+ else if (TokenMatch(filePtr,"Flat",4))
+ {
+ mat.mShading = Discreet3DS::Flat;
+ }
+ else if (TokenMatch(filePtr,"Wire",4))
+ {
+ mat.mShading = Discreet3DS::Wire;
+ }
+ else
+ {
+ // assume gouraud shading
+ mat.mShading = Discreet3DS::Gouraud;
+ SkipToNextToken();
+ }
+ continue;
+ }
+ // material transparency
+ if (TokenMatch(filePtr,"MATERIAL_TRANSPARENCY",21))
+ {
+ ParseLV4MeshFloat(mat.mTransparency);
+ mat.mTransparency = 1.0f - mat.mTransparency;continue;
+ }
+ // material self illumination
+ if (TokenMatch(filePtr,"MATERIAL_SELFILLUM",18))
+ {
+ float f = 0.0f;
+ ParseLV4MeshFloat(f);
+
+ mat.mEmissive.r = f;
+ mat.mEmissive.g = f;
+ mat.mEmissive.b = f;
+ continue;
+ }
+ // material shininess
+ if (TokenMatch(filePtr,"MATERIAL_SHINE",14) )
+ {
+ ParseLV4MeshFloat(mat.mSpecularExponent);
+ mat.mSpecularExponent *= 15;
+ continue;
+ }
+ // two-sided material
+ if (TokenMatch(filePtr,"MATERIAL_TWOSIDED",17) )
+ {
+ mat.mTwoSided = true;
+ continue;
+ }
+ // material shininess strength
+ if (TokenMatch(filePtr,"MATERIAL_SHINESTRENGTH",22))
+ {
+ ParseLV4MeshFloat(mat.mShininessStrength);
+ continue;
+ }
+ // diffuse color map
+ if (TokenMatch(filePtr,"MAP_DIFFUSE",11))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexDiffuse);
+ continue;
+ }
+ // ambient color map
+ if (TokenMatch(filePtr,"MAP_AMBIENT",11))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexAmbient);
+ continue;
+ }
+ // specular color map
+ if (TokenMatch(filePtr,"MAP_SPECULAR",12))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexSpecular);
+ continue;
+ }
+ // opacity map
+ if (TokenMatch(filePtr,"MAP_OPACITY",11))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexOpacity);
+ continue;
+ }
+ // emissive map
+ if (TokenMatch(filePtr,"MAP_SELFILLUM",13))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexEmissive);
+ continue;
+ }
+ // bump map
+ if (TokenMatch(filePtr,"MAP_BUMP",8))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexBump);
+ }
+ // specular/shininess map
+ if (TokenMatch(filePtr,"MAP_SHINESTRENGTH",17))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexShininess);
+ continue;
+ }
+ // number of submaterials
+ if (TokenMatch(filePtr,"NUMSUBMTLS",10))
+ {
+ ParseLV4MeshLong(iNumSubMaterials);
+
+ // allocate enough storage
+ mat.avSubMaterials.resize(iNumSubMaterials);
+ }
+ // submaterial chunks
+ if (TokenMatch(filePtr,"SUBMATERIAL",11))
+ {
+
+ unsigned int iIndex = 0;
+ ParseLV4MeshLong(iIndex);
+
+ if (iIndex >= iNumSubMaterials)
+ {
+ LogWarning("Out of range: submaterial index is too large");
+ iIndex = iNumSubMaterials-1;
+ }
+
+ // get a reference to the material
+ Material& sMat = mat.avSubMaterials[iIndex];
+
+ // parse the material block
+ ParseLV2MaterialBlock(sMat);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("2","*MATERIAL");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MapBlock(Texture& map)
+{
+ AI_ASE_PARSER_INIT();
+
+ // ***********************************************************
+ // *BITMAP should not be there if *MAP_CLASS is not BITMAP,
+ // but we need to expect that case ... if the path is
+ // empty the texture won't be used later.
+ // ***********************************************************
+ bool parsePath = true;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ // type of map
+ if (TokenMatch(filePtr,"MAP_CLASS" ,9))
+ {
+ std::string temp;
+ if (!ParseString(temp,"*MAP_CLASS"))
+ SkipToNextToken();
+ if (temp != "Bitmap")
+ {
+ DefaultLogger::get()->warn("ASE: Skipping unknown map type: " + temp);
+ parsePath = false;
+ }
+ continue;
+ }
+ // path to the texture
+ if (parsePath && TokenMatch(filePtr,"BITMAP" ,6))
+ {
+ if (!ParseString(map.mMapName,"*BITMAP"))
+ SkipToNextToken();
+
+ if (map.mMapName == "None")
+ {
+ // Files with 'None' as map name are produced by
+ // an Maja to ASE exporter which name I forgot ..
+ DefaultLogger::get()->warn("ASE: Skipping invalid map entry");
+ map.mMapName = "";
+ }
+
+ continue;
+ }
+ // offset on the u axis
+ if (TokenMatch(filePtr,"UVW_U_OFFSET" ,12))
+ {
+ ParseLV4MeshFloat(map.mOffsetU);
+ continue;
+ }
+ // offset on the v axis
+ if (TokenMatch(filePtr,"UVW_V_OFFSET" ,12))
+ {
+ ParseLV4MeshFloat(map.mOffsetV);
+ continue;
+ }
+ // tiling on the u axis
+ if (TokenMatch(filePtr,"UVW_U_TILING" ,12))
+ {
+ ParseLV4MeshFloat(map.mScaleU);
+ continue;
+ }
+ // tiling on the v axis
+ if (TokenMatch(filePtr,"UVW_V_TILING" ,12))
+ {
+ ParseLV4MeshFloat(map.mScaleV);
+ continue;
+ }
+ // rotation around the z-axis
+ if (TokenMatch(filePtr,"UVW_ANGLE" ,9))
+ {
+ ParseLV4MeshFloat(map.mRotation);
+ continue;
+ }
+ // map blending factor
+ if (TokenMatch(filePtr,"MAP_AMOUNT" ,10))
+ {
+ ParseLV4MeshFloat(map.mTextureBlend);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MAP_XXXXXX");
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Parser::ParseString(std::string& out,const char* szName)
+{
+ char szBuffer[1024];
+ if (!SkipSpaces(&filePtr))
+ {
+
+ sprintf(szBuffer,"Unable to parse %s block: Unexpected EOL",szName);
+ LogWarning(szBuffer);
+ return false;
+ }
+ // there must be '"'
+ if ('\"' != *filePtr)
+ {
+
+ sprintf(szBuffer,"Unable to parse %s block: Strings are expected "
+ "to be enclosed in double quotation marks",szName);
+ LogWarning(szBuffer);
+ return false;
+ }
+ ++filePtr;
+ const char* sz = filePtr;
+ while (true)
+ {
+ if ('\"' == *sz)break;
+ else if ('\0' == *sz)
+ {
+ sprintf(szBuffer,"Unable to parse %s block: Strings are expected to "
+ "be enclosed in double quotation marks but EOF was reached before "
+ "a closing quotation mark was encountered",szName);
+ LogWarning(szBuffer);
+ return false;
+ }
+ sz++;
+ }
+ out = std::string(filePtr,(uintptr_t)sz-(uintptr_t)filePtr);
+ filePtr = sz+1;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV1ObjectBlock(ASE::BaseNode& node)
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // first process common tokens such as node name and transform
+ // name of the mesh/node
+ if (TokenMatch(filePtr,"NODE_NAME" ,9))
+ {
+ if (!ParseString(node.mName,"*NODE_NAME"))
+ SkipToNextToken();
+ continue;
+ }
+ // name of the parent of the node
+ if (TokenMatch(filePtr,"NODE_PARENT" ,11) )
+ {
+ if (!ParseString(node.mParent,"*NODE_PARENT"))
+ SkipToNextToken();
+ continue;
+ }
+ // transformation matrix of the node
+ if (TokenMatch(filePtr,"NODE_TM" ,7))
+ {
+ ParseLV2NodeTransformBlock(node);
+ continue;
+ }
+ // animation data of the node
+ if (TokenMatch(filePtr,"TM_ANIMATION" ,12))
+ {
+ ParseLV2AnimationBlock(node);
+ continue;
+ }
+
+ if (node.mType == BaseNode::Light)
+ {
+ // light settings
+ if (TokenMatch(filePtr,"LIGHT_SETTINGS" ,14))
+ {
+ ParseLV2LightSettingsBlock((ASE::Light&)node);
+ continue;
+ }
+ // type of the light source
+ if (TokenMatch(filePtr,"LIGHT_TYPE" ,10))
+ {
+ if (!ASSIMP_strincmp("omni",filePtr,4))
+ {
+ ((ASE::Light&)node).mLightType = ASE::Light::OMNI;
+ }
+ else if (!ASSIMP_strincmp("target",filePtr,6))
+ {
+ ((ASE::Light&)node).mLightType = ASE::Light::TARGET;
+ }
+ else if (!ASSIMP_strincmp("free",filePtr,4))
+ {
+ ((ASE::Light&)node).mLightType = ASE::Light::FREE;
+ }
+ else if (!ASSIMP_strincmp("directional",filePtr,11))
+ {
+ ((ASE::Light&)node).mLightType = ASE::Light::DIRECTIONAL;
+ }
+ else
+ {
+ LogWarning("Unknown kind of light source");
+ }
+ continue;
+ }
+ }
+ else if (node.mType == BaseNode::Camera)
+ {
+ // Camera settings
+ if (TokenMatch(filePtr,"CAMERA_SETTINGS" ,15))
+ {
+ ParseLV2CameraSettingsBlock((ASE::Camera&)node);
+ continue;
+ }
+ else if (TokenMatch(filePtr,"CAMERA_TYPE" ,11))
+ {
+ if (!ASSIMP_strincmp("target",filePtr,6))
+ {
+ ((ASE::Camera&)node).mCameraType = ASE::Camera::TARGET;
+ }
+ else if (!ASSIMP_strincmp("free",filePtr,4))
+ {
+ ((ASE::Camera&)node).mCameraType = ASE::Camera::FREE;
+ }
+ else
+ {
+ LogWarning("Unknown kind of camera");
+ }
+ continue;
+ }
+ }
+ else if (node.mType == BaseNode::Mesh)
+ {
+ // mesh data
+ // FIX: Older files use MESH_SOFTSKIN
+ if (TokenMatch(filePtr,"MESH" ,4) ||
+ TokenMatch(filePtr,"MESH_SOFTSKIN",13))
+ {
+ ParseLV2MeshBlock((ASE::Mesh&)node);
+ continue;
+ }
+ // mesh material index
+ if (TokenMatch(filePtr,"MATERIAL_REF" ,12))
+ {
+ ParseLV4MeshLong(((ASE::Mesh&)node).iMaterialIndex);
+ continue;
+ }
+ }
+ }
+ AI_ASE_HANDLE_TOP_LEVEL_SECTION();
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV2CameraSettingsBlock(ASE::Camera& camera)
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ if (TokenMatch(filePtr,"CAMERA_NEAR" ,11))
+ {
+ ParseLV4MeshFloat(camera.mNear);
+ continue;
+ }
+ if (TokenMatch(filePtr,"CAMERA_FAR" ,10))
+ {
+ ParseLV4MeshFloat(camera.mFar);
+ continue;
+ }
+ if (TokenMatch(filePtr,"CAMERA_FOV" ,10))
+ {
+ ParseLV4MeshFloat(camera.mFOV);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("2","CAMERA_SETTINGS");
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV2LightSettingsBlock(ASE::Light& light)
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ if (TokenMatch(filePtr,"LIGHT_COLOR" ,11))
+ {
+ ParseLV4MeshFloatTriple(&light.mColor.r);
+ continue;
+ }
+ if (TokenMatch(filePtr,"LIGHT_INTENS" ,12))
+ {
+ ParseLV4MeshFloat(light.mIntensity);
+ continue;
+ }
+ if (TokenMatch(filePtr,"LIGHT_HOTSPOT" ,13))
+ {
+ ParseLV4MeshFloat(light.mAngle);
+ continue;
+ }
+ if (TokenMatch(filePtr,"LIGHT_FALLOFF" ,13))
+ {
+ ParseLV4MeshFloat(light.mFalloff);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("2","LIGHT_SETTINGS");
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV2AnimationBlock(ASE::BaseNode& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ ASE::Animation* anim = &mesh.mAnim;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ if (TokenMatch(filePtr,"NODE_NAME" ,9))
+ {
+ std::string temp;
+ if (!ParseString(temp,"*NODE_NAME"))
+ SkipToNextToken();
+
+ // If the name of the node contains .target it
+ // represents an animated camera or spot light
+ // target.
+ if (std::string::npos != temp.find(".Target"))
+ {
+ if ((mesh.mType != BaseNode::Camera || ((ASE::Camera&)mesh).mCameraType != ASE::Camera::TARGET) &&
+ ( mesh.mType != BaseNode::Light || ((ASE::Light&)mesh).mLightType != ASE::Light::TARGET))
+ {
+
+ DefaultLogger::get()->error("ASE: Found target animation channel "
+ "but the node is neither a camera nor a spot light");
+ anim = NULL;
+ }
+ else anim = &mesh.mTargetAnim;
+ }
+ continue;
+ }
+
+ // position keyframes
+ if (TokenMatch(filePtr,"CONTROL_POS_TRACK" ,17) ||
+ TokenMatch(filePtr,"CONTROL_POS_BEZIER" ,18) ||
+ TokenMatch(filePtr,"CONTROL_POS_TCB" ,15))
+ {
+ if (!anim)SkipSection();
+ else ParseLV3PosAnimationBlock(*anim);
+ continue;
+ }
+ // scaling keyframes
+ if (TokenMatch(filePtr,"CONTROL_SCALE_TRACK" ,19) ||
+ TokenMatch(filePtr,"CONTROL_SCALE_BEZIER" ,20) ||
+ TokenMatch(filePtr,"CONTROL_SCALE_TCB" ,17))
+ {
+ if (!anim || anim == &mesh.mTargetAnim)
+ {
+ // Target animation channels may have no rotation channels
+ DefaultLogger::get()->error("ASE: Ignoring scaling channel in target animation");
+ SkipSection();
+ }
+ else ParseLV3ScaleAnimationBlock(*anim);
+ continue;
+ }
+ // rotation keyframes
+ if (TokenMatch(filePtr,"CONTROL_ROT_TRACK" ,17) ||
+ TokenMatch(filePtr,"CONTROL_ROT_BEZIER" ,18) ||
+ TokenMatch(filePtr,"CONTROL_ROT_TCB" ,15))
+ {
+ if (!anim || anim == &mesh.mTargetAnim)
+ {
+ // Target animation channels may have no rotation channels
+ DefaultLogger::get()->error("ASE: Ignoring rotation channel in target animation");
+ SkipSection();
+ }
+ else ParseLV3RotAnimationBlock(*anim);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("2","TM_ANIMATION");
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3ScaleAnimationBlock(ASE::Animation& anim)
+{
+ AI_ASE_PARSER_INIT();
+ unsigned int iIndex;
+
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ bool b = false;
+
+ // For the moment we're just reading the three floats -
+ // we ignore the dditional information for bezier's and TCBs
+
+ // simple scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_SCALE_SAMPLE" ,20))
+ {
+ b = true;
+ anim.mScalingType = ASE::Animation::TRACK;
+ }
+
+ // Bezier scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_BEZIER_SCALE_KEY" ,24))
+ {
+ b = true;
+ anim.mScalingType = ASE::Animation::BEZIER;
+ }
+ // TCB scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_TCB_SCALE_KEY" ,21))
+ {
+ b = true;
+ anim.mScalingType = ASE::Animation::TCB;
+ }
+ if (b)
+ {
+ anim.akeyScaling.push_back(aiVectorKey());
+ aiVectorKey& key = anim.akeyScaling.back();
+ ParseLV4MeshFloatTriple(&key.mValue.x,iIndex);
+ key.mTime = (double)iIndex;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*CONTROL_POS_TRACK");
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3PosAnimationBlock(ASE::Animation& anim)
+{
+ AI_ASE_PARSER_INIT();
+ unsigned int iIndex;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ bool b = false;
+
+ // For the moment we're just reading the three floats -
+ // we ignore the dditional information for bezier's and TCBs
+
+ // simple scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_POS_SAMPLE" ,18))
+ {
+ b = true;
+ anim.mPositionType = ASE::Animation::TRACK;
+ }
+
+ // Bezier scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_BEZIER_POS_KEY" ,22))
+ {
+ b = true;
+ anim.mPositionType = ASE::Animation::BEZIER;
+ }
+ // TCB scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_TCB_POS_KEY" ,19))
+ {
+ b = true;
+ anim.mPositionType = ASE::Animation::TCB;
+ }
+ if (b)
+ {
+ anim.akeyPositions.push_back(aiVectorKey());
+ aiVectorKey& key = anim.akeyPositions.back();
+ ParseLV4MeshFloatTriple(&key.mValue.x,iIndex);
+ key.mTime = (double)iIndex;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*CONTROL_POS_TRACK");
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3RotAnimationBlock(ASE::Animation& anim)
+{
+ AI_ASE_PARSER_INIT();
+ unsigned int iIndex;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ bool b = false;
+
+ // For the moment we're just reading the floats -
+ // we ignore the dditional information for bezier's and TCBs
+
+ // simple scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_ROT_SAMPLE" ,18))
+ {
+ b = true;
+ anim.mRotationType = ASE::Animation::TRACK;
+ }
+
+ // Bezier scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_BEZIER_ROT_KEY" ,22))
+ {
+ b = true;
+ anim.mRotationType = ASE::Animation::BEZIER;
+ }
+ // TCB scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_TCB_ROT_KEY" ,19))
+ {
+ b = true;
+ anim.mRotationType = ASE::Animation::TCB;
+ }
+ if (b)
+ {
+ anim.akeyRotations.push_back(aiQuatKey());
+ aiQuatKey& key = anim.akeyRotations.back();
+ aiVector3D v;float f;
+ ParseLV4MeshFloatTriple(&v.x,iIndex);
+ ParseLV4MeshFloat(f);
+ key.mTime = (double)iIndex;
+ key.mValue = aiQuaternion(v,f);
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*CONTROL_ROT_TRACK");
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode& mesh)
+{
+ AI_ASE_PARSER_INIT();
+ int mode = 0;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ // name of the node
+ if (TokenMatch(filePtr,"NODE_NAME" ,9))
+ {
+ std::string temp;
+ if (!ParseString(temp,"*NODE_NAME"))
+ SkipToNextToken();
+
+ std::string::size_type s;
+ if (temp == mesh.mName)
+ {
+ mode = 1;
+ }
+ else if (std::string::npos != (s = temp.find(".Target")) &&
+ mesh.mName == temp.substr(0,s))
+ {
+ // This should be either a target light or a target camera
+ if ( (mesh.mType == BaseNode::Light && ((ASE::Light&)mesh) .mLightType == ASE::Light::TARGET) ||
+ (mesh.mType == BaseNode::Camera && ((ASE::Camera&)mesh).mCameraType == ASE::Camera::TARGET))
+ {
+ mode = 2;
+ }
+ else DefaultLogger::get()->error("ASE: Ignoring target transform, "
+ "this is no spot light or target camera");
+ }
+ else
+ {
+ DefaultLogger::get()->error("ASE: Unknown node transformation: " + temp);
+ // mode = 0
+ }
+ continue;
+ }
+ if (mode)
+ {
+ // fourth row of the transformation matrix - and also the
+ // only information here that is interesting for targets
+ if (TokenMatch(filePtr,"TM_ROW3" ,7))
+ {
+ ParseLV4MeshFloatTriple((mode == 1 ? mesh.mTransform[3] : &mesh.mTargetPosition.x));
+ continue;
+ }
+ if (mode == 1)
+ {
+ // first row of the transformation matrix
+ if (TokenMatch(filePtr,"TM_ROW0" ,7))
+ {
+ ParseLV4MeshFloatTriple(mesh.mTransform[0]);
+ continue;
+ }
+ // second row of the transformation matrix
+ if (TokenMatch(filePtr,"TM_ROW1" ,7))
+ {
+ ParseLV4MeshFloatTriple(mesh.mTransform[1]);
+ continue;
+ }
+ // third row of the transformation matrix
+ if (TokenMatch(filePtr,"TM_ROW2" ,7))
+ {
+ ParseLV4MeshFloatTriple(mesh.mTransform[2]);
+ continue;
+ }
+ // inherited position axes
+ if (TokenMatch(filePtr,"INHERIT_POS" ,11))
+ {
+ unsigned int aiVal[3];
+ ParseLV4MeshLongTriple(aiVal);
+
+ for (unsigned int i = 0; i < 3;++i)
+ mesh.inherit.abInheritPosition[i] = aiVal[i] != 0;
+ continue;
+ }
+ // inherited rotation axes
+ if (TokenMatch(filePtr,"INHERIT_ROT" ,11))
+ {
+ unsigned int aiVal[3];
+ ParseLV4MeshLongTriple(aiVal);
+
+ for (unsigned int i = 0; i < 3;++i)
+ mesh.inherit.abInheritRotation[i] = aiVal[i] != 0;
+ continue;
+ }
+ // inherited scaling axes
+ if (TokenMatch(filePtr,"INHERIT_SCL" ,11))
+ {
+ unsigned int aiVal[3];
+ ParseLV4MeshLongTriple(aiVal);
+
+ for (unsigned int i = 0; i < 3;++i)
+ mesh.inherit.abInheritScaling[i] = aiVal[i] != 0;
+ continue;
+ }
+ }
+ }
+ }
+ AI_ASE_HANDLE_SECTION("2","*NODE_TM");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV2MeshBlock(ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ unsigned int iNumVertices = 0;
+ unsigned int iNumFaces = 0;
+ unsigned int iNumTVertices = 0;
+ unsigned int iNumTFaces = 0;
+ unsigned int iNumCVertices = 0;
+ unsigned int iNumCFaces = 0;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ // Number of vertices in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMVERTEX" ,14))
+ {
+ ParseLV4MeshLong(iNumVertices);
+ continue;
+ }
+ // Number of texture coordinates in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMTVERTEX" ,15))
+ {
+ ParseLV4MeshLong(iNumTVertices);
+ continue;
+ }
+ // Number of vertex colors in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMCVERTEX" ,15))
+ {
+ ParseLV4MeshLong(iNumCVertices);
+ continue;
+ }
+ // Number of regular faces in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMFACES" ,13))
+ {
+ ParseLV4MeshLong(iNumFaces);
+ continue;
+ }
+ // Number of UVWed faces in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMTVFACES" ,15))
+ {
+ ParseLV4MeshLong(iNumTFaces);
+ continue;
+ }
+ // Number of colored faces in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMCVFACES" ,15))
+ {
+ ParseLV4MeshLong(iNumCFaces);
+ continue;
+ }
+ // mesh vertex list block
+ if (TokenMatch(filePtr,"MESH_VERTEX_LIST" ,16))
+ {
+ ParseLV3MeshVertexListBlock(iNumVertices,mesh);
+ continue;
+ }
+ // mesh face list block
+ if (TokenMatch(filePtr,"MESH_FACE_LIST" ,14))
+ {
+ ParseLV3MeshFaceListBlock(iNumFaces,mesh);
+ continue;
+ }
+ // mesh texture vertex list block
+ if (TokenMatch(filePtr,"MESH_TVERTLIST" ,14))
+ {
+ ParseLV3MeshTListBlock(iNumTVertices,mesh);
+ continue;
+ }
+ // mesh texture face block
+ if (TokenMatch(filePtr,"MESH_TFACELIST" ,14))
+ {
+ ParseLV3MeshTFaceListBlock(iNumTFaces,mesh);
+ continue;
+ }
+ // mesh color vertex list block
+ if (TokenMatch(filePtr,"MESH_CVERTLIST" ,14))
+ {
+ ParseLV3MeshCListBlock(iNumCVertices,mesh);
+ continue;
+ }
+ // mesh color face block
+ if (TokenMatch(filePtr,"MESH_CFACELIST" ,14))
+ {
+ ParseLV3MeshCFaceListBlock(iNumCFaces,mesh);
+ continue;
+ }
+ // mesh normals
+ if (TokenMatch(filePtr,"MESH_NORMALS" ,12))
+ {
+ ParseLV3MeshNormalListBlock(mesh);
+ continue;
+ }
+ // another mesh UV channel ...
+ if (TokenMatch(filePtr,"MESH_MAPPINGCHANNEL" ,19))
+ {
+
+ unsigned int iIndex = 0;
+ ParseLV4MeshLong(iIndex);
+
+ if (iIndex < 2)
+ {
+ LogWarning("Mapping channel has an invalid index. Skipping UV channel");
+ // skip it ...
+ SkipSection();
+ }
+ if (iIndex > AI_MAX_NUMBER_OF_TEXTURECOORDS)
+ {
+ LogWarning("Too many UV channels specified. Skipping channel ..");
+ // skip it ...
+ SkipSection();
+ }
+ else
+ {
+ // parse the mapping channel
+ ParseLV3MappingChannel(iIndex-1,mesh);
+ }
+ continue;
+ }
+ // mesh animation keyframe. Not supported
+ if (TokenMatch(filePtr,"MESH_ANIMATION" ,14))
+ {
+
+ LogWarning("Found *MESH_ANIMATION element in ASE/ASK file. "
+ "Keyframe animation is not supported by Assimp, this element "
+ "will be ignored");
+ //SkipSection();
+ continue;
+ }
+ if (TokenMatch(filePtr,"MESH_WEIGHTS" ,12))
+ {
+ ParseLV3MeshWeightsBlock(mesh);continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("2","*MESH");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ unsigned int iNumVertices = 0, iNumBones = 0;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Number of bone vertices ...
+ if (TokenMatch(filePtr,"MESH_NUMVERTEX" ,14))
+ {
+ ParseLV4MeshLong(iNumVertices);
+ continue;
+ }
+ // Number of bones
+ if (TokenMatch(filePtr,"MESH_NUMBONE" ,11))
+ {
+ ParseLV4MeshLong(iNumBones);
+ continue;
+ }
+ // parse the list of bones
+ if (TokenMatch(filePtr,"MESH_BONE_LIST" ,14))
+ {
+ ParseLV4MeshBones(iNumBones,mesh);
+ continue;
+ }
+ // parse the list of bones vertices
+ if (TokenMatch(filePtr,"MESH_BONE_VERTEX_LIST" ,21) )
+ {
+ ParseLV4MeshBonesVertices(iNumVertices,mesh);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_WEIGHTS");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshBones(unsigned int iNumBones,ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+ mesh.mBones.resize(iNumBones);
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Mesh bone with name ...
+ if (TokenMatch(filePtr,"MESH_BONE_NAME" ,16))
+ {
+ // parse an index ...
+ if (SkipSpaces(&filePtr))
+ {
+ unsigned int iIndex = strtol10(filePtr,&filePtr);
+ if (iIndex >= iNumBones)
+ {
+ continue;
+ LogWarning("Bone index is out of bounds");
+ }
+ if (!ParseString(mesh.mBones[iIndex].mName,"*MESH_BONE_NAME"))
+ SkipToNextToken();
+ continue;
+ }
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_BONE_LIST");
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices,ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+ mesh.mBoneVertices.resize(iNumVertices);
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Mesh bone vertex
+ if (TokenMatch(filePtr,"MESH_BONE_VERTEX" ,16))
+ {
+ // read the vertex index
+ unsigned int iIndex = strtol10(filePtr,&filePtr);
+ if (iIndex >= mesh.mPositions.size())
+ {
+ iIndex = (unsigned int)mesh.mPositions.size()-1;
+ LogWarning("Bone vertex index is out of bounds. Using the largest valid "
+ "bone vertex index instead");
+ }
+
+ // --- ignored
+ float afVert[3];
+ ParseLV4MeshFloatTriple(afVert);
+
+ std::pair<int,float> pairOut;
+ while (true)
+ {
+ // first parse the bone index ...
+ if (!SkipSpaces(&filePtr))break;
+ pairOut.first = strtol10(filePtr,&filePtr);
+
+ // then parse the vertex weight
+ if (!SkipSpaces(&filePtr))break;
+ filePtr = fast_atof_move(filePtr,pairOut.second);
+
+ // -1 marks unused entries
+ if (-1 != pairOut.first)
+ {
+ mesh.mBoneVertices[iIndex].mBoneWeights.push_back(pairOut);
+ }
+ }
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("4","*MESH_BONE_VERTEX");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshVertexListBlock(
+ unsigned int iNumVertices, ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ // allocate enough storage in the array
+ mesh.mPositions.resize(iNumVertices);
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Vertex entry
+ if (TokenMatch(filePtr,"MESH_VERTEX" ,11))
+ {
+
+ aiVector3D vTemp;
+ unsigned int iIndex;
+ ParseLV4MeshFloatTriple(&vTemp.x,iIndex);
+
+ if (iIndex >= iNumVertices)
+ {
+ LogWarning("Invalid vertex index. It will be ignored");
+ }
+ else mesh.mPositions[iIndex] = vTemp;
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_VERTEX_LIST");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ // allocate enough storage in the face array
+ mesh.mFaces.resize(iNumFaces);
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Face entry
+ if (TokenMatch(filePtr,"MESH_FACE" ,9))
+ {
+
+ ASE::Face mFace;
+ ParseLV4MeshFace(mFace);
+
+ if (mFace.iFace >= iNumFaces)
+ {
+ LogWarning("Face has an invalid index. It will be ignored");
+ }
+ else mesh.mFaces[mFace.iFace] = mFace;
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_FACE_LIST");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices,
+ ASE::Mesh& mesh, unsigned int iChannel)
+{
+ AI_ASE_PARSER_INIT();
+
+ // allocate enough storage in the array
+ mesh.amTexCoords[iChannel].resize(iNumVertices);
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Vertex entry
+ if (TokenMatch(filePtr,"MESH_TVERT" ,10))
+ {
+ aiVector3D vTemp;
+ unsigned int iIndex;
+ ParseLV4MeshFloatTriple(&vTemp.x,iIndex);
+
+ if (iIndex >= iNumVertices)
+ {
+ LogWarning("Tvertex has an invalid index. It will be ignored");
+ }
+ else mesh.amTexCoords[iChannel][iIndex] = vTemp;
+
+ if (0.0f != vTemp.z)
+ {
+ // we need 3 coordinate channels
+ mesh.mNumUVComponents[iChannel] = 3;
+ }
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_TVERT_LIST");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces,
+ ASE::Mesh& mesh, unsigned int iChannel)
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Face entry
+ if (TokenMatch(filePtr,"MESH_TFACE" ,10))
+ {
+ unsigned int aiValues[3];
+ unsigned int iIndex = 0;
+
+ ParseLV4MeshLongTriple(aiValues,iIndex);
+ if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size())
+ {
+ LogWarning("UV-Face has an invalid index. It will be ignored");
+ }
+ else
+ {
+ // copy UV indices
+ mesh.mFaces[iIndex].amUVIndices[iChannel][0] = aiValues[0];
+ mesh.mFaces[iIndex].amUVIndices[iChannel][1] = aiValues[1];
+ mesh.mFaces[iIndex].amUVIndices[iChannel][2] = aiValues[2];
+ }
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_TFACE_LIST");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ unsigned int iNumTVertices = 0;
+ unsigned int iNumTFaces = 0;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Number of texture coordinates in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMTVERTEX" ,15))
+ {
+ ParseLV4MeshLong(iNumTVertices);
+ continue;
+ }
+ // Number of UVWed faces in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMTVFACES" ,15))
+ {
+ ParseLV4MeshLong(iNumTFaces);
+ continue;
+ }
+ // mesh texture vertex list block
+ if (TokenMatch(filePtr,"MESH_TVERTLIST" ,14))
+ {
+ ParseLV3MeshTListBlock(iNumTVertices,mesh,iChannel);
+ continue;
+ }
+ // mesh texture face block
+ if (TokenMatch(filePtr,"MESH_TFACELIST" ,14))
+ {
+ ParseLV3MeshTFaceListBlock(iNumTFaces,mesh, iChannel);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_MAPPING_CHANNEL");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ // allocate enough storage in the array
+ mesh.mVertexColors.resize(iNumVertices);
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Vertex entry
+ if (TokenMatch(filePtr,"MESH_VERTCOL" ,12))
+ {
+ aiColor4D vTemp;
+ vTemp.a = 1.0f;
+ unsigned int iIndex;
+ ParseLV4MeshFloatTriple(&vTemp.r,iIndex);
+
+ if (iIndex >= iNumVertices)
+ {
+ LogWarning("Vertex color has an invalid index. It will be ignored");
+ }
+ else mesh.mVertexColors[iIndex] = vTemp;
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_CVERTEX_LIST");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Face entry
+ if (TokenMatch(filePtr,"MESH_CFACE" ,11))
+ {
+ unsigned int aiValues[3];
+ unsigned int iIndex = 0;
+
+ ParseLV4MeshLongTriple(aiValues,iIndex);
+ if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size())
+ {
+ LogWarning("UV-Face has an invalid index. It will be ignored");
+ }
+ else
+ {
+ // copy color indices
+ mesh.mFaces[iIndex].mColorIndices[0] = aiValues[0];
+ mesh.mFaces[iIndex].mColorIndices[1] = aiValues[1];
+ mesh.mFaces[iIndex].mColorIndices[2] = aiValues[2];
+ }
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_CFACE_LIST");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh& sMesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ // Allocate enough storage for the normals
+ sMesh.mNormals.resize(sMesh.mFaces.size()*3,aiVector3D( 0.f, 0.f, 0.f ));
+ unsigned int index, faceIdx = 0xffffffff;
+
+ // FIXME: rewrite this and find out how to interpret the normals
+ // correctly.
+
+ // Smooth the vertex and face normals together. The result
+ // will be edgy then, but otherwise everything would be soft ...
+ while (true) {
+ if ('*' == *filePtr) {
+ ++filePtr;
+ if (faceIdx != 0xffffffff && TokenMatch(filePtr,"MESH_VERTEXNORMAL",17)) {
+ aiVector3D vNormal;
+ ParseLV4MeshFloatTriple(&vNormal.x,index);
+ if (faceIdx >= sMesh.mFaces.size())
+ continue;
+
+ // Make sure we assign it to the correct face
+ const ASE::Face& face = sMesh.mFaces[faceIdx];
+ if (index == face.mIndices[0])
+ index = 0;
+ else if (index == face.mIndices[1])
+ index = 1;
+ else if (index == face.mIndices[2])
+ index = 2;
+ else {
+ DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_VERTEXNORMAL section");
+ continue;
+ }
+ // We'll renormalize later
+ sMesh.mNormals[faceIdx*3+index] += vNormal;
+ continue;
+ }
+ if (TokenMatch(filePtr,"MESH_FACENORMAL",15)) {
+ aiVector3D vNormal;
+ ParseLV4MeshFloatTriple(&vNormal.x,faceIdx);
+
+ if (faceIdx >= sMesh.mFaces.size()) {
+ DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_FACENORMAL section");
+ continue;
+ }
+
+ // We'll renormalize later
+ sMesh.mNormals[faceIdx*3] += vNormal;
+ sMesh.mNormals[faceIdx*3+1] += vNormal;
+ sMesh.mNormals[faceIdx*3+2] += vNormal;
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_NORMALS");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshFace(ASE::Face& out)
+{
+ // skip spaces and tabs
+ if (!SkipSpaces(&filePtr))
+ {
+ LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL [#1]");
+ SkipToNextToken();
+ return;
+ }
+
+ // parse the face index
+ out.iFace = strtol10(filePtr,&filePtr);
+
+ // next character should be ':'
+ if (!SkipSpaces(&filePtr))
+ {
+ // FIX: there are some ASE files which haven't got : here ....
+ LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. \':\' expected [#2]");
+ SkipToNextToken();
+ return;
+ }
+ // FIX: There are some ASE files which haven't got ':' here
+ if (':' == *filePtr)++filePtr;
+
+ // Parse all mesh indices
+ for (unsigned int i = 0; i < 3;++i)
+ {
+ unsigned int iIndex = 0;
+ if (!SkipSpaces(&filePtr))
+ {
+ LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL");
+ SkipToNextToken();
+ return;
+ }
+ switch (*filePtr)
+ {
+ case 'A':
+ case 'a':
+ break;
+ case 'B':
+ case 'b':
+ iIndex = 1;
+ break;
+ case 'C':
+ case 'c':
+ iIndex = 2;
+ break;
+ default:
+ LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. "
+ "A,B or C expected [#3]");
+ SkipToNextToken();
+ return;
+ };
+ ++filePtr;
+
+ // next character should be ':'
+ if (!SkipSpaces(&filePtr) || ':' != *filePtr)
+ {
+ LogWarning("Unable to parse *MESH_FACE Element: "
+ "Unexpected EOL. \':\' expected [#2]");
+ SkipToNextToken();
+ return;
+ }
+
+ ++filePtr;
+ if (!SkipSpaces(&filePtr))
+ {
+ LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. "
+ "Vertex index ecpected [#4]");
+ SkipToNextToken();
+ return;
+ }
+ out.mIndices[iIndex] = strtol10(filePtr,&filePtr);
+ }
+
+ // now we need to skip the AB, BC, CA blocks.
+ while (true)
+ {
+ if ('*' == *filePtr)break;
+ if (IsLineEnd(*filePtr))
+ {
+ //iLineNumber++;
+ return;
+ }
+ filePtr++;
+ }
+
+ // parse the smoothing group of the face
+ if (TokenMatch(filePtr,"*MESH_SMOOTHING",15))
+ {
+ if (!SkipSpaces(&filePtr))
+ {
+ LogWarning("Unable to parse *MESH_SMOOTHING Element: "
+ "Unexpected EOL. Smoothing group(s) expected [#5]");
+ SkipToNextToken();
+ return;
+ }
+
+ // Parse smoothing groups until we don't anymore see commas
+ // FIX: There needn't always be a value, sad but true
+ while (true)
+ {
+ if (*filePtr < '9' && *filePtr >= '0')
+ {
+ out.iSmoothGroup |= (1 << strtol10(filePtr,&filePtr));
+ }
+ SkipSpaces(&filePtr);
+ if (',' != *filePtr)
+ {
+ break;
+ }
+ ++filePtr;
+ SkipSpaces(&filePtr);
+ }
+ }
+
+ // *MESH_MTLID is optional, too
+ while (true)
+ {
+ if ('*' == *filePtr)break;
+ if (IsLineEnd(*filePtr))
+ {
+ return;
+ }
+ filePtr++;
+ }
+
+ if (TokenMatch(filePtr,"*MESH_MTLID",11))
+ {
+ if (!SkipSpaces(&filePtr))
+ {
+ LogWarning("Unable to parse *MESH_MTLID Element: Unexpected EOL. "
+ "Material index expected [#6]");
+ SkipToNextToken();
+ return;
+ }
+ out.iMaterial = strtol10(filePtr,&filePtr);
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshLongTriple(unsigned int* apOut)
+{
+ ai_assert(NULL != apOut);
+
+ for (unsigned int i = 0; i < 3;++i)
+ ParseLV4MeshLong(apOut[i]);
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut)
+{
+ ai_assert(NULL != apOut);
+
+ // parse the index
+ ParseLV4MeshLong(rIndexOut);
+
+ // parse the three others
+ ParseLV4MeshLongTriple(apOut);
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut)
+{
+ ai_assert(NULL != apOut);
+
+ // parse the index
+ ParseLV4MeshLong(rIndexOut);
+
+ // parse the three others
+ ParseLV4MeshFloatTriple(apOut);
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshFloatTriple(float* apOut)
+{
+ ai_assert(NULL != apOut);
+
+ for (unsigned int i = 0; i < 3;++i)
+ ParseLV4MeshFloat(apOut[i]);
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshFloat(float& fOut)
+{
+ // skip spaces and tabs
+ if (!SkipSpaces(&filePtr))
+ {
+ // LOG
+ LogWarning("Unable to parse float: unexpected EOL [#1]");
+ fOut = 0.0f;
+ ++iLineNumber;
+ return;
+ }
+ // parse the first float
+ filePtr = fast_atof_move(filePtr,fOut);
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshLong(unsigned int& iOut)
+{
+ // Skip spaces and tabs
+ if (!SkipSpaces(&filePtr))
+ {
+ // LOG
+ LogWarning("Unable to parse long: unexpected EOL [#1]");
+ iOut = 0;
+ ++iLineNumber;
+ return;
+ }
+ // parse the value
+ iOut = strtol10(filePtr,&filePtr);
+}
diff --git a/src/3rdparty/assimp/code/ASEParser.h b/src/3rdparty/assimp/code/ASEParser.h
new file mode 100644
index 000000000..57fb6c553
--- /dev/null
+++ b/src/3rdparty/assimp/code/ASEParser.h
@@ -0,0 +1,669 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file Defines the helper data structures for importing ASE files */
+#ifndef AI_ASEFILEHELPER_H_INC
+#define AI_ASEFILEHELPER_H_INC
+
+// STL/CRT headers
+#include <string>
+#include <vector>
+#include <list>
+
+// public ASSIMP headers
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+// for some helper routines like IsSpace()
+#include "ParsingUtils.h"
+#include "qnan.h"
+
+// ASE is quite similar to 3ds. We can reuse some structures
+#include "3DSLoader.h"
+
+namespace Assimp {
+namespace ASE {
+
+using namespace D3DS;
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing an ASE material */
+struct Material : public D3DS::Material
+{
+ //! Default constructor
+ Material() : pcInstance(NULL), bNeed (false)
+ {}
+
+ //! Contains all sub materials of this material
+ std::vector<Material> avSubMaterials;
+
+ //! MaterialHelper object
+ MaterialHelper* pcInstance;
+
+ //! Can we remove this material?
+ bool bNeed;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE file face */
+struct Face : public FaceWithSmoothingGroup
+{
+ //! Default constructor. Initializes everything with 0
+ Face()
+ {
+ mColorIndices[0] = mColorIndices[1] = mColorIndices[2] = 0;
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
+ {
+ amUVIndices[i][0] = amUVIndices[i][1] = amUVIndices[i][2] = 0;
+ }
+
+ iMaterial = DEFAULT_MATINDEX;
+ iFace = 0;
+ }
+
+ //! special value to indicate that no material index has
+ //! been assigned to a face. The default material index
+ //! will replace this value later.
+ static const unsigned int DEFAULT_MATINDEX = 0xFFFFFFFF;
+
+
+
+ //! Indices into each list of texture coordinates
+ unsigned int amUVIndices[AI_MAX_NUMBER_OF_TEXTURECOORDS][3];
+
+ //! Index into the list of vertex colors
+ unsigned int mColorIndices[3];
+
+ //! (Sub)Material index to be assigned to this face
+ unsigned int iMaterial;
+
+ //! Index of the face. It is not specified whether it is
+ //! a requirement of the file format that all faces are
+ //! written in sequential order, so we have to expect this case
+ unsigned int iFace;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE file bone */
+struct Bone
+{
+ //! Constructor
+ Bone()
+ {
+ static int iCnt = 0;
+
+ // Generate a default name for the bone
+ char szTemp[128];
+ ::sprintf(szTemp,"UNNAMED_%i",iCnt++);
+ mName = szTemp;
+ }
+
+ //! Construction from an existing name
+ Bone( const std::string& name)
+ : mName (name)
+ {}
+
+ //! Name of the bone
+ std::string mName;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE file bone vertex */
+struct BoneVertex
+{
+ //! Bone and corresponding vertex weight.
+ //! -1 for unrequired bones ....
+ std::vector<std::pair<int,float> > mBoneWeights;
+
+ //! Position of the bone vertex.
+ //! MUST be identical to the vertex position
+ //aiVector3D mPosition;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE file animation */
+struct Animation
+{
+ enum Type
+ {
+ TRACK = 0x0,
+ BEZIER = 0x1,
+ TCB = 0x2
+ } mRotationType, mScalingType, mPositionType;
+
+ Animation()
+ : mRotationType (TRACK)
+ , mScalingType (TRACK)
+ , mPositionType (TRACK)
+ {}
+
+ //! List of track rotation keyframes
+ std::vector< aiQuatKey > akeyRotations;
+
+ //! List of track position keyframes
+ std::vector< aiVectorKey > akeyPositions;
+
+ //! List of track scaling keyframes
+ std::vector< aiVectorKey > akeyScaling;
+
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent the inheritance information of an ASE node */
+struct InheritanceInfo
+{
+ //! Default constructor
+ InheritanceInfo()
+ {
+ // set the inheritance flag for all axes by default to true
+ for (unsigned int i = 0; i < 3;++i)
+ abInheritPosition[i] = abInheritRotation[i] = abInheritScaling[i] = true;
+ }
+
+ //! Inherit the parent's position?, axis order is x,y,z
+ bool abInheritPosition[3];
+
+ //! Inherit the parent's rotation?, axis order is x,y,z
+ bool abInheritRotation[3];
+
+ //! Inherit the parent's scaling?, axis order is x,y,z
+ bool abInheritScaling[3];
+};
+
+// ---------------------------------------------------------------------------
+/** Represents an ASE file node. Base class for mesh, light and cameras */
+struct BaseNode
+{
+ enum Type {Light, Camera, Mesh, Dummy} mType;
+
+ //! Constructor. Creates a default name for the node
+ BaseNode(Type _mType)
+ : mType (_mType)
+ , mProcessed (false)
+ {
+ // generate a default name for the node
+ static int iCnt = 0;
+ char szTemp[128]; // should be sufficiently large
+ ::sprintf(szTemp,"UNNAMED_%i",iCnt++);
+ mName = szTemp;
+
+ // Set mTargetPosition to qnan
+ const float qnan = get_qnan();
+ mTargetPosition.x = qnan;
+ }
+
+ //! Name of the mesh
+ std::string mName;
+
+ //! Name of the parent of the node
+ //! "" if there is no parent ...
+ std::string mParent;
+
+ //! Transformation matrix of the node
+ aiMatrix4x4 mTransform;
+
+ //! Target position (target lights and cameras)
+ aiVector3D mTargetPosition;
+
+ //! Specifies which axes transformations a node inherits
+ //! from its parent ...
+ InheritanceInfo inherit;
+
+ //! Animation channels for the node
+ Animation mAnim;
+
+ //! Needed for lights and cameras: target animation channel
+ //! Should contain position keys only.
+ Animation mTargetAnim;
+
+ bool mProcessed;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE file mesh */
+struct Mesh : public MeshWithSmoothingGroups<ASE::Face>, public BaseNode
+{
+ //! Constructor.
+ Mesh()
+ : BaseNode (BaseNode::Mesh)
+ , bSkip (false)
+ {
+ // use 2 texture vertex components by default
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
+ this->mNumUVComponents[c] = 2;
+
+ // setup the default material index by default
+ iMaterialIndex = Face::DEFAULT_MATINDEX;
+ }
+
+ //! List of all texture coordinate sets
+ std::vector<aiVector3D> amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+
+ //! List of all vertex color sets.
+ std::vector<aiColor4D> mVertexColors;
+
+ //! List of all bone vertices
+ std::vector<BoneVertex> mBoneVertices;
+
+ //! List of all bones
+ std::vector<Bone> mBones;
+
+ //! Material index of the mesh
+ unsigned int iMaterialIndex;
+
+ //! Number of vertex components for each UVW set
+ unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+
+ //! used internally
+ bool bSkip;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE light source */
+struct Light : public BaseNode
+{
+ enum LightType
+ {
+ OMNI,
+ TARGET,
+ FREE,
+ DIRECTIONAL
+ };
+
+ //! Constructor.
+ Light()
+ : BaseNode (BaseNode::Light)
+ , mLightType (OMNI)
+ , mColor (1.f,1.f,1.f)
+ , mIntensity (1.f) // light is white by default
+ , mAngle (45.f)
+ , mFalloff (0.f)
+ {
+ }
+
+ LightType mLightType;
+ aiColor3D mColor;
+ float mIntensity;
+ float mAngle; // in degrees
+ float mFalloff;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE camera */
+struct Camera : public BaseNode
+{
+ enum CameraType
+ {
+ FREE,
+ TARGET
+ };
+
+ //! Constructor
+ Camera()
+ : BaseNode (BaseNode::Camera)
+ , mFOV (0.75f) // in radians
+ , mNear (0.1f)
+ , mFar (1000.f) // could be zero
+ , mCameraType (FREE)
+ {
+ }
+
+ float mFOV, mNear, mFar;
+ CameraType mCameraType;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE helper object (dummy) */
+struct Dummy : public BaseNode
+{
+ //! Constructor
+ Dummy()
+ : BaseNode (BaseNode::Dummy)
+ {
+ }
+};
+
+// Parameters to Parser::Parse()
+#define AI_ASE_NEW_FILE_FORMAT 200
+#define AI_ASE_OLD_FILE_FORMAT 110
+
+// Internally we're a little bit more tolerant
+#define AI_ASE_IS_NEW_FILE_FORMAT() (iFileFormat >= 200)
+#define AI_ASE_IS_OLD_FILE_FORMAT() (iFileFormat < 200)
+
+// -------------------------------------------------------------------------------
+/** \brief Class to parse ASE files
+ */
+class Parser
+{
+
+private:
+
+ Parser() {}
+
+public:
+
+ // -------------------------------------------------------------------
+ //! Construct a parser from a given input file which is
+ //! guaranted to be terminated with zero.
+ //! @param szFile Input file
+ //! @param fileFormatDefault Assumed file format version. If the
+ //! file format is specified in the file the new value replaces
+ //! the default value.
+ Parser (const char* szFile, unsigned int fileFormatDefault);
+
+ // -------------------------------------------------------------------
+ //! Parses the file into the parsers internal representation
+ void Parse();
+
+
+private:
+
+ // -------------------------------------------------------------------
+ //! Parse the *SCENE block in a file
+ void ParseLV1SceneBlock();
+
+ // -------------------------------------------------------------------
+ //! Parse the *MESH_SOFTSKINVERTS block in a file
+ void ParseLV1SoftSkinBlock();
+
+ // -------------------------------------------------------------------
+ //! Parse the *MATERIAL_LIST block in a file
+ void ParseLV1MaterialListBlock();
+
+ // -------------------------------------------------------------------
+ //! Parse a *<xxx>OBJECT block in a file
+ //! \param mesh Node to be filled
+ void ParseLV1ObjectBlock(BaseNode& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MATERIAL blocks in a material list
+ //! \param mat Material structure to be filled
+ void ParseLV2MaterialBlock(Material& mat);
+
+ // -------------------------------------------------------------------
+ //! Parse a *NODE_TM block in a file
+ //! \param mesh Node (!) object to be filled
+ void ParseLV2NodeTransformBlock(BaseNode& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *TM_ANIMATION block in a file
+ //! \param mesh Mesh object to be filled
+ void ParseLV2AnimationBlock(BaseNode& mesh);
+ void ParseLV3PosAnimationBlock(ASE::Animation& anim);
+ void ParseLV3ScaleAnimationBlock(ASE::Animation& anim);
+ void ParseLV3RotAnimationBlock(ASE::Animation& anim);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH block in a file
+ //! \param mesh Mesh object to be filled
+ void ParseLV2MeshBlock(Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *LIGHT_SETTINGS block in a file
+ //! \param light Light object to be filled
+ void ParseLV2LightSettingsBlock(Light& light);
+
+ // -------------------------------------------------------------------
+ //! Parse a *CAMERA_SETTINGS block in a file
+ //! \param cam Camera object to be filled
+ void ParseLV2CameraSettingsBlock(Camera& cam);
+
+ // -------------------------------------------------------------------
+ //! Parse the *MAP_XXXXXX blocks in a material
+ //! \param map Texture structure to be filled
+ void ParseLV3MapBlock(Texture& map);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_VERTEX_LIST block in a file
+ //! \param iNumVertices Value of *MESH_NUMVERTEX, if present.
+ //! Otherwise zero. This is used to check the consistency of the file.
+ //! A warning is sent to the logger if the validations fails.
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MeshVertexListBlock(
+ unsigned int iNumVertices,Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_FACE_LIST block in a file
+ //! \param iNumFaces Value of *MESH_NUMFACES, if present.
+ //! Otherwise zero. This is used to check the consistency of the file.
+ //! A warning is sent to the logger if the validations fails.
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MeshFaceListBlock(
+ unsigned int iNumFaces,Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_TVERT_LIST block in a file
+ //! \param iNumVertices Value of *MESH_NUMTVERTEX, if present.
+ //! Otherwise zero. This is used to check the consistency of the file.
+ //! A warning is sent to the logger if the validations fails.
+ //! \param mesh Mesh object to be filled
+ //! \param iChannel Output UVW channel
+ void ParseLV3MeshTListBlock(
+ unsigned int iNumVertices,Mesh& mesh, unsigned int iChannel = 0);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_TFACELIST block in a file
+ //! \param iNumFaces Value of *MESH_NUMTVFACES, if present.
+ //! Otherwise zero. This is used to check the consistency of the file.
+ //! A warning is sent to the logger if the validations fails.
+ //! \param mesh Mesh object to be filled
+ //! \param iChannel Output UVW channel
+ void ParseLV3MeshTFaceListBlock(
+ unsigned int iNumFaces,Mesh& mesh, unsigned int iChannel = 0);
+
+ // -------------------------------------------------------------------
+ //! Parse an additional mapping channel
+ //! (specified via *MESH_MAPPINGCHANNEL)
+ //! \param iChannel Channel index to be filled
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MappingChannel(
+ unsigned int iChannel, Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_CVERTLIST block in a file
+ //! \param iNumVertices Value of *MESH_NUMCVERTEX, if present.
+ //! Otherwise zero. This is used to check the consistency of the file.
+ //! A warning is sent to the logger if the validations fails.
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MeshCListBlock(
+ unsigned int iNumVertices, Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_CFACELIST block in a file
+ //! \param iNumFaces Value of *MESH_NUMCVFACES, if present.
+ //! Otherwise zero. This is used to check the consistency of the file.
+ //! A warning is sent to the logger if the validations fails.
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MeshCFaceListBlock(
+ unsigned int iNumFaces, Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_NORMALS block in a file
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MeshNormalListBlock(Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_WEIGHTSblock in a file
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MeshWeightsBlock(Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse the bone list of a file
+ //! \param mesh Mesh object to be filled
+ //! \param iNumBones Number of bones in the mesh
+ void ParseLV4MeshBones(unsigned int iNumBones,Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse the bone vertices list of a file
+ //! \param mesh Mesh object to be filled
+ //! \param iNumVertices Number of vertices to be parsed
+ void ParseLV4MeshBonesVertices(unsigned int iNumVertices,Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_FACE block in a file
+ //! \param out receive the face data
+ void ParseLV4MeshFace(ASE::Face& out);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_VERT block in a file
+ //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...)
+ //! \param apOut Output buffer (3 floats)
+ //! \param rIndexOut Output index
+ void ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_VERT block in a file
+ //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...)
+ //! \param apOut Output buffer (3 floats)
+ void ParseLV4MeshFloatTriple(float* apOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_TFACE block in a file
+ //! (also works for MESH_CFACE)
+ //! \param apOut Output buffer (3 ints)
+ //! \param rIndexOut Output index
+ void ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_TFACE block in a file
+ //! (also works for MESH_CFACE)
+ //! \param apOut Output buffer (3 ints)
+ void ParseLV4MeshLongTriple(unsigned int* apOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a single float element
+ //! \param fOut Output float
+ void ParseLV4MeshFloat(float& fOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a single int element
+ //! \param iOut Output integer
+ void ParseLV4MeshLong(unsigned int& iOut);
+
+ // -------------------------------------------------------------------
+ //! Skip everything to the next: '*' or '\0'
+ bool SkipToNextToken();
+
+ // -------------------------------------------------------------------
+ //! Skip the current section until the token after the closing }.
+ //! This function handles embedded subsections correctly
+ bool SkipSection();
+
+ // -------------------------------------------------------------------
+ //! Output a warning to the logger
+ //! \param szWarn Warn message
+ void LogWarning(const char* szWarn);
+
+ // -------------------------------------------------------------------
+ //! Output a message to the logger
+ //! \param szWarn Message
+ void LogInfo(const char* szWarn);
+
+ // -------------------------------------------------------------------
+ //! Output an error to the logger
+ //! \param szWarn Error message
+ void LogError(const char* szWarn);
+
+ // -------------------------------------------------------------------
+ //! Parse a string, enclosed in double quotation marks
+ //! \param out Output string
+ //! \param szName Name of the enclosing element -> used in error
+ //! messages.
+ //! \return false if an error occured
+ bool ParseString(std::string& out,const char* szName);
+
+public:
+
+ //! Pointer to current data
+ const char* filePtr;
+
+ //! background color to be passed to the viewer
+ //! QNAN if none was found
+ aiColor3D m_clrBackground;
+
+ //! Base ambient color to be passed to all materials
+ //! QNAN if none was found
+ aiColor3D m_clrAmbient;
+
+ //! List of all materials found in the file
+ std::vector<Material> m_vMaterials;
+
+ //! List of all meshes found in the file
+ std::vector<Mesh> m_vMeshes;
+
+ //! List of all dummies found in the file
+ std::vector<Dummy> m_vDummies;
+
+ //! List of all lights found in the file
+ std::vector<Light> m_vLights;
+
+ //! List of all cameras found in the file
+ std::vector<Camera> m_vCameras;
+
+ //! Current line in the file
+ unsigned int iLineNumber;
+
+ //! First frame
+ unsigned int iFirstFrame;
+
+ //! Last frame
+ unsigned int iLastFrame;
+
+ //! Frame speed - frames per second
+ unsigned int iFrameSpeed;
+
+ //! Ticks per frame
+ unsigned int iTicksPerFrame;
+
+ //! true if the last character read was an end-line character
+ bool bLastWasEndLine;
+
+ //! File format version
+ unsigned int iFileFormat;
+};
+
+
+} // Namespace ASE
+} // Namespace ASSIMP
+
+#endif // !! include guard
diff --git a/src/3rdparty/assimp/code/Assimp.cpp b/src/3rdparty/assimp/code/Assimp.cpp
new file mode 100644
index 000000000..e2a1f2f7b
--- /dev/null
+++ b/src/3rdparty/assimp/code/Assimp.cpp
@@ -0,0 +1,731 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file Assimp.cpp
+ * @brief Implementation of the Plain-C API
+ */
+
+#include "AssimpPCH.h"
+#include "../include/assimp.h"
+#include "../include/aiFileIO.h"
+
+#include "GenericProperty.h"
+
+// ------------------------------------------------------------------------------------------------
+#ifdef AI_C_THREADSAFE
+# include <boost/thread/thread.hpp>
+# include <boost/thread/mutex.hpp>
+#endif
+// ------------------------------------------------------------------------------------------------
+using namespace Assimp;
+
+namespace Assimp
+{
+ /** Stores the importer objects for all active import processes */
+ typedef std::map<const aiScene*, Assimp::Importer*> ImporterMap;
+
+ /** Stores the LogStream objects for all active C log streams */
+ struct mpred {
+ bool operator () (const aiLogStream& s0, const aiLogStream& s1) const {
+ return s0.callback<s1.callback&&s0.user<s1.user;
+ }
+ };
+ typedef std::map<aiLogStream, Assimp::LogStream*, mpred> LogStreamMap;
+
+ /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */
+ typedef std::list<Assimp::LogStream*> PredefLogStreamMap;
+
+ /** Local storage of all active import processes */
+ static ImporterMap gActiveImports;
+
+ /** Local storage of all active log streams */
+ static LogStreamMap gActiveLogStreams;
+
+ /** Local storage of LogStreams allocated by #aiGetPredefinedLogStream */
+ static PredefLogStreamMap gPredefinedStreams;
+
+ /** Error message of the last failed import process */
+ static std::string gLastErrorString;
+
+ /** Verbose logging active or not? */
+ static aiBool gVerboseLogging = false;
+}
+
+/** Configuration properties */
+static ImporterPimpl::IntPropertyMap gIntProperties;
+static ImporterPimpl::FloatPropertyMap gFloatProperties;
+static ImporterPimpl::StringPropertyMap gStringProperties;
+
+#ifdef AI_C_THREADSAFE
+/** Global mutex to manage the access to the importer map */
+static boost::mutex gMutex;
+
+/** Global mutex to manage the access to the logstream map */
+static boost::mutex gLogStreamMutex;
+#endif
+
+class CIOSystemWrapper;
+class CIOStreamWrapper;
+
+// ------------------------------------------------------------------------------------------------
+// Custom IOStream implementation for the C-API
+class CIOStreamWrapper : public IOStream
+{
+ friend class CIOSystemWrapper;
+public:
+
+ CIOStreamWrapper(aiFile* pFile)
+ : mFile(pFile)
+ {}
+
+ // ...................................................................
+ size_t 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 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 Seek(size_t pOffset,
+ aiOrigin pOrigin
+ ){
+ return mFile->SeekProc(mFile,pOffset,pOrigin);
+ }
+
+ // ...................................................................
+ size_t Tell(void) const {
+ return mFile->TellProc(mFile);
+ }
+
+ // ...................................................................
+ size_t FileSize() const {
+ return mFile->FileSizeProc(mFile);
+ }
+
+ // ...................................................................
+ void Flush () {
+ return mFile->FlushProc(mFile);
+ }
+
+private:
+ aiFile* mFile;
+};
+
+// ------------------------------------------------------------------------------------------------
+// Custom IOStream implementation for the C-API
+class CIOSystemWrapper : public IOSystem
+{
+public:
+ CIOSystemWrapper(aiFileIO* pFile)
+ : mFileSystem(pFile)
+ {}
+
+ // ...................................................................
+ bool Exists( const char* pFile) const {
+ CIOSystemWrapper* pip = const_cast<CIOSystemWrapper*>(this);
+ IOStream* p = pip->Open(pFile);
+ if (p){
+ pip->Close(p);
+ return true;
+ }
+ return false;
+ }
+
+ // ...................................................................
+ char getOsSeparator() const {
+#ifndef _WIN32
+ return '/';
+#else
+ return '\\';
+#endif
+ }
+
+ // ...................................................................
+ IOStream* Open(const char* pFile,const char* pMode = "rb") {
+ aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,pMode);
+ if (!p) {
+ return NULL;
+ }
+ return new CIOStreamWrapper(p);
+ }
+
+ // ...................................................................
+ void Close( IOStream* pFile) {
+ if (!pFile) {
+ return;
+ }
+ mFileSystem->CloseProc(mFileSystem,((CIOStreamWrapper*) pFile)->mFile);
+ delete pFile;
+ }
+private:
+ aiFileIO* mFileSystem;
+};
+
+// ------------------------------------------------------------------------------------------------
+// Custom LogStream implementation for the C-API
+class LogToCallbackRedirector : public LogStream
+{
+public:
+ LogToCallbackRedirector(const aiLogStream& s)
+ : stream (s) {
+ ai_assert(NULL != s.callback);
+ }
+
+ ~LogToCallbackRedirector() {
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gLogStreamMutex);
+#endif
+ // (HACK) Check whether the 'stream.user' pointer points to a
+ // custom LogStream allocated by #aiGetPredefinedLogStream.
+ // In this case, we need to delete it, too. Of course, this
+ // might cause strange problems, but the chance is quite low.
+
+ PredefLogStreamMap::iterator it = std::find(gPredefinedStreams.begin(),
+ gPredefinedStreams.end(), (Assimp::LogStream*)stream.user);
+
+ if (it != gPredefinedStreams.end()) {
+ delete *it;
+ gPredefinedStreams.erase(it);
+ }
+ }
+
+ /** @copydoc LogStream::write */
+ void write(const char* message) {
+ stream.callback(message,stream.user);
+ }
+
+private:
+ aiLogStream stream;
+};
+
+// ------------------------------------------------------------------------------------------------
+void ReportSceneNotFoundError()
+{
+ DefaultLogger::get()->error("Unable to find the Assimp::Importer for this aiScene. "
+ "Are you playing fools with us? Don't mix cpp and c API. Thanks.");
+
+ assert(false);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the given file and returns its content.
+const aiScene* aiImportFile( const char* pFile, unsigned int pFlags)
+{
+ return aiImportFileEx(pFile,pFlags,NULL);
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiScene* aiImportFileEx( const char* pFile, unsigned int pFlags,
+ aiFileIO* pFS)
+{
+ ai_assert(NULL != pFile);
+
+ const aiScene* scene = NULL;
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // create an Importer for this file
+ Assimp::Importer* imp = new Assimp::Importer();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+ // copy the global property lists to the Importer instance
+ imp->pimpl->mIntProperties = gIntProperties;
+ imp->pimpl->mFloatProperties = gFloatProperties;
+ imp->pimpl->mStringProperties = gStringProperties;
+
+#ifdef AI_C_THREADSAFE
+ lock.unlock();
+#endif
+
+ // setup a custom IO system if necessary
+ if (pFS) {
+ imp->SetIOHandler( new CIOSystemWrapper (pFS) );
+ }
+
+ // and have it read the file
+ scene = imp->ReadFile( pFile, pFlags);
+
+ // if succeeded, place it in the collection of active processes
+ if ( scene) {
+#ifdef AI_C_THREADSAFE
+ lock.lock();
+#endif
+ gActiveImports[scene] = imp;
+ }
+ else {
+ // if failed, extract error code and destroy the import
+ gLastErrorString = imp->GetErrorString();
+ delete imp;
+ }
+
+ // return imported data. If the import failed the pointer is NULL anyways
+ ASSIMP_END_EXCEPTION_REGION(const aiScene*);
+ return scene;
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiScene* aiImportFileFromMemory(
+ const char* pBuffer,
+ unsigned int pLength,
+ unsigned int pFlags,
+ const char* pHint)
+{
+ ai_assert(NULL != pBuffer && 0 != pLength);
+
+ const aiScene* scene = NULL;
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // create an Importer for this file
+ Assimp::Importer* imp = new Assimp::Importer();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+ // copy the global property lists to the Importer instance
+ imp->pimpl->mIntProperties = gIntProperties;
+ imp->pimpl->mFloatProperties = gFloatProperties;
+ imp->pimpl->mStringProperties = gStringProperties;
+
+#ifdef AI_C_THREADSAFE
+ lock.unlock();
+#endif
+
+ // and have it read the file from the memory buffer
+ scene = imp->ReadFileFromMemory( pBuffer, pLength, pFlags,pHint);
+
+ // if succeeded, place it in the collection of active processes
+ if ( scene) {
+#ifdef AI_C_THREADSAFE
+ lock.lock();
+#endif
+ gActiveImports[scene] = imp;
+ }
+ else {
+ // if failed, extract error code and destroy the import
+ gLastErrorString = imp->GetErrorString();
+ delete imp;
+ }
+ // return imported data. If the import failed the pointer is NULL anyways
+ ASSIMP_END_EXCEPTION_REGION(const aiScene*);
+ return scene;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Releases all resources associated with the given import process.
+void aiReleaseImport( const aiScene* pScene)
+{
+ if (!pScene) {
+ return;
+ }
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+
+ // find the importer associated with this data
+ ImporterMap::iterator it = gActiveImports.find( pScene);
+ // it should be there... else the user is playing fools with us
+ if ( it == gActiveImports.end()) {
+ ReportSceneNotFoundError();
+ return;
+ }
+
+ // kill the importer, the data dies with it
+ delete it->second;
+ gActiveImports.erase( it);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API const aiScene* aiApplyPostProcessing(const aiScene* pScene,
+ unsigned int pFlags)
+{
+ const aiScene* sc = NULL;
+
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+ // find the importer associated with this data
+ ImporterMap::iterator it = gActiveImports.find( pScene);
+ // it should be there... else the user is playing fools with us
+ if ( it == gActiveImports.end()) {
+ ReportSceneNotFoundError();
+ return NULL;
+ }
+#ifdef AI_C_THREADSAFE
+ lock.unlock();
+#endif
+ sc = it->second->ApplyPostProcessing(pFlags);
+#ifdef AI_C_THREADSAFE
+ lock.lock();
+#endif
+ if (!sc) {
+ // kill the importer, the data dies with it
+ delete it->second;
+ gActiveImports.erase( it);
+ return NULL;
+ }
+
+ ASSIMP_END_EXCEPTION_REGION(const aiScene*);
+ return sc;
+}
+
+// ------------------------------------------------------------------------------------------------
+void CallbackToLogRedirector (const char* msg, char* dt)
+{
+ ai_assert(NULL != msg && NULL != dt);
+ LogStream* s = (LogStream*)dt;
+
+ s->write(msg);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiLogStream aiGetPredefinedLogStream(aiDefaultLogStream pStream,const char* file)
+{
+ aiLogStream sout;
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ LogStream* stream = LogStream::createDefaultStream(pStream,file);
+ if (!stream) {
+ sout.callback = NULL;
+ sout.user = NULL;
+ }
+ else {
+ sout.callback = &CallbackToLogRedirector;
+ sout.user = (char*)stream;
+ }
+ gPredefinedStreams.push_back(stream);
+ ASSIMP_END_EXCEPTION_REGION(aiLogStream);
+ return sout;
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiAttachLogStream( const aiLogStream* stream )
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gLogStreamMutex);
+#endif
+
+ LogStream* lg = new LogToCallbackRedirector(*stream);
+ gActiveLogStreams[*stream] = lg;
+
+ if (DefaultLogger::isNullLogger()) {
+ DefaultLogger::create(NULL,(gVerboseLogging == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL));
+ }
+ DefaultLogger::get()->attachStream(lg);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiReturn aiDetachLogStream( const aiLogStream* stream)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gLogStreamMutex);
+#endif
+ // find the logstream associated with this data
+ LogStreamMap::iterator it = gActiveLogStreams.find( *stream);
+ // it should be there... else the user is playing fools with us
+ if ( it == gActiveLogStreams.end()) {
+ return AI_FAILURE;
+ }
+ DefaultLogger::get()->detatchStream( it->second );
+ delete it->second;
+
+ gActiveLogStreams.erase( it);
+
+ if (gActiveLogStreams.empty()) {
+ DefaultLogger::kill();
+ }
+ ASSIMP_END_EXCEPTION_REGION(aiReturn);
+ return AI_SUCCESS;
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiDetachAllLogStreams(void)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gLogStreamMutex);
+#endif
+ for (LogStreamMap::iterator it = gActiveLogStreams.begin(); it != gActiveLogStreams.end(); ++it) {
+ DefaultLogger::get()->detatchStream( it->second );
+ delete it->second;
+ }
+ gActiveLogStreams.clear();
+ DefaultLogger::kill();
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiEnableVerboseLogging(aiBool d)
+{
+ if (!DefaultLogger::isNullLogger()) {
+ DefaultLogger::get()->setLogSeverity((d == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL));
+ }
+ gVerboseLogging = d;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the error text of the last failed import process.
+const char* aiGetErrorString()
+{
+ return gLastErrorString.c_str();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the error text of the last failed import process.
+aiBool aiIsExtensionSupported(const char* szExtension)
+{
+ ai_assert(NULL != szExtension);
+ aiBool candoit=AI_FALSE;
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+
+ if (!gActiveImports.empty()) {
+ return ((*(gActiveImports.begin())).second->IsExtensionSupported( szExtension )) ? AI_TRUE : AI_FALSE;
+ }
+
+ // fixme: no need to create a temporary Importer instance just for that ..
+ Assimp::Importer tmp;
+ candoit = tmp.IsExtensionSupported(std::string(szExtension)) ? AI_TRUE : AI_FALSE;
+
+ ASSIMP_END_EXCEPTION_REGION(aiBool);
+ return candoit;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all file extensions supported by ASSIMP
+void aiGetExtensionList(aiString* szOut)
+{
+ ai_assert(NULL != szOut);
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+
+ if (!gActiveImports.empty()) {
+ (*(gActiveImports.begin())).second->GetExtensionList(*szOut);
+ return;
+ }
+ // fixme: no need to create a temporary Importer instance just for that ..
+ Assimp::Importer tmp;
+ tmp.GetExtensionList(*szOut);
+
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the memory requirements for a particular import.
+void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn,
+ C_STRUCT aiMemoryInfo* in)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+
+ // find the importer associated with this data
+ ImporterMap::iterator it = gActiveImports.find( pIn);
+ // it should be there... else the user is playing fools with us
+ if ( it == gActiveImports.end()) {
+ ReportSceneNotFoundError();
+ return;
+ }
+ // get memory statistics
+#ifdef AI_C_THREADSAFE
+ lock.unlock();
+#endif
+ it->second->GetMemoryRequirements(*in);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyInteger
+ASSIMP_API void aiSetImportPropertyInteger(const char* szName, int value)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+ SetGenericProperty<int>(gIntProperties,szName,value,NULL);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyFloat
+ASSIMP_API void aiSetImportPropertyFloat(const char* szName, float value)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+ SetGenericProperty<float>(gFloatProperties,szName,value,NULL);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyString
+ASSIMP_API void aiSetImportPropertyString(const char* szName,
+ const C_STRUCT aiString* st)
+{
+ if (!st) {
+ return;
+ }
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+ SetGenericProperty<std::string>(gStringProperties,szName,
+ std::string( st->data ),NULL);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Rotation matrix to quaternion
+ASSIMP_API void aiCreateQuaternionFromMatrix(aiQuaternion* quat,const aiMatrix3x3* mat)
+{
+ ai_assert(NULL != quat && NULL != mat);
+ *quat = aiQuaternion(*mat);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix decomposition
+ASSIMP_API void aiDecomposeMatrix(const aiMatrix4x4* mat,aiVector3D* scaling,
+ aiQuaternion* rotation,
+ aiVector3D* position)
+{
+ ai_assert(NULL != rotation && NULL != position && NULL != scaling && NULL != mat);
+ mat->Decompose(*scaling,*rotation,*position);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix transpose
+ASSIMP_API void aiTransposeMatrix3(aiMatrix3x3* mat)
+{
+ ai_assert(NULL != mat);
+ mat->Transpose();
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiTransposeMatrix4(aiMatrix4x4* mat)
+{
+ ai_assert(NULL != mat);
+ mat->Transpose();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Vector transformation
+ASSIMP_API void aiTransformVecByMatrix3(C_STRUCT aiVector3D* vec,
+ const C_STRUCT aiMatrix3x3* mat)
+{
+ ai_assert(NULL != mat && NULL != vec);
+ *vec *= (*mat);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiTransformVecByMatrix4(C_STRUCT aiVector3D* vec,
+ const C_STRUCT aiMatrix4x4* mat)
+{
+ ai_assert(NULL != mat && NULL != vec);
+ *vec *= (*mat);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix multiplication
+ASSIMP_API void aiMultiplyMatrix4(
+ C_STRUCT aiMatrix4x4* dst,
+ const C_STRUCT aiMatrix4x4* src)
+{
+ ai_assert(NULL != dst && NULL != src);
+ *dst = (*dst) * (*src);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiMultiplyMatrix3(
+ C_STRUCT aiMatrix3x3* dst,
+ const C_STRUCT aiMatrix3x3* src)
+{
+ ai_assert(NULL != dst && NULL != src);
+ *dst = (*dst) * (*src);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix identity
+ASSIMP_API void aiIdentityMatrix3(
+ C_STRUCT aiMatrix3x3* mat)
+{
+ ai_assert(NULL != mat);
+ *mat = aiMatrix3x3();
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiIdentityMatrix4(
+ C_STRUCT aiMatrix4x4* mat)
+{
+ ai_assert(NULL != mat);
+ *mat = aiMatrix4x4();
+}
+
+
diff --git a/src/3rdparty/assimp/code/AssimpPCH.cpp b/src/3rdparty/assimp/code/AssimpPCH.cpp
new file mode 100644
index 000000000..cdc007c56
--- /dev/null
+++ b/src/3rdparty/assimp/code/AssimpPCH.cpp
@@ -0,0 +1,70 @@
+
+// Actually just a dummy, used by the compiler to build the precompiled header.
+
+#include "AssimpPCH.h"
+#include "./../include/aiVersion.h"
+
+// --------------------------------------------------------------------------------
+// Legal information string - dont't remove from image!
+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-2010, ASSIMP Development 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 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get Assimp major version
+ASSIMP_API unsigned int aiGetVersionMajor () {
+ return 2;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 SVNRevision;
+}
+
diff --git a/src/3rdparty/assimp/code/AssimpPCH.h b/src/3rdparty/assimp/code/AssimpPCH.h
new file mode 100644
index 000000000..a0eb1faec
--- /dev/null
+++ b/src/3rdparty/assimp/code/AssimpPCH.h
@@ -0,0 +1,149 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file AssimpPCH.h
+ * PCH master include. Every unit in Assimp has to include it.
+ */
+
+#ifndef ASSIMP_PCH_INCLUDED
+#define ASSIMP_PCH_INCLUDED
+#define ASSIMP_INTERNAL_BUILD
+
+// ----------------------------------------------------------------------------------------
+/* General compile config taken from aiDefines.h. It is important that the user compiles
+ * using exactly the same settings in aiDefines.h. Settings in AssimpPCH.h may differ,
+ * they won't affect the public API.
+ */
+#include "../include/aiDefines.h"
+
+/* Include our stdint.h replacement header for MSVC, take the global header for gcc/mingw
+ */
+#ifdef _MSC_VER
+# include "pstdint.h"
+#else
+# include <stdint.h>
+#endif
+
+/* Undefine the min/max macros defined by some platform headers (namely Windows.h) to
+ * avoid obvious conflicts with std::min() and std::max().
+ */
+#undef min
+#undef max
+
+/* Concatenate two tokens after evaluating them
+ */
+#define _AI_CONCAT(a,b) a ## b
+#define AI_CONCAT(a,b) _AI_CONCAT(a,b)
+
+/* Helper macro to set a pointer to NULL in debug builds
+ */
+#if (defined _DEBUG)
+# define AI_DEBUG_INVALIDATE_PTR(x) x = NULL;
+#else
+# define AI_DEBUG_INVALIDATE_PTR(x)
+#endif
+
+/* Beginning with MSVC8 some C string manipulation functions are mapped to their _safe_
+ * counterparts (e.g. _itoa_s). This avoids a lot of trouble with deprecation warnings.
+ */
+#if _MSC_VER >= 1400 && !(defined _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
+# define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
+#endif
+
+/* size_t to unsigned int, possible loss of data. The compiler is right with his warning
+ * but this loss of data won't be a problem for us. So shut up, little boy.
+ */
+#ifdef _MSC_VER
+# pragma warning (disable : 4267)
+#endif
+
+// ----------------------------------------------------------------------------------------
+/* Actually that's not required for MSVC. It is included somewhere in the deeper parts of
+ * the MSVC STL but it's necessary for proper build with STLport.
+ */
+#include <ctype.h>
+
+// Runtime/STL headers
+#include <vector>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+#include <sstream>
+#include <iomanip>
+#include <cassert>
+#include <stack>
+#include <queue>
+#include <iostream>
+#include <algorithm>
+#include <numeric>
+#include <new>
+#include <cstdio>
+
+// Boost headers
+#include <boost/pointer_cast.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/scoped_array.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/shared_array.hpp>
+//#include <boost/make_shared.hpp>
+#include <boost/format.hpp>
+#include <boost/foreach.hpp>
+#include <boost/static_assert.hpp>
+#include <boost/lexical_cast.hpp>
+
+// Public ASSIMP headers
+#include "../include/DefaultLogger.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
+#include "../include/aiScene.h"
+#include "../include/aiPostProcess.h"
+#include "../include/assimp.hpp"
+
+// Internal utility headers
+#include "BaseImporter.h"
+#include "MaterialSystem.h"
+#include "StringComparison.h"
+#include "StreamReader.h"
+#include "qnan.h"
+
+
+#endif // !! ASSIMP_PCH_INCLUDED
diff --git a/src/3rdparty/assimp/code/B3DImporter.cpp b/src/3rdparty/assimp/code/B3DImporter.cpp
new file mode 100644
index 000000000..d38f9aba4
--- /dev/null
+++ b/src/3rdparty/assimp/code/B3DImporter.cpp
@@ -0,0 +1,672 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file B3DImporter.cpp
+ * @brief Implementation of the b3d importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
+
+// internal headers
+#include "B3DImporter.h"
+#include "TextureTransform.h"
+#include "ConvertToLHProcess.h"
+
+using namespace Assimp;
+using namespace std;
+
+// (fixme, Aramis) quick workaround to get rid of all those signed to unsigned warnings
+#ifdef _MSC_VER
+# pragma warning (disable: 4018)
+#endif
+
+//#define DEBUG_B3D
+
+// ------------------------------------------------------------------------------------------------
+bool B3DImporter::CanRead( const std::string& pFile, IOSystem*, bool) const{
+
+ size_t pos=pFile.find_last_of( '.' );
+ if ( pos==string::npos ) return false;
+
+ string ext=pFile.substr( pos+1 );
+ if ( ext.size()!=3 ) return false;
+
+ return (ext[0]=='b' || ext[0]=='B') && (ext[1]=='3') && (ext[2]=='d' || ext[2]=='D');
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::GetExtensionList( std::set<std::string>& extensions ){
+ extensions.insert("b3d");
+}
+
+#ifdef DEBUG_B3D
+ extern "C"{ void _stdcall AllocConsole(); }
+#endif
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler){
+
+#ifdef DEBUG_B3D
+ AllocConsole();
+ freopen( "conin$","r",stdin );
+ freopen( "conout$","w",stdout );
+ freopen( "conout$","w",stderr );
+ cout<<"Hello world from the B3DImporter!"<<endl;
+#endif
+
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open B3D file " + pFile + ".");
+
+ // check whether the .b3d file is large enough to contain
+ // at least one chunk.
+ size_t fileSize = file->FileSize();
+ if ( fileSize<8 ) throw DeadlyImportError( "B3D File is too small.");
+
+ _pos=0;
+ _buf.resize( fileSize );
+ file->Read( &_buf[0],1,fileSize );
+ _stack.clear();
+
+ ReadBB3D( pScene );
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::Oops(){
+ throw DeadlyImportError( "B3D Importer - INTERNAL ERROR" );
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::Fail( string str ){
+#ifdef DEBUG_B3D
+ cout<<"Error in B3D file data: "<<str<<endl;
+#endif
+ throw DeadlyImportError( "B3D Importer - error in B3D file data: "+str );
+}
+
+// ------------------------------------------------------------------------------------------------
+int B3DImporter::ReadByte(){
+ if ( _pos<_buf.size() ) return _buf[_pos++];
+ Fail( "EOF" );
+ return 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+int B3DImporter::ReadInt(){
+ if ( _pos+4<=_buf.size() ){
+ int n=*(int*)&_buf[_pos];
+ _pos+=4;
+ return n;
+ }
+ Fail( "EOF" );
+ return 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+float B3DImporter::ReadFloat(){
+ if ( _pos+4<=_buf.size() ){
+ float n=*(float*)&_buf[_pos];
+ _pos+=4;
+ return n;
+ }
+ Fail( "EOF" );
+ return 0.0f;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector2D B3DImporter::ReadVec2(){
+ float x=ReadFloat();
+ float y=ReadFloat();
+ return aiVector2D( x,y );
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector3D B3DImporter::ReadVec3(){
+ float x=ReadFloat();
+ float y=ReadFloat();
+ float z=ReadFloat();
+ return aiVector3D( x,y,z );
+}
+
+// ------------------------------------------------------------------------------------------------
+aiQuaternion B3DImporter::ReadQuat(){
+ // (aramis_acg) Fix to adapt the loader to changed quat orientation
+ float w=-ReadFloat();
+ float x=ReadFloat();
+ float y=ReadFloat();
+ float z=ReadFloat();
+ return aiQuaternion( w,x,y,z );
+}
+
+// ------------------------------------------------------------------------------------------------
+string B3DImporter::ReadString(){
+ string str;
+ while ( _pos<_buf.size() ){
+ char c=(char)ReadByte();
+ if ( !c ) return str;
+ str+=c;
+ }
+ Fail( "EOF" );
+ return string();
+}
+
+// ------------------------------------------------------------------------------------------------
+string B3DImporter::ReadChunk(){
+ string tag;
+ for ( int i=0;i<4;++i ){
+ tag+=char( ReadByte() );
+ }
+#ifdef DEBUG_B3D
+// cout<<"ReadChunk:"<<tag<<endl;
+#endif
+ unsigned sz=(unsigned)ReadInt();
+ _stack.push_back( _pos+sz );
+ return tag;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ExitChunk(){
+ _pos=_stack.back();
+ _stack.pop_back();
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned B3DImporter::ChunkSize(){
+ return _stack.back()-_pos;
+}
+// ------------------------------------------------------------------------------------------------
+
+template<class T>
+T *B3DImporter::to_array( const vector<T> &v ){
+ if ( !v.size() ) return 0;
+ T *p=new T[v.size()];
+ for ( size_t i=0;i<v.size();++i ){
+ p[i]=v[i];
+ }
+ return p;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadTEXS(){
+ while ( ChunkSize() ){
+ string name=ReadString();
+ /*int flags=*/ReadInt();
+ /*int blend=*/ReadInt();
+ /*aiVector2D pos=*/ReadVec2();
+ /*aiVector2D scale=*/ReadVec2();
+ /*float rot=*/ReadFloat();
+
+ _textures.push_back( name );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadBRUS(){
+ int n_texs=ReadInt();
+ if ( n_texs<0 || n_texs>8 ){
+ Fail( "Bad texture count" );
+ }
+ while ( ChunkSize() ){
+ string name=ReadString();
+ aiVector3D color=ReadVec3();
+ float alpha=ReadFloat();
+ float shiny=ReadFloat();
+ /*int blend=**/ReadInt();
+ int fx=ReadInt();
+
+ MaterialHelper *mat=new MaterialHelper;
+ _materials.push_back( mat );
+
+ // Name
+ aiString ainame( name );
+ mat->AddProperty( &ainame,AI_MATKEY_NAME );
+
+ // Diffuse color
+ mat->AddProperty( &color,1,AI_MATKEY_COLOR_DIFFUSE );
+
+ // Opacity
+ mat->AddProperty( &alpha,1,AI_MATKEY_OPACITY );
+
+ // Specular color
+ aiColor3D speccolor( shiny,shiny,shiny );
+ mat->AddProperty( &speccolor,1,AI_MATKEY_COLOR_SPECULAR );
+
+ // Specular power
+ float specpow=shiny*128;
+ mat->AddProperty( &specpow,1,AI_MATKEY_SHININESS );
+
+ // Double sided
+ if ( fx & 0x10 ){
+ int i=1;
+ mat->AddProperty( &i,1,AI_MATKEY_TWOSIDED );
+ }
+
+ //Textures
+ for ( int i=0;i<n_texs;++i ){
+ int texid=ReadInt();
+ if ( texid<-1 || (texid>=0 && texid>=static_cast<int>(_textures.size())) ){
+ Fail( "Bad texture id" );
+ }
+ if ( i==0 && texid>=0 ){
+ aiString texname( _textures[texid] );
+ mat->AddProperty( &texname,AI_MATKEY_TEXTURE_DIFFUSE(0) );
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadVRTS(){
+ _vflags=ReadInt();
+ _tcsets=ReadInt();
+ _tcsize=ReadInt();
+ if ( _tcsets<0 || _tcsets>4 || _tcsize<0 || _tcsize>4 ){
+ Fail( "Bad texcoord data" );
+ }
+
+ int sz=12+(_vflags&1?12:0)+(_vflags&2?16:0)+(_tcsets*_tcsize*4);
+ int n_verts=ChunkSize()/sz;
+
+ int v0=_vertices.size();
+ _vertices.resize( v0+n_verts );
+
+ for ( int i=0;i<n_verts;++i ){
+ Vertex &v=_vertices[v0+i];
+
+ memset( v.bones,0,sizeof(v.bones) );
+ memset( v.weights,0,sizeof(v.weights) );
+
+ v.vertex=ReadVec3();
+
+ if ( _vflags & 1 ) v.normal=ReadVec3();
+
+ if ( _vflags & 2 ) ReadQuat(); //skip v 4bytes...
+
+ for ( int i=0;i<_tcsets;++i ){
+ float t[4]={0,0,0,0};
+ for ( int j=0;j<_tcsize;++j ){
+ t[j]=ReadFloat();
+ }
+ t[1]=1-t[1];
+ if ( !i ) v.texcoords=aiVector3D( t[0],t[1],t[2] );
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadTRIS( int v0 ){
+ int matid=ReadInt();
+ if ( matid==-1 ){
+ matid=0;
+ }else if ( matid<0 || matid>=(int)_materials.size() ){
+#ifdef DEBUG_B3D
+ cout<<"material id="<<matid<<endl;
+#endif
+ Fail( "Bad material id" );
+ }
+
+ aiMesh *mesh=new aiMesh;
+ _meshes.push_back( mesh );
+
+ mesh->mMaterialIndex=matid;
+ mesh->mNumFaces=0;
+ mesh->mPrimitiveTypes=aiPrimitiveType_TRIANGLE;
+
+ int n_tris=ChunkSize()/12;
+ aiFace *face=mesh->mFaces=new aiFace[n_tris];
+
+ for ( int i=0;i<n_tris;++i ){
+ int i0=ReadInt()+v0;
+ int i1=ReadInt()+v0;
+ int i2=ReadInt()+v0;
+ if ( i0<0 || i0>=(int)_vertices.size() || i1<0 || i1>=(int)_vertices.size() || i2<0 || i2>=(int)_vertices.size() ){
+#ifdef DEBUG_B3D
+ cout<<"Bad triangle index: i0="<<i0<<", i1="<<i1<<", i2="<<i2<<endl;
+#endif
+ Fail( "Bad triangle index" );
+ continue;
+ }
+ face->mNumIndices=3;
+ face->mIndices=new unsigned[3];
+ face->mIndices[0]=i0;
+ face->mIndices[1]=i1;
+ face->mIndices[2]=i2;
+ ++mesh->mNumFaces;
+ ++face;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadMESH(){
+ /*int matid=*/ReadInt();
+
+ int v0=_vertices.size();
+
+ while ( ChunkSize() ){
+ string t=ReadChunk();
+ if ( t=="VRTS" ){
+ ReadVRTS();
+ }else if ( t=="TRIS" ){
+ ReadTRIS( v0 );
+ }
+ ExitChunk();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadBONE( int id ){
+ while ( ChunkSize() ){
+ int vertex=ReadInt();
+ float weight=ReadFloat();
+ if ( vertex<0 || vertex>=(int)_vertices.size() ){
+ Fail( "Bad vertex index" );
+ }
+
+ Vertex &v=_vertices[vertex];
+ int i;
+ for ( i=0;i<4;++i ){
+ if ( !v.weights[i] ){
+ v.bones[i]=id;
+ v.weights[i]=weight;
+ break;
+ }
+ }
+#ifdef DEBUG_B3D
+ if ( i==4 ){
+ cout<<"Too many bone weights"<<endl;
+ }
+#endif
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadKEYS( aiNodeAnim *nodeAnim ){
+ vector<aiVectorKey> trans,scale;
+ vector<aiQuatKey> rot;
+ int flags=ReadInt();
+ while ( ChunkSize() ){
+ int frame=ReadInt();
+ if ( flags & 1 ){
+ trans.push_back( aiVectorKey( frame,ReadVec3() ) );
+ }
+ if ( flags & 2 ){
+ scale.push_back( aiVectorKey( frame,ReadVec3() ) );
+ }
+ if ( flags & 4 ){
+ rot.push_back( aiQuatKey( frame,ReadQuat() ) );
+ }
+ }
+
+ if ( flags & 1 ){
+ nodeAnim->mNumPositionKeys=trans.size();
+ nodeAnim->mPositionKeys=to_array( trans );
+ }
+
+ if ( flags & 2 ){
+ nodeAnim->mNumScalingKeys=scale.size();
+ nodeAnim->mScalingKeys=to_array( scale );
+ }
+
+ if ( flags & 4 ){
+ nodeAnim->mNumRotationKeys=rot.size();
+ nodeAnim->mRotationKeys=to_array( rot );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadANIM(){
+ /*int flags=*/ReadInt();
+ int frames=ReadInt();
+ float fps=ReadFloat();
+
+ aiAnimation *anim=new aiAnimation;
+ _animations.push_back( anim );
+
+ anim->mDuration=frames;
+ anim->mTicksPerSecond=fps;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiNode *B3DImporter::ReadNODE( aiNode *parent ){
+
+ string name=ReadString();
+ aiVector3D t=ReadVec3();
+ aiVector3D s=ReadVec3();
+ aiQuaternion r=ReadQuat();
+
+ aiMatrix4x4 trans,scale,rot;
+
+ aiMatrix4x4::Translation( t,trans );
+ aiMatrix4x4::Scaling( s,scale );
+ rot=aiMatrix4x4( r.GetMatrix() );
+
+ aiMatrix4x4 tform=trans * rot * scale;
+
+ int nodeid=_nodes.size();
+
+ aiNode *node=new aiNode( name );
+ _nodes.push_back( node );
+
+ node->mParent=parent;
+ node->mTransformation=tform;
+
+ aiNodeAnim *nodeAnim=0;
+ vector<unsigned> meshes;
+ vector<aiNode*> children;
+
+ while ( ChunkSize() ){
+ string t=ReadChunk();
+ if ( t=="MESH" ){
+ int n=_meshes.size();
+ ReadMESH();
+ for ( int i=n;i<(int)_meshes.size();++i ){
+ meshes.push_back( i );
+ }
+ }else if ( t=="BONE" ){
+ ReadBONE( nodeid );
+ }else if ( t=="ANIM" ){
+ ReadANIM();
+ }else if ( t=="KEYS" ){
+ if ( !nodeAnim ){
+ nodeAnim=new aiNodeAnim;
+ _nodeAnims.push_back( nodeAnim );
+ nodeAnim->mNodeName=node->mName;
+ }
+ ReadKEYS( nodeAnim );
+ }else if ( t=="NODE" ){
+ aiNode *child=ReadNODE( node );
+ children.push_back( child );
+ }
+ ExitChunk();
+ }
+
+ node->mNumMeshes=meshes.size();
+ node->mMeshes=to_array( meshes );
+
+ node->mNumChildren=children.size();
+ node->mChildren=to_array( children );
+
+ return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadBB3D( aiScene *scene ){
+
+ _textures.clear();
+ _materials.size();
+
+ _vertices.clear();
+ _meshes.clear();
+
+ _nodes.clear();
+ _nodeAnims.clear();
+ _animations.clear();
+
+ string t=ReadChunk();
+ if ( t=="BB3D" ){
+ int version=ReadInt();
+
+ if (!DefaultLogger::isNullLogger()) {
+ char dmp[128];
+ sprintf(dmp,"B3D file format version: %i",version);
+ DefaultLogger::get()->info(dmp);
+ }
+
+ while ( ChunkSize() ){
+ string t=ReadChunk();
+ if ( t=="TEXS" ){
+ ReadTEXS();
+ }else if ( t=="BRUS" ){
+ ReadBRUS();
+ }else if ( t=="NODE" ){
+ ReadNODE( 0 );
+ }
+ ExitChunk();
+ }
+ }
+ ExitChunk();
+
+ if ( !_nodes.size() ) Fail( "No nodes" );
+
+ if ( !_meshes.size() ) Fail( "No meshes" );
+
+ //Fix nodes/meshes/bones
+ for (size_t i=0;i<_nodes.size();++i ){
+ aiNode *node=_nodes[i];
+
+ for ( size_t j=0;j<node->mNumMeshes;++j ){
+ aiMesh *mesh=_meshes[node->mMeshes[j]];
+
+ int n_tris=mesh->mNumFaces;
+ int n_verts=mesh->mNumVertices=n_tris * 3;
+
+ aiVector3D *mv=mesh->mVertices=new aiVector3D[ n_verts ],*mn=0,*mc=0;
+ if ( _vflags & 1 ) mn=mesh->mNormals=new aiVector3D[ n_verts ];
+ if ( _tcsets ) mc=mesh->mTextureCoords[0]=new aiVector3D[ n_verts ];
+
+ aiFace *face=mesh->mFaces;
+
+ vector< vector<aiVertexWeight> > vweights( _nodes.size() );
+
+ for ( int i=0;i<n_verts;i+=3 ){
+ for ( int j=0;j<3;++j ){
+ Vertex &v=_vertices[face->mIndices[j]];
+
+ *mv++=v.vertex;
+ if ( mn ) *mn++=v.normal;
+ if ( mc ) *mc++=v.texcoords;
+
+ face->mIndices[j]=i+j;
+
+ for ( int k=0;k<4;++k ){
+ if ( !v.weights[k] ) break;
+
+ int bone=v.bones[k];
+ float weight=v.weights[k];
+
+ vweights[bone].push_back( aiVertexWeight(i+j,weight) );
+ }
+ }
+ ++face;
+ }
+
+ vector<aiBone*> bones;
+ for (size_t i=0;i<vweights.size();++i ){
+ vector<aiVertexWeight> &weights=vweights[i];
+ if ( !weights.size() ) continue;
+
+ aiBone *bone=new aiBone;
+ bones.push_back( bone );
+
+ aiNode *bnode=_nodes[i];
+
+ bone->mName=bnode->mName;
+ bone->mNumWeights=weights.size();
+ bone->mWeights=to_array( weights );
+
+ aiMatrix4x4 mat=bnode->mTransformation;
+ while ( bnode->mParent ){
+ bnode=bnode->mParent;
+ mat=bnode->mTransformation * mat;
+ }
+ bone->mOffsetMatrix=mat.Inverse();
+ }
+ mesh->mNumBones=bones.size();
+ mesh->mBones=to_array( bones );
+ }
+ }
+
+ //nodes
+ scene->mRootNode=_nodes[0];
+
+ //material
+ if ( !_materials.size() ){
+ _materials.push_back( new MaterialHelper );
+ }
+ scene->mNumMaterials=_materials.size();
+ scene->mMaterials=to_array( _materials );
+
+ //meshes
+ scene->mNumMeshes=_meshes.size();
+ scene->mMeshes=to_array( _meshes );
+
+ //animations
+ if ( _animations.size()==1 && _nodeAnims.size() ){
+
+ aiAnimation *anim=_animations.back();
+ anim->mNumChannels=_nodeAnims.size();
+ anim->mChannels=to_array( _nodeAnims );
+
+ scene->mNumAnimations=_animations.size();
+ scene->mAnimations=to_array( _animations );
+ }
+
+ // convert to RH
+ MakeLeftHandedProcess makeleft;
+ makeleft.Execute( scene );
+
+ FlipWindingOrderProcess flip;
+ flip.Execute( scene );
+}
+
+#endif // !! ASSIMP_BUILD_NO_B3D_IMPORTER
diff --git a/src/3rdparty/assimp/code/B3DImporter.h b/src/3rdparty/assimp/code/B3DImporter.h
new file mode 100644
index 000000000..fa627694f
--- /dev/null
+++ b/src/3rdparty/assimp/code/B3DImporter.h
@@ -0,0 +1,126 @@
+
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Definition of the .b3d importer class. */
+
+#ifndef AI_B3DIMPORTER_H_INC
+#define AI_B3DIMPORTER_H_INC
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiMaterial.h"
+
+#include <string>
+#include <vector>
+
+namespace Assimp{
+
+class B3DImporter : public BaseImporter{
+public:
+
+ virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+
+protected:
+
+ virtual void GetExtensionList(std::set<std::string>& extensions);
+ virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+
+private:
+
+ int ReadByte();
+ int ReadInt();
+ float ReadFloat();
+ aiVector2D ReadVec2();
+ aiVector3D ReadVec3();
+ aiQuaternion ReadQuat();
+ std::string ReadString();
+ std::string ReadChunk();
+ void ExitChunk();
+ unsigned ChunkSize();
+
+ template<class T>
+ T *to_array( const std::vector<T> &v );
+
+ struct Vertex{
+ aiVector3D vertex;
+ aiVector3D normal;
+ aiVector3D texcoords;
+ unsigned char bones[4];
+ float weights[4];
+ };
+
+ void Oops();
+ void Fail( std::string str );
+
+ void ReadTEXS();
+ void ReadBRUS();
+
+ void ReadVRTS();
+ void ReadTRIS( int v0 );
+ void ReadMESH();
+ void ReadBONE( int id );
+ void ReadKEYS( aiNodeAnim *nodeAnim );
+ void ReadANIM();
+
+ aiNode *ReadNODE( aiNode *parent );
+
+ void ReadBB3D( aiScene *scene );
+
+ unsigned _pos;
+// unsigned _size;
+ std::vector<unsigned char> _buf;
+ std::vector<unsigned> _stack;
+
+ std::vector<std::string> _textures;
+ std::vector<aiMaterial*> _materials;
+
+ int _vflags,_tcsets,_tcsize;
+ std::vector<Vertex> _vertices;
+
+ std::vector<aiNode*> _nodes;
+ std::vector<aiMesh*> _meshes;
+ std::vector<aiNodeAnim*> _nodeAnims;
+ std::vector<aiAnimation*> _animations;
+};
+
+}
+
+#endif
diff --git a/src/3rdparty/assimp/code/BVHLoader.cpp b/src/3rdparty/assimp/code/BVHLoader.cpp
new file mode 100644
index 000000000..48c1788b7
--- /dev/null
+++ b/src/3rdparty/assimp/code/BVHLoader.cpp
@@ -0,0 +1,505 @@
+/** Implementation of the BVH loader */
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
+
+#include "BVHLoader.h"
+#include "fast_atof.h"
+#include "SkeletonMeshBuilder.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+BVHLoader::BVHLoader() : mLine(0), mAnimTickDuration(1.), mAnimNumFrames(0)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+BVHLoader::~BVHLoader()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool BVHLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const
+{
+ // check file extension
+ const std::string extension = GetExtension(pFile);
+
+ if ( extension == "bvh")
+ return true;
+
+ if ((!extension.length() || cs) && pIOHandler) {
+ const char* tokens[] = {"HIERARCHY"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void BVHLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+ mFileName = pFile;
+
+ // read file into memory
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open file " + pFile + ".");
+
+ size_t fileSize = file->FileSize();
+ if ( fileSize == 0)
+ throw DeadlyImportError( "File is too small.");
+
+ mBuffer.resize( fileSize);
+ file->Read( &mBuffer.front(), 1, fileSize);
+
+ // start reading
+ mReader = mBuffer.begin();
+ mLine = 1;
+ ReadStructure( pScene);
+
+ // build a dummy mesh for the skeleton so that we see something at least
+ SkeletonMeshBuilder meshBuilder( pScene);
+
+ // construct an animation from all the motion data we read
+ CreateAnimation( pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the file
+void BVHLoader::ReadStructure( aiScene* pScene)
+{
+ // first comes hierarchy
+ std::string header = GetNextToken();
+ if ( header != "HIERARCHY")
+ ThrowException( "Expected header string \"HIERARCHY\".");
+ ReadHierarchy( pScene);
+
+ // then comes the motion data
+ std::string motion = GetNextToken();
+ if ( motion != "MOTION")
+ ThrowException( "Expected beginning of motion data \"MOTION\".");
+ ReadMotion( pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the hierarchy
+void BVHLoader::ReadHierarchy( aiScene* pScene)
+{
+ std::string root = GetNextToken();
+ if ( root != "ROOT")
+ ThrowException( "Expected root node \"ROOT\".");
+
+ // Go read the hierarchy from here
+ pScene->mRootNode = ReadNode();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a node and recursively its childs and returns the created node;
+aiNode* BVHLoader::ReadNode()
+{
+ // first token is name
+ std::string nodeName = GetNextToken();
+ if ( nodeName.empty() || nodeName == "{")
+ ThrowException( boost::str( boost::format( "Expected node name, but found \"%s\".") % nodeName));
+
+ // then an opening brace should follow
+ std::string openBrace = GetNextToken();
+ if ( openBrace != "{")
+ ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace));
+
+ // Create a node
+ aiNode* node = new aiNode( nodeName);
+ std::vector<aiNode*> childNodes;
+
+ // and create an bone entry for it
+ mNodes.push_back( Node( node));
+ Node& internNode = mNodes.back();
+
+ // now read the node's contents
+ while ( 1)
+ {
+ std::string token = GetNextToken();
+
+ // node offset to parent node
+ if ( token == "OFFSET")
+ ReadNodeOffset( node);
+ else if ( token == "CHANNELS")
+ ReadNodeChannels( internNode);
+ else if ( token == "JOINT")
+ {
+ // child node follows
+ aiNode* child = ReadNode();
+ child->mParent = node;
+ childNodes.push_back( child);
+ }
+ else if ( token == "End")
+ {
+ // The real symbol is "End Site". Second part comes in a separate token
+ std::string siteToken = GetNextToken();
+ if ( siteToken != "Site")
+ ThrowException( boost::str( boost::format( "Expected \"End Site\" keyword, but found \"%s %s\".") % token % siteToken));
+
+ aiNode* child = ReadEndSite( nodeName);
+ child->mParent = node;
+ childNodes.push_back( child);
+ }
+ else if ( token == "}")
+ {
+ // we're done with that part of the hierarchy
+ break;
+ } else
+ {
+ // everything else is a parse error
+ ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token));
+ }
+ }
+
+ // add the child nodes if there are any
+ if ( childNodes.size() > 0)
+ {
+ node->mNumChildren = childNodes.size();
+ node->mChildren = new aiNode*[node->mNumChildren];
+ std::copy( childNodes.begin(), childNodes.end(), node->mChildren);
+ }
+
+ // and return the sub-hierarchy we built here
+ return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an end node and returns the created node.
+aiNode* BVHLoader::ReadEndSite( const std::string& pParentName)
+{
+ // check opening brace
+ std::string openBrace = GetNextToken();
+ if ( openBrace != "{")
+ ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace));
+
+ // Create a node
+ aiNode* node = new aiNode( "EndSite_" + pParentName);
+
+ // now read the node's contents. Only possible entry is "OFFSET"
+ while ( 1)
+ {
+ std::string token = GetNextToken();
+
+ // end node's offset
+ if ( token == "OFFSET")
+ {
+ ReadNodeOffset( node);
+ }
+ else if ( token == "}")
+ {
+ // we're done with the end node
+ break;
+ } else
+ {
+ // everything else is a parse error
+ ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token));
+ }
+ }
+
+ // and return the sub-hierarchy we built here
+ return node;
+}
+// ------------------------------------------------------------------------------------------------
+// Reads a node offset for the given node
+void BVHLoader::ReadNodeOffset( aiNode* pNode)
+{
+ // Offset consists of three floats to read
+ aiVector3D offset;
+ offset.x = GetNextTokenAsFloat();
+ offset.y = GetNextTokenAsFloat();
+ offset.z = GetNextTokenAsFloat();
+
+ // build a transformation matrix from it
+ pNode->mTransformation = aiMatrix4x4( 1.0f, 0.0f, 0.0f, offset.x, 0.0f, 1.0f, 0.0f, offset.y,
+ 0.0f, 0.0f, 1.0f, offset.z, 0.0f, 0.0f, 0.0f, 1.0f);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the animation channels for the given node
+void BVHLoader::ReadNodeChannels( BVHLoader::Node& pNode)
+{
+ // number of channels. Use the float reader because we're lazy
+ float numChannelsFloat = GetNextTokenAsFloat();
+ unsigned int numChannels = (unsigned int) numChannelsFloat;
+
+ for ( unsigned int a = 0; a < numChannels; a++)
+ {
+ std::string channelToken = GetNextToken();
+
+ if ( channelToken == "Xposition")
+ pNode.mChannels.push_back( Channel_PositionX);
+ else if ( channelToken == "Yposition")
+ pNode.mChannels.push_back( Channel_PositionY);
+ else if ( channelToken == "Zposition")
+ pNode.mChannels.push_back( Channel_PositionZ);
+ else if ( channelToken == "Xrotation")
+ pNode.mChannels.push_back( Channel_RotationX);
+ else if ( channelToken == "Yrotation")
+ pNode.mChannels.push_back( Channel_RotationY);
+ else if ( channelToken == "Zrotation")
+ pNode.mChannels.push_back( Channel_RotationZ);
+ else
+ ThrowException( boost::str( boost::format( "Invalid channel specifier \"%s\".") % channelToken));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the motion data
+void BVHLoader::ReadMotion( aiScene*)
+{
+ // Read number of frames
+ std::string tokenFrames = GetNextToken();
+ if ( tokenFrames != "Frames:")
+ ThrowException( boost::str( boost::format( "Expected frame count \"Frames:\", but found \"%s\".") % tokenFrames));
+
+ float numFramesFloat = GetNextTokenAsFloat();
+ mAnimNumFrames = (unsigned int) numFramesFloat;
+
+ // Read frame duration
+ std::string tokenDuration1 = GetNextToken();
+ std::string tokenDuration2 = GetNextToken();
+ if ( tokenDuration1 != "Frame" || tokenDuration2 != "Time:")
+ ThrowException( boost::str( boost::format( "Expected frame duration \"Frame Time:\", but found \"%s %s\".") % tokenDuration1 % tokenDuration2));
+
+ mAnimTickDuration = GetNextTokenAsFloat();
+
+ // resize value vectors for each node
+ for ( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
+ it->mChannelValues.reserve( it->mChannels.size() * mAnimNumFrames);
+
+ // now read all the data and store it in the corresponding node's value vector
+ for ( unsigned int frame = 0; frame < mAnimNumFrames; ++frame)
+ {
+ // on each line read the values for all nodes
+ for ( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
+ {
+ // get as many values as the node has channels
+ for ( unsigned int c = 0; c < it->mChannels.size(); ++c)
+ it->mChannelValues.push_back( GetNextTokenAsFloat());
+ }
+
+ // after one frame worth of values for all nodes there should be a newline, but we better don't rely on it
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Retrieves the next token
+std::string BVHLoader::GetNextToken()
+{
+ // skip any preceeding whitespace
+ while ( mReader != mBuffer.end())
+ {
+ if ( !isspace( *mReader))
+ break;
+
+ // count lines
+ if ( *mReader == '\n')
+ mLine++;
+
+ ++mReader;
+ }
+
+ // collect all chars till the next whitespace. BVH is easy in respect to that.
+ std::string token;
+ while ( mReader != mBuffer.end())
+ {
+ if ( isspace( *mReader))
+ break;
+
+ token.push_back( *mReader);
+ ++mReader;
+
+ // little extra logic to make sure braces are counted correctly
+ if ( token == "{" || token == "}")
+ break;
+ }
+
+ // empty token means end of file, which is just fine
+ return token;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the next token as a float
+float BVHLoader::GetNextTokenAsFloat()
+{
+ std::string token = GetNextToken();
+ if ( token.empty())
+ ThrowException( "Unexpected end of file while trying to read a float");
+
+ // check if the float is valid by testing if the atof() function consumed every char of the token
+ const char* ctoken = token.c_str();
+ float result = 0.0f;
+ ctoken = fast_atof_move( ctoken, result);
+
+ if ( ctoken != token.c_str() + token.length())
+ ThrowException( boost::str( boost::format( "Expected a floating point number, but found \"%s\".") % token));
+
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Aborts the file reading with an exception
+void BVHLoader::ThrowException( const std::string& pError)
+{
+ throw DeadlyImportError( boost::str( boost::format( "%s:%d - %s") % mFileName % mLine % pError));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructs an animation for the motion data and stores it in the given scene
+void BVHLoader::CreateAnimation( aiScene* pScene)
+{
+ // create the animation
+ pScene->mNumAnimations = 1;
+ pScene->mAnimations = new aiAnimation*[1];
+ aiAnimation* anim = new aiAnimation;
+ pScene->mAnimations[0] = anim;
+
+ // put down the basic parameters
+ anim->mName.Set( "Motion");
+ anim->mTicksPerSecond = 1.0 / double( mAnimTickDuration);
+ anim->mDuration = double( mAnimNumFrames - 1);
+
+ // now generate the tracks for all nodes
+ anim->mNumChannels = mNodes.size();
+ anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
+
+ // FIX: set the array elements to NULL to ensure proper deletion if an exception is thrown
+ for (unsigned int i = 0; i < anim->mNumChannels;++i)
+ anim->mChannels[i] = NULL;
+
+ for ( unsigned int a = 0; a < anim->mNumChannels; a++)
+ {
+ const Node& node = mNodes[a];
+ const std::string nodeName = std::string( node.mNode->mName.data );
+ aiNodeAnim* nodeAnim = new aiNodeAnim;
+ anim->mChannels[a] = nodeAnim;
+ nodeAnim->mNodeName.Set( nodeName);
+
+ // translational part, if given
+ if ( node.mChannels.size() == 6)
+ {
+ nodeAnim->mNumPositionKeys = mAnimNumFrames;
+ nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames];
+ aiVectorKey* poskey = nodeAnim->mPositionKeys;
+ for ( unsigned int fr = 0; fr < mAnimNumFrames; ++fr)
+ {
+ poskey->mTime = double( fr);
+
+ // Now compute all translations in the right order
+ for ( unsigned int channel = 0; channel < 3; ++channel)
+ {
+ switch( node.mChannels[channel])
+ {
+ case Channel_PositionX: poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
+ case Channel_PositionY: poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
+ case Channel_PositionZ: poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
+ default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName );
+ }
+ }
+ ++poskey;
+ }
+ } else
+ {
+ // if no translation part is given, put a default sequence
+ aiVector3D nodePos( node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4);
+ nodeAnim->mNumPositionKeys = 1;
+ nodeAnim->mPositionKeys = new aiVectorKey[1];
+ nodeAnim->mPositionKeys[0].mTime = 0.0;
+ nodeAnim->mPositionKeys[0].mValue = nodePos;
+ }
+
+ // rotation part. Always present. First find value offsets
+ {
+ unsigned int rotOffset = 0;
+ if ( node.mChannels.size() == 6)
+ {
+ // Offset all further calculations
+ rotOffset = 3;
+ }
+
+ // Then create the number of rotation keys
+ nodeAnim->mNumRotationKeys = mAnimNumFrames;
+ nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames];
+ aiQuatKey* rotkey = nodeAnim->mRotationKeys;
+ for ( unsigned int fr = 0; fr < mAnimNumFrames; ++fr)
+ {
+ aiMatrix4x4 temp;
+ aiMatrix3x3 rotMatrix;
+
+ for ( unsigned int channel = 0; channel < 3; ++channel)
+ {
+ // translate ZXY euler angels into a quaternion
+ const float angle = node.mChannelValues[fr * node.mChannels.size() + rotOffset + channel] * float( AI_MATH_PI) / 180.0f;
+
+ // Compute rotation transformations in the right order
+ switch (node.mChannels[rotOffset+channel])
+ {
+ case Channel_RotationX: aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
+ case Channel_RotationY: aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
+ case Channel_RotationZ: aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
+ default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName );
+ }
+ }
+
+ rotkey->mTime = double( fr);
+ rotkey->mValue = aiQuaternion( rotMatrix);
+ ++rotkey;
+ }
+ }
+
+ // scaling part. Always just a default track
+ {
+ nodeAnim->mNumScalingKeys = 1;
+ nodeAnim->mScalingKeys = new aiVectorKey[1];
+ nodeAnim->mScalingKeys[0].mTime = 0.0;
+ nodeAnim->mScalingKeys[0].mValue.Set( 1.0f, 1.0f, 1.0f);
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER
diff --git a/src/3rdparty/assimp/code/BVHLoader.h b/src/3rdparty/assimp/code/BVHLoader.h
new file mode 100644
index 000000000..74800a187
--- /dev/null
+++ b/src/3rdparty/assimp/code/BVHLoader.h
@@ -0,0 +1,173 @@
+/** Defines the BHV motion capturing loader class */
+
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file BVHLoader.h
+ * @brief Biovision BVH import
+ */
+
+#ifndef AI_BVHLOADER_H_INC
+#define AI_BVHLOADER_H_INC
+
+#include "BaseImporter.h"
+
+namespace Assimp
+{
+
+// --------------------------------------------------------------------------------
+/** Loader class to read Motion Capturing data from a .bvh file.
+ *
+ * This format only contains a hierarchy of joints and a series of keyframes for
+ * the hierarchy. It contains no actual mesh data, but we generate a dummy mesh
+ * inside the loader just to be able to see something.
+*/
+class BVHLoader : public BaseImporter
+{
+ friend class Importer;
+
+ /** Possible animation channels for which the motion data holds the values */
+ enum ChannelType
+ {
+ Channel_PositionX,
+ Channel_PositionY,
+ Channel_PositionZ,
+ Channel_RotationX,
+ Channel_RotationY,
+ Channel_RotationZ
+ };
+
+ /** Collected list of node. Will be bones of the dummy mesh some day, addressed by their array index */
+ struct Node
+ {
+ const aiNode* mNode;
+ std::vector<ChannelType> mChannels;
+ std::vector<float> mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames
+
+ Node() { }
+ Node( const aiNode* pNode) : mNode( pNode) { }
+ };
+
+protected:
+ /** Constructor to be privately used by Importer */
+ BVHLoader();
+
+ /** Destructor, private as well */
+ ~BVHLoader();
+
+public:
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details. */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const;
+
+protected:
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions)
+ {
+ extensions.insert("bvh");
+ }
+
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+
+protected:
+ /** Reads the file */
+ void ReadStructure( aiScene* pScene);
+
+ /** Reads the hierarchy */
+ void ReadHierarchy( aiScene* pScene);
+
+ /** Reads a node and recursively its childs and returns the created node. */
+ aiNode* ReadNode();
+
+ /** Reads an end node and returns the created node. */
+ aiNode* ReadEndSite( const std::string& pParentName);
+
+ /** Reads a node offset for the given node */
+ void ReadNodeOffset( aiNode* pNode);
+
+ /** Reads the animation channels into the given node */
+ void ReadNodeChannels( BVHLoader::Node& pNode);
+
+ /** Reads the motion data */
+ void ReadMotion( aiScene* pScene);
+
+ /** Retrieves the next token */
+ std::string GetNextToken();
+
+ /** Reads the next token as a float */
+ float GetNextTokenAsFloat();
+
+ /** Aborts the file reading with an exception */
+ void ThrowException( const std::string& pError);
+
+ /** Constructs an animation for the motion data and stores it in the given scene */
+ void CreateAnimation( aiScene* pScene);
+
+protected:
+ /** Filename, for a verbose error message */
+ std::string mFileName;
+
+ /** Buffer to hold the loaded file */
+ std::vector<char> mBuffer;
+
+ /** Next char to read from the buffer */
+ std::vector<char>::const_iterator mReader;
+
+ /** Current line, for error messages */
+ unsigned int mLine;
+
+ /** Collected list of nodes. Will be bones of the dummy mesh some day, addressed by their array index.
+ * Also contain the motion data for the node's channels
+ */
+ std::vector<Node> mNodes;
+
+ /** basic Animation parameters */
+ float mAnimTickDuration;
+ unsigned int mAnimNumFrames;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_BVHLOADER_H_INC
diff --git a/src/3rdparty/assimp/code/BaseImporter.cpp b/src/3rdparty/assimp/code/BaseImporter.cpp
new file mode 100644
index 000000000..9771749d4
--- /dev/null
+++ b/src/3rdparty/assimp/code/BaseImporter.cpp
@@ -0,0 +1,529 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file BaseImporter.cpp
+ * @brief Implementation of BaseImporter
+ */
+
+#include "AssimpPCH.h"
+#include "BaseImporter.h"
+#include "FileSystemFilter.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+BaseImporter::BaseImporter()
+: 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)
+{
+ progress = pImp->GetProgressHandler();
+ ai_assert(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
+ ScopeGuard<aiScene> sc(new aiScene());
+
+ // dispatch importing
+ try
+ {
+ InternReadFile( pFile, sc, &filter);
+
+ } catch( const std::exception& err ) {
+ // extract error description
+ mErrorText = err.what();
+ DefaultLogger::get()->error(mErrorText);
+ return NULL;
+ }
+
+ // return what we gathered from the import.
+ sc.dismiss();
+ return sc;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BaseImporter::SetupProperties(const Importer*)
+{
+ // the default implementation does nothing
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem* pIOHandler,
+ const std::string& pFile,
+ const char** tokens,
+ unsigned int numTokens,
+ unsigned int searchBytes /* = 200 */)
+{
+ ai_assert(NULL != tokens && 0 != numTokens && 0 != searchBytes);
+ if (!pIOHandler)
+ return false;
+
+ boost::scoped_ptr<IOStream> pStream (pIOHandler->Open(pFile));
+ if (pStream.get() ) {
+ // read 200 characters from the file
+ boost::scoped_array<char> _buffer (new char[searchBytes+1 /* for the '\0' */]);
+ char* buffer = _buffer.get();
+
+ const unsigned int read = pStream->Read(buffer,1,searchBytes);
+ if (!read)
+ return false;
+
+ for (unsigned int i = 0; i < read;++i)
+ buffer[i] = ::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';
+
+ for (unsigned int i = 0; i < numTokens;++i) {
+ ai_assert(NULL != tokens[i]);
+
+ if (::strstr(buffer,tokens[i])) {
+ DefaultLogger::get()->debug(std::string("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
+/*static*/ std::string BaseImporter::GetExtension (const std::string& pFile)
+{
+ std::string::size_type pos = pFile.find_last_of('.');
+
+ // no file extension at all
+ if ( pos == std::string::npos)
+ return "";
+
+ std::string ret = pFile.substr(pos+1);
+ std::transform(ret.begin(),ret.end(),ret.begin(),::tolower); // thanks to Andy Maloney for the hint
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 && _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);
+ boost::scoped_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 convinience, 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/ConvertUTF/ConvertUTF.h"
+
+// ------------------------------------------------------------------------------------------------
+void ReportResult(ConversionResult res)
+{
+ if (res == sourceExhausted) {
+ DefaultLogger::get()->error("Source ends with incomplete character sequence, transformation to UTF-8 fails");
+ }
+ else if (res == sourceIllegal) {
+ DefaultLogger::get()->error("Source contains illegal character sequence, transformation to UTF-8 fails");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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) {
+ DefaultLogger::get()->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 endianess ..
+ 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) {
+ DefaultLogger::get()->debug("Found UTF-32 BOM ...");
+
+ const uint32_t* sstart = (uint32_t*)&data.front()+1, *send = (uint32_t*)&data.back()+1;
+ char* dstart,*dend;
+ std::vector<char> output;
+ do {
+ output.resize(output.size()?output.size()*3/2:data.size()/2);
+ dstart = &output.front(),dend = &output.back()+1;
+
+ result = ConvertUTF32toUTF8((const UTF32**)&sstart,(const UTF32*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion);
+ } while (result == targetExhausted);
+
+ ReportResult(result);
+
+ // copy to output buffer.
+ const size_t outlen = (size_t)(dstart-&output.front());
+ data.assign(output.begin(),output.begin()+outlen);
+ return;
+ }
+
+ // UTF 16 BE with BOM
+ if (*((uint16_t*)&data.front()) == 0xFFFE) {
+
+ // swap the endianess ..
+ 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) {
+ DefaultLogger::get()->debug("Found UTF-16 BOM ...");
+
+ const uint16_t* sstart = (uint16_t*)&data.front()+1, *send = (uint16_t*)&data.back()+1;
+ char* dstart,*dend;
+ std::vector<char> output;
+ do {
+ output.resize(output.size()?output.size()*3/2:data.size()*3/4);
+ dstart = &output.front(),dend = &output.back()+1;
+
+ result = ConvertUTF16toUTF8((const UTF16**)&sstart,(const UTF16*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion);
+ } while (result == targetExhausted);
+
+ ReportResult(result);
+
+ // copy to output buffer.
+ const size_t outlen = (size_t)(dstart-&output.front());
+ data.assign(output.begin(),output.begin()+outlen);
+ return;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BaseImporter::TextFileToBuffer(IOStream* stream,
+ std::vector<char>& data)
+{
+ ai_assert(NULL != stream);
+
+ const size_t fileSize = stream->FileSize();
+ if (!fileSize) {
+ throw DeadlyImportError("File is empty");
+ }
+
+ data.reserve(fileSize+1);
+ data.resize(fileSize);
+ 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;
+ }
+
+ const std::string file;
+ unsigned int flags;
+ unsigned int refCnt;
+ aiScene* scene;
+ bool loaded;
+ BatchLoader::PropertyMap map;
+ unsigned int id;
+
+ bool operator== (const std::string& f) {
+ return file == f;
+ }
+ };
+}
+
+// ------------------------------------------------------------------------------------------------
+// BatchLoader::pimpl data structure
+struct Assimp::BatchData
+{
+ BatchData()
+ : pIOSystem(0)
+ , pImporter(0)
+ , next_id(0xffff)
+ {}
+
+ // 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;
+};
+
+// ------------------------------------------------------------------------------------------------
+BatchLoader::BatchLoader(IOSystem* pIO)
+{
+ ai_assert(NULL != pIO);
+
+ data = new BatchData();
+ data->pIOSystem = pIO;
+
+ data->pImporter = new Importer();
+ data->pImporter->SetIOHandler(data->pIOSystem);
+}
+
+// ------------------------------------------------------------------------------------------------
+BatchLoader::~BatchLoader()
+{
+ // delete all scenes wthat have not been polled by the user
+ for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it) {
+
+ delete (*it).scene;
+ }
+ data->pImporter->SetIOHandler(NULL); /* get pointer back into our posession */
+ delete data->pImporter;
+ delete data;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+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
+ std::list<LoadRequest>::iterator it;
+ for (it = data->requests.begin();it != data->requests.end(); ++it) {
+
+ // Call IOSystem's path comparison function here
+ if (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 ...
+ data->requests.push_back(LoadRequest(file,steps,map,data->next_id));
+ return data->next_id++;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiScene* BatchLoader::GetImport (unsigned int which)
+{
+ for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it) {
+
+ if ((*it).id == which && (*it).loaded) {
+
+ aiScene* sc = (*it).scene;
+ if (!(--(*it).refCnt)) {
+ data->requests.erase(it);
+ }
+ return sc;
+ }
+ }
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BatchLoader::LoadAll()
+{
+ // no threaded implementation for the moment
+ for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it) {
+ // force validation in debug builds
+ unsigned int pp = (*it).flags;
+#ifdef _DEBUG
+ pp |= aiProcess_ValidateDataStructure;
+#endif
+ // setup config properties if necessary
+ data->pImporter->pimpl->mFloatProperties = (*it).map.floats;
+ data->pImporter->pimpl->mIntProperties = (*it).map.ints;
+ data->pImporter->pimpl->mStringProperties = (*it).map.strings;
+
+ if (!DefaultLogger::isNullLogger())
+ {
+ DefaultLogger::get()->info("%%% BEGIN EXTERNAL FILE %%%");
+ DefaultLogger::get()->info("File: " + (*it).file);
+ }
+ data->pImporter->ReadFile((*it).file,pp);
+ (*it).scene = const_cast<aiScene*>(data->pImporter->GetOrphanedScene());
+ (*it).loaded = true;
+
+ DefaultLogger::get()->info("%%% END EXTERNAL FILE %%%");
+ }
+}
+
+
+
+
diff --git a/src/3rdparty/assimp/code/BaseImporter.h b/src/3rdparty/assimp/code/BaseImporter.h
new file mode 100644
index 000000000..39ac3bf58
--- /dev/null
+++ b/src/3rdparty/assimp/code/BaseImporter.h
@@ -0,0 +1,499 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Definition of the base class for all importer worker classes. */
+#ifndef INCLUDED_AI_BASEIMPORTER_H
+#define INCLUDED_AI_BASEIMPORTER_H
+
+#include "Exceptional.h"
+
+#include <string>
+#include <map>
+#include <vector>
+#include "./../include/aiTypes.h"
+
+struct aiScene;
+
+namespace Assimp {
+
+class IOSystem;
+class Importer;
+class BaseImporter;
+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]))
+
+// ---------------------------------------------------------------------------
+template <typename T>
+struct ScopeGuard
+{
+ ScopeGuard(T* obj) : obj(obj), mdismiss() {}
+ ~ScopeGuard () throw() {
+ if (!mdismiss) {
+ delete obj;
+ }
+ obj = NULL;
+ }
+
+ T* dismiss() {
+ mdismiss=true;
+ return obj;
+ }
+
+ operator T*() {
+ return obj;
+ }
+
+ T* operator -> () {
+ return obj;
+ }
+
+private:
+ T* obj;
+ bool mdismiss;
+};
+
+//! @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 ASSIMP_API ImporterPimpl
+{
+public:
+
+ // Data type to store the key hash
+ typedef unsigned int KeyType;
+
+ // typedefs for our three 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, float> FloatPropertyMap;
+ typedef std::map<KeyType, std::string> StringPropertyMap;
+
+public:
+
+ /** 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;
+
+ /** Used for testing - extra verbose mode causes the ValidateDataStructure-Step
+ * to be executed before and after every single postprocess step */
+ bool bExtraVerbose;
+
+ /** Used by post-process steps to share data */
+ SharedPostProcessInfo* mPPShared;
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+/** 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;
+
+protected:
+
+ /** Constructor to be privately used by #Importer */
+ BaseImporter();
+
+ /** Destructor, private as well */
+ virtual ~BaseImporter();
+
+public:
+ // -------------------------------------------------------------------
+ /** 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 occured.
+ * @return A description of the last error that occured. An empty
+ * string if there was no error.
+ */
+ const std::string& GetErrorText() const {
+ return mErrorText;
+ }
+
+ // -------------------------------------------------------------------
+ /** 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
+ );
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * Implementations are expected to insert() all file extensions
+ * handled by them into the extension set. A loader capable of
+ * reading certain files with the extension BLA would place the
+ * string bla (lower-case!) in the output set.
+ * @param extensions Output set. */
+ virtual void GetExtensionList(
+ std::set<std::string>& extensions
+ ) = 0;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+
+ // -------------------------------------------------------------------
+ /** @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 convinence, 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);
+
+ // -------------------------------------------------------------------
+ /** 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. */
+ static void TextFileToBuffer(
+ IOStream* stream,
+ std::vector<char>& data);
+
+protected:
+
+ /** Error description in case there was one. */
+ std::string mErrorText;
+
+ /** Currently set progress handler */
+ ProgressHandler* progress;
+};
+
+struct BatchData;
+
+// ---------------------------------------------------------------------------
+/** FOR IMPORTER PLUGINS ONLY: A helper class for the pleasure of importers
+ * which need to load many extern 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;
+
+ bool operator == (const PropertyMap& prop) const {
+ // fixme: really isocpp? gcc complains
+ return ints == prop.ints && floats == prop.floats && strings == prop.strings;
+ }
+
+ bool empty () const {
+ return ints.empty() && floats.empty() && strings.empty();
+ }
+ };
+ //! @endcond
+
+public:
+
+
+ // -------------------------------------------------------------------
+ /** Construct a batch loader from a given IO system to be used
+ * to acess external files */
+ BatchLoader(IOSystem* pIO);
+ ~BatchLoader();
+
+
+ // -------------------------------------------------------------------
+ /** 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* data;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_BASEIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/BaseProcess.cpp b/src/3rdparty/assimp/code/BaseProcess.cpp
new file mode 100644
index 000000000..02cee1484
--- /dev/null
+++ b/src/3rdparty/assimp/code/BaseProcess.cpp
@@ -0,0 +1,96 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of BaseProcess */
+
+#include "AssimpPCH.h"
+#include "BaseImporter.h"
+#include "BaseProcess.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+BaseProcess::BaseProcess()
+: 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();
+ DefaultLogger::get()->error(pImp->pimpl->mErrorString);
+
+ // and kill the partially imported data
+ delete pImp->pimpl->mScene;
+ pImp->pimpl->mScene = NULL;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BaseProcess::SetupProperties(const Importer*)
+{
+ // the default implementation does nothing
+}
diff --git a/src/3rdparty/assimp/code/BaseProcess.h b/src/3rdparty/assimp/code/BaseProcess.h
new file mode 100644
index 000000000..6d32d441d
--- /dev/null
+++ b/src/3rdparty/assimp/code/BaseProcess.h
@@ -0,0 +1,289 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Base class of all import post processing steps */
+#ifndef INCLUDED_AI_BASEPROCESS_H
+#define INCLUDED_AI_BASEPROCESS_H
+
+#include <map>
+
+#include "../include/aiTypes.h"
+#include "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 ASSIMP_API 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
+ {
+ 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
+ {
+ 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 BaseProcess
+{
+ friend class Importer;
+
+public:
+
+ /** Constructor to be privately used by Importer */
+ BaseProcess();
+
+ /** Destructor, private as well */
+ virtual ~BaseProcess();
+
+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.
+ */
+ virtual bool IsActive( unsigned int pFlags) const = 0;
+
+ // -------------------------------------------------------------------
+ /** 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/src/3rdparty/assimp/code/BlenderDNA.cpp b/src/3rdparty/assimp/code/BlenderDNA.cpp
new file mode 100644
index 000000000..75ef61f7b
--- /dev/null
+++ b/src/3rdparty/assimp/code/BlenderDNA.cpp
@@ -0,0 +1,372 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file BlenderDNA.cpp
+ * @brief Implementation of the Blender `DNA`, that is its own
+ * serialized set of data structures.
+ */
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+#include "BlenderDNA.h"
+#include "StreamReader.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+using namespace Assimp::Formatter;
+
+#define for_each BOOST_FOREACH
+bool match4(StreamReaderAny& stream, const char* string) {
+ char tmp[] = {
+ (stream).GetI1(),
+ (stream).GetI1(),
+ (stream).GetI1(),
+ (stream).GetI1()
+ };
+ return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]);
+}
+
+struct Type {
+ size_t size;
+ std::string name;
+};
+
+// ------------------------------------------------------------------------------------------------
+void DNAParser :: Parse ()
+{
+ StreamReaderAny& stream = *db.reader.get();
+ DNA& dna = db.dna;
+
+ if (!match4(stream,"SDNA")) {
+ throw DeadlyImportError("BlenderDNA: Expected SDNA chunk");
+ }
+
+ // name dictionary
+ if (!match4(stream,"NAME")) {
+ throw DeadlyImportError("BlenderDNA: Expected NAME field");
+ }
+
+ std::vector<std::string> names (stream.GetI4());
+ for_each(std::string& s, names) {
+ while (char c = stream.GetI1()) {
+ s += c;
+ }
+ }
+
+ // type dictionary
+ for (;stream.GetCurrentPos() & 0x3; stream.GetI1()) {}
+ if (!match4(stream,"TYPE")) {
+ throw DeadlyImportError("BlenderDNA: Expected TYPE field");
+ }
+
+ std::vector<Type> types (stream.GetI4());
+ for_each(Type& s, types) {
+ while (char c = stream.GetI1()) {
+ s.name += c;
+ }
+ }
+
+ // type length dictionary
+ for (;stream.GetCurrentPos() & 0x3; stream.GetI1()) {}
+ if (!match4(stream,"TLEN")) {
+ throw DeadlyImportError("BlenderDNA: Expected TLEN field");
+ }
+
+ for_each(Type& s, types) {
+ s.size = stream.GetI2();
+ }
+
+ // structures dictionary
+ for (;stream.GetCurrentPos() & 0x3; stream.GetI1()) {}
+ if (!match4(stream,"STRC")) {
+ throw DeadlyImportError("BlenderDNA: Expected STRC field");
+ }
+
+ size_t end = stream.GetI4(), fields = 0;
+
+ dna.structures.reserve(end);
+ for (size_t i = 0; i != end; ++i) {
+
+ uint16_t n = stream.GetI2();
+ if (n >= types.size()) {
+ throw DeadlyImportError((format(),
+ "BlenderDNA: Invalid type index in structure name" ,n,
+ " (there are only ", types.size(), " entries)"
+ ));
+ }
+
+ // maintain separate indexes
+ dna.indices[types[n].name] = dna.structures.size();
+
+ dna.structures.push_back(Structure());
+ Structure& s = dna.structures.back();
+ s.name = types[n].name;
+ //s.index = dna.structures.size()-1;
+
+ n = stream.GetI2();
+ s.fields.reserve(n);
+
+ size_t offset = 0;
+ for (size_t m = 0; m < n; ++m, ++fields) {
+
+ uint16_t j = stream.GetI2();
+ if (j >= types.size()) {
+ throw DeadlyImportError((format(),
+ "BlenderDNA: Invalid type index in structure field ", j,
+ " (there are only ", types.size(), " entries)"
+ ));
+ }
+ s.fields.push_back(Field());
+ Field& f = s.fields.back();
+ f.offset = offset;
+
+ f.type = types[j].name;
+ f.size = types[j].size;
+
+ j = stream.GetI2();
+ if (j >= names.size()) {
+ throw DeadlyImportError((format(),
+ "BlenderDNA: Invalid name index in structure field ", j,
+ " (there are only ", names.size(), " entries)"
+ ));
+ }
+
+ f.name = names[j];
+ f.flags = 0u;
+
+ // pointers always specify the size of the pointee instead of their own.
+ // The pointer asterisk remains a property of the lookup name.
+ if (f.name[0] == '*') {
+ f.size = db.i64bit ? 8 : 4;
+ f.flags |= FieldFlag_Pointer;
+ }
+
+ // arrays, however, specify the size of a single element so we
+ // need to parse the (possibly multi-dimensional) array declaration
+ // in order to obtain the actual size of the array in the file.
+ // Also we need to alter the lookup name to include no array
+ // brackets anymore or size fixup won't work (if our size does
+ // not match the size read from the DNA).
+ if (*f.name.rbegin() == ']') {
+ const std::string::size_type rb = f.name.find('[');
+ if (rb == std::string::npos) {
+ throw DeadlyImportError((format(),
+ "BlenderDNA: Encountered invalid array declaration ",
+ f.name
+ ));
+ }
+
+ f.flags |= FieldFlag_Array;
+ DNA::ExtractArraySize(f.name,f.array_sizes);
+ f.name = f.name.substr(0,rb);
+
+ f.size *= f.array_sizes[0] * f.array_sizes[1];
+ }
+
+ // maintain separate indexes
+ s.indices[f.name] = s.fields.size()-1;
+ offset += f.size;
+ }
+ s.size = offset;
+ }
+
+ DefaultLogger::get()->debug((format(),"BlenderDNA: Got ",dna.structures.size(),
+ " structures with totally ",fields," fields"));
+
+#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+ dna.DumpToFile();
+#endif
+
+ dna.AddPrimitiveStructures();
+ dna.RegisterConverters();
+}
+
+
+#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+
+#include <fstream>
+// ------------------------------------------------------------------------------------------------
+void DNA :: DumpToFile()
+{
+ // we dont't bother using the VFS here for this is only for debugging.
+ // (and all your bases are belong to us).
+
+ std::ofstream f("dna.txt");
+ if (f.fail()) {
+ DefaultLogger::get()->error("Could not dump dna to dna.txt");
+ return;
+ }
+ f << "Field format: type name offset size" << "\n";
+ f << "Structure format: name size" << "\n";
+
+ for_each(const Structure& s, structures) {
+ f << s.name << " " << s.size << "\n\n";
+ for_each(const Field& ff, s.fields) {
+ f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << std::endl;
+ }
+ f << std::endl;
+ }
+ DefaultLogger::get()->info("BlenderDNA: Dumped dna to dna.txt");
+}
+#endif
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void DNA :: ExtractArraySize(
+ const std::string& out,
+ size_t array_sizes[2]
+)
+{
+ array_sizes[0] = array_sizes[1] = 1;
+ std::string::size_type pos = out.find('[');
+ if (pos++ == std::string::npos) {
+ return;
+ }
+ array_sizes[0] = strtol10(&out[pos]);
+
+ pos = out.find('[',pos);
+ if (pos++ == std::string::npos) {
+ return;
+ }
+ array_sizes[1] = strtol10(&out[pos]);
+}
+
+// ------------------------------------------------------------------------------------------------
+boost::shared_ptr< ElemBase > DNA :: ConvertBlobToStructure(
+ const Structure& structure,
+ const FileDatabase& db
+) const
+{
+ std::map<std::string, FactoryPair >::const_iterator it = converters.find(structure.name);
+ if (it == converters.end()) {
+ return boost::shared_ptr< ElemBase >();
+ }
+
+ boost::shared_ptr< ElemBase > ret = (structure.*((*it).second.first))();
+ (structure.*((*it).second.second))(ret,db);
+
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+DNA::FactoryPair DNA :: GetBlobToStructureConverter(
+ const Structure& structure,
+ const FileDatabase&
+) const
+{
+ std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name);
+ return it == converters.end() ? FactoryPair() : (*it).second;
+}
+
+// basing on http://www.blender.org/development/architecture/notes-on-sdna/
+// ------------------------------------------------------------------------------------------------
+void DNA :: AddPrimitiveStructures()
+{
+ // NOTE: these are just dummies. Their presence enforces
+ // Structure::Convert<target_type> to be called on these
+ // empty structures. These converters are special
+ // overloads which scan the name of the structure and
+ // perform the required data type conversion if one
+ // of these special names is found in the structure
+ // in question.
+
+ indices["int"] = structures.size();
+ structures.push_back( Structure() );
+ structures.back().name = "int";
+ structures.back().size = 4;
+
+ indices["short"] = structures.size();
+ structures.push_back( Structure() );
+ structures.back().name = "short";
+ structures.back().size = 2;
+
+
+ indices["char"] = structures.size();
+ structures.push_back( Structure() );
+ structures.back().name = "char";
+ structures.back().size = 1;
+
+
+ indices["float"] = structures.size();
+ structures.push_back( Structure() );
+ structures.back().name = "float";
+ structures.back().size = 4;
+
+
+ indices["double"] = structures.size();
+ structures.push_back( Structure() );
+ structures.back().name = "double";
+ structures.back().size = 8;
+
+ // no long, seemingly.
+}
+
+// ------------------------------------------------------------------------------------------------
+void SectionParser :: Next()
+{
+ stream.SetCurrentPos(current.start + current.size);
+
+ const char tmp[] = {
+ stream.GetI1(),
+ stream.GetI1(),
+ stream.GetI1(),
+ stream.GetI1()
+ };
+ current.id = std::string(tmp,tmp[3]?4:tmp[2]?3:tmp[1]?2:1);
+
+ current.size = stream.GetI4();
+ current.address.val = ptr64 ? stream.GetU8() : stream.GetU4();
+
+ current.dna_index = stream.GetI4();
+ current.num = stream.GetI4();
+
+ current.start = stream.GetCurrentPos();
+ if (stream.GetRemainingSizeToLimit() < current.size) {
+ throw DeadlyImportError("BLEND: invalid size of file block");
+ }
+
+#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+ DefaultLogger::get()->debug(current.id);
+#endif
+}
+
+
+
+#endif
diff --git a/src/3rdparty/assimp/code/BlenderDNA.h b/src/3rdparty/assimp/code/BlenderDNA.h
new file mode 100644
index 000000000..0e3c13d76
--- /dev/null
+++ b/src/3rdparty/assimp/code/BlenderDNA.h
@@ -0,0 +1,867 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file BlenderDNA.h
+ * @brief Blender `DNA` (file format specification embedded in
+ * blend file itself) loader.
+ */
+#ifndef INCLUDED_AI_BLEND_DNA_H
+#define INCLUDED_AI_BLEND_DNA_H
+
+#include "BaseImporter.h"
+#include "TinyFormatter.h"
+
+// enable verbose log output. really verbose, so be careful.
+#ifdef _DEBUG
+# define ASSIMP_BUILD_BLENDER_DEBUG
+#endif
+
+// #define ASSIMP_BUILD_BLENDER_NO_STATS
+
+namespace Assimp {
+ template <bool,bool> class StreamReader;
+ typedef StreamReader<true,true> StreamReaderAny;
+
+ namespace Blender {
+ class FileDatabase;
+ struct FileBlockHead;
+
+ template <template <typename> class TOUT>
+ class ObjectCache;
+
+// -------------------------------------------------------------------------------
+/** Exception class used by the blender loader to selectively catch exceptions
+ * thrown in its own code (DeadlyImportErrors thrown in general utility
+ * functions are untouched then). If such an exception is not caught by
+ * the loader itself, it will still be caught by Assimp due to its
+ * ancestry. */
+// -------------------------------------------------------------------------------
+struct Error : DeadlyImportError
+{
+ Error (const std::string& s)
+ : DeadlyImportError(s)
+ {}
+};
+
+// -------------------------------------------------------------------------------
+/** The only purpose of this structure is to feed a virtual dtor into its
+ * descendents. It serves as base class for all data structure fields. */
+// -------------------------------------------------------------------------------
+struct ElemBase
+{
+ virtual ~ElemBase() {}
+
+ /** Type name of the element. The type
+ * string points is the `c_str` of the `name` attribute of the
+ * corresponding `Structure`, that is, it is only valid as long
+ * as the DNA is not modified. The dna_type is only set if the
+ * data type is not static, i.e. a boost::shared_ptr<ElemBase>
+ * in the scene description would have its type resolved
+ * at runtime, so this member is always set. */
+ const char* dna_type;
+};
+
+
+// -------------------------------------------------------------------------------
+/** Represents a generic pointer to a memory location, which can be either 32
+ * or 64 bits. These pointers are loaded from the BLEND file and finally
+ * fixed to point to the real, converted representation of the objects
+ * they used to point to.*/
+// -------------------------------------------------------------------------------
+struct Pointer
+{
+ Pointer() : val() {}
+ uint64_t val;
+};
+
+// -------------------------------------------------------------------------------
+/** Represents a generic offset within a BLEND file */
+// -------------------------------------------------------------------------------
+struct FileOffset
+{
+ FileOffset() : val() {}
+ uint64_t val;
+};
+
+// -------------------------------------------------------------------------------
+/** Dummy derivate of std::vector to be able to use it in templates simultaenously
+ * with boost::shared_ptr, which takes only one template argument
+ * while std::vector takes three. Also we need to provide some special member
+ * functions of shared_ptr */
+// -------------------------------------------------------------------------------
+template <typename T>
+class vector : public std::vector<T>
+{
+public:
+ using std::vector<T>::resize;
+ using std::vector<T>::empty;
+
+ void reset() {
+ resize(0);
+ }
+
+ operator bool () const {
+ return !empty();
+ }
+};
+
+// -------------------------------------------------------------------------------
+/** Mixed flags for use in #Field */
+// -------------------------------------------------------------------------------
+enum FieldFlags
+{
+ FieldFlag_Pointer = 0x1,
+ FieldFlag_Array = 0x2
+};
+
+// -------------------------------------------------------------------------------
+/** Represents a single member of a data structure in a BLEND file */
+// -------------------------------------------------------------------------------
+struct Field
+{
+ std::string name;
+ std::string type;
+
+ size_t size;
+ size_t offset;
+
+ /** Size of each array dimension. For flat arrays,
+ * the second dimension is set to 1. */
+ size_t array_sizes[2];
+
+ /** Any of the #FieldFlags enumerated values */
+ unsigned int flags;
+};
+
+// -------------------------------------------------------------------------------
+/** Range of possible behaviours for fields absend in the input file. Some are
+ * mission critical so we need them, while others can silently be default
+ * initialized and no animations are harmed. */
+// -------------------------------------------------------------------------------
+enum ErrorPolicy
+{
+ /** Substitute default value and ignore */
+ ErrorPolicy_Igno,
+ /** Substitute default value and write to log */
+ ErrorPolicy_Warn,
+ /** Substitute a massive error message and crash the whole matrix. Its time for another zion */
+ ErrorPolicy_Fail
+};
+
+#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+# define ErrorPolicy_Igno ErrorPolicy_Warn
+#endif
+
+// -------------------------------------------------------------------------------
+/** Represents a data structure in a BLEND file. A Structure defines n fields
+ * and their locatios and encodings the input stream. Usually, every
+ * Structure instance pertains to one equally-named data structure in the
+ * BlenderScene.h header. This class defines various utilities to map a
+ * binary `blob` read from the file to such a structure instance with
+ * meaningful contents. */
+// -------------------------------------------------------------------------------
+class Structure
+{
+ template <template <typename> class> friend class ObjectCache;
+
+public:
+
+ Structure()
+ : size(0)
+ , cache_idx(-1)
+ {}
+
+public:
+
+ // publicly accessible members
+ std::string name;
+ vector< Field > fields;
+ std::map<std::string, size_t> indices;
+
+ size_t size;
+
+public:
+
+ // --------------------------------------------------------
+ /** Access a field of the structure by its canonical name. The pointer version
+ * returns NULL on failure while the reference version raises an import error. */
+ inline const Field& operator [] (const std::string& ss) const;
+ inline const Field* Get (const std::string& ss) const;
+
+ // --------------------------------------------------------
+ /** Access a field of the structure by its index */
+ inline const Field& operator [] (const size_t i) const;
+
+ // --------------------------------------------------------
+ inline bool operator== (const Structure& other) const {
+ return name == other.name; // name is meant to be an unique identifier
+ }
+
+ // --------------------------------------------------------
+ inline bool operator!= (const Structure& other) const {
+ return name != other.name;
+ }
+
+public:
+
+ // --------------------------------------------------------
+ /** Try to read an instance of the structure from the stream
+ * and attempt to convert to `T`. This is done by
+ * an appropriate specialization. If none is available,
+ * a compiler complain is the result.
+ * @param dest Destination value to be written
+ * @param db File database, including input stream. */
+ template <typename T> inline void Convert (T& dest,
+ const FileDatabase& db) const;
+
+
+
+ // --------------------------------------------------------
+ // generic converter
+ template <typename T>
+ void Convert(boost::shared_ptr<ElemBase> in,const FileDatabase& db) const;
+
+ // --------------------------------------------------------
+ // generic allocator
+ template <typename T> boost::shared_ptr<ElemBase> Allocate() const;
+
+
+
+ // --------------------------------------------------------
+ // field parsing for 1d arrays
+ template <int error_policy, typename T, size_t M>
+ void ReadFieldArray(T (& out)[M], const char* name,
+ const FileDatabase& db) const;
+
+ // --------------------------------------------------------
+ // field parsing for 2d arrays
+ template <int error_policy, typename T, size_t M, size_t N>
+ void ReadFieldArray2(T (& out)[M][N], const char* name,
+ const FileDatabase& db) const;
+
+ // --------------------------------------------------------
+ // field parsing for pointer or dynamic array types
+ // (boost::shared_ptr or boost::shared_array)
+ template <int error_policy, template <typename> class TOUT, typename T>
+ void ReadFieldPtr(TOUT<T>& out, const char* name,
+ const FileDatabase& db) const;
+
+ // --------------------------------------------------------
+ // field parsing for static arrays of pointer or dynamic
+ // array types (boost::shared_ptr[] or boost::shared_array[])
+ template <int error_policy, template <typename> class TOUT, typename T, size_t N>
+ void ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
+ const FileDatabase& db) const;
+
+ // --------------------------------------------------------
+ // field parsing for `normal` values
+ template <int error_policy, typename T>
+ void ReadField(T& out, const char* name,
+ const FileDatabase& db) const;
+
+private:
+
+ // --------------------------------------------------------
+ template <template <typename> class TOUT, typename T>
+ void ResolvePointer(TOUT<T>& out, const Pointer & ptrval,
+ const FileDatabase& db, const Field& f) const;
+
+ // --------------------------------------------------------
+ template <template <typename> class TOUT, typename T>
+ void ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval,
+ const FileDatabase& db, const Field& f) const;
+
+ // --------------------------------------------------------
+ void ResolvePointer( boost::shared_ptr< FileOffset >& out, const Pointer & ptrval,
+ const FileDatabase& db, const Field& f) const;
+
+ // --------------------------------------------------------
+ inline const FileBlockHead* LocateFileBlockForAddress(
+ const Pointer & ptrval,
+ const FileDatabase& db) const;
+
+private:
+
+ // ------------------------------------------------------------------------------
+ template <typename T> T* _allocate(boost::shared_ptr<T>& out, size_t& s) const {
+ out = boost::shared_ptr<T>(new T());
+ s = 1;
+ return out.get();
+ }
+
+ template <typename T> T* _allocate(vector<T>& out, size_t& s) const {
+ out.resize(s);
+ return s ? &out.front() : NULL;
+ }
+
+ // --------------------------------------------------------
+ template <int error_policy>
+ struct _defaultInitializer {
+
+ template <typename T, unsigned int N>
+ void operator ()(T (& out)[N], const char* = NULL) {
+ for (unsigned int i = 0; i < N; ++i) {
+ out[i] = T();
+ }
+ }
+
+ template <typename T, unsigned int N, unsigned int M>
+ void operator ()(T (& out)[N][M], const char* = NULL) {
+ for (unsigned int i = 0; i < N; ++i) {
+ for (unsigned int j = 0; j < M; ++j) {
+ out[i][j] = T();
+ }
+ }
+ }
+
+ template <typename T>
+ void operator ()(T& out, const char* = NULL) {
+ out = T();
+ }
+ };
+
+private:
+
+ mutable size_t cache_idx;
+};
+
+// --------------------------------------------------------
+template <> struct Structure :: _defaultInitializer<ErrorPolicy_Warn> {
+
+ template <typename T>
+ void operator ()(T& out, const char* reason = "<add reason>") {
+ DefaultLogger::get()->warn(reason);
+
+ // ... and let the show go on
+ _defaultInitializer<0 /*ErrorPolicy_Igno*/>()(out);
+ }
+};
+
+template <> struct Structure :: _defaultInitializer<ErrorPolicy_Fail> {
+
+ template <typename T>
+ void operator ()(T&,const char* = "") {
+ // obviously, it is crucial that _DefaultInitializer is used
+ // only from within a catch clause.
+ throw;
+ }
+};
+
+// -------------------------------------------------------------------------------------------------------
+template <> inline void Structure :: ResolvePointer<boost::shared_ptr,ElemBase>(boost::shared_ptr<ElemBase>& out,
+ const Pointer & ptrval,
+ const FileDatabase& db,
+ const Field& f
+ ) const;
+
+
+// -------------------------------------------------------------------------------
+/** Represents the full data structure information for a single BLEND file.
+ * This data is extracted from the DNA1 chunk in the file.
+ * #DNAParser does the reading and represents currently the only place where
+ * DNA is altered.*/
+// -------------------------------------------------------------------------------
+class DNA
+{
+public:
+
+ typedef void (Structure::*ConvertProcPtr) (
+ boost::shared_ptr<ElemBase> in,
+ const FileDatabase&
+ ) const;
+
+ typedef boost::shared_ptr<ElemBase> (
+ Structure::*AllocProcPtr) () const;
+
+ typedef std::pair< AllocProcPtr, ConvertProcPtr > FactoryPair;
+
+public:
+
+ std::map<std::string, FactoryPair > converters;
+ vector<Structure > structures;
+ std::map<std::string, size_t> indices;
+
+public:
+
+ // --------------------------------------------------------
+ /** Access a structure by its canonical name, the pointer version returns NULL on failure
+ * while the reference version raises an error. */
+ inline const Structure& operator [] (const std::string& ss) const;
+ inline const Structure* Get (const std::string& ss) const;
+
+ // --------------------------------------------------------
+ /** Access a structure by its index */
+ inline const Structure& operator [] (const size_t i) const;
+
+public:
+
+ // --------------------------------------------------------
+ /** Add structure definitions for all the primitive types,
+ * i.e. integer, short, char, float */
+ void AddPrimitiveStructures();
+
+ // --------------------------------------------------------
+ /** Fill the @c converters member with converters for all
+ * known data types. The implementation of this method is
+ * in BlenderScene.cpp and is machine-generated.
+ * Converters are used to quickly handle objects whose
+ * exact data type is a runtime-property and not yet
+ * known at compile time (consier Object::data).*/
+ void RegisterConverters();
+
+
+ // --------------------------------------------------------
+ /** Take an input blob from the stream, interpret it according to
+ * a its structure name and convert it to the intermediate
+ * representation.
+ * @param structure Destination structure definition
+ * @param db File database.
+ * @return A null pointer if no appropriate converter is available.*/
+ boost::shared_ptr< ElemBase > ConvertBlobToStructure(
+ const Structure& structure,
+ const FileDatabase& db
+ ) const;
+
+ // --------------------------------------------------------
+ /** Find a suitable conversion function for a given Structure.
+ * Such a converter function takes a blob from the input
+ * stream, reads as much as it needs, and builds up a
+ * complete object in intermediate representation.
+ * @param structure Destination structure definition
+ * @param db File database.
+ * @return A null pointer in .first if no appropriate converter is available.*/
+ FactoryPair GetBlobToStructureConverter(
+ const Structure& structure,
+ const FileDatabase& db
+ ) const;
+
+
+#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+ // --------------------------------------------------------
+ /** Dump the DNA to a text file. This is for debugging purposes.
+ * The output file is `dna.txt` in the current working folder*/
+ void DumpToFile();
+#endif
+
+ // --------------------------------------------------------
+ /** Extract array dimensions from a C array declaration, such
+ * as `...[4][6]`. Returned string would be `...[][]`.
+ * @param out
+ * @param array_sizes Receive maximally two array dimensions,
+ * the second element is set to 1 if the array is flat.
+ * Both are set to 1 if the input is not an array.
+ * @throw DeadlyImportError if more than 2 dimensions are
+ * encountered. */
+ static void ExtractArraySize(
+ const std::string& out,
+ size_t array_sizes[2]
+ );
+};
+
+// special converters for primitive types
+template <> inline void Structure :: Convert<int> (int& dest,const FileDatabase& db) const;
+template <> inline void Structure :: Convert<short> (short& dest,const FileDatabase& db) const;
+template <> inline void Structure :: Convert<char> (char& dest,const FileDatabase& db) const;
+template <> inline void Structure :: Convert<float> (float& dest,const FileDatabase& db) const;
+template <> inline void Structure :: Convert<double> (double& dest,const FileDatabase& db) const;
+template <> inline void Structure :: Convert<Pointer> (Pointer& dest,const FileDatabase& db) const;
+
+// -------------------------------------------------------------------------------
+/** Describes a master file block header. Each master file sections holds n
+ * elements of a certain SDNA structure (or otherwise unspecified data). */
+// -------------------------------------------------------------------------------
+struct FileBlockHead
+{
+ // points right after the header of the file block
+ StreamReaderAny::pos start;
+
+ std::string id;
+ size_t size;
+
+ // original memory address of the data
+ Pointer address;
+
+ // index into DNA
+ unsigned int dna_index;
+
+ // number of structure instances to follow
+ size_t num;
+
+
+
+ // file blocks are sorted by address to quickly locate specific memory addresses
+ bool operator < (const FileBlockHead& o) const {
+ return address.val < o.address.val;
+ }
+
+ // for std::upper_bound
+ operator const Pointer& () const {
+ return address;
+ }
+};
+
+// for std::upper_bound
+inline bool operator< (const Pointer& a, const Pointer& b) {
+ return a.val < b.val;
+}
+
+// -------------------------------------------------------------------------------
+/** Utility to read all master file blocks in turn. */
+// -------------------------------------------------------------------------------
+class SectionParser
+{
+public:
+
+ // --------------------------------------------------------
+ /** @param stream Inout stream, must point to the
+ * first section in the file. Call Next() once
+ * to have it read.
+ * @param ptr64 Pointer size in file is 64 bits? */
+ SectionParser(StreamReaderAny& stream,bool ptr64)
+ : stream(stream)
+ , ptr64(ptr64)
+ {
+ current.size = current.start = 0;
+ }
+
+public:
+
+ // --------------------------------------------------------
+ const FileBlockHead& GetCurrent() const {
+ return current;
+ }
+
+
+public:
+
+ // --------------------------------------------------------
+ /** Advance to the next section.
+ * @throw DeadlyImportError if the last chunk was passed. */
+ void Next();
+
+public:
+
+ FileBlockHead current;
+ StreamReaderAny& stream;
+ bool ptr64;
+};
+
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+// -------------------------------------------------------------------------------
+/** Import statistics, i.e. number of file blocks read*/
+// -------------------------------------------------------------------------------
+class Statistics {
+
+public:
+
+ Statistics ()
+ : fields_read ()
+ , pointers_resolved ()
+ , cache_hits ()
+// , blocks_read ()
+ , cached_objects ()
+ {}
+
+public:
+
+ /** total number of fields we read */
+ unsigned int fields_read;
+
+ /** total number of resolved pointers */
+ unsigned int pointers_resolved;
+
+ /** number of pointers resolved from the cache */
+ unsigned int cache_hits;
+
+ /** number of blocks (from FileDatabase::entries)
+ we did actually read from. */
+ // unsigned int blocks_read;
+
+ /** objects in FileData::cache */
+ unsigned int cached_objects;
+};
+#endif
+
+// -------------------------------------------------------------------------------
+/** The object cache - all objects addressed by pointers are added here. This
+ * avoids circular references and avoids object duplication. */
+// -------------------------------------------------------------------------------
+template <template <typename> class TOUT>
+class ObjectCache
+{
+public:
+
+ typedef std::map< Pointer, TOUT<ElemBase> > StructureCache;
+
+public:
+
+ ObjectCache(const FileDatabase& db)
+ : db(db)
+ {
+ // currently there are only ~400 structure records per blend file.
+ // we read only a small part of them and don't cache objects
+ // which we don't need, so this should suffice.
+ caches.reserve(64);
+ }
+
+public:
+
+ // --------------------------------------------------------
+ /** Check whether a specific item is in the cache.
+ * @param s Data type of the item
+ * @param out Output pointer. Unchanged if the
+ * cache doens't know the item yet.
+ * @param ptr Item address to look for. */
+ template <typename T> void get (
+ const Structure& s,
+ TOUT<T>& out,
+ const Pointer& ptr) const;
+
+ // --------------------------------------------------------
+ /** Add an item to the cache after the item has
+ * been fully read. Do not insert anything that
+ * may be faulty or might cause the loading
+ * to abort.
+ * @param s Data type of the item
+ * @param out Item to insert into the cache
+ * @param ptr address (cache key) of the item. */
+ template <typename T> void set
+ (const Structure& s,
+ const TOUT<T>& out,
+ const Pointer& ptr);
+
+private:
+
+ mutable vector<StructureCache> caches;
+ const FileDatabase& db;
+};
+
+// -------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------
+template <> class ObjectCache<Blender::vector>
+{
+public:
+
+ ObjectCache(const FileDatabase&) {}
+
+ template <typename T> void get(const Structure&, vector<T>&, const Pointer&) {}
+ template <typename T> void set(const Structure&, const vector<T>&, const Pointer&) {}
+};
+
+#ifdef _MSC_VER
+# pragma warning(disable:4355)
+#endif
+
+// -------------------------------------------------------------------------------
+/** Memory representation of a full BLEND file and all its dependencies. The
+ * output aiScene is constructed from an instance of this data structure. */
+// -------------------------------------------------------------------------------
+class FileDatabase
+{
+ template <template <typename> class TOUT> friend class ObjectCache;
+
+public:
+
+
+ FileDatabase()
+ : next_cache_idx()
+ , _cacheArrays(*this)
+ , _cache(*this)
+ {}
+
+public:
+
+ // publicly accessible fields
+ bool i64bit;
+ bool little;
+
+ DNA dna;
+ boost::shared_ptr< StreamReaderAny > reader;
+ vector< FileBlockHead > entries;
+
+public:
+
+ Statistics& stats() const {
+ return _stats;
+ }
+
+ // For all our templates to work on both shared_ptr's and vector's
+ // using the same code, a dummy cache for arrays is provided. Actually,
+ // arrays of objects are never cached because we can't easily
+ // ensure their proper destruction.
+ template <typename T>
+ ObjectCache<boost::shared_ptr>& cache(boost::shared_ptr<T>&) const {
+ return _cache;
+ }
+
+ template <typename T>
+ ObjectCache<vector>& cache(vector<T>&) const {
+ return _cacheArrays;
+ }
+
+private:
+
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ mutable Statistics _stats;
+#endif
+
+ mutable size_t next_cache_idx;
+
+ mutable ObjectCache<vector> _cacheArrays;
+ mutable ObjectCache<boost::shared_ptr> _cache;
+};
+
+#ifdef _MSC_VER
+# pragma warning(default:4355)
+#endif
+
+// -------------------------------------------------------------------------------
+/** Factory to extract a #DNA from the DNA1 file block in a BLEND file. */
+// -------------------------------------------------------------------------------
+class DNAParser
+{
+
+public:
+
+ /** Bind the parser to a empty DNA and an input stream */
+ DNAParser(FileDatabase& db)
+ : db(db)
+ {}
+
+public:
+
+ // --------------------------------------------------------
+ /** Locate the DNA in the file and parse it. The input
+ * stream is expected to point to the beginning of the DN1
+ * chunk at the time this method is called and is
+ * undefined afterwards.
+ * @throw DeadlyImportError if the DNA cannot be read.
+ * @note The position of the stream pointer is undefined
+ * afterwards.*/
+ void Parse ();
+
+public:
+
+ /** Obtain a reference to the extracted DNA information */
+ const Blender::DNA& GetDNA() const {
+ return db.dna;
+ }
+
+private:
+
+ FileDatabase& db;
+};
+
+//--------------------------------------------------------------------------------
+template <> inline void Structure :: ResolvePointer<boost::shared_ptr,ElemBase>(boost::shared_ptr<ElemBase>& out,
+ const Pointer & ptrval,
+ const FileDatabase& db,
+ const Field& /* f */
+ ) const
+{
+ // Special case when the data type needs to be determined at runtime.
+ // Less secure than in the `strongly-typed` case.
+
+ out.reset();
+ if (!ptrval.val) {
+ return;
+ }
+
+ // find the file block the pointer is pointing to
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
+
+ // determine the target type from the block header
+ const Structure& s = db.dna[block->dna_index];
+
+ // try to retrieve the object from the cache
+ db.cache(out).get(s,out,ptrval);
+ if (out) {
+ return;
+ }
+
+ // seek to this location, but save the previous stream pointer.
+ const StreamReaderAny::pos pold = db.reader->GetCurrentPos();
+ db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) ));
+ // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems.
+ // I really ought to improve StreamReader to work with 64 bit indices exclusively.
+
+ // continue conversion after allocating the required storage
+ DNA::FactoryPair builders = db.dna.GetBlobToStructureConverter(s,db);
+ if (!builders.first) {
+ // this might happen if DNA::RegisterConverters hasn't been called so far
+ // or if the target type is not contained in `our` DNA.
+ out.reset();
+ DefaultLogger::get()->warn((Formatter::format(),
+ "Failed to find a converter for the `",s.name,"` structure"
+ ));
+ return;
+ }
+
+ // allocate the object hull
+ out = (s.*builders.first)();
+
+ // cache the object immediately to prevent infinite recursion in a
+ // circular list with a single element (i.e. a self-referencing element).
+ db.cache(out).set(s,out,ptrval);
+
+ // and do the actual conversion
+ (s.*builders.second)(out,db);
+ db.reader->SetCurrentPos(pold);
+
+ // store a pointer to the name string of the actual type
+ // in the object itself. This allows the conversion code
+ // to perform additional type checking.
+ out->dna_type = s.name.c_str();
+
+
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().pointers_resolved;
+#endif
+}
+
+ } // end Blend
+} // end Assimp
+
+#include "BlenderDNA.inl"
+
+#endif
diff --git a/src/3rdparty/assimp/code/BlenderDNA.inl b/src/3rdparty/assimp/code/BlenderDNA.inl
new file mode 100644
index 000000000..511e7878c
--- /dev/null
+++ b/src/3rdparty/assimp/code/BlenderDNA.inl
@@ -0,0 +1,638 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file BlenderDNA.inl
+ * @brief Blender `DNA` (file format specification embedded in
+ * blend file itself) loader.
+ */
+#ifndef INCLUDED_AI_BLEND_DNA_INL
+#define INCLUDED_AI_BLEND_DNA_INL
+
+namespace Assimp {
+ namespace Blender {
+
+//--------------------------------------------------------------------------------
+const Field& Structure :: operator [] (const std::string& ss) const
+{
+ std::map<std::string, size_t>::const_iterator it = indices.find(ss);
+ if (it == indices.end()) {
+ throw Error((Formatter::format(),
+ "BlendDNA: Did not find a field named `",ss,"` in structure `",name,"`"
+ ));
+ }
+
+ return fields[(*it).second];
+}
+
+//--------------------------------------------------------------------------------
+const Field* Structure :: Get (const std::string& ss) const
+{
+ std::map<std::string, size_t>::const_iterator it = indices.find(ss);
+ return it == indices.end() ? NULL : &fields[(*it).second];
+}
+
+//--------------------------------------------------------------------------------
+const Field& Structure :: operator [] (const size_t i) const
+{
+ if (i >= fields.size()) {
+ throw Error((Formatter::format(),
+ "BlendDNA: There is no field with index `",i,"` in structure `",name,"`"
+ ));
+ }
+
+ return fields[i];
+}
+
+//--------------------------------------------------------------------------------
+template <typename T> boost::shared_ptr<ElemBase> Structure :: Allocate() const
+{
+ return boost::shared_ptr<T>(new T());
+}
+
+//--------------------------------------------------------------------------------
+template <typename T> void Structure :: Convert(
+ boost::shared_ptr<ElemBase> in,
+ const FileDatabase& db) const
+{
+ Convert<T> (*static_cast<T*> ( in.get() ),db);
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, typename T, size_t M>
+void Structure :: ReadFieldArray(T (& out)[M], const char* name, const FileDatabase& db) const
+{
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ try {
+ const Field& f = (*this)[name];
+ const Structure& s = db.dna[f.type];
+
+ // is the input actually an array?
+ if (!(f.flags & FieldFlag_Array)) {
+ throw Error((Formatter::format(),"Field `",name,"` of structure `",
+ this->name,"` ought to be an array of size ",M
+ ));
+ }
+
+ db.reader->IncPtr(f.offset);
+
+ // size conversions are always allowed, regardless of error_policy
+ unsigned int i = 0;
+ for (; i < std::min(f.array_sizes[0],M); ++i) {
+ s.Convert(out[i],db);
+ }
+ for (; i < M; ++i) {
+ _defaultInitializer<ErrorPolicy_Igno>()(out[i]);
+ }
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+ }
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, typename T, size_t M, size_t N>
+void Structure :: ReadFieldArray2(T (& out)[M][N], const char* name, const FileDatabase& db) const
+{
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ try {
+ const Field& f = (*this)[name];
+ const Structure& s = db.dna[f.type];
+
+ // is the input actually an array?
+ if (!(f.flags & FieldFlag_Array)) {
+ throw Error((Formatter::format(),"Field `",name,"` of structure `",
+ this->name,"` ought to be an array of size ",M,"*",N
+ ));
+ }
+
+ db.reader->IncPtr(f.offset);
+
+ // size conversions are always allowed, regardless of error_policy
+ unsigned int i = 0;
+ for (; i < std::min(f.array_sizes[0],M); ++i) {
+ unsigned int j = 0;
+ for (; j < std::min(f.array_sizes[1],N); ++j) {
+ s.Convert(out[i][j],db);
+ }
+ for (; j < N; ++j) {
+ _defaultInitializer<ErrorPolicy_Igno>()(out[i][j]);
+ }
+ }
+ for (; i < M; ++i) {
+ _defaultInitializer<ErrorPolicy_Igno>()(out[i]);
+ }
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+ }
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, template <typename> class TOUT, typename T>
+void Structure :: ReadFieldPtr(TOUT<T>& out, const char* name, const FileDatabase& db) const
+{
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ Pointer ptrval;
+ const Field* f;
+ try {
+ f = &(*this)[name];
+
+ // sanity check, should never happen if the genblenddna script is right
+ if (!(f->flags & FieldFlag_Pointer)) {
+ throw Error((Formatter::format(),"Field `",name,"` of structure `",
+ this->name,"` ought to be a pointer"));
+ }
+
+ db.reader->IncPtr(f->offset);
+ Convert(ptrval,db);
+ // actually it is meaningless on which Structure the Convert is called
+ // because the `Pointer` argument triggers a special implementation.
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+
+ out.reset();
+ return;
+ }
+
+ // resolve the pointer and load the corresponding structure
+ ResolvePointer(out,ptrval,db,*f);
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, template <typename> class TOUT, typename T, size_t N>
+void Structure :: ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
+ const FileDatabase& db) const
+{
+ // XXX see if we can reduce this to call to the 'normal' ReadFieldPtr
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ Pointer ptrval[N];
+ const Field* f;
+ try {
+ f = &(*this)[name];
+
+ // sanity check, should never happen if the genblenddna script is right
+ if ((FieldFlag_Pointer|FieldFlag_Pointer) != (f->flags & (FieldFlag_Pointer|FieldFlag_Pointer))) {
+ throw Error((Formatter::format(),"Field `",name,"` of structure `",
+ this->name,"` ought to be a pointer AND an array"));
+ }
+
+ db.reader->IncPtr(f->offset);
+
+ size_t i = 0;
+ for (; i < std::min(f->array_sizes[0],N); ++i) {
+ Convert(ptrval[i],db);
+ }
+ for (; i < N; ++i) {
+ _defaultInitializer<ErrorPolicy_Igno>()(ptrval[i]);
+ }
+
+ // actually it is meaningless on which Structure the Convert is called
+ // because the `Pointer` argument triggers a special implementation.
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+ for (size_t i = 0; i < N; ++i) {
+ out[i].reset();
+ }
+ return;
+ }
+ for (size_t i = 0; i < N; ++i) {
+ // resolve the pointer and load the corresponding structure
+ ResolvePointer(out[i],ptrval[i],db,*f);
+ }
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, typename T>
+void Structure :: ReadField(T& out, const char* name, const FileDatabase& db) const
+{
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ try {
+ const Field& f = (*this)[name];
+ // find the structure definition pertaining to this field
+ const Structure& s = db.dna[f.type];
+
+ db.reader->IncPtr(f.offset);
+ s.Convert(out,db);
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+ }
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+}
+
+
+//--------------------------------------------------------------------------------
+template <template <typename> class TOUT, typename T>
+void Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const FileDatabase& db, const Field& f) const
+{
+ out.reset();
+ if (!ptrval.val) {
+ return;
+ }
+ const Structure& s = db.dna[f.type];
+ // find the file block the pointer is pointing to
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
+
+ // also determine the target type from the block header
+ // and check if it matches the type which we expect.
+ const Structure& ss = db.dna[block->dna_index];
+ if (ss != s) {
+ throw Error((Formatter::format(),"Expected target to be of type `",s.name,
+ "` but seemingly it is a `",ss.name,"` instead"
+ ));
+ }
+
+ // try to retrieve the object from the cache
+ db.cache(out).get(s,out,ptrval);
+ if (out) {
+ return;
+ }
+
+ // seek to this location, but save the previous stream pointer.
+ const StreamReaderAny::pos pold = db.reader->GetCurrentPos();
+ db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) ));
+ // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems.
+ // I really ought to improve StreamReader to work with 64 bit indices exclusively.
+
+ // continue conversion after allocating the required storage
+ size_t num = block->size / ss.size;
+ T* o = _allocate(out,num);
+
+ // cache the object before we convert it to avoid cyclic recursion.
+ db.cache(out).set(s,out,ptrval);
+
+ for (size_t i = 0; i < num; ++i,++o) {
+ s.Convert(*o,db);
+ }
+
+ db.reader->SetCurrentPos(pold);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ if (out) {
+ ++db.stats().pointers_resolved;
+ }
+#endif
+}
+
+//--------------------------------------------------------------------------------
+inline void Structure :: ResolvePointer( boost::shared_ptr< FileOffset >& out, const Pointer & ptrval, const FileDatabase& db, const Field& /* f */ ) const
+{
+ // Currently used exclusively by PackedFile::data to represent
+ // a simple offset into the mapped BLEND file.
+ out.reset();
+ if (!ptrval.val) {
+ return;
+ }
+
+ // find the file block the pointer is pointing to
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
+
+ out = boost::shared_ptr< FileOffset > (new FileOffset());
+ out->val = block->start+ static_cast<size_t>((ptrval.val - block->address.val) );
+}
+
+//--------------------------------------------------------------------------------
+template <template <typename> class TOUT, typename T>
+void Structure :: ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval, const FileDatabase& db, const Field& f) const
+{
+ // This is a function overload, not a template specialization. According to
+ // the partial ordering rules, it should be selected by the compiler
+ // for array-of-pointer inputs, i.e. Object::mats.
+
+ out.reset();
+ if (!ptrval.val) {
+ return;
+ }
+
+ // find the file block the pointer is pointing to
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
+ const size_t num = block->size / (db.i64bit?8:4);
+
+ // keep the old stream position
+ const StreamReaderAny::pos pold = db.reader->GetCurrentPos();
+ db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) ));
+
+ // allocate raw storage for the array
+ out.resize(num);
+ for (size_t i = 0; i< num; ++i) {
+ Pointer val;
+ Convert(val,db);
+
+ // and resolve the pointees
+ ResolvePointer(out[i],val,db,f);
+ }
+
+ db.reader->SetCurrentPos(pold);
+}
+
+//--------------------------------------------------------------------------------
+const FileBlockHead* Structure :: LocateFileBlockForAddress(const Pointer & ptrval, const FileDatabase& db) const
+{
+ // the file blocks appear in list sorted by
+ // with ascending base addresses so we can run a
+ // binary search to locate the pointee quickly.
+
+ // NOTE: Blender seems to distinguish between side-by-side
+ // data (stored in the same data block) and far pointers,
+ // which are only used for structures starting with an ID.
+ // We don't need to make this distinction, our algorithm
+ // works regardless where the data is stored.
+ vector<FileBlockHead>::const_iterator it = std::lower_bound(db.entries.begin(),db.entries.end(),ptrval);
+ if (it == db.entries.end()) {
+ // this is crucial, pointers may not be invalid.
+ // this is either a corrupted file or an attempted attack.
+ throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x",
+ std::hex,ptrval.val,", no file block falls into this address range"
+ ));
+ }
+ if (ptrval.val >= (*it).address.val + (*it).size) {
+ throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x",
+ std::hex,ptrval.val,", nearest file block starting at 0x",
+ (*it).address.val," ends at 0x",
+ (*it).address.val + (*it).size
+ ));
+ }
+ return &*it;
+}
+
+// ------------------------------------------------------------------------------------------------
+// NOTE: The MSVC debugger keeps showing up this annoying `a cast to a smaller data type has
+// caused a loss of data`-warning. Avoid this warning by a masking with an appropriate bitmask.
+
+template <typename T> struct signless;
+template <> struct signless<char> {typedef unsigned char type;};
+template <> struct signless<short> {typedef unsigned short type;};
+template <> struct signless<int> {typedef unsigned int type;};
+
+template <typename T>
+struct static_cast_silent {
+ template <typename V>
+ T operator()(V in) {
+ return static_cast<T>(in & static_cast<typename signless<T>::type>(-1));
+ }
+};
+
+template <> struct static_cast_silent<float> {
+ template <typename V> float operator()(V in) {
+ return static_cast<float> (in);
+ }
+};
+
+template <> struct static_cast_silent<double> {
+ template <typename V> double operator()(V in) {
+ return static_cast<double>(in);
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+template <typename T> inline void ConvertDispatcher(T& out, const Structure& in,const FileDatabase& db)
+{
+ if (in.name == "int") {
+ out = static_cast_silent<T>()(db.reader->GetU4());
+ }
+ else if (in.name == "short") {
+ out = static_cast_silent<T>()(db.reader->GetU2());
+ }
+ else if (in.name == "char") {
+ out = static_cast_silent<T>()(db.reader->GetU1());
+ }
+ else if (in.name == "float") {
+ out = static_cast<T>(db.reader->GetF4());
+ }
+ else if (in.name == "double") {
+ out = static_cast<T>(db.reader->GetF8());
+ }
+ else {
+ throw DeadlyImportError("Unknown source for conversion to primitive data type: "+in.name);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<int> (int& dest,const FileDatabase& db) const
+{
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<short> (short& dest,const FileDatabase& db) const
+{
+ // automatic rescaling from short to float and vice versa (seems to be used by normals)
+ if (name == "float") {
+ dest = static_cast<short>(db.reader->GetF4() * 32767.f);
+ //db.reader->IncPtr(-4);
+ return;
+ }
+ else if (name == "double") {
+ dest = static_cast<short>(db.reader->GetF8() * 32767.);
+ //db.reader->IncPtr(-8);
+ return;
+ }
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<char> (char& dest,const FileDatabase& db) const
+{
+ // automatic rescaling from char to float and vice versa (seems useful for RGB colors)
+ if (name == "float") {
+ dest = static_cast<char>(db.reader->GetF4() * 255.f);
+ return;
+ }
+ else if (name == "double") {
+ dest = static_cast<char>(db.reader->GetF8() * 255.f);
+ return;
+ }
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<float> (float& dest,const FileDatabase& db) const
+{
+ // automatic rescaling from char to float and vice versa (seems useful for RGB colors)
+ if (name == "char") {
+ dest = db.reader->GetI1() / 255.f;
+ return;
+ }
+ // automatic rescaling from short to float and vice versa (used by normals)
+ else if (name == "short") {
+ dest = db.reader->GetI2() / 32767.f;
+ return;
+ }
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<double> (double& dest,const FileDatabase& db) const
+{
+ if (name == "char") {
+ dest = db.reader->GetI1() / 255.;
+ return;
+ }
+ else if (name == "short") {
+ dest = db.reader->GetI2() / 32767.;
+ return;
+ }
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<Pointer> (Pointer& dest,const FileDatabase& db) const
+{
+ if (db.i64bit) {
+ dest.val = db.reader->GetU8();
+ //db.reader->IncPtr(-8);
+ return;
+ }
+ dest.val = db.reader->GetU4();
+ //db.reader->IncPtr(-4);
+}
+
+//--------------------------------------------------------------------------------
+const Structure& DNA :: operator [] (const std::string& ss) const
+{
+ std::map<std::string, size_t>::const_iterator it = indices.find(ss);
+ if (it == indices.end()) {
+ throw Error((Formatter::format(),
+ "BlendDNA: Did not find a structure named `",ss,"`"
+ ));
+ }
+
+ return structures[(*it).second];
+}
+
+//--------------------------------------------------------------------------------
+const Structure* DNA :: Get (const std::string& ss) const
+{
+ std::map<std::string, size_t>::const_iterator it = indices.find(ss);
+ return it == indices.end() ? NULL : &structures[(*it).second];
+}
+
+//--------------------------------------------------------------------------------
+const Structure& DNA :: operator [] (const size_t i) const
+{
+ if (i >= structures.size()) {
+ throw Error((Formatter::format(),
+ "BlendDNA: There is no structure with index `",i,"`"
+ ));
+ }
+
+ return structures[i];
+}
+
+//--------------------------------------------------------------------------------
+template <template <typename> class TOUT> template <typename T> void ObjectCache<TOUT> :: get (
+ const Structure& s,
+ TOUT<T>& out,
+ const Pointer& ptr
+) const {
+
+ if (s.cache_idx == static_cast<size_t>(-1)) {
+ s.cache_idx = db.next_cache_idx++;
+ caches.resize(db.next_cache_idx);
+ return;
+ }
+
+ typename StructureCache::const_iterator it = caches[s.cache_idx].find(ptr);
+ if (it != caches[s.cache_idx].end()) {
+ out = boost::static_pointer_cast<T>( (*it).second );
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().cache_hits;
+#endif
+ }
+ // otherwise, out remains untouched
+}
+
+
+//--------------------------------------------------------------------------------
+template <template <typename> class TOUT> template <typename T> void ObjectCache<TOUT> :: set (
+ const Structure& s,
+ const TOUT<T>& out,
+ const Pointer& ptr
+) {
+ if (s.cache_idx == static_cast<size_t>(-1)) {
+ s.cache_idx = db.next_cache_idx++;
+ caches.resize(db.next_cache_idx);
+ }
+ caches[s.cache_idx][ptr] = boost::static_pointer_cast<ElemBase>( out );
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().cached_objects;
+#endif
+}
+
+}}
+#endif
diff --git a/src/3rdparty/assimp/code/BlenderIntermediate.h b/src/3rdparty/assimp/code/BlenderIntermediate.h
new file mode 100644
index 000000000..99e0e64e0
--- /dev/null
+++ b/src/3rdparty/assimp/code/BlenderIntermediate.h
@@ -0,0 +1,183 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file BlenderIntermediate.h
+ * @brief Internal utility structures for the BlenderLoader. It also serves
+ * as master include file for the whole (internal) Blender subsystem.
+ */
+#ifndef INCLUDED_AI_BLEND_INTERMEDIATE_H
+#define INCLUDED_AI_BLEND_INTERMEDIATE_H
+
+#include "BlenderLoader.h"
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+#include "BlenderSceneGen.h"
+
+#define for_each(x,y) BOOST_FOREACH(x,y)
+
+namespace Assimp {
+namespace Blender {
+
+ // --------------------------------------------------------------------
+ /** Mini smart-array to avoid pulling in even more boost stuff. usable with vector and deque */
+ // --------------------------------------------------------------------
+ template <template <typename,typename> class TCLASS, typename T>
+ struct TempArray {
+ typedef TCLASS< T*,std::allocator<T*> > mywrap;
+
+ TempArray() {
+ }
+
+ ~TempArray () {
+ for_each(T* elem, arr) {
+ delete elem;
+ }
+ }
+
+ void dismiss() {
+ arr.clear();
+ }
+
+ mywrap* operator -> () {
+ return &arr;
+ }
+
+ operator mywrap& () {
+ return arr;
+ }
+
+ operator const mywrap& () const {
+ return arr;
+ }
+
+ mywrap& get () {
+ return arr;
+ }
+
+ const mywrap& get () const {
+ return arr;
+ }
+
+ T* operator[] (size_t idx) const {
+ return arr[idx];
+ }
+
+ T*& operator[] (size_t idx) {
+ return arr[idx];
+ }
+
+ private:
+ // no copy semantics
+ void operator= (const TempArray&) {
+ }
+
+ TempArray(const TempArray& arr) {
+ }
+
+ private:
+ mywrap arr;
+ };
+
+#ifdef _MSC_VER
+# pragma warning(disable:4351)
+#endif
+ // --------------------------------------------------------------------
+ /** ConversionData acts as intermediate storage location for
+ * the various ConvertXXX routines in BlenderImporter.*/
+ // --------------------------------------------------------------------
+ struct ConversionData
+ {
+ ConversionData(const FileDatabase& db)
+ : sentinel_cnt()
+ , next_texture()
+ , db(db)
+ {}
+
+ std::set<const Object*> objects;
+
+ TempArray <std::vector, aiMesh> meshes;
+ TempArray <std::vector, aiCamera> cameras;
+ TempArray <std::vector, aiLight> lights;
+ TempArray <std::vector, aiMaterial> materials;
+ TempArray <std::vector, aiTexture> textures;
+
+ // set of all materials referenced by at least one mesh in the scene
+ std::deque< boost::shared_ptr< Material > > materials_raw;
+
+ // counter to name sentinel textures inserted as substitutes for procedural textures.
+ unsigned int sentinel_cnt;
+
+ // next texture ID for each texture type, respectively
+ unsigned int next_texture[aiTextureType_UNKNOWN+1];
+
+ // original file data
+ const FileDatabase& db;
+ };
+#ifdef _MSC_VER
+# pragma warning(default:4351)
+#endif
+
+// ------------------------------------------------------------------------------------------------
+inline const char* GetTextureTypeDisplayString(Tex::Type t)
+{
+ switch (t) {
+ case Tex::Type_CLOUDS : return "Clouds";
+ case Tex::Type_WOOD : return "Wood";
+ case Tex::Type_MARBLE : return "Marble";
+ case Tex::Type_MAGIC : return "Magic";
+ case Tex::Type_BLEND : return "Blend";
+ case Tex::Type_STUCCI : return "Stucci";
+ case Tex::Type_NOISE : return "Noise";
+ case Tex::Type_PLUGIN : return "Plugin";
+ case Tex::Type_MUSGRAVE : return "Musgrave";
+ case Tex::Type_VORONOI : return "Voronoi";
+ case Tex::Type_DISTNOISE : return "DistortedNoise";
+ case Tex::Type_ENVMAP : return "EnvMap";
+ case Tex::Type_IMAGE : return "Image";
+ default:
+ break;
+ }
+ return "<Unknown>";
+}
+
+} // ! Blender
+} // ! Assimp
+
+#endif // ! INCLUDED_AI_BLEND_INTERMEDIATE_H
diff --git a/src/3rdparty/assimp/code/BlenderLoader.cpp b/src/3rdparty/assimp/code/BlenderLoader.cpp
new file mode 100644
index 000000000..da5342e63
--- /dev/null
+++ b/src/3rdparty/assimp/code/BlenderLoader.cpp
@@ -0,0 +1,1012 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include <QtGlobal>
+
+/** @file BlenderLoader.cpp
+ * @brief Implementation of the Blender3D importer class.
+ */
+#include "AssimpPCH.h"
+
+//#define ASSIMP_BUILD_NO_COMPRESSED_BLEND
+// Uncomment this to disable support for (gzip)compressed .BLEND files
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderIntermediate.h"
+#include "BlenderModifier.h"
+
+#include "StreamReader.h"
+#include "TinyFormatter.h"
+#include "MemoryIOWrapper.h"
+
+// zlib is needed for compressed blend files
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
+# ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+# include <zlib.h>
+# else
+# include "../contrib/zlib/zlib.h"
+# endif
+#endif
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+using namespace Assimp::Formatter;
+
+
+static const aiLoaderDesc blenderDesc = {
+ "Blender 3D Importer \nhttp://www.blender3d.org",
+ "Assimp Team",
+ "",
+ "",
+ aiLoaderFlags_SupportBinaryFlavour | aiLoaderFlags_Experimental,
+ 0,
+ 0,
+ 2,
+ 50
+};
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+BlenderImporter::BlenderImporter()
+: modifier_cache(new BlenderModifierShowcase())
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+BlenderImporter::~BlenderImporter()
+{
+ delete modifier_cache;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool BlenderImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string& extension = GetExtension(pFile);
+ if (extension == "blend") {
+ return true;
+ }
+
+ else if ((!extension.length() || checkSig) && pIOHandler) {
+ // note: this won't catch compressed files
+ const char* tokens[] = {"BLENDER"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// List all extensions handled by this loader
+void BlenderImporter::GetExtensionList(std::set<std::string>& app)
+{
+ app.insert("blend");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader registry entry
+const aiLoaderDesc& BlenderImporter::GetInfo () const
+{
+ return blenderDesc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void BlenderImporter::SetupProperties(const Importer* /* pImp */)
+{
+ // nothing to be done for the moment
+}
+
+struct free_it
+{
+ free_it(void* free) : free(free) {}
+ ~free_it() {
+ ::free(this->free);
+ }
+
+ void* free;
+};
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void BlenderImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
+ Bytef* dest = NULL;
+ free_it free_it_really(dest);
+#endif
+
+ FileDatabase file;
+ boost::shared_ptr<IOStream> stream(pIOHandler->Open(pFile,"rb"));
+ if (!stream) {
+ ThrowException("Could not open file for reading");
+ }
+
+ char magic[8] = {0};
+ stream->Read(magic,7,1);
+ if (strcmp(magic,"BLENDER")) {
+ // Check for presence of the gzip header. If yes, assume it is a
+ // compressed blend file and try uncompressing it, else fail. This is to
+ // avoid uncompressing random files which our loader might end up with.
+#ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND
+ ThrowException("BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?");
+#else
+
+ if (magic[0] != 0x1f || static_cast<uint8_t>(magic[1]) != 0x8b) {
+ ThrowException("BLENDER magic bytes are missing, couldn't find GZIP header either");
+ }
+
+ LogDebug("Found no BLENDER magic word but a GZIP header, might be a compressed file");
+ if (magic[2] != 8) {
+ ThrowException("Unsupported GZIP compression method");
+ }
+
+ // http://www.gzip.org/zlib/rfc-gzip.html#header-trailer
+ stream->Seek(0L,aiOrigin_SET);
+ boost::shared_ptr<StreamReaderLE> reader = boost::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
+
+ // build a zlib stream
+ 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
+ inflateInit2(&zstream, 16+MAX_WBITS);
+
+ zstream.next_in = reinterpret_cast<Bytef*>( reader->GetPtr() );
+ zstream.avail_in = reader->GetRemainingSize();
+
+ size_t total = 0l;
+
+ // and decompress the data .... do 1k chunks in the hope that we won't kill the stack
+#define MYBLOCK 1024
+ Bytef block[MYBLOCK];
+ int ret;
+ do {
+ zstream.avail_out = MYBLOCK;
+ zstream.next_out = block;
+ ret = inflate(&zstream, Z_NO_FLUSH);
+
+ if (ret != Z_STREAM_END && ret != Z_OK) {
+ ThrowException("Failure decompressing this file using gzip, seemingly it is NOT a compressed .BLEND file");
+ }
+ const size_t have = MYBLOCK - zstream.avail_out;
+ total += have;
+ dest = reinterpret_cast<Bytef*>( realloc(dest,total) );
+ memcpy(dest + total - have,block,have);
+ }
+ while (ret != Z_STREAM_END);
+
+ // terminate zlib
+ inflateEnd(&zstream);
+
+ // replace the input stream with a memory stream
+ stream.reset(new MemoryIOStream(reinterpret_cast<uint8_t*>(dest),total));
+
+ // .. and retry
+ stream->Read(magic,7,1);
+ if (strcmp(magic,"BLENDER")) {
+ ThrowException("Found no BLENDER magic word in decompressed GZIP file");
+ }
+#endif
+ }
+
+ file.i64bit = (stream->Read(magic,1,1),magic[0]=='-');
+ file.little = (stream->Read(magic,1,1),magic[0]=='v');
+
+ stream->Read(magic,3,1);
+ magic[3] = '\0';
+
+ LogInfo((format(),"Blender version is ",magic[0],".",magic+1,
+ " (64bit: ",file.i64bit?"true":"false",
+ ", little endian: ",file.little?"true":"false",")"
+ ));
+
+ ParseBlendFile(file,stream);
+
+ Scene scene;
+ ExtractScene(scene,file);
+
+ ConvertBlendFile(pScene,scene,file);
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ParseBlendFile(FileDatabase& out, boost::shared_ptr<IOStream> stream)
+{
+ out.reader = boost::shared_ptr<StreamReaderAny>(new StreamReaderAny(stream,out.little));
+
+ DNAParser dna_reader(out);
+ const DNA* dna = NULL;
+
+ out.entries.reserve(128); { // even small BLEND files tend to consist of many file blocks
+ SectionParser parser(*out.reader.get(),out.i64bit);
+
+ // first parse the file in search for the DNA and insert all other sections into the database
+ while ((parser.Next(),1)) {
+ const FileBlockHead& head = parser.GetCurrent();
+
+ if (head.id == "ENDB") {
+ break; // only valid end of the file
+ }
+ else if (head.id == "DNA1") {
+ dna_reader.Parse();
+ dna = &dna_reader.GetDNA();
+ continue;
+ }
+
+ out.entries.push_back(head);
+ }
+ }
+ if (!dna) {
+ ThrowException("SDNA not found");
+ }
+
+ std::sort(out.entries.begin(),out.entries.end());
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ExtractScene(Scene& out, const FileDatabase& file)
+{
+ const FileBlockHead* block = NULL;
+ std::map<std::string,size_t>::const_iterator it = file.dna.indices.find("Scene");
+ if (it == file.dna.indices.end()) {
+ ThrowException("There is no `Scene` structure record");
+ }
+
+ const Structure& ss = file.dna.structures[(*it).second];
+
+ // we need a scene somewhere to start with.
+ for_each(const FileBlockHead& bl,file.entries) {
+ if (bl.id == "SC") {
+ block = &bl;
+ break;
+ }
+ }
+
+ if (!block) {
+ ThrowException("There is not a single `Scene` record to load");
+ }
+
+ file.reader->SetCurrentPos(block->start);
+ ss.Convert(out,file);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ DefaultLogger::get()->info((format(),
+ "(Stats) Fields read: " ,file.stats().fields_read,
+ ", pointers resolved: " ,file.stats().pointers_resolved,
+ ", cache hits: " ,file.stats().cache_hits,
+ ", cached objects: " ,file.stats().cached_objects
+ ));
+#endif
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ConvertBlendFile(aiScene* out, const Scene& in,const FileDatabase& file)
+{
+ ConversionData conv(file);
+
+ // FIXME it must be possible to take the hierarchy directly from
+ // the file. This is terrible. Here, we're first looking for
+ // all objects which don't have parent objects at all -
+ std::deque<const Object*> no_parents;
+ for (boost::shared_ptr<Base> cur = boost::static_pointer_cast<Base> ( in.base.first ); cur; cur = cur->next) {
+ if (cur->object) {
+ if (!cur->object->parent) {
+ no_parents.push_back(cur->object.get());
+ }
+ else conv.objects.insert(cur->object.get());
+ }
+ }
+ for (boost::shared_ptr<Base> cur = in.basact; cur; cur = cur->next) {
+ if (cur->object) {
+ if (cur->object->parent) {
+ conv.objects.insert(cur->object.get());
+ }
+ }
+ }
+
+ if (no_parents.empty()) {
+ ThrowException("Expected at least one object with no parent");
+ }
+
+ aiNode* root = out->mRootNode = new aiNode("<BlenderRoot>");
+
+ root->mNumChildren = static_cast<unsigned int>(no_parents.size());
+ root->mChildren = new aiNode*[root->mNumChildren]();
+ for (unsigned int i = 0; i < root->mNumChildren; ++i) {
+ root->mChildren[i] = ConvertNode(in, no_parents[i], conv);
+ root->mChildren[i]->mParent = root;
+ }
+
+ BuildMaterials(conv);
+
+ if (conv.meshes->size()) {
+ out->mMeshes = new aiMesh*[out->mNumMeshes = static_cast<unsigned int>( conv.meshes->size() )];
+ std::copy(conv.meshes->begin(),conv.meshes->end(),out->mMeshes);
+ conv.meshes.dismiss();
+ }
+
+ if (conv.lights->size()) {
+ out->mLights = new aiLight*[out->mNumLights = static_cast<unsigned int>( conv.lights->size() )];
+ std::copy(conv.lights->begin(),conv.lights->end(),out->mLights);
+ conv.lights.dismiss();
+ }
+
+ if (conv.cameras->size()) {
+ out->mCameras = new aiCamera*[out->mNumCameras = static_cast<unsigned int>( conv.cameras->size() )];
+ std::copy(conv.cameras->begin(),conv.cameras->end(),out->mCameras);
+ conv.cameras.dismiss();
+ }
+
+ if (conv.materials->size()) {
+ out->mMaterials = new aiMaterial*[out->mNumMaterials = static_cast<unsigned int>( conv.materials->size() )];
+ std::copy(conv.materials->begin(),conv.materials->end(),out->mMaterials);
+ conv.materials.dismiss();
+ }
+
+ if (conv.textures->size()) {
+ out->mTextures = new aiTexture*[out->mNumTextures = static_cast<unsigned int>( conv.textures->size() )];
+ std::copy(conv.textures->begin(),conv.textures->end(),out->mTextures);
+ conv.textures.dismiss();
+ }
+
+ // acknowledge that the scene might come out incomplete
+ // by Assimps definition of `complete`: blender scenes
+ // can consist of thousands of cameras or lights with
+ // not a single mesh between them.
+ if (!out->mNumMeshes) {
+ out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ResolveImage(MaterialHelper* out, const Material* mat, const MTex* tex, const Image* img, ConversionData& conv_data)
+{
+ Q_UNUSED(mat);
+ Q_UNUSED(tex);
+ aiString name;
+
+ // check if the file contents are bundled with the BLEND file
+ if (img->packedfile) {
+ name.data[0] = '*';
+ name.length = 1+ ASSIMP_itoa10(name.data+1,MAXLEN-1,conv_data.textures->size());
+
+ conv_data.textures->push_back(new aiTexture());
+ aiTexture* tex = conv_data.textures->back();
+
+ // usually 'img->name' will be the original file name of the embedded textures,
+ // so we can extract the file extension from it.
+ const size_t nlen = strlen( img->name );
+ const char* s = img->name+nlen, *e = s;
+
+ while (s >= img->name && *s != '.')--s;
+
+ tex->achFormatHint[0] = s+1>e ? '\0' : s[1];
+ tex->achFormatHint[1] = s+2>e ? '\0' : s[2];
+ tex->achFormatHint[2] = s+3>e ? '\0' : s[3];
+ tex->achFormatHint[3] = '\0';
+
+ // tex->mHeight = 0;
+ tex->mWidth = img->packedfile->size;
+ uint8_t* ch = new uint8_t[tex->mWidth];
+
+ conv_data.db.reader->SetCurrentPos(static_cast<size_t>( img->packedfile->data->val));
+ conv_data.db.reader->CopyAndAdvance(ch,tex->mWidth);
+
+ tex->pcData = reinterpret_cast<aiTexel*>(ch);
+
+ LogInfo("Reading embedded texture, original file was "+std::string(img->name));
+ }
+ else {
+ name = aiString( img->name );
+ }
+ out->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(
+ conv_data.next_texture[aiTextureType_DIFFUSE]++)
+ );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::AddSentinelTexture(MaterialHelper* out, const Material* mat, const MTex* tex, ConversionData& conv_data)
+{
+ Q_UNUSED(mat);
+ aiString name;
+ name.length = sprintf(name.data, "Procedural,num=%i,type=%s",conv_data.sentinel_cnt++,
+ GetTextureTypeDisplayString(tex->tex->type)
+ );
+ out->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(
+ conv_data.next_texture[aiTextureType_DIFFUSE]++)
+ );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ResolveTexture(MaterialHelper* out, const Material* mat, const MTex* tex, ConversionData& conv_data)
+{
+ const Tex* rtex = tex->tex.get();
+ if (!rtex || !rtex->type) {
+ return;
+ }
+
+ // We can't support most of the texture types because the're mostly procedural.
+ // These are substituted by a dummy texture.
+ const char* dispnam = "";
+ switch( rtex->type )
+ {
+ // these are listed in blender's UI
+ case Tex::Type_CLOUDS :
+ case Tex::Type_WOOD :
+ case Tex::Type_MARBLE :
+ case Tex::Type_MAGIC :
+ case Tex::Type_BLEND :
+ case Tex::Type_STUCCI :
+ case Tex::Type_NOISE :
+ case Tex::Type_PLUGIN :
+ case Tex::Type_MUSGRAVE :
+ case Tex::Type_VORONOI :
+ case Tex::Type_DISTNOISE :
+ case Tex::Type_ENVMAP :
+
+ // these do no appear in the UI, why?
+ case Tex::Type_POINTDENSITY :
+ case Tex::Type_VOXELDATA :
+
+ LogWarn(std::string("Encountered a texture with an unsupported type: ")+dispnam);
+ AddSentinelTexture(out, mat, tex, conv_data);
+ break;
+
+ case Tex::Type_IMAGE :
+ if (!rtex->ima) {
+ LogError("A texture claims to be an Image, but no image reference is given");
+ break;
+ }
+ ResolveImage(out, mat, tex, rtex->ima.get(),conv_data);
+ break;
+
+ default:
+ ai_assert(false);
+ };
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::BuildMaterials(ConversionData& conv_data)
+{
+ conv_data.materials->reserve(conv_data.materials_raw.size());
+
+ // add a default material if necessary
+ unsigned int index = static_cast<unsigned int>( -1 );
+ for_each( aiMesh* mesh, conv_data.meshes.get() ) {
+ if (mesh->mMaterialIndex == static_cast<unsigned int>( -1 )) {
+
+ if (index == static_cast<unsigned int>( -1 )) {
+
+ // ok, we need to add a dedicated default material for some poor material-less meshes
+ boost::shared_ptr<Material> p(new Material());
+ strcpy( p->id.name+2, AI_DEFAULT_MATERIAL_NAME );
+
+ p->r = p->g = p->b = 0.6f;
+ p->specr = p->specg = p->specb = 0.6f;
+ p->ambir = p->ambig = p->ambib = 0.0f;
+ p->mirr = p->mirg = p->mirb = 0.0f;
+ p->emit = 0.f;
+ p->alpha = 0.f;
+
+ // XXX add more / or add default c'tor to Material
+
+ index = static_cast<unsigned int>( conv_data.materials_raw.size() );
+ conv_data.materials_raw.push_back(p);
+
+ LogInfo("Adding default material ...");
+ }
+ mesh->mMaterialIndex = index;
+ }
+ }
+
+ for_each(boost::shared_ptr<Material> mat, conv_data.materials_raw) {
+
+ // reset per material global counters
+ for (size_t i = 0; i < sizeof(conv_data.next_texture)/sizeof(conv_data.next_texture[0]);++i) {
+ conv_data.next_texture[i] = 0 ;
+ }
+
+ MaterialHelper* mout = new MaterialHelper();
+ conv_data.materials->push_back(mout);
+
+ // set material name
+ aiString name = aiString(mat->id.name+2); // skip over the name prefix 'MA'
+ mout->AddProperty(&name,AI_MATKEY_NAME);
+
+
+ // basic material colors
+ aiColor3D col(mat->r,mat->g,mat->b);
+ if (mat->r || mat->g || mat->b ) {
+
+ // Usually, zero diffuse color means no diffuse color at all in the equation - seemingly.
+ // So we ommit this member to express this intent.
+ mout->AddProperty(&col,1,AI_MATKEY_COLOR_DIFFUSE);
+ }
+
+ col = aiColor3D(mat->specr,mat->specg,mat->specb);
+ mout->AddProperty(&col,1,AI_MATKEY_COLOR_SPECULAR);
+
+ col = aiColor3D(mat->ambir,mat->ambig,mat->ambib);
+ mout->AddProperty(&col,1,AI_MATKEY_COLOR_AMBIENT);
+
+ col = aiColor3D(mat->mirr,mat->mirg,mat->mirb);
+ mout->AddProperty(&col,1,AI_MATKEY_COLOR_REFLECTIVE);
+
+ for (size_t i = 0; i < sizeof(mat->mtex) / sizeof(mat->mtex[0]); ++i) {
+ if (!mat->mtex[i]) {
+ continue;
+ }
+
+ ResolveTexture(mout,mat.get(),mat->mtex[i].get(),conv_data);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::CheckActualType(const ElemBase* dt, const char* check)
+{
+ ai_assert(dt);
+ if (strcmp(dt->dna_type,check)) {
+ ThrowException((format(),
+ "Expected object at ",std::hex,dt," to be of type `",check,
+ "`, but it claims to be a `",dt->dna_type,"`instead"
+ ));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::NotSupportedObjectType(const Object* obj, const char* type)
+{
+ LogWarn((format(), "Object `",obj->id.name,"` - type is unsupported: `",type, "`, skipping" ));
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ConvertMesh(const Scene&, const Object*, const Mesh* mesh,
+ ConversionData& conv_data, TempArray<std::vector,aiMesh>& temp
+ )
+{
+ typedef std::pair<const int,size_t> MyPair;
+ if (!mesh->totface || !mesh->totvert) {
+ return;
+ }
+
+ // some sanity checks
+ if (static_cast<size_t> ( mesh->totface ) > mesh->mface.size() ){
+ ThrowException("Number of faces is larger than the corresponding array");
+ }
+
+ if (static_cast<size_t> ( mesh->totvert ) > mesh->mvert.size()) {
+ ThrowException("Number of vertices is larger than the corresponding array");
+ }
+
+ // collect per-submesh numbers
+ std::map<int,size_t> per_mat;
+ for (int i = 0; i < mesh->totface; ++i) {
+
+ const MFace& mf = mesh->mface[i];
+ per_mat[ mf.mat_nr ]++;
+ }
+
+ // ... and allocate the corresponding meshes
+ const size_t old = temp->size();
+ temp->reserve(temp->size() + per_mat.size());
+
+ std::map<size_t,size_t> mat_num_to_mesh_idx;
+ for_each(MyPair& it, per_mat) {
+
+ mat_num_to_mesh_idx[it.first] = temp->size();
+ temp->push_back(new aiMesh());
+
+ aiMesh* out = temp->back();
+ out->mVertices = new aiVector3D[it.second*4];
+ out->mNormals = new aiVector3D[it.second*4];
+
+ //out->mNumFaces = 0
+ //out->mNumVertices = 0
+ out->mFaces = new aiFace[it.second]();
+
+ // all submeshes created from this mesh are named equally. this allows
+ // curious users to recover the original adjacency.
+ out->mName = aiString(mesh->id.name+2);
+ // skip over the name prefix 'ME'
+
+ // resolve the material reference and add this material to the set of
+ // output materials. The (temporary) material index is the index
+ // of the material entry within the list of resolved materials.
+ if (mesh->mat) {
+
+ if (static_cast<size_t> ( it.first ) >= mesh->mat.size() ) {
+ ThrowException("Material index is out of range");
+ }
+
+ boost::shared_ptr<Material> mat = mesh->mat[it.first];
+ const std::deque< boost::shared_ptr<Material> >::iterator has = std::find(
+ conv_data.materials_raw.begin(),
+ conv_data.materials_raw.end(),mat
+ );
+
+ if (has != conv_data.materials_raw.end()) {
+ out->mMaterialIndex = static_cast<unsigned int>( std::distance(conv_data.materials_raw.begin(),has));
+ }
+ else {
+ out->mMaterialIndex = static_cast<unsigned int>( conv_data.materials_raw.size() );
+ conv_data.materials_raw.push_back(mat);
+ }
+ }
+ else out->mMaterialIndex = static_cast<unsigned int>( -1 );
+ }
+
+ for (int i = 0; i < mesh->totface; ++i) {
+
+ const MFace& mf = mesh->mface[i];
+
+ aiMesh* const out = temp[ mat_num_to_mesh_idx[ mf.mat_nr ] ];
+ aiFace& f = out->mFaces[out->mNumFaces++];
+
+ f.mIndices = new unsigned int[ f.mNumIndices = mf.v4?4:3 ];
+ aiVector3D* vo = out->mVertices + out->mNumVertices;
+ aiVector3D* vn = out->mNormals + out->mNumVertices;
+
+ // XXX we can't fold this easily, because we are restricted
+ // to the member names from the BLEND file (v1,v2,v3,v4)
+ // which are assigned by the genblenddna.py script and
+ // cannot be changed without breaking the entire
+ // import process.
+
+ if (mf.v1 >= mesh->totvert) {
+ ThrowException("Vertex index v1 out of range");
+ }
+ const MVert* v = &mesh->mvert[mf.v1];
+ vo->x = v->co[0];
+ vo->y = v->co[1];
+ vo->z = v->co[2];
+ vn->x = v->no[0];
+ vn->y = v->no[1];
+ vn->z = v->no[2];
+ f.mIndices[0] = out->mNumVertices++;
+ ++vo;
+ ++vn;
+
+ // if (f.mNumIndices >= 2) {
+ if (mf.v2 >= mesh->totvert) {
+ ThrowException("Vertex index v2 out of range");
+ }
+ v = &mesh->mvert[mf.v2];
+ vo->x = v->co[0];
+ vo->y = v->co[1];
+ vo->z = v->co[2];
+ vn->x = v->no[0];
+ vn->y = v->no[1];
+ vn->z = v->no[2];
+ f.mIndices[1] = out->mNumVertices++;
+ ++vo;
+ ++vn;
+
+ if (mf.v3 >= mesh->totvert) {
+ ThrowException("Vertex index v3 out of range");
+ }
+ // if (f.mNumIndices >= 3) {
+ v = &mesh->mvert[mf.v3];
+ vo->x = v->co[0];
+ vo->y = v->co[1];
+ vo->z = v->co[2];
+ vn->x = v->no[0];
+ vn->y = v->no[1];
+ vn->z = v->no[2];
+ f.mIndices[2] = out->mNumVertices++;
+ ++vo;
+ ++vn;
+
+ if (mf.v4 >= mesh->totvert) {
+ ThrowException("Vertex index v4 out of range");
+ }
+ // if (f.mNumIndices >= 4) {
+ if (mf.v4) {
+ v = &mesh->mvert[mf.v4];
+ vo->x = v->co[0];
+ vo->y = v->co[1];
+ vo->z = v->co[2];
+ vn->x = v->no[0];
+ vn->y = v->no[1];
+ vn->z = v->no[2];
+ f.mIndices[3] = out->mNumVertices++;
+ ++vo;
+ ++vn;
+
+ out->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ }
+ else out->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+
+ // }
+ // }
+ // }
+ }
+
+ // collect texture coordinates, they're stored in a separate per-face buffer
+ if (mesh->mtface) {
+ if (mesh->totface > static_cast<int> ( mesh->mtface.size())) {
+ ThrowException("Number of UV faces is larger than the corresponding UV face array (#1)");
+ }
+ for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
+ ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
+
+ (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
+ (*it)->mNumFaces = (*it)->mNumVertices = 0;
+ }
+
+ for (int i = 0; i < mesh->totface; ++i) {
+ const MTFace* v = &mesh->mtface[i];
+
+ aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ];
+ const aiFace& f = out->mFaces[out->mNumFaces++];
+
+ aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
+ for (unsigned int i = 0; i < f.mNumIndices; ++i,++vo,++out->mNumVertices) {
+ vo->x = v->uv[i][0];
+ vo->y = v->uv[i][1];
+ }
+ }
+ }
+
+ // collect texture coordinates, old-style (marked as deprecated in current blender sources)
+ if (mesh->tface) {
+ if (mesh->totface > static_cast<int> ( mesh->mtface.size())) {
+ ThrowException("Number of faces is larger than the corresponding UV face array (#2)");
+ }
+ for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
+ ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
+
+ (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
+ (*it)->mNumFaces = (*it)->mNumVertices = 0;
+ }
+
+ for (int i = 0; i < mesh->totface; ++i) {
+ const TFace* v = &mesh->tface[i];
+
+ aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ];
+ const aiFace& f = out->mFaces[out->mNumFaces++];
+
+ aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
+ for (unsigned int i = 0; i < f.mNumIndices; ++i,++vo,++out->mNumVertices) {
+ vo->x = v->uv[i][0];
+ vo->y = v->uv[i][1];
+ }
+ }
+ }
+
+ // collect vertex colors, stored separately as well
+ if (mesh->mcol) {
+ if (mesh->totface > static_cast<int> ( (mesh->mcol.size()/4)) ) {
+ ThrowException("Number of faces is larger than the corresponding color face array");
+ }
+ for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
+ ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
+
+ (*it)->mColors[0] = new aiColor4D[(*it)->mNumVertices];
+ (*it)->mNumFaces = (*it)->mNumVertices = 0;
+ }
+
+ for (int i = 0; i < mesh->totface; ++i) {
+
+ aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ];
+ const aiFace& f = out->mFaces[out->mNumFaces++];
+
+ aiColor4D* vo = &out->mColors[0][out->mNumVertices];
+ for (unsigned int n = 0; n < f.mNumIndices; ++n, ++vo,++out->mNumVertices) {
+ const MCol* col = &mesh->mcol[(i<<2)+n];
+
+ vo->r = col->r;
+ vo->g = col->g;
+ vo->b = col->b;
+ vo->a = col->a;
+ }
+ for (unsigned int n = f.mNumIndices; n < 4; ++n) {}
+ }
+ }
+
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiCamera* BlenderImporter::ConvertCamera(const Scene&, const Object*, const Camera* , ConversionData&)
+{
+ ScopeGuard<aiCamera> out(new aiCamera());
+
+ return NULL ; //out.dismiss();
+}
+
+// ------------------------------------------------------------------------------------------------
+aiLight* BlenderImporter::ConvertLight(const Scene&, const Object*, const Lamp*, ConversionData&)
+{
+ ScopeGuard<aiLight> out(new aiLight());
+
+ return NULL ; //out.dismiss();
+}
+
+// ------------------------------------------------------------------------------------------------
+aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, ConversionData& conv_data)
+{
+ std::deque<const Object*> children;
+ for (std::set<const Object*>::iterator it = conv_data.objects.begin(); it != conv_data.objects.end() ;) {
+ const Object* object = *it;
+ if (object->parent.get() == obj) {
+ children.push_back(object);
+
+ conv_data.objects.erase(it++);
+ continue;
+ }
+ ++it;
+ }
+
+ ScopeGuard<aiNode> node(new aiNode(obj->id.name+2)); // skip over the name prefix 'OB'
+ if (obj->data) {
+ switch (obj->type)
+ {
+ case Object :: Type_EMPTY:
+ break; // do nothing
+
+
+ // supported object types
+ case Object :: Type_MESH: {
+ const size_t old = conv_data.meshes->size();
+
+ CheckActualType(obj->data.get(),"Mesh");
+ ConvertMesh(in,obj,static_cast<const Mesh*>(obj->data.get()),conv_data,conv_data.meshes);
+
+ if (conv_data.meshes->size() > old) {
+ node->mMeshes = new unsigned int[node->mNumMeshes = static_cast<unsigned int>(conv_data.meshes->size()-old)];
+ for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
+ node->mMeshes[i] = i + old;
+ }
+ }}
+ break;
+ case Object :: Type_LAMP: {
+ CheckActualType(obj->data.get(),"Lamp");
+ aiLight* mesh = ConvertLight(in,obj,static_cast<const Lamp*>(
+ obj->data.get()),conv_data);
+
+ if (mesh) {
+ conv_data.lights->push_back(mesh);
+ }}
+ break;
+ case Object :: Type_CAMERA: {
+ CheckActualType(obj->data.get(),"Camera");
+ aiCamera* mesh = ConvertCamera(in,obj,static_cast<const Camera*>(
+ obj->data.get()),conv_data);
+
+ if (mesh) {
+ conv_data.cameras->push_back(mesh);
+ }}
+ break;
+
+
+ // unsupported object types / log, but do not break
+ case Object :: Type_CURVE:
+ NotSupportedObjectType(obj,"Curve");
+ break;
+ case Object :: Type_SURF:
+ NotSupportedObjectType(obj,"Surface");
+ break;
+ case Object :: Type_FONT:
+ NotSupportedObjectType(obj,"Font");
+ break;
+ case Object :: Type_MBALL:
+ NotSupportedObjectType(obj,"MetaBall");
+ break;
+ case Object :: Type_WAVE:
+ NotSupportedObjectType(obj,"Wave");
+ break;
+ case Object :: Type_LATTICE:
+ NotSupportedObjectType(obj,"Lattice");
+ break;
+
+ // invalid or unknown type
+ default:
+ break;
+ }
+ }
+
+ for (unsigned int x = 0; x < 4; ++x) {
+ for (unsigned int y = 0; y < 4; ++y) {
+ node->mTransformation[y][x] = obj->parentinv[x][y];
+ }
+ }
+
+ aiMatrix4x4 m;
+ for (unsigned int x = 0; x < 4; ++x) {
+ for (unsigned int y = 0; y < 4; ++y) {
+ m[y][x] = obj->obmat[x][y];
+ }
+ }
+
+ node->mTransformation = m*node->mTransformation;
+
+ if (children.size()) {
+ node->mNumChildren = static_cast<unsigned int>(children.size());
+ aiNode** nd = node->mChildren = new aiNode*[node->mNumChildren]();
+ for_each (const Object* nobj,children) {
+ *nd = ConvertNode(in,nobj,conv_data);
+ (*nd++)->mParent = node;
+ }
+ }
+
+ // apply modifiers
+ modifier_cache->ApplyModifiers(*node,conv_data,in,*obj);
+
+ return node.dismiss();
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void BlenderImporter::ThrowException(const std::string& msg)
+{
+ throw DeadlyImportError("BLEND: "+msg);
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void BlenderImporter::LogWarn(const Formatter::format& message) {
+ DefaultLogger::get()->warn(std::string("BLEND: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void BlenderImporter::LogError(const Formatter::format& message) {
+ DefaultLogger::get()->error(std::string("BLEND: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void BlenderImporter::LogInfo(const Formatter::format& message) {
+ DefaultLogger::get()->info(std::string("BLEND: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void BlenderImporter::LogDebug(const Formatter::format& message) {
+ DefaultLogger::get()->debug(std::string("BLEND: ")+=message);
+}
+
+#endif
diff --git a/src/3rdparty/assimp/code/BlenderLoader.h b/src/3rdparty/assimp/code/BlenderLoader.h
new file mode 100644
index 000000000..ee95de836
--- /dev/null
+++ b/src/3rdparty/assimp/code/BlenderLoader.h
@@ -0,0 +1,261 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file BlenderLoader.h
+ * @brief Declaration of the Blender 3D (*.blend) importer class.
+ */
+#ifndef INCLUDED_AI_BLEND_LOADER_H
+#define INCLUDED_AI_BLEND_LOADER_H
+
+#include "BaseImporter.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;
+ }
+
+ // BlenderDNA.h
+ namespace Blender {
+ class FileDatabase;
+ struct ElemBase;
+ }
+
+ // BlenderScene.h
+ namespace Blender {
+ struct Scene;
+ struct Object;
+ struct Mesh;
+ struct Camera;
+ struct Lamp;
+ struct MTex;
+ struct Image;
+ struct Material;
+ }
+
+ // BlenderIntermediate.h
+ namespace Blender {
+ struct ConversionData;
+ template <template <typename,typename> class TCLASS, typename T> struct TempArray;
+ }
+
+ // BlenderModifier.h
+ namespace Blender {
+ class BlenderModifierShowcase;
+ class BlenderModifier;
+ }
+
+enum aiLoaderFlags
+{
+ aiLoaderFlags_SupportAsciiFlavour = 0x1,
+ aiLoaderFlags_SupportBinaryFlavour = 0x2,
+ aiLoaderFlags_SupportCompressedFlavour = 0x4,
+
+ aiLoaderFlags_LimitedSupport = 0x8,
+
+ aiLoaderFlags_Experimental = 0x10,
+ aiLoaderFlags_Testing = 0x20,
+ aiLoaderFlags_Production = 0x40,
+};
+
+struct aiLoaderDesc
+{
+ const char* mName;
+ const char* mAuthor;
+ const char* mMaintainer;
+ const char* mComments;
+ unsigned int mFlags;
+
+ unsigned int mMinMajor;
+ unsigned int mMinMinor;
+ unsigned int mMaxMajor;
+ unsigned int mMaxMinor;
+};
+
+
+// -------------------------------------------------------------------------------------------
+/** Load blenders official binary format. The actual file structure (the `DNA` how they
+ * call it is outsourced to BlenderDNA.cpp/BlenderDNA.h. This class only performs the
+ * conversion from intermediate format to aiScene. */
+// -------------------------------------------------------------------------------------------
+class BlenderImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+
+ /** Constructor to be privately used by Importer */
+ BlenderImporter();
+
+ /** Destructor, private as well */
+ ~BlenderImporter();
+
+public:
+
+ // --------------------
+ bool CanRead( const std::string& pFile,
+ IOSystem* pIOHandler,
+ bool checkSig
+ ) const;
+
+protected:
+
+ // --------------------
+ const aiLoaderDesc& GetInfo () const;
+
+ // --------------------
+ void GetExtensionList(std::set<std::string>& app);
+
+ // --------------------
+ void SetupProperties(const Importer* pImp);
+
+ // --------------------
+ void InternReadFile( const std::string& pFile,
+ aiScene* pScene,
+ IOSystem* pIOHandler
+ );
+
+ // --------------------
+ void ParseBlendFile(Blender::FileDatabase& out,
+ boost::shared_ptr<IOStream> stream
+ );
+
+ // --------------------
+ void ExtractScene(Blender::Scene& out,
+ const Blender::FileDatabase& file
+ );
+
+ // --------------------
+ void ConvertBlendFile(aiScene* out,
+ const Blender::Scene& in,
+ const Blender::FileDatabase& file
+ );
+
+private:
+
+ // --------------------
+ aiNode* ConvertNode(const Blender::Scene& in,
+ const Blender::Object* obj,
+ Blender::ConversionData& conv_info
+ );
+
+ // --------------------
+ void ConvertMesh(const Blender::Scene& in,
+ const Blender::Object* obj,
+ const Blender::Mesh* mesh,
+ Blender::ConversionData& conv_data,
+ Blender::TempArray<std::vector,aiMesh>& temp
+ );
+
+ // --------------------
+ aiLight* ConvertLight(const Blender::Scene& in,
+ const Blender::Object* obj,
+ const Blender::Lamp* mesh,
+ Blender::ConversionData& conv_data
+ );
+
+ // --------------------
+ aiCamera* ConvertCamera(const Blender::Scene& in,
+ const Blender::Object* obj,
+ const Blender::Camera* mesh,
+ Blender::ConversionData& conv_data
+ );
+
+ // --------------------
+ void BuildMaterials(
+ Blender::ConversionData& conv_data
+ ) ;
+
+ // --------------------
+ void ResolveTexture(
+ MaterialHelper* out,
+ const Blender::Material* mat,
+ const Blender::MTex* tex,
+ Blender::ConversionData& conv_data
+ );
+
+ // --------------------
+ void ResolveImage(
+ MaterialHelper* out,
+ const Blender::Material* mat,
+ const Blender::MTex* tex,
+ const Blender::Image* img,
+ Blender::ConversionData& conv_data
+ );
+
+ void AddSentinelTexture(
+ MaterialHelper* out,
+ const Blender::Material* mat,
+ const Blender::MTex* tex,
+ Blender::ConversionData& conv_data
+ );
+
+private: // static stuff, mostly logging and error reporting.
+
+ // --------------------
+ static void CheckActualType(const Blender::ElemBase* dt,
+ const char* check
+ );
+
+ // --------------------
+ static void NotSupportedObjectType(const Blender::Object* obj,
+ const char* type
+ );
+
+ // -------------------------------------------------------------------
+ /** Prepend 'BLEND: ' and throw msg.*/
+ static void ThrowException(const std::string& msg);
+
+ // -------------------------------------------------------------------
+ /** @defgroup blog Prepend 'BLEND: ' and write @c message to log.*/
+ static void LogWarn (const Formatter::format& message); //! @ingroup blog
+ static void LogError (const Formatter::format& message); //! @ingroup blog
+ static void LogInfo (const Formatter::format& message); //! @ingroup blog
+ static void LogDebug (const Formatter::format& message); //! @ingroup blog
+
+private:
+
+ Blender::BlenderModifierShowcase* modifier_cache;
+
+}; // !class BlenderImporter
+
+} // end of namespace Assimp
+#endif // AI_UNREALIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/BlenderModifier.cpp b/src/3rdparty/assimp/code/BlenderModifier.cpp
new file mode 100644
index 000000000..2b8e680d5
--- /dev/null
+++ b/src/3rdparty/assimp/code/BlenderModifier.cpp
@@ -0,0 +1,313 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file BlenderModifier.cpp
+ * @brief Implementation of some blender modifiers (i.e subdivision, mirror).
+ */
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+#include "BlenderModifier.h"
+#include "SceneCombiner.h"
+#include "Subdivision.h"
+
+#include <functional>
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+
+template <typename T> BlenderModifier* god() {
+ return new T();
+}
+
+// add all available modifiers here
+typedef BlenderModifier* (*fpCreateModifier)();
+static const fpCreateModifier creators[] = {
+ &god<BlenderModifier_Mirror>,
+ &god<BlenderModifier_Subdivision>,
+
+ NULL // sentinel
+};
+
+// ------------------------------------------------------------------------------------------------
+// just testing out some new macros to simplify logging
+#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)
+
+
+// ------------------------------------------------------------------------------------------------
+struct SharedModifierData : ElemBase
+{
+ ModifierData modifier;
+};
+
+// ------------------------------------------------------------------------------------------------
+void BlenderModifierShowcase::ApplyModifiers(aiNode& out, ConversionData& conv_data, const Scene& in, const Object& orig_object )
+{
+ size_t cnt = 0u, ful = 0u;
+
+ // NOTE: this cast is potentially unsafe by design, so we need to perform type checks before
+ // we're allowed to dereference the pointers without risking to crash. We might still be
+ // invoking UB btw - we're assuming that the ModifierData member of the respective modifier
+ // structures is at offset sizeof(vftable) with no padding.
+ const SharedModifierData* cur = boost::static_pointer_cast<const SharedModifierData> ( orig_object.modifiers.first.get() );
+ for (; cur; cur = boost::static_pointer_cast<const SharedModifierData> ( cur->modifier.next.get() ), ++ful) {
+ ai_assert(cur->dna_type);
+
+ const Structure* s = conv_data.db.dna.Get( cur->dna_type );
+ if (!s) {
+ ASSIMP_LOG_WARN_F("BlendModifier: could not resolve DNA name: ",cur->dna_type);
+ continue;
+ }
+
+ // this is a common trait of all XXXMirrorData structures in BlenderDNA
+ const Field* f = s->Get("modifier");
+ if (!f || f->offset != 0) {
+ ASSIMP_LOG_WARN("BlendModifier: expected a `modifier` member at offset 0");
+ continue;
+ }
+
+ s = conv_data.db.dna.Get( f->type );
+ if (!s || s->name != "ModifierData") {
+ ASSIMP_LOG_WARN("BlendModifier: expected a ModifierData structure as first member");
+ continue;
+ }
+
+ // now, we can be sure that we should be fine to dereference *cur* as
+ // ModifierData (with the above note).
+ const ModifierData& dat = cur->modifier;
+
+ const fpCreateModifier* curgod = creators;
+ std::vector< BlenderModifier* >::iterator curmod = cached_modifiers->begin(), endmod = cached_modifiers->end();
+
+ for (;*curgod;++curgod,++curmod) { // allocate modifiers on the fly
+ if (curmod == endmod) {
+ cached_modifiers->push_back((*curgod)());
+
+ endmod = cached_modifiers->end();
+ curmod = endmod-1;
+ }
+
+ BlenderModifier* const modifier = *curmod;
+ if (modifier->IsActive(dat)) {
+ modifier->DoIt(out,conv_data,*boost::static_pointer_cast<const ElemBase>(cur),in,orig_object);
+ cnt++;
+
+ curgod = NULL;
+ break;
+ }
+ }
+ if (curgod) {
+ ASSIMP_LOG_WARN_F("Couldn't find a handler for modifier: ",dat.name);
+ }
+ }
+
+ // Even though we managed to resolve some or all of the modifiers on this
+ // object, we still can't say whether our modifier implementations were
+ // able to fully do their job.
+ if (ful) {
+ ASSIMP_LOG_DEBUG_F("BlendModifier: found handlers for ",cnt," of ",ful," modifiers on `",orig_object.id.name,
+ "`, check log messages above for errors");
+ }
+}
+
+
+
+// ------------------------------------------------------------------------------------------------
+bool BlenderModifier_Mirror :: IsActive (const ModifierData& modin)
+{
+ return modin.type == ModifierData::eModifierType_Mirror;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderModifier_Mirror :: DoIt(aiNode& out, ConversionData& conv_data, const ElemBase& orig_modifier,
+ const Scene&,
+ const Object& orig_object )
+{
+ // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers()
+ const MirrorModifierData& mir = static_cast<const MirrorModifierData&>(orig_modifier);
+ ai_assert(mir.modifier.type == ModifierData::eModifierType_Mirror);
+
+ // XXX not entirely correct, mirroring on two axes results in 4 distinct objects in blender ...
+
+ // take all input meshes and clone them
+ for (unsigned int i = 0; i < out.mNumMeshes; ++i) {
+ aiMesh* mesh;
+ SceneCombiner::Copy(&mesh,conv_data.meshes[out.mMeshes[i]]);
+
+ const float xs = mir.flag & MirrorModifierData::Flags_AXIS_X ? -1.f : 1.f;
+ const float ys = mir.flag & MirrorModifierData::Flags_AXIS_Y ? -1.f : 1.f;
+ const float zs = mir.flag & MirrorModifierData::Flags_AXIS_Z ? -1.f : 1.f;
+
+ if (mir.mirror_ob) {
+ const aiVector3D center( mir.mirror_ob->obmat[3][0],mir.mirror_ob->obmat[3][1],mir.mirror_ob->obmat[3][2] );
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ aiVector3D& v = mesh->mVertices[i];
+
+ v.x = center.x + xs*(center.x - v.x);
+ v.y = center.y + ys*(center.y - v.y);
+ v.z = center.z + zs*(center.z - v.z);
+ }
+ }
+ else {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ aiVector3D& v = mesh->mVertices[i];
+ v.x *= xs;v.y *= ys;v.z *= zs;
+ }
+ }
+
+ if (mesh->mNormals) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ aiVector3D& v = mesh->mNormals[i];
+ v.x *= xs;v.y *= ys;v.z *= zs;
+ }
+ }
+
+ if (mesh->mTangents) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ aiVector3D& v = mesh->mTangents[i];
+ v.x *= xs;v.y *= ys;v.z *= zs;
+ }
+ }
+
+ if (mesh->mBitangents) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ aiVector3D& v = mesh->mBitangents[i];
+ v.x *= xs;v.y *= ys;v.z *= zs;
+ }
+ }
+
+ const float us = mir.flag & MirrorModifierData::Flags_MIRROR_U ? -1.f : 1.f;
+ const float vs = mir.flag & MirrorModifierData::Flags_MIRROR_V ? -1.f : 1.f;
+
+ for (unsigned int n = 0; mesh->HasTextureCoords(n); ++n) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ aiVector3D& v = mesh->mTextureCoords[n][i];
+ v.x *= us;v.y *= vs;
+ }
+ }
+
+ conv_data.meshes->push_back(mesh);
+ }
+ unsigned int* nind = new unsigned int[out.mNumMeshes*2];
+
+ std::copy(out.mMeshes,out.mMeshes+out.mNumMeshes,nind);
+ std::transform(out.mMeshes,out.mMeshes+out.mNumMeshes,nind+out.mNumMeshes,
+ std::bind1st(std::plus< unsigned int >(),out.mNumMeshes));
+
+ delete[] out.mMeshes;
+ out.mMeshes = nind;
+ out.mNumMeshes *= 2;
+
+ ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Mirror` modifier to `",
+ orig_object.id.name,"`");
+}
+
+
+
+
+// ------------------------------------------------------------------------------------------------
+bool BlenderModifier_Subdivision :: IsActive (const ModifierData& modin)
+{
+ return modin.type == ModifierData::eModifierType_Subsurf;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderModifier_Subdivision :: DoIt(aiNode& out, ConversionData& conv_data, const ElemBase& orig_modifier,
+ const Scene&,
+ const Object& orig_object )
+{
+ // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers()
+ const SubsurfModifierData& mir = static_cast<const SubsurfModifierData&>(orig_modifier);
+ ai_assert(mir.modifier.type == ModifierData::eModifierType_Subsurf);
+
+ Subdivider::Algorithm algo;
+ switch (mir.subdivType)
+ {
+ case SubsurfModifierData::TYPE_CatmullClarke:
+ algo = Subdivider::CATMULL_CLARKE;
+ break;
+
+ case SubsurfModifierData::TYPE_Simple:
+ ASSIMP_LOG_WARN("BlendModifier: The `SIMPLE` subdivision algorithm is not currently implemented, using Catmull-Clarke");
+ algo = Subdivider::CATMULL_CLARKE;
+ break;
+
+ default:
+ ASSIMP_LOG_WARN_F("BlendModifier: Unrecognized subdivision algorithm: ",mir.subdivType);
+ return;
+ };
+
+ boost::scoped_ptr<Subdivider> subd(Subdivider::Create(algo));
+ ai_assert(subd);
+
+ aiMesh** const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes];
+ boost::scoped_array<aiMesh*> tempmeshes(new aiMesh*[out.mNumMeshes]());
+
+ subd->Subdivide(meshes,out.mNumMeshes,tempmeshes.get(),std::max( mir.renderLevels, mir.levels ),true);
+ std::copy(tempmeshes.get(),tempmeshes.get()+out.mNumMeshes,meshes);
+
+ ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Subdivision` modifier to `",
+ orig_object.id.name,"`");
+}
+
+#endif
diff --git a/src/3rdparty/assimp/code/BlenderModifier.h b/src/3rdparty/assimp/code/BlenderModifier.h
new file mode 100644
index 000000000..7cd67302f
--- /dev/null
+++ b/src/3rdparty/assimp/code/BlenderModifier.h
@@ -0,0 +1,155 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file BlenderModifier.h
+ * @brief Declare dedicated helper classes to simulate some blender modifiers (i.e. mirror)
+ */
+#ifndef INCLUDED_AI_BLEND_MODIFIER_H
+#define INCLUDED_AI_BLEND_MODIFIER_H
+
+#include "BlenderIntermediate.h"
+#include "TinyFormatter.h"
+namespace Assimp {
+ namespace Blender {
+
+// -------------------------------------------------------------------------------------------
+/** Dummy base class for all blender modifiers. Modifiers are reused between imports, so
+ * they should be stateless and not try to cache model data. */
+// -------------------------------------------------------------------------------------------
+class BlenderModifier
+{
+public:
+
+ virtual ~BlenderModifier() {
+ }
+
+public:
+
+ // --------------------
+ /** Check if *this* modifier is active, given a ModifierData& block.*/
+ virtual bool IsActive( const ModifierData&) {
+ return false;
+ }
+
+ // --------------------
+ /** Apply the modifier to a given output node. The original data used
+ * to construct the node is given as well. Not called unless IsActive()
+ * was called and gave positive response. */
+ virtual void DoIt(aiNode&,
+ ConversionData&,
+ const ElemBase& orig_modifier,
+ const Scene&,
+ const Object&
+ ) {
+ DefaultLogger::get()->warn((Formatter::format("This modifier is not supported, skipping: "),orig_modifier.dna_type));
+ return;
+ }
+};
+
+
+// -------------------------------------------------------------------------------------------
+/** Manage all known modifiers and instance and apply them if necessary */
+// -------------------------------------------------------------------------------------------
+class BlenderModifierShowcase
+{
+public:
+
+ // --------------------
+ /** Apply all requested modifiers provided we support them. */
+ void ApplyModifiers(aiNode& out,
+ ConversionData& conv_data,
+ const Scene& in,
+ const Object& orig_object
+ );
+
+private:
+
+ TempArray< std::vector,BlenderModifier > cached_modifiers;
+};
+
+
+
+
+
+// MODIFIERS
+
+
+
+// -------------------------------------------------------------------------------------------
+/** Mirror modifier. Status: implemented. */
+// -------------------------------------------------------------------------------------------
+class BlenderModifier_Mirror : public BlenderModifier
+{
+public:
+
+ // --------------------
+ virtual bool IsActive( const ModifierData& modin);
+
+ // --------------------
+ virtual void DoIt(aiNode& out,
+ ConversionData& conv_data,
+ const ElemBase& orig_modifier,
+ const Scene& in,
+ const Object& orig_object
+ ) ;
+};
+
+// -------------------------------------------------------------------------------------------
+/** Subdivision modifier. Status: dummy. */
+// -------------------------------------------------------------------------------------------
+class BlenderModifier_Subdivision : public BlenderModifier
+{
+public:
+
+ // --------------------
+ virtual bool IsActive( const ModifierData& modin);
+
+ // --------------------
+ virtual void DoIt(aiNode& out,
+ ConversionData& conv_data,
+ const ElemBase& orig_modifier,
+ const Scene& in,
+ const Object& orig_object
+ ) ;
+};
+
+
+}}
+#endif // !INCLUDED_AI_BLEND_MODIFIER_H
diff --git a/src/3rdparty/assimp/code/BlenderScene.cpp b/src/3rdparty/assimp/code/BlenderScene.cpp
new file mode 100644
index 000000000..f8d13e177
--- /dev/null
+++ b/src/3rdparty/assimp/code/BlenderScene.cpp
@@ -0,0 +1,596 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file BlenderScene.cpp
+ * @brief MACHINE GENERATED BY ./scripts/BlenderImporter/genblenddna.py
+ */
+#include "AssimpPCH.h"
+#ifndef AI_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+#include "BlenderSceneGen.h"
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Object> (
+ Object& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadField<ErrorPolicy_Fail>((int&)dest.type,"type",db);
+ ReadFieldArray2<ErrorPolicy_Warn>(dest.obmat,"obmat",db);
+ ReadFieldArray2<ErrorPolicy_Warn>(dest.parentinv,"parentinv",db);
+ ReadFieldArray<ErrorPolicy_Warn>(dest.parsubstr,"parsubstr",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.parent,"*parent",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.track,"*track",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy,"*proxy",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy_from,"*proxy_from",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy_group,"*proxy_group",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.dup_group,"*dup_group",db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.data,"*data",db);
+ ReadField<ErrorPolicy_Igno>(dest.modifiers,"modifiers",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Group> (
+ Group& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadField<ErrorPolicy_Igno>(dest.layer,"layer",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.gobject,"*gobject",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MTex> (
+ MTex& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Igno>((int&)dest.blendtype,"blendtype",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.object,"*object",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.tex,"*tex",db);
+ ReadFieldArray<ErrorPolicy_Igno>(dest.uvname,"uvname",db);
+ ReadField<ErrorPolicy_Igno>((int&)dest.projx,"projx",db);
+ ReadField<ErrorPolicy_Igno>((int&)dest.projy,"projy",db);
+ ReadField<ErrorPolicy_Igno>((int&)dest.projz,"projz",db);
+ ReadField<ErrorPolicy_Igno>(dest.mapping,"mapping",db);
+ ReadFieldArray<ErrorPolicy_Igno>(dest.ofs,"ofs",db);
+ ReadFieldArray<ErrorPolicy_Igno>(dest.size,"size",db);
+ ReadField<ErrorPolicy_Igno>(dest.rot,"rot",db);
+ ReadField<ErrorPolicy_Igno>(dest.texflag,"texflag",db);
+ ReadField<ErrorPolicy_Igno>(dest.colormodel,"colormodel",db);
+ ReadField<ErrorPolicy_Igno>(dest.pmapto,"pmapto",db);
+ ReadField<ErrorPolicy_Igno>(dest.pmaptoneg,"pmaptoneg",db);
+ ReadField<ErrorPolicy_Warn>(dest.r,"r",db);
+ ReadField<ErrorPolicy_Warn>(dest.g,"g",db);
+ ReadField<ErrorPolicy_Warn>(dest.b,"b",db);
+ ReadField<ErrorPolicy_Warn>(dest.k,"k",db);
+ ReadField<ErrorPolicy_Igno>(dest.colspecfac,"colspecfac",db);
+ ReadField<ErrorPolicy_Igno>(dest.mirrfac,"mirrfac",db);
+ ReadField<ErrorPolicy_Igno>(dest.alphafac,"alphafac",db);
+ ReadField<ErrorPolicy_Igno>(dest.difffac,"difffac",db);
+ ReadField<ErrorPolicy_Igno>(dest.specfac,"specfac",db);
+ ReadField<ErrorPolicy_Igno>(dest.emitfac,"emitfac",db);
+ ReadField<ErrorPolicy_Igno>(dest.hardfac,"hardfac",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<TFace> (
+ TFace& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldArray2<ErrorPolicy_Fail>(dest.uv,"uv",db);
+ ReadFieldArray<ErrorPolicy_Fail>(dest.col,"col",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+ ReadField<ErrorPolicy_Igno>(dest.mode,"mode",db);
+ ReadField<ErrorPolicy_Igno>(dest.tile,"tile",db);
+ ReadField<ErrorPolicy_Igno>(dest.unwrap,"unwrap",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<SubsurfModifierData> (
+ SubsurfModifierData& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.modifier,"modifier",db);
+ ReadField<ErrorPolicy_Igno>(dest.subdivType,"subdivType",db);
+ ReadField<ErrorPolicy_Igno>(dest.levels,"levels",db);
+ ReadField<ErrorPolicy_Igno>(dest.renderLevels,"renderLevels",db);
+ ReadField<ErrorPolicy_Igno>(dest.flags,"flags",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MFace> (
+ MFace& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.v1,"v1",db);
+ ReadField<ErrorPolicy_Fail>(dest.v2,"v2",db);
+ ReadField<ErrorPolicy_Fail>(dest.v3,"v3",db);
+ ReadField<ErrorPolicy_Fail>(dest.v4,"v4",db);
+ ReadField<ErrorPolicy_Fail>(dest.mat_nr,"mat_nr",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Lamp> (
+ Lamp& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadField<ErrorPolicy_Fail>((int&)dest.type,"type",db);
+ ReadField<ErrorPolicy_Igno>(dest.flags,"flags",db);
+ ReadField<ErrorPolicy_Igno>(dest.colormodel,"colormodel",db);
+ ReadField<ErrorPolicy_Igno>(dest.totex,"totex",db);
+ ReadField<ErrorPolicy_Warn>(dest.r,"r",db);
+ ReadField<ErrorPolicy_Warn>(dest.g,"g",db);
+ ReadField<ErrorPolicy_Warn>(dest.b,"b",db);
+ ReadField<ErrorPolicy_Warn>(dest.k,"k",db);
+ ReadField<ErrorPolicy_Igno>(dest.energy,"energy",db);
+ ReadField<ErrorPolicy_Igno>(dest.dist,"dist",db);
+ ReadField<ErrorPolicy_Igno>(dest.spotsize,"spotsize",db);
+ ReadField<ErrorPolicy_Igno>(dest.spotblend,"spotblend",db);
+ ReadField<ErrorPolicy_Igno>(dest.att1,"att1",db);
+ ReadField<ErrorPolicy_Igno>(dest.att2,"att2",db);
+ ReadField<ErrorPolicy_Igno>((int&)dest.falloff_type,"falloff_type",db);
+ ReadField<ErrorPolicy_Igno>(dest.sun_brightness,"sun_brightness",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MDeformWeight> (
+ MDeformWeight& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.def_nr,"def_nr",db);
+ ReadField<ErrorPolicy_Fail>(dest.weight,"weight",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<PackedFile> (
+ PackedFile& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Warn>(dest.size,"size",db);
+ ReadField<ErrorPolicy_Warn>(dest.seek,"seek",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.data,"*data",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Base> (
+ Base& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.prev,"*prev",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.next,"*next",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.object,"*object",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MTFace> (
+ MTFace& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldArray2<ErrorPolicy_Fail>(dest.uv,"uv",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+ ReadField<ErrorPolicy_Igno>(dest.mode,"mode",db);
+ ReadField<ErrorPolicy_Igno>(dest.tile,"tile",db);
+ ReadField<ErrorPolicy_Igno>(dest.unwrap,"unwrap",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Material> (
+ Material& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadField<ErrorPolicy_Warn>(dest.r,"r",db);
+ ReadField<ErrorPolicy_Warn>(dest.g,"g",db);
+ ReadField<ErrorPolicy_Warn>(dest.b,"b",db);
+ ReadField<ErrorPolicy_Warn>(dest.specr,"specr",db);
+ ReadField<ErrorPolicy_Warn>(dest.specg,"specg",db);
+ ReadField<ErrorPolicy_Warn>(dest.specb,"specb",db);
+ ReadField<ErrorPolicy_Warn>(dest.ambir,"ambir",db);
+ ReadField<ErrorPolicy_Warn>(dest.ambig,"ambig",db);
+ ReadField<ErrorPolicy_Warn>(dest.ambib,"ambib",db);
+ ReadField<ErrorPolicy_Igno>(dest.mirr,"mirr",db);
+ ReadField<ErrorPolicy_Igno>(dest.mirg,"mirg",db);
+ ReadField<ErrorPolicy_Igno>(dest.mirb,"mirb",db);
+ ReadField<ErrorPolicy_Warn>(dest.emit,"emit",db);
+ ReadField<ErrorPolicy_Warn>(dest.alpha,"alpha",db);
+ ReadField<ErrorPolicy_Igno>(dest.ref,"ref",db);
+ ReadField<ErrorPolicy_Igno>(dest.translucency,"translucency",db);
+ ReadField<ErrorPolicy_Igno>(dest.roughness,"roughness",db);
+ ReadField<ErrorPolicy_Igno>(dest.darkness,"darkness",db);
+ ReadField<ErrorPolicy_Igno>(dest.refrac,"refrac",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.group,"*group",db);
+ ReadField<ErrorPolicy_Warn>(dest.diff_shader,"diff_shader",db);
+ ReadField<ErrorPolicy_Warn>(dest.spec_shader,"spec_shader",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mtex,"*mtex",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Mesh> (
+ Mesh& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadField<ErrorPolicy_Fail>(dest.totface,"totface",db);
+ ReadField<ErrorPolicy_Fail>(dest.totedge,"totedge",db);
+ ReadField<ErrorPolicy_Fail>(dest.totvert,"totvert",db);
+ ReadField<ErrorPolicy_Igno>(dest.subdiv,"subdiv",db);
+ ReadField<ErrorPolicy_Igno>(dest.subdivr,"subdivr",db);
+ ReadField<ErrorPolicy_Igno>(dest.subsurftype,"subsurftype",db);
+ ReadField<ErrorPolicy_Igno>(dest.smoothresh,"smoothresh",db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.mface,"*mface",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mtface,"*mtface",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.tface,"*tface",db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.mvert,"*mvert",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.medge,"*medge",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.dvert,"*dvert",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mcol,"*mcol",db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.mat,"**mat",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MDeformVert> (
+ MDeformVert& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.dw,"*dw",db);
+ ReadField<ErrorPolicy_Igno>(dest.totweight,"totweight",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<World> (
+ World& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MVert> (
+ MVert& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldArray<ErrorPolicy_Fail>(dest.co,"co",db);
+ ReadFieldArray<ErrorPolicy_Fail>(dest.no,"no",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+ ReadField<ErrorPolicy_Warn>(dest.mat_nr,"mat_nr",db);
+ ReadField<ErrorPolicy_Igno>(dest.bweight,"bweight",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MEdge> (
+ MEdge& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.v1,"v1",db);
+ ReadField<ErrorPolicy_Fail>(dest.v2,"v2",db);
+ ReadField<ErrorPolicy_Igno>(dest.crease,"crease",db);
+ ReadField<ErrorPolicy_Igno>(dest.bweight,"bweight",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<GroupObject> (
+ GroupObject& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.prev,"*prev",db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.next,"*next",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.ob,"*ob",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<ListBase> (
+ ListBase& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.first,"*first",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.last,"*last",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<ModifierData> (
+ ModifierData& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.next,"*next",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.prev,"*prev",db);
+ ReadField<ErrorPolicy_Igno>(dest.type,"type",db);
+ ReadField<ErrorPolicy_Igno>(dest.mode,"mode",db);
+ ReadFieldArray<ErrorPolicy_Igno>(dest.name,"name",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<ID> (
+ ID& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldArray<ErrorPolicy_Warn>(dest.name,"name",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MCol> (
+ MCol& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.r,"r",db);
+ ReadField<ErrorPolicy_Fail>(dest.g,"g",db);
+ ReadField<ErrorPolicy_Fail>(dest.b,"b",db);
+ ReadField<ErrorPolicy_Fail>(dest.a,"a",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Image> (
+ Image& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadFieldArray<ErrorPolicy_Warn>(dest.name,"name",db);
+ ReadField<ErrorPolicy_Igno>(dest.ok,"ok",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+ ReadField<ErrorPolicy_Igno>(dest.source,"source",db);
+ ReadField<ErrorPolicy_Igno>(dest.type,"type",db);
+ ReadField<ErrorPolicy_Igno>(dest.pad,"pad",db);
+ ReadField<ErrorPolicy_Igno>(dest.pad1,"pad1",db);
+ ReadField<ErrorPolicy_Igno>(dest.lastframe,"lastframe",db);
+ ReadField<ErrorPolicy_Igno>(dest.tpageflag,"tpageflag",db);
+ ReadField<ErrorPolicy_Igno>(dest.totbind,"totbind",db);
+ ReadField<ErrorPolicy_Igno>(dest.xrep,"xrep",db);
+ ReadField<ErrorPolicy_Igno>(dest.yrep,"yrep",db);
+ ReadField<ErrorPolicy_Igno>(dest.twsta,"twsta",db);
+ ReadField<ErrorPolicy_Igno>(dest.twend,"twend",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.packedfile,"*packedfile",db);
+ ReadField<ErrorPolicy_Igno>(dest.lastupdate,"lastupdate",db);
+ ReadField<ErrorPolicy_Igno>(dest.lastused,"lastused",db);
+ ReadField<ErrorPolicy_Igno>(dest.animspeed,"animspeed",db);
+ ReadField<ErrorPolicy_Igno>(dest.gen_x,"gen_x",db);
+ ReadField<ErrorPolicy_Igno>(dest.gen_y,"gen_y",db);
+ ReadField<ErrorPolicy_Igno>(dest.gen_type,"gen_type",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Scene> (
+ Scene& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.camera,"*camera",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.world,"*world",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.basact,"*basact",db);
+ ReadField<ErrorPolicy_Igno>(dest.base,"base",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Library> (
+ Library& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadFieldArray<ErrorPolicy_Warn>(dest.name,"name",db);
+ ReadFieldArray<ErrorPolicy_Fail>(dest.filename,"filename",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.parent,"*parent",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Tex> (
+ Tex& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>((int&)dest.type,"type",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.ima,"*ima",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Camera> (
+ Camera& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadField<ErrorPolicy_Warn>((int&)dest.type,"type",db);
+ ReadField<ErrorPolicy_Warn>((int&)dest.flag,"flag",db);
+ ReadField<ErrorPolicy_Warn>(dest.angle,"angle",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MirrorModifierData> (
+ MirrorModifierData& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.modifier,"modifier",db);
+ ReadField<ErrorPolicy_Igno>(dest.axis,"axis",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+ ReadField<ErrorPolicy_Igno>(dest.tolerance,"tolerance",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mirror_ob,"*mirror_ob",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+void DNA::RegisterConverters() {
+
+ converters["Object"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<Object>), static_cast<ConvertProcPtr>(&Structure::Convert<Object>) );
+ converters["Group"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<Group>), static_cast<ConvertProcPtr>(&Structure::Convert<Group>) );
+ converters["MTex"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<MTex>), static_cast<ConvertProcPtr>(&Structure::Convert<MTex>) );
+ converters["TFace"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<TFace>), static_cast<ConvertProcPtr>(&Structure::Convert<TFace>) );
+ converters["SubsurfModifierData"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<SubsurfModifierData>), static_cast<ConvertProcPtr>(&Structure::Convert<SubsurfModifierData>) );
+ converters["MFace"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<MFace>), static_cast<ConvertProcPtr>(&Structure::Convert<MFace>) );
+ converters["Lamp"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<Lamp>), static_cast<ConvertProcPtr>(&Structure::Convert<Lamp>) );
+ converters["MDeformWeight"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<MDeformWeight>), static_cast<ConvertProcPtr>(&Structure::Convert<MDeformWeight>) );
+ converters["PackedFile"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<PackedFile>), static_cast<ConvertProcPtr>(&Structure::Convert<PackedFile>) );
+ converters["Base"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<Base>), static_cast<ConvertProcPtr>(&Structure::Convert<Base>) );
+ converters["MTFace"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<MTFace>), static_cast<ConvertProcPtr>(&Structure::Convert<MTFace>) );
+ converters["Material"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<Material>), static_cast<ConvertProcPtr>(&Structure::Convert<Material>) );
+ converters["Mesh"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<Mesh>), static_cast<ConvertProcPtr>(&Structure::Convert<Mesh>) );
+ converters["MDeformVert"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<MDeformVert>), static_cast<ConvertProcPtr>(&Structure::Convert<MDeformVert>) );
+ converters["World"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<World>), static_cast<ConvertProcPtr>(&Structure::Convert<World>) );
+ converters["MVert"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<MVert>), static_cast<ConvertProcPtr>(&Structure::Convert<MVert>) );
+ converters["MEdge"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<MEdge>), static_cast<ConvertProcPtr>(&Structure::Convert<MEdge>) );
+ converters["GroupObject"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<GroupObject>), static_cast<ConvertProcPtr>(&Structure::Convert<GroupObject>) );
+ converters["ListBase"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<ListBase>), static_cast<ConvertProcPtr>(&Structure::Convert<ListBase>) );
+ converters["ModifierData"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<ModifierData>), static_cast<ConvertProcPtr>(&Structure::Convert<ModifierData>) );
+ converters["ID"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<ID>), static_cast<ConvertProcPtr>(&Structure::Convert<ID>) );
+ converters["MCol"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<MCol>), static_cast<ConvertProcPtr>(&Structure::Convert<MCol>) );
+ converters["Image"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<Image>), static_cast<ConvertProcPtr>(&Structure::Convert<Image>) );
+ converters["Scene"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<Scene>), static_cast<ConvertProcPtr>(&Structure::Convert<Scene>) );
+ converters["Library"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<Library>), static_cast<ConvertProcPtr>(&Structure::Convert<Library>) );
+ converters["Tex"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<Tex>), static_cast<ConvertProcPtr>(&Structure::Convert<Tex>) );
+ converters["Camera"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<Camera>), static_cast<ConvertProcPtr>(&Structure::Convert<Camera>) );
+ converters["MirrorModifierData"] = DNA::FactoryPair( static_cast<AllocProcPtr>(&Structure::Allocate<MirrorModifierData>), static_cast<ConvertProcPtr>(&Structure::Convert<MirrorModifierData>) );
+}
+
+
+#endif
diff --git a/src/3rdparty/assimp/code/BlenderScene.h b/src/3rdparty/assimp/code/BlenderScene.h
new file mode 100644
index 000000000..5cfb7092f
--- /dev/null
+++ b/src/3rdparty/assimp/code/BlenderScene.h
@@ -0,0 +1,683 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file BlenderScene.h
+ * @brief Intermediate representation of a BLEND scene.
+ */
+#ifndef INCLUDED_AI_BLEND_SCENE_H
+#define INCLUDED_AI_BLEND_SCENE_H
+
+namespace Assimp {
+ namespace Blender {
+
+// Minor parts of this file are extracts from blender data structures,
+// declared in the ./source/blender/makesdna directory.
+// Stuff that is not used by Assimp is commented.
+
+
+// NOTE
+// this file serves as input data to the `./scripts/genblenddna.py`
+// script. This script generates the actual binding code to read a
+// blender file with a possibly different DNA into our structures.
+// Only `struct` declarations are considered and the following
+// rules must be obeyed in order for the script to work properly:
+//
+// * C++ style comments only
+//
+// * Structures may include the primitive types char, int, short,
+// float, double. Signedness specifiers are not allowed on
+// integers. Enum types are allowed, but they must have been
+// defined in this header.
+//
+// * Structures may aggregate other structures, unless not defined
+// in this header.
+//
+// * Pointers to other structures or primitive types are allowed.
+// No references or double pointers or arrays of pointers.
+// A pointer to a T is written as boost::shared_ptr, while a
+// pointer to an array of elements is written as boost::
+// shared_array.
+//
+// * Arrays can have maximally two-dimensions. Any non-pointer
+// type can form them.
+//
+// * Multiple fields can be declare in a single line (i.e `int a,b;`)
+// provided they are neither pointers nor arrays.
+//
+// * One of WARN, FAIL can be appended to the declaration (
+// prior to the semiolon to specifiy the error handling policy if
+// this field is missing in the input DNA). If none of those
+// is specified the default policy is to subtitute a default
+// value for the field.
+//
+
+#define WARN // warn if field is missing, substitute default value
+#define FAIL // fail the import if the field does not exist
+
+struct Object;
+struct MTex;
+
+#define AI_BLEND_MESH_MAX_VERTS 2000000000L
+
+// -------------------------------------------------------------------------------
+struct ID : ElemBase {
+
+ char name[24] WARN;
+ short flag;
+};
+
+// -------------------------------------------------------------------------------
+struct ListBase : ElemBase {
+
+ boost::shared_ptr<ElemBase> first;
+ boost::shared_ptr<ElemBase> last;
+};
+
+
+// -------------------------------------------------------------------------------
+struct PackedFile : ElemBase {
+ int size WARN;
+ int seek WARN;
+ boost::shared_ptr< FileOffset > data WARN;
+};
+
+// -------------------------------------------------------------------------------
+struct GroupObject : ElemBase {
+
+ boost::shared_ptr<GroupObject> prev,next FAIL;
+ boost::shared_ptr<Object> ob;
+};
+
+// -------------------------------------------------------------------------------
+struct Group : ElemBase {
+ ID id FAIL;
+ int layer;
+
+ boost::shared_ptr<GroupObject> gobject;
+};
+
+// -------------------------------------------------------------------------------
+struct World : ElemBase {
+ ID id FAIL;
+
+};
+
+// -------------------------------------------------------------------------------
+struct MVert : ElemBase {
+ float co[3] FAIL;
+ float no[3] FAIL;
+ char flag;
+ int mat_nr WARN;
+ int bweight;
+};
+
+// -------------------------------------------------------------------------------
+struct MEdge : ElemBase {
+ int v1, v2 FAIL;
+ char crease, bweight;
+ short flag;
+};
+
+// -------------------------------------------------------------------------------
+struct MCol : ElemBase {
+ char r,g,b,a FAIL;
+};
+
+// -------------------------------------------------------------------------------
+struct MFace : ElemBase {
+ int v1,v2,v3,v4 FAIL;
+ int mat_nr FAIL;
+ char flag;
+};
+
+// -------------------------------------------------------------------------------
+struct TFace : ElemBase {
+ float uv[4][2] FAIL;
+ int col[4] FAIL;
+ char flag;
+ short mode;
+ short tile;
+ short unwrap;
+};
+
+// -------------------------------------------------------------------------------
+struct MTFace : ElemBase {
+
+ float uv[4][2] FAIL;
+ char flag;
+ short mode;
+ short tile;
+ short unwrap;
+
+ // boost::shared_ptr<Image> tpage;
+};
+
+// -------------------------------------------------------------------------------
+struct MDeformWeight : ElemBase {
+ int def_nr FAIL;
+ float weight FAIL;
+};
+
+// -------------------------------------------------------------------------------
+struct MDeformVert : ElemBase {
+
+ vector<MDeformWeight> dw WARN;
+ int totweight;
+};
+
+// -------------------------------------------------------------------------------
+struct Material : ElemBase {
+ ID id FAIL;
+
+ float r,g,b WARN;
+ float specr,specg,specb WARN;
+ float ambir,ambig,ambib WARN;
+ float mirr,mirg,mirb;
+ float emit WARN;
+ float alpha WARN;
+ float ref;
+ float translucency;
+ float roughness;
+ float darkness;
+ float refrac;
+
+ boost::shared_ptr<Group> group;
+
+ short diff_shader WARN;
+ short spec_shader WARN;
+
+ boost::shared_ptr<MTex> mtex[18];
+};
+
+// -------------------------------------------------------------------------------
+struct Mesh : ElemBase {
+ ID id FAIL;
+
+ int totface FAIL;
+ int totedge FAIL;
+ int totvert FAIL;
+
+ short subdiv;
+ short subdivr;
+ short subsurftype;
+ short smoothresh;
+
+ vector<MFace> mface FAIL;
+ vector<MTFace> mtface;
+ vector<TFace> tface;
+ vector<MVert> mvert FAIL;
+ vector<MEdge> medge WARN;
+ vector<MDeformVert> dvert;
+ vector<MCol> mcol;
+
+ vector< boost::shared_ptr<Material> > mat FAIL;
+};
+
+// -------------------------------------------------------------------------------
+struct Library : ElemBase {
+ ID id FAIL;
+
+ char name[240] WARN;
+ char filename[240] FAIL;
+ boost::shared_ptr<Library> parent WARN;
+};
+
+// -------------------------------------------------------------------------------
+struct Camera : ElemBase {
+ enum Type {
+ Type_PERSP = 0
+ ,Type_ORTHO = 1
+ };
+
+ ID id FAIL;
+
+ // struct AnimData *adt;
+
+ Type type,flag WARN;
+ float angle WARN;
+ //float passepartalpha, angle;
+ //float clipsta, clipend;
+ //float lens, ortho_scale, drawsize;
+ //float shiftx, shifty;
+
+ //float YF_dofdist, YF_aperture;
+ //short YF_bkhtype, YF_bkhbias;
+ //float YF_bkhrot;
+};
+
+
+// -------------------------------------------------------------------------------
+struct Lamp : ElemBase {
+
+ enum FalloffType {
+ FalloffType_Constant = 0x0
+ ,FalloffType_InvLinear = 0x1
+ ,FalloffType_InvSquare = 0x2
+ //,FalloffType_Curve = 0x3
+ //,FalloffType_Sliders = 0x4
+ };
+
+ enum Type {
+ Type_Local = 0x0
+ ,Type_Sun = 0x1
+ ,Type_Spot = 0x2
+ ,Type_Hemi = 0x3
+ ,Type_Area = 0x4
+ //,Type_YFPhoton = 0x5
+ };
+
+ ID id FAIL;
+ //AnimData *adt;
+
+ Type type FAIL;
+ short flags;
+
+ //int mode;
+
+ short colormodel, totex;
+ float r,g,b,k WARN;
+ //float shdwr, shdwg, shdwb;
+
+ float energy, dist, spotsize, spotblend;
+ //float haint;
+
+ float att1, att2;
+ //struct CurveMapping *curfalloff;
+ FalloffType falloff_type;
+
+ //float clipsta, clipend, shadspotsize;
+ //float bias, soft, compressthresh;
+ //short bufsize, samp, buffers, filtertype;
+ //char bufflag, buftype;
+
+ //short ray_samp, ray_sampy, ray_sampz;
+ //short ray_samp_type;
+ //short area_shape;
+ //float area_size, area_sizey, area_sizez;
+ //float adapt_thresh;
+ //short ray_samp_method;
+
+ //short texact, shadhalostep;
+
+ //short sun_effect_type;
+ //short skyblendtype;
+ //float horizon_brightness;
+ //float spread;
+ float sun_brightness;
+ //float sun_size;
+ //float backscattered_light;
+ //float sun_intensity;
+ //float atm_turbidity;
+ //float atm_inscattering_factor;
+ //float atm_extinction_factor;
+ //float atm_distance_factor;
+ //float skyblendfac;
+ //float sky_exposure;
+ //short sky_colorspace;
+
+ // int YF_numphotons, YF_numsearch;
+ // short YF_phdepth, YF_useqmc, YF_bufsize, YF_pad;
+ // float YF_causticblur, YF_ltradius;
+
+ // float YF_glowint, YF_glowofs;
+ // short YF_glowtype, YF_pad2;
+
+ //struct Ipo *ipo;
+ //struct MTex *mtex[18];
+ // short pr_texture;
+
+ //struct PreviewImage *preview;
+};
+
+// -------------------------------------------------------------------------------
+struct ModifierData : ElemBase {
+ enum ModifierType {
+ eModifierType_None = 0,
+ eModifierType_Subsurf,
+ eModifierType_Lattice,
+ eModifierType_Curve,
+ eModifierType_Build,
+ eModifierType_Mirror,
+ eModifierType_Decimate,
+ eModifierType_Wave,
+ eModifierType_Armature,
+ eModifierType_Hook,
+ eModifierType_Softbody,
+ eModifierType_Boolean,
+ eModifierType_Array,
+ eModifierType_EdgeSplit,
+ eModifierType_Displace,
+ eModifierType_UVProject,
+ eModifierType_Smooth,
+ eModifierType_Cast,
+ eModifierType_MeshDeform,
+ eModifierType_ParticleSystem,
+ eModifierType_ParticleInstance,
+ eModifierType_Explode,
+ eModifierType_Cloth,
+ eModifierType_Collision,
+ eModifierType_Bevel,
+ eModifierType_Shrinkwrap,
+ eModifierType_Fluidsim,
+ eModifierType_Mask,
+ eModifierType_SimpleDeform,
+ eModifierType_Multires,
+ eModifierType_Surface,
+ eModifierType_Smoke,
+ eModifierType_ShapeKey
+ };
+
+ boost::shared_ptr<ElemBase> next WARN;
+ boost::shared_ptr<ElemBase> prev WARN;
+
+ int type, mode;
+ char name[32];
+};
+
+// -------------------------------------------------------------------------------
+struct SubsurfModifierData : ElemBase {
+
+ enum Type {
+
+ TYPE_CatmullClarke = 0x0,
+ TYPE_Simple = 0x1
+ };
+
+ enum Flags {
+ // some ommitted
+ FLAGS_SubsurfUV =1<<3
+ };
+
+ ModifierData modifier FAIL;
+ short subdivType WARN;
+ short levels FAIL;
+ short renderLevels ;
+ short flags;
+};
+
+// -------------------------------------------------------------------------------
+struct MirrorModifierData : ElemBase {
+
+ enum Flags {
+ Flags_CLIPPING =1<<0,
+ Flags_MIRROR_U =1<<1,
+ Flags_MIRROR_V =1<<2,
+ Flags_AXIS_X =1<<3,
+ Flags_AXIS_Y =1<<4,
+ Flags_AXIS_Z =1<<5,
+ Flags_VGROUP =1<<6
+ };
+
+ ModifierData modifier FAIL;
+
+ short axis, flag;
+ float tolerance;
+ boost::shared_ptr<Object> mirror_ob;
+};
+
+// -------------------------------------------------------------------------------
+struct Object : ElemBase {
+ ID id FAIL;
+
+ enum Type {
+ Type_EMPTY = 0
+ ,Type_MESH = 1
+ ,Type_CURVE = 2
+ ,Type_SURF = 3
+ ,Type_FONT = 4
+ ,Type_MBALL = 5
+
+ ,Type_LAMP = 10
+ ,Type_CAMERA = 11
+
+ ,Type_WAVE = 21
+ ,Type_LATTICE = 22
+ };
+
+ Type type FAIL;
+ float obmat[4][4] WARN;
+ float parentinv[4][4] WARN;
+ char parsubstr[32] WARN;
+
+ boost::shared_ptr<Object> parent WARN;
+ boost::shared_ptr<Object> track WARN;
+
+ boost::shared_ptr<Object> proxy,proxy_from,proxy_group WARN;
+ boost::shared_ptr<Group> dup_group WARN;
+ boost::shared_ptr<ElemBase> data FAIL;
+
+ ListBase modifiers;
+};
+
+
+// -------------------------------------------------------------------------------
+struct Base : ElemBase {
+ boost::shared_ptr<Base> prev WARN;
+ boost::shared_ptr<Base> next WARN;
+ boost::shared_ptr<Object> object WARN;
+};
+
+// -------------------------------------------------------------------------------
+struct Scene : ElemBase {
+ ID id FAIL;
+
+ boost::shared_ptr<Object> camera WARN;
+ boost::shared_ptr<World> world WARN;
+ boost::shared_ptr<Base> basact WARN;
+
+ ListBase base;
+};
+
+
+// -------------------------------------------------------------------------------
+struct Image : ElemBase {
+ ID id FAIL;
+
+ char name[240] WARN;
+
+ //struct anim *anim;
+
+ short ok, flag;
+ short source, type, pad, pad1;
+ int lastframe;
+
+ short tpageflag, totbind;
+ short xrep, yrep;
+ short twsta, twend;
+ //unsigned int bindcode;
+ //unsigned int *repbind;
+
+ boost::shared_ptr<PackedFile> packedfile;
+ //struct PreviewImage * preview;
+
+ float lastupdate;
+ int lastused;
+ short animspeed;
+
+ short gen_x, gen_y, gen_type;
+};
+
+// -------------------------------------------------------------------------------
+struct Tex : ElemBase {
+
+ // actually, the only texture type we support is Type_IMAGE
+ enum Type {
+ Type_CLOUDS = 1
+ ,Type_WOOD = 2
+ ,Type_MARBLE = 3
+ ,Type_MAGIC = 4
+ ,Type_BLEND = 5
+ ,Type_STUCCI = 6
+ ,Type_NOISE = 7
+ ,Type_IMAGE = 8
+ ,Type_PLUGIN = 9
+ ,Type_ENVMAP = 10
+ ,Type_MUSGRAVE = 11
+ ,Type_VORONOI = 12
+ ,Type_DISTNOISE = 13
+ ,Type_POINTDENSITY = 14
+ ,Type_VOXELDATA = 15
+ };
+
+ ID id FAIL;
+ // AnimData *adt;
+
+ //float noisesize, turbul;
+ //float bright, contrast, rfac, gfac, bfac;
+ //float filtersize;
+
+ //float mg_H, mg_lacunarity, mg_octaves, mg_offset, mg_gain;
+ //float dist_amount, ns_outscale;
+
+ //float vn_w1;
+ //float vn_w2;
+ //float vn_w3;
+ //float vn_w4;
+ //float vn_mexp;
+ //short vn_distm, vn_coltype;
+
+ //short noisedepth, noisetype;
+ //short noisebasis, noisebasis2;
+
+ //short imaflag, flag;
+ Type type FAIL;
+ //short stype;
+
+ //float cropxmin, cropymin, cropxmax, cropymax;
+ //int texfilter;
+ //int afmax;
+ //short xrepeat, yrepeat;
+ //short extend;
+
+ //short fie_ima;
+ //int len;
+ //int frames, offset, sfra;
+
+ //float checkerdist, nabla;
+ //float norfac;
+
+ //ImageUser iuser;
+
+ //bNodeTree *nodetree;
+ //Ipo *ipo;
+ boost::shared_ptr<Image> ima WARN;
+ //PluginTex *plugin;
+ //ColorBand *coba;
+ //EnvMap *env;
+ //PreviewImage * preview;
+ //PointDensity *pd;
+ //VoxelData *vd;
+
+ //char use_nodes;
+};
+
+// -------------------------------------------------------------------------------
+struct MTex : ElemBase {
+
+ enum Projection {
+ Proj_N = 0
+ ,Proj_X = 1
+ ,Proj_Y = 2
+ ,Proj_Z = 3
+ };
+
+ enum Flag {
+ Flag_RGBTOINT = 0x1
+ ,Flag_STENCIL = 0x2
+ ,Flag_NEGATIVE = 0x4
+ ,Flag_ALPHAMIX = 0x8
+ ,Flag_VIEWSPACE = 0x10
+ };
+
+ enum BlendType {
+ BlendType_BLEND = 0
+ ,BlendType_MUL = 1
+ ,BlendType_ADD = 2
+ ,BlendType_SUB = 3
+ ,BlendType_DIV = 4
+ ,BlendType_DARK = 5
+ ,BlendType_DIFF = 6
+ ,BlendType_LIGHT = 7
+ ,BlendType_SCREEN = 8
+ ,BlendType_OVERLAY = 9
+ ,BlendType_BLEND_HUE = 10
+ ,BlendType_BLEND_SAT = 11
+ ,BlendType_BLEND_VAL = 12
+ ,BlendType_BLEND_COLOR = 13
+ };
+
+ // short texco, mapto, maptoneg;
+
+ BlendType blendtype;
+ boost::shared_ptr<Object> object;
+ boost::shared_ptr<Tex> tex;
+ char uvname[32];
+
+ Projection projx,projy,projz;
+ char mapping;
+ float ofs[3], size[3], rot;
+
+ int texflag;
+ short colormodel, pmapto, pmaptoneg;
+ //short normapspace, which_output;
+ //char brush_map_mode;
+ float r,g,b,k WARN;
+ //float def_var, rt;
+
+ //float colfac, varfac;
+
+ //float norfac, dispfac, warpfac;
+ float colspecfac, mirrfac, alphafac;
+ float difffac, specfac, emitfac, hardfac;
+ //float raymirrfac, translfac, ambfac;
+ //float colemitfac, colreflfac, coltransfac;
+ //float densfac, scatterfac, reflfac;
+
+ //float timefac, lengthfac, clumpfac;
+ //float kinkfac, roughfac, padensfac;
+ //float lifefac, sizefac, ivelfac, pvelfac;
+ //float shadowfac;
+ //float zenupfac, zendownfac, blendfac;
+};
+
+
+ }
+}
+#endif
diff --git a/src/3rdparty/assimp/code/BlenderSceneGen.h b/src/3rdparty/assimp/code/BlenderSceneGen.h
new file mode 100644
index 000000000..3ef5d341d
--- /dev/null
+++ b/src/3rdparty/assimp/code/BlenderSceneGen.h
@@ -0,0 +1,223 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file BlenderSceneGen.h
+ * @brief MACHINE GENERATED BY ./scripts/BlenderImporter/genblenddna.py
+ */
+#ifndef INCLUDED_AI_BLEND_SCENEGEN_H
+#define INCLUDED_AI_BLEND_SCENEGEN_H
+
+namespace Assimp {
+ namespace Blender {
+
+
+template <> void Structure :: Convert<Object> (
+ Object& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Group> (
+ Group& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MTex> (
+ MTex& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<TFace> (
+ TFace& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<SubsurfModifierData> (
+ SubsurfModifierData& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MFace> (
+ MFace& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Lamp> (
+ Lamp& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MDeformWeight> (
+ MDeformWeight& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<PackedFile> (
+ PackedFile& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Base> (
+ Base& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MTFace> (
+ MTFace& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Material> (
+ Material& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Mesh> (
+ Mesh& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MDeformVert> (
+ MDeformVert& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<World> (
+ World& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MVert> (
+ MVert& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MEdge> (
+ MEdge& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<GroupObject> (
+ GroupObject& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<ListBase> (
+ ListBase& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<ModifierData> (
+ ModifierData& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<ID> (
+ ID& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MCol> (
+ MCol& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Image> (
+ Image& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Scene> (
+ Scene& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Library> (
+ Library& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Tex> (
+ Tex& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Camera> (
+ Camera& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MirrorModifierData> (
+ MirrorModifierData& dest,
+ const FileDatabase& db
+ ) const
+;
+
+
+ }
+}
+
+#endif
diff --git a/src/3rdparty/assimp/code/BoostWorkaround/boost/LICENSE_1_0.txt b/src/3rdparty/assimp/code/BoostWorkaround/boost/LICENSE_1_0.txt
new file mode 100644
index 000000000..36b7cd93c
--- /dev/null
+++ b/src/3rdparty/assimp/code/BoostWorkaround/boost/LICENSE_1_0.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+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.
diff --git a/src/3rdparty/assimp/code/BoostWorkaround/boost/foreach.hpp b/src/3rdparty/assimp/code/BoostWorkaround/boost/foreach.hpp
new file mode 100644
index 000000000..e244cd57e
--- /dev/null
+++ b/src/3rdparty/assimp/code/BoostWorkaround/boost/foreach.hpp
@@ -0,0 +1,93 @@
+
+#ifndef BOOST_FOREACH
+
+///////////////////////////////////////////////////////////////////////////////
+// A stripped down version of FOREACH for
+// illustration purposes. NOT FOR GENERAL USE.
+// For a complete implementation, see BOOST_FOREACH at
+// http://boost-sandbox.sourceforge.net/vault/index.php?directory=eric_niebler
+//
+// Copyright 2004 Eric Niebler.
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// Adapted to Assimp November 29th, 2008 (Alexander Gessler).
+// Added code to handle both const and non-const iterators, simplified some
+// parts.
+///////////////////////////////////////////////////////////////////////////////
+
+namespace boost {
+namespace foreach_detail {
+
+///////////////////////////////////////////////////////////////////////////////
+// auto_any
+
+struct auto_any_base
+{
+ operator bool() const { return false; }
+};
+
+template<typename T>
+struct auto_any : auto_any_base
+{
+ auto_any(T const& t) : item(t) {}
+ mutable T item;
+};
+
+template<typename T>
+T& auto_any_cast(auto_any_base const& any)
+{
+ return static_cast<auto_any<T> const&>(any).item;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FOREACH helper function
+
+template<typename T>
+auto_any<typename T::const_iterator> begin(T const& t)
+{
+ return t.begin();
+}
+
+template<typename T>
+auto_any<typename T::const_iterator> end(T const& t)
+{
+ return t.end();
+}
+
+// iterator
+template<typename T>
+bool done(auto_any_base const& cur, auto_any_base const& end, T&)
+{
+ typedef typename T::iterator iter_type;
+ return auto_any_cast<iter_type>(cur) == auto_any_cast<iter_type>(end);
+}
+
+template<typename T>
+void next(auto_any_base const& cur, T&)
+{
+ ++auto_any_cast<typename T::iterator>(cur);
+}
+
+template<typename T>
+typename T::reference deref(auto_any_base const& cur, T&)
+{
+ return *auto_any_cast<typename T::iterator>(cur);
+}
+
+} // end foreach_detail
+
+///////////////////////////////////////////////////////////////////////////////
+// FOREACH
+
+#define BOOST_FOREACH(item, container) \
+ if (boost::foreach_detail::auto_any_base const& b = boost::foreach_detail::begin(container)) {} else \
+ if (boost::foreach_detail::auto_any_base const& e = boost::foreach_detail::end(container)) {} else \
+ for (;!boost::foreach_detail::done(b,e,container); boost::foreach_detail::next(b,container)) \
+ if (bool ugly_and_unique_break = false) {} else \
+ for (item = boost::foreach_detail::deref(b,container); !ugly_and_unique_break; ugly_and_unique_break = true)
+
+} // end boost
+
+#endif
diff --git a/src/3rdparty/assimp/code/BoostWorkaround/boost/format.hpp b/src/3rdparty/assimp/code/BoostWorkaround/boost/format.hpp
new file mode 100644
index 000000000..30404adad
--- /dev/null
+++ b/src/3rdparty/assimp/code/BoostWorkaround/boost/format.hpp
@@ -0,0 +1,85 @@
+
+
+
+/* DEPRECATED! - use code/TinyFormatter.h instead.
+ *
+ *
+ * */
+
+#ifndef AI_BOOST_FORMAT_DUMMY_INCLUDED
+#define AI_BOOST_FORMAT_DUMMY_INCLUDED
+
+#if (!defined BOOST_FORMAT_HPP) || (defined ASSIMP_FORCE_NOBOOST)
+
+#include <string>
+#include <vector>
+
+#if defined(__QNXNTO__)
+#include <unistd.h>
+#endif
+
+namespace boost
+{
+
+
+ class format
+ {
+ public:
+ format (const std::string& _d)
+ : d(_d)
+ {
+ }
+
+ template <typename T>
+ format& operator % (T in)
+ {
+ // XXX add replacement for boost::lexical_cast?
+
+ std::stringstream ss;
+ ss << in; // note: ss cannot be an rvalue, or the global operator << (const char*) is not called for T == const char*.
+ chunks.push_back( ss.str());
+ return *this;
+ }
+
+
+ operator std::string () const {
+ std::string res; // pray for NRVO to kick in
+
+ size_t start = 0, last = 0;
+
+ std::vector<std::string>::const_iterator chunkin = chunks.begin();
+
+ for ( start = d.find('%');start != std::string::npos; start = d.find('%',last)) {
+ res += d.substr(last,start-last);
+ last = start+2;
+ if (d[start+1] == '%') {
+ res += "%";
+ continue;
+ }
+
+ if (chunkin == chunks.end()) {
+ break;
+ }
+
+ res += *chunkin++;
+ }
+ res += d.substr(last);
+ return res;
+ }
+
+ private:
+ std::string d;
+ std::vector<std::string> chunks;
+ };
+
+ inline std::string str(const std::string& s) {
+ return s;
+ }
+}
+
+
+#else
+# error "format.h was already included"
+#endif //
+#endif // !! AI_BOOST_FORMAT_DUMMY_INCLUDED
+
diff --git a/src/3rdparty/assimp/code/BoostWorkaround/boost/lexical_cast.hpp b/src/3rdparty/assimp/code/BoostWorkaround/boost/lexical_cast.hpp
new file mode 100644
index 000000000..6c3f78198
--- /dev/null
+++ b/src/3rdparty/assimp/code/BoostWorkaround/boost/lexical_cast.hpp
@@ -0,0 +1,23 @@
+/// A quick replacement for boost::lexical_cast for all the Boost haters out there
+
+#ifndef __AI_BOOST_WORKAROUND_LEXICAL_CAST
+#define __AI_BOOST_WORKAROUND_LEXICAL_CAST
+
+namespace boost
+{
+
+ /// A quick replacement for boost::lexical_cast - should work for all types a stringstream can handle
+ template <typename TargetType, typename SourceType>
+ TargetType lexical_cast( const SourceType& source)
+ {
+ std::stringstream stream;
+ TargetType result;
+
+ stream << source;
+ stream >> result;
+ return result;
+ }
+
+} // namespace boost
+
+#endif // __AI_BOOST_WORKAROUND_LEXICAL_CAST
diff --git a/src/3rdparty/assimp/code/BoostWorkaround/boost/math/common_factor_rt.hpp b/src/3rdparty/assimp/code/BoostWorkaround/boost/math/common_factor_rt.hpp
new file mode 100644
index 000000000..76dd9b109
--- /dev/null
+++ b/src/3rdparty/assimp/code/BoostWorkaround/boost/math/common_factor_rt.hpp
@@ -0,0 +1,37 @@
+
+
+#ifndef BOOST_MATH_COMMON_FACTOR_RT_HPP
+#define BOOST_MATH_COMMON_FACTOR_RT_HPP
+
+
+namespace boost {
+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;
+}
+
+}}
+
+#endif
diff --git a/src/3rdparty/assimp/code/BoostWorkaround/boost/pointer_cast.hpp b/src/3rdparty/assimp/code/BoostWorkaround/boost/pointer_cast.hpp
new file mode 100644
index 000000000..ce188c13c
--- /dev/null
+++ b/src/3rdparty/assimp/code/BoostWorkaround/boost/pointer_cast.hpp
@@ -0,0 +1,45 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2005.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef BOOST_POINTER_CAST_HPP
+#define BOOST_POINTER_CAST_HPP
+
+namespace boost {
+
+//static_pointer_cast overload for raw pointers
+template<class T, class U>
+inline T* static_pointer_cast(U *ptr)
+{
+ return static_cast<T*>(ptr);
+}
+
+//dynamic_pointer_cast overload for raw pointers
+template<class T, class U>
+inline T* dynamic_pointer_cast(U *ptr)
+{
+ return dynamic_cast<T*>(ptr);
+}
+
+//const_pointer_cast overload for raw pointers
+template<class T, class U>
+inline T* const_pointer_cast(U *ptr)
+{
+ return const_cast<T*>(ptr);
+}
+
+//reinterpret_pointer_cast overload for raw pointers
+template<class T, class U>
+inline T* reinterpret_pointer_cast(U *ptr)
+{
+ return reinterpret_cast<T*>(ptr);
+}
+
+} // namespace boost
+
+#endif //BOOST_POINTER_CAST_HPP
diff --git a/src/3rdparty/assimp/code/BoostWorkaround/boost/scoped_array.hpp b/src/3rdparty/assimp/code/BoostWorkaround/boost/scoped_array.hpp
new file mode 100644
index 000000000..ebb97171b
--- /dev/null
+++ b/src/3rdparty/assimp/code/BoostWorkaround/boost/scoped_array.hpp
@@ -0,0 +1,79 @@
+
+#ifndef __AI_BOOST_SCOPED_ARRAY_INCLUDED
+#define __AI_BOOST_SCOPED_ARRAY_INCLUDED
+
+#ifndef BOOST_SCOPED_ARRAY_HPP_INCLUDED
+
+namespace boost {
+
+// small replacement for boost::scoped_array
+template <class T>
+class scoped_array
+{
+public:
+
+ // provide a default construtctor
+ scoped_array()
+ : ptr(0)
+ {
+ }
+
+ // construction from an existing heap object of type T
+ scoped_array(T* _ptr)
+ : ptr(_ptr)
+ {
+ }
+
+ // automatic destruction of the wrapped object at the
+ // end of our lifetime
+ ~scoped_array()
+ {
+ delete[] ptr;
+ }
+
+ inline T* get()
+ {
+ return ptr;
+ }
+
+ inline T* operator-> ()
+ {
+ return ptr;
+ }
+
+ inline void reset (T* t = 0)
+ {
+ delete[] ptr;
+ ptr = t;
+ }
+
+ T & operator[](std::ptrdiff_t i) const
+ {
+ return ptr[i];
+ }
+
+ void swap(scoped_array & b)
+ {
+ std::swap(ptr, b.ptr);
+ }
+
+private:
+
+ // encapsulated object pointer
+ T* ptr;
+
+};
+
+template<class T>
+inline void swap(scoped_array<T> & a, scoped_array<T> & b)
+{
+ a.swap(b);
+}
+
+} // end of namespace boost
+
+#else
+# error "scoped_array.h was already included"
+#endif
+#endif // __AI_BOOST_SCOPED_ARRAY_INCLUDED
+
diff --git a/src/3rdparty/assimp/code/BoostWorkaround/boost/scoped_ptr.hpp b/src/3rdparty/assimp/code/BoostWorkaround/boost/scoped_ptr.hpp
new file mode 100644
index 000000000..1ddee564a
--- /dev/null
+++ b/src/3rdparty/assimp/code/BoostWorkaround/boost/scoped_ptr.hpp
@@ -0,0 +1,79 @@
+
+#ifndef __AI_BOOST_SCOPED_PTR_INCLUDED
+#define __AI_BOOST_SCOPED_PTR_INCLUDED
+
+#ifndef BOOST_SCOPED_PTR_HPP_INCLUDED
+
+namespace boost {
+
+// small replacement for boost::scoped_ptr
+template <class T>
+class scoped_ptr
+{
+public:
+
+ // provide a default construtctor
+ scoped_ptr()
+ : ptr(0)
+ {
+ }
+
+ // construction from an existing heap object of type T
+ scoped_ptr(T* _ptr)
+ : ptr(_ptr)
+ {
+ }
+
+ // automatic destruction of the wrapped object at the
+ // end of our lifetime
+ ~scoped_ptr()
+ {
+ delete ptr;
+ }
+
+ inline T* get() const
+ {
+ return ptr;
+ }
+
+ inline operator T*()
+ {
+ return ptr;
+ }
+
+ inline T* operator-> ()
+ {
+ return ptr;
+ }
+
+ inline void reset (T* t = 0)
+ {
+ delete ptr;
+ ptr = t;
+ }
+
+ void swap(scoped_ptr & b)
+ {
+ std::swap(ptr, b.ptr);
+ }
+
+private:
+
+ // encapsulated object pointer
+ T* ptr;
+
+};
+
+template<class T>
+inline void swap(scoped_ptr<T> & a, scoped_ptr<T> & b)
+{
+ a.swap(b);
+}
+
+} // end of namespace boost
+
+#else
+# error "scoped_ptr.h was already included"
+#endif
+#endif // __AI_BOOST_SCOPED_PTR_INCLUDED
+
diff --git a/src/3rdparty/assimp/code/BoostWorkaround/boost/shared_array.hpp b/src/3rdparty/assimp/code/BoostWorkaround/boost/shared_array.hpp
new file mode 100644
index 000000000..ad58bd0d4
--- /dev/null
+++ b/src/3rdparty/assimp/code/BoostWorkaround/boost/shared_array.hpp
@@ -0,0 +1,228 @@
+
+#ifndef INCLUDED_AI_BOOST_SHARED_ARRAY
+#define INCLUDED_AI_BOOST_SHARED_ARRAY
+
+#ifndef BOOST_SCOPED_ARRAY_HPP_INCLUDED
+
+// ------------------------------
+// Internal stub
+namespace boost {
+ namespace array_detail {
+ class controller {
+ public:
+
+ controller()
+ : cnt(1)
+ {}
+
+ public:
+
+ template <typename T>
+ controller* decref(T* pt) {
+ if (--cnt <= 0) {
+ delete this;
+ delete[] pt;
+ }
+ return NULL;
+ }
+
+ controller* incref() {
+ ++cnt;
+ return this;
+ }
+
+ long get() const {
+ return cnt;
+ }
+
+ private:
+ long cnt;
+ };
+
+ struct empty {};
+
+ template <typename DEST, typename SRC>
+ struct is_convertible_stub {
+
+ struct yes {char s[1];};
+ struct no {char s[2];};
+
+ static yes foo(DEST*);
+ static no foo(...);
+
+ enum {result = (sizeof(foo((SRC*)0)) == sizeof(yes) ? 1 : 0)};
+ };
+
+ template <bool> struct enable_if {};
+ template <> struct enable_if<true> {
+ typedef empty result;
+ };
+
+ template <typename DEST, typename SRC>
+ struct is_convertible : public enable_if<is_convertible_stub<DEST,SRC>::result > {
+ };
+ }
+
+// ------------------------------
+// Small replacement for boost::shared_array, not threadsafe because no
+// atomic reference counter is in use.
+// ------------------------------
+template <class T>
+class shared_array
+{
+ template <typename TT> friend class shared_array;
+
+ template<class TT> friend bool operator== (const shared_array<TT>& a, const shared_array<TT>& b);
+ template<class TT> friend bool operator!= (const shared_array<TT>& a, const shared_array<TT>& b);
+ template<class TT> friend bool operator< (const shared_array<TT>& a, const shared_array<TT>& b);
+
+public:
+
+ typedef T element_type;
+
+public:
+
+ // provide a default constructor
+ shared_array()
+ : ptr()
+ , ctr(NULL)
+ {
+ }
+
+ // construction from an existing object of type T
+ explicit shared_array(T* ptr)
+ : ptr(ptr)
+ , ctr(ptr ? new array_detail::controller() : NULL)
+ {
+ }
+
+ shared_array(const shared_array& r)
+ : ptr(r.ptr)
+ , ctr(r.ctr ? r.ctr->incref() : NULL)
+ {
+ }
+
+ template <typename Y>
+ shared_array(const shared_array<Y>& r,typename detail::is_convertible<T,Y>::result = detail::empty())
+ : ptr(r.ptr)
+ , ctr(r.ctr ? r.ctr->incref() : NULL)
+ {
+ }
+
+ // automatic destruction of the wrapped object when all
+ // references are freed.
+ ~shared_array() {
+ if (ctr) {
+ ctr = ctr->decref(ptr);
+ }
+ }
+
+ shared_array& operator=(const shared_array& r) {
+ if (this == &r) {
+ return *this;
+ }
+ if (ctr) {
+ ctr->decref(ptr);
+ }
+ ptr = r.ptr;
+ ctr = ptr?r.ctr->incref():NULL;
+ return *this;
+ }
+
+ template <typename Y>
+ shared_array& operator=(const shared_array<Y>& r) {
+ if (this == &r) {
+ return *this;
+ }
+ if (ctr) {
+ ctr->decref(ptr);
+ }
+ ptr = r.ptr;
+ ctr = ptr?r.ctr->incref():NULL;
+ return *this;
+ }
+
+ // pointer access
+ inline operator T*() {
+ return ptr;
+ }
+
+ inline T* operator-> () const {
+ return ptr;
+ }
+
+ // standard semantics
+ inline T* get() {
+ return ptr;
+ }
+
+ T& operator[] (std::ptrdiff_t index) const {
+ return ptr[index];
+ }
+
+ inline const T* get() const {
+ return ptr;
+ }
+
+ inline operator bool () const {
+ return ptr != NULL;
+ }
+
+ inline bool unique() const {
+ return use_count() == 1;
+ }
+
+ inline long use_count() const {
+ return ctr->get();
+ }
+
+ inline void reset (T* t = 0) {
+ if (ctr) {
+ ctr->decref(ptr);
+ }
+ ptr = t;
+ ctr = ptr?new array_detail::controller():NULL;
+ }
+
+ void swap(shared_array & b) {
+ std::swap(ptr, b.ptr);
+ std::swap(ctr, b.ctr);
+ }
+
+
+private:
+
+ // encapsulated object pointer
+ T* ptr;
+
+ // control block
+ array_detail::controller* ctr;
+};
+
+template<class T>
+inline void swap(shared_array<T> & a, shared_array<T> & b)
+{
+ a.swap(b);
+}
+
+template<class T>
+bool operator== (const shared_array<T>& a, const shared_array<T>& b) {
+ return a.ptr == b.ptr;
+}
+template<class T>
+bool operator!= (const shared_array<T>& a, const shared_array<T>& b) {
+ return a.ptr != b.ptr;
+}
+
+template<class T>
+bool operator< (const shared_array<T>& a, const shared_array<T>& b) {
+ return a.ptr < b.ptr;
+}
+
+
+} // end of namespace boost
+
+#else
+# error "shared_array.h was already included"
+#endif
+#endif // INCLUDED_AI_BOOST_SHARED_ARRAY
diff --git a/src/3rdparty/assimp/code/BoostWorkaround/boost/shared_ptr.hpp b/src/3rdparty/assimp/code/BoostWorkaround/boost/shared_ptr.hpp
new file mode 100644
index 000000000..e5f430c2a
--- /dev/null
+++ b/src/3rdparty/assimp/code/BoostWorkaround/boost/shared_ptr.hpp
@@ -0,0 +1,257 @@
+
+#ifndef INCLUDED_AI_BOOST_SHARED_PTR
+#define INCLUDED_AI_BOOST_SHARED_PTR
+
+#ifndef BOOST_SCOPED_PTR_HPP_INCLUDED
+
+// ------------------------------
+// Internal stub
+namespace boost {
+ namespace detail {
+ class controller {
+ public:
+
+ controller()
+ : cnt(1)
+ {}
+
+ public:
+
+ template <typename T>
+ controller* decref(T* pt) {
+ if (--cnt <= 0) {
+ delete this;
+ delete pt;
+ }
+ return NULL;
+ }
+
+ controller* incref() {
+ ++cnt;
+ return this;
+ }
+
+ long get() const {
+ return cnt;
+ }
+
+ private:
+ long cnt;
+ };
+
+ struct empty {};
+
+ template <typename DEST, typename SRC>
+ struct is_convertible_stub {
+
+ struct yes {char s[1];};
+ struct no {char s[2];};
+
+ static yes foo(DEST*);
+ static no foo(...);
+
+ enum {result = (sizeof(foo((SRC*)0)) == sizeof(yes) ? 1 : 0)};
+ };
+
+ template <bool> struct enable_if {};
+ template <> struct enable_if<true> {
+ typedef empty result;
+ };
+
+ template <typename DEST, typename SRC>
+ struct is_convertible : public enable_if<is_convertible_stub<DEST,SRC>::result > {
+ };
+ }
+
+// ------------------------------
+// Small replacement for boost::shared_ptr, not threadsafe because no
+// atomic reference counter is in use.
+// ------------------------------
+template <class T>
+class shared_ptr
+{
+ template <typename TT> friend class shared_ptr;
+
+ template<class TT, class U> friend shared_ptr<TT> static_pointer_cast (shared_ptr<U> ptr);
+ template<class TT, class U> friend shared_ptr<TT> dynamic_pointer_cast (shared_ptr<U> ptr);
+ template<class TT, class U> friend shared_ptr<TT> const_pointer_cast (shared_ptr<U> ptr);
+
+ template<class TT> friend bool operator== (const shared_ptr<TT>& a, const shared_ptr<TT>& b);
+ template<class TT> friend bool operator!= (const shared_ptr<TT>& a, const shared_ptr<TT>& b);
+ template<class TT> friend bool operator< (const shared_ptr<TT>& a, const shared_ptr<TT>& b);
+
+public:
+
+ typedef T element_type;
+
+public:
+
+ // provide a default constructor
+ shared_ptr()
+ : ptr()
+ , ctr(NULL)
+ {
+ }
+
+ // construction from an existing object of type T
+ explicit shared_ptr(T* ptr)
+ : ptr(ptr)
+ , ctr(ptr ? new detail::controller() : NULL)
+ {
+ }
+
+ shared_ptr(const shared_ptr& r)
+ : ptr(r.ptr)
+ , ctr(r.ctr ? r.ctr->incref() : NULL)
+ {
+ }
+
+ template <typename Y>
+ shared_ptr(const shared_ptr<Y>& r,typename detail::is_convertible<T,Y>::result = detail::empty())
+ : ptr(r.ptr)
+ , ctr(r.ctr ? r.ctr->incref() : NULL)
+ {
+ }
+
+ // automatic destruction of the wrapped object when all
+ // references are freed.
+ ~shared_ptr() {
+ if (ctr) {
+ ctr = ctr->decref(ptr);
+ }
+ }
+
+ shared_ptr& operator=(const shared_ptr& r) {
+ if (this == &r) {
+ return *this;
+ }
+ if (ctr) {
+ ctr->decref(ptr);
+ }
+ ptr = r.ptr;
+ ctr = ptr?r.ctr->incref():NULL;
+ return *this;
+ }
+
+ template <typename Y>
+ shared_ptr& operator=(const shared_ptr<Y>& r) {
+ if (this == &r) {
+ return *this;
+ }
+ if (ctr) {
+ ctr->decref(ptr);
+ }
+ ptr = r.ptr;
+ ctr = ptr?r.ctr->incref():NULL;
+ return *this;
+ }
+
+ // pointer access
+ inline operator T*() {
+ return ptr;
+ }
+
+ inline T* operator-> () const {
+ return ptr;
+ }
+
+ // standard semantics
+ inline T* get() {
+ return ptr;
+ }
+
+ inline const T* get() const {
+ return ptr;
+ }
+
+ inline operator bool () const {
+ return ptr != NULL;
+ }
+
+ inline bool unique() const {
+ return use_count() == 1;
+ }
+
+ inline long use_count() const {
+ return ctr->get();
+ }
+
+ inline void reset (T* t = 0) {
+ if (ctr) {
+ ctr->decref(ptr);
+ }
+ ptr = t;
+ ctr = ptr?new detail::controller():NULL;
+ }
+
+ void swap(shared_ptr & b) {
+ std::swap(ptr, b.ptr);
+ std::swap(ctr, b.ctr);
+ }
+
+private:
+
+
+ // for use by the various xxx_pointer_cast helper templates
+ explicit shared_ptr(T* ptr, detail::controller* ctr)
+ : ptr(ptr)
+ , ctr(ctr->incref())
+ {
+ }
+
+private:
+
+ // encapsulated object pointer
+ T* ptr;
+
+ // control block
+ detail::controller* ctr;
+};
+
+template<class T>
+inline void swap(shared_ptr<T> & a, shared_ptr<T> & b)
+{
+ a.swap(b);
+}
+
+template<class T>
+bool operator== (const shared_ptr<T>& a, const shared_ptr<T>& b) {
+ return a.ptr == b.ptr;
+}
+template<class T>
+bool operator!= (const shared_ptr<T>& a, const shared_ptr<T>& b) {
+ return a.ptr != b.ptr;
+}
+
+template<class T>
+bool operator< (const shared_ptr<T>& a, const shared_ptr<T>& b) {
+ return a.ptr < b.ptr;
+}
+
+
+template<class T, class U>
+inline shared_ptr<T> static_pointer_cast( shared_ptr<U> ptr)
+{
+ return shared_ptr<T>(static_cast<T*>(ptr.ptr),ptr.ctr);
+}
+
+template<class T, class U>
+inline shared_ptr<T> dynamic_pointer_cast( shared_ptr<U> ptr)
+{
+ return shared_ptr<T>(dynamic_cast<T*>(ptr.ptr),ptr.ctr);
+}
+
+template<class T, class U>
+inline shared_ptr<T> const_pointer_cast( shared_ptr<U> ptr)
+{
+ return shared_ptr<T>(const_cast<T*>(ptr.ptr),ptr.ctr);
+}
+
+
+
+} // end of namespace boost
+
+#else
+# error "shared_ptr.h was already included"
+#endif
+#endif // INCLUDED_AI_BOOST_SCOPED_PTR
diff --git a/src/3rdparty/assimp/code/BoostWorkaround/boost/static_assert.hpp b/src/3rdparty/assimp/code/BoostWorkaround/boost/static_assert.hpp
new file mode 100644
index 000000000..4b6489a79
--- /dev/null
+++ b/src/3rdparty/assimp/code/BoostWorkaround/boost/static_assert.hpp
@@ -0,0 +1,20 @@
+
+#ifndef AI_BOOST_STATIC_ASSERT_INCLUDED
+#define AI_BOOST_STATIC_ASSERT_INCLUDED
+
+#ifndef BOOST_STATIC_ASSERT
+
+namespace boost {
+ namespace detail {
+
+ template <bool b> class static_assertion_failure;
+ template <> class static_assertion_failure<true> {};
+ }
+}
+
+
+#define BOOST_STATIC_ASSERT(eval) \
+{boost::detail::static_assertion_failure<(eval)> assert_dummy;assert_dummy;}
+
+#endif
+#endif // !! AI_BOOST_STATIC_ASSERT_INCLUDED
diff --git a/src/3rdparty/assimp/code/BoostWorkaround/boost/timer.hpp b/src/3rdparty/assimp/code/BoostWorkaround/boost/timer.hpp
new file mode 100644
index 000000000..df93b25f6
--- /dev/null
+++ b/src/3rdparty/assimp/code/BoostWorkaround/boost/timer.hpp
@@ -0,0 +1,72 @@
+// boost timer.hpp header file ---------------------------------------------//
+
+// Copyright Beman Dawes 1994-99. Distributed under the Boost
+// Software License, Version 1.0. (See accompanying file
+// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See http://www.boost.org/libs/timer for documentation.
+
+// Revision History
+// 01 Apr 01 Modified to use new <boost/limits.hpp> header. (JMaddock)
+// 12 Jan 01 Change to inline implementation to allow use without library
+// builds. See docs for more rationale. (Beman Dawes)
+// 25 Sep 99 elapsed_max() and elapsed_min() added (John Maddock)
+// 16 Jul 99 Second beta
+// 6 Jul 99 Initial boost version
+
+#ifndef BOOST_TIMER_HPP
+#define BOOST_TIMER_HPP
+
+//#include <boost/config.hpp>
+#include <ctime>
+//#include <boost/limits.hpp>
+
+# ifdef BOOST_NO_STDC_NAMESPACE
+ namespace std { using ::clock_t; using ::clock; }
+# endif
+
+
+namespace boost {
+
+// timer -------------------------------------------------------------------//
+
+// A timer object measures elapsed time.
+
+// It is recommended that implementations measure wall clock rather than CPU
+// time since the intended use is performance measurement on systems where
+// total elapsed time is more important than just process or CPU time.
+
+// Warnings: The maximum measurable elapsed time may well be only 596.5+ hours
+// due to implementation limitations. The accuracy of timings depends on the
+// accuracy of timing information provided by the underlying platform, and
+// this varies a great deal from platform to platform.
+
+class timer
+{
+ public:
+ timer() { _start_time = std::clock(); } // postcondition: elapsed()==0
+// timer( const timer& src ); // post: elapsed()==src.elapsed()
+// ~timer(){}
+// timer& operator=( const timer& src ); // post: elapsed()==src.elapsed()
+ void restart() { _start_time = std::clock(); } // post: elapsed()==0
+ double elapsed() const // return elapsed time in seconds
+ { return double(std::clock() - _start_time) / CLOCKS_PER_SEC; }
+
+ double elapsed_max() const // return estimated maximum value for elapsed()
+ // Portability warning: elapsed_max() may return too high a value on systems
+ // where std::clock_t overflows or resets at surprising values.
+ {
+ return (double((std::numeric_limits<std::clock_t>::max)())
+ - double(_start_time)) / double(CLOCKS_PER_SEC);
+ }
+
+ double elapsed_min() const // return minimum value for elapsed()
+ { return double(1)/double(CLOCKS_PER_SEC); }
+
+ private:
+ std::clock_t _start_time;
+}; // timer
+
+} // namespace boost
+
+#endif // BOOST_TIMER_HPP
diff --git a/src/3rdparty/assimp/code/BoostWorkaround/boost/tuple/tuple.hpp b/src/3rdparty/assimp/code/BoostWorkaround/boost/tuple/tuple.hpp
new file mode 100644
index 000000000..2245a8db1
--- /dev/null
+++ b/src/3rdparty/assimp/code/BoostWorkaround/boost/tuple/tuple.hpp
@@ -0,0 +1,285 @@
+// A very small replacement for boost::tuple
+// (c) Alexander Gessler, 2008 [alexander.gessler@gmx.net]
+
+#ifndef BOOST_TUPLE_INCLUDED
+#define BOOST_TUPLE_INCLUDED
+
+namespace boost {
+ namespace detail {
+
+ // Represents an empty tuple slot (up to 5 supported)
+ struct nulltype {};
+
+ // For readable error messages
+ struct tuple_component_idx_out_of_bounds;
+
+ // To share some code for the const/nonconst versions of the getters
+ template <bool b, typename T>
+ struct ConstIf {
+ typedef T t;
+ };
+
+ template <typename T>
+ struct ConstIf<true,T> {
+ typedef const T t;
+ };
+
+ // Predeclare some stuff
+ template <typename, unsigned, typename, bool, unsigned> struct value_getter;
+
+ // Helper to obtain the type of a tuple element
+ template <typename T, unsigned NIDX, typename TNEXT, unsigned N /*= 0*/>
+ struct type_getter {
+ typedef type_getter<typename TNEXT::type,NIDX+1,typename TNEXT::next_type,N> next_elem_getter;
+ typedef typename next_elem_getter::type type;
+ };
+
+ template <typename T, unsigned NIDX, typename TNEXT >
+ struct type_getter <T,NIDX,TNEXT,NIDX> {
+ typedef T type;
+ };
+
+ // Base class for all explicit specializations of list_elem
+ template <typename T, unsigned NIDX, typename TNEXT >
+ struct list_elem_base {
+
+ // Store template parameters
+ typedef TNEXT next_type;
+ typedef T type;
+
+ static const unsigned nidx = NIDX;
+ };
+
+ // Represents an element in the tuple component list
+ template <typename T, unsigned NIDX, typename TNEXT >
+ struct list_elem : list_elem_base<T,NIDX,TNEXT>{
+
+ // Real members
+ T me;
+ TNEXT next;
+
+ // Get the value of a specific tuple element
+ template <unsigned N>
+ typename type_getter<T,NIDX,TNEXT,N>::type& get () {
+ value_getter <T,NIDX,TNEXT,false,N> s;
+ return s(*this);
+ }
+
+ // Get the value of a specific tuple element
+ template <unsigned N>
+ const typename type_getter<T,NIDX,TNEXT,N>::type& get () const {
+ value_getter <T,NIDX,TNEXT,true,N> s;
+ return s(*this);
+ }
+
+ // Explicit cast
+ template <typename T2, typename TNEXT2 >
+ operator list_elem<T2,NIDX,TNEXT2> () const {
+ list_elem<T2,NIDX,TNEXT2> ret;
+ ret.me = (T2)me;
+ ret.next = next;
+ return ret;
+ }
+
+ // Recursively compare two elements (last element returns always true)
+ bool operator == (const list_elem& s) const {
+ return (me == s.me && next == s.next);
+ }
+ };
+
+ // Represents a non-used tuple element - the very last element processed
+ template <typename TNEXT, unsigned NIDX >
+ struct list_elem<nulltype,NIDX,TNEXT> : list_elem_base<nulltype,NIDX,TNEXT> {
+ template <unsigned N, bool IS_CONST = true> struct value_getter {
+ /* just dummy members to produce readable error messages */
+ tuple_component_idx_out_of_bounds operator () (typename ConstIf<IS_CONST,list_elem>::t& me);
+ };
+ template <unsigned N> struct type_getter {
+ /* just dummy members to produce readable error messages */
+ typedef tuple_component_idx_out_of_bounds type;
+ };
+
+ // dummy
+ list_elem& operator = (const list_elem& other) {
+ return *this;
+ }
+
+ // dummy
+ bool operator == (const list_elem& other) {
+ return true;
+ }
+ };
+
+ // Represents the absolute end of the list
+ typedef list_elem<nulltype,0,int> list_end;
+
+ // Helper obtain to query the value of a tuple element
+ // NOTE: This can't be a nested class as the compiler won't accept a full or
+ // partial specialization of a nested class of a non-specialized template
+ template <typename T, unsigned NIDX, typename TNEXT, bool IS_CONST, unsigned N>
+ struct value_getter {
+
+ // calling list_elem
+ typedef list_elem<T,NIDX,TNEXT> outer_elem;
+
+ // typedef for the getter for next element
+ typedef value_getter<typename TNEXT::type,NIDX+1,typename TNEXT::next_type,
+ IS_CONST, N> next_value_getter;
+
+ typename ConstIf<IS_CONST,typename type_getter<T,NIDX,TNEXT,N>::type>::t&
+ operator () (typename ConstIf<IS_CONST,outer_elem >::t& me) {
+
+ next_value_getter s;
+ return s(me.next);
+ }
+ };
+
+ template <typename T, unsigned NIDX, typename TNEXT, bool IS_CONST>
+ struct value_getter <T,NIDX,TNEXT,IS_CONST,NIDX> {
+ typedef list_elem<T,NIDX,TNEXT> outer_elem;
+
+ typename ConstIf<IS_CONST,T>::t& operator () (typename ConstIf<IS_CONST,outer_elem >::t& me) {
+ return me.me;
+ }
+ };
+ };
+
+ // A very minimal implementation for up to 5 elements
+ template <typename T0 = detail::nulltype,
+ typename T1 = detail::nulltype,
+ typename T2 = detail::nulltype,
+ typename T3 = detail::nulltype,
+ typename T4 = detail::nulltype>
+ class tuple {
+
+ template <typename T0b,
+ typename T1b,
+ typename T2b,
+ typename T3b,
+ typename T4b >
+ friend class tuple;
+
+ private:
+
+ typedef detail::list_elem<T0,0,
+ detail::list_elem<T1,1,
+ detail::list_elem<T2,2,
+ detail::list_elem<T3,3,
+ detail::list_elem<T4,4,
+ detail::list_end > > > > > very_long;
+
+ very_long m;
+
+ public:
+
+ // Get a specific tuple element
+ template <unsigned N>
+ typename detail::type_getter<T0,0,typename very_long::next_type, N>::type& get () {
+ return m.get<N>();
+ }
+
+ // ... and the const version
+ template <unsigned N>
+ typename const detail::type_getter<T0,0,typename very_long::next_type, N>::type& get () const {
+ return m.get<N>();
+ }
+
+
+ // comparison operators
+ bool operator== (const tuple& other) const {
+ return m == other.m;
+ }
+
+ // ... and the other way round
+ bool operator!= (const tuple& other) const {
+ return !(m == other.m);
+ }
+
+ // cast to another tuple - all single elements must be convertible
+ template < typename T0, typename T1,typename T2,
+ typename T3, typename T4>
+
+ operator tuple <T0,T1,T2,T3,T4> () const {
+ tuple <T0,T1,T2,T3,T4> s;
+ s.m = (tuple <T0,T1,T2,T3,T4>::very_long)m;
+ return s;
+ }
+ };
+
+ // Another way to access an element ...
+ template <unsigned N,typename T0,typename T1,typename T2,typename T3,typename T4>
+ inline typename tuple<T0,T1,T2,T3,T4>::very_long::type_getter<N>::type& get (
+ tuple<T0,T1,T2,T3,T4>& m) {
+ return m.get<N>();
+ }
+
+ // ... and the const version
+ template <unsigned N,typename T0,typename T1,typename T2,typename T3,typename T4>
+ inline const typename tuple<T0,T1,T2,T3,T4>::very_long::type_getter<N>::type& get (
+ const tuple<T0,T1,T2,T3,T4>& m) {
+ return m.get<N>();
+ }
+
+ // Constructs a tuple with 5 elements
+ template <typename T0,typename T1,typename T2,typename T3,typename T4>
+ inline tuple <T0,T1,T2,T3,T4> make_tuple (const T0& t0,
+ const T1& t1,const T2& t2,const T3& t3,const T4& t4) {
+
+ tuple <T0,T1,T2,T3,T4> t;
+ t.get<0>() = t0;
+ t.get<1>() = t1;
+ t.get<2>() = t2;
+ t.get<3>() = t3;
+ t.get<4>() = t4;
+ return t;
+ }
+
+ // Constructs a tuple with 4 elements
+ template <typename T0,typename T1,typename T2,typename T3>
+ inline tuple <T0,T1,T2,T3> make_tuple (const T0& t0,
+ const T1& t1,const T2& t2,const T3& t3) {
+ tuple <T0,T1,T2,T3> t;
+ t.get<0>() = t0;
+ t.get<1>() = t1;
+ t.get<2>() = t2;
+ t.get<3>() = t3;
+ return t;
+ }
+
+ // Constructs a tuple with 3 elements
+ template <typename T0,typename T1,typename T2>
+ inline tuple <T0,T1,T2> make_tuple (const T0& t0,
+ const T1& t1,const T2& t2) {
+ tuple <T0,T1,T2> t;
+ t.get<0>() = t0;
+ t.get<1>() = t1;
+ t.get<2>() = t2;
+ return t;
+ }
+
+ // Constructs a tuple with 2 elements (TODO: use std::pair instead!)
+ template <typename T0,typename T1>
+ inline tuple <T0,T1> make_tuple (const T0& t0,
+ const T1& t1) {
+ tuple <T0,T1> t;
+ t.get<0>() = t0;
+ t.get<1>() = t1;
+ return t;
+ }
+
+ // Constructs a tuple with 1 elements (no comment ...)
+ template <typename T0>
+ inline tuple <T0> make_tuple (const T0& t0) {
+ tuple <T0> t;
+ t.get<0>() = t0;
+ return t;
+ }
+
+ // Constructs a tuple with 0 elements (ehm? Try http://www.promillerechner.net)
+ inline tuple <> make_tuple () {
+ tuple <> t;
+ return t;
+ }
+};
+
+#endif // !! BOOST_TUPLE_INCLUDED
diff --git a/src/3rdparty/assimp/code/ByteSwap.h b/src/3rdparty/assimp/code/ByteSwap.h
new file mode 100644
index 000000000..c61b96df4
--- /dev/null
+++ b/src/3rdparty/assimp/code/ByteSwap.h
@@ -0,0 +1,245 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Helper class tp perform various byte oder swappings
+ (e.g. little to big endian) */
+#ifndef AI_BYTESWAP_H_INC
+#define AI_BYTESWAP_H_INC
+
+#include "../include/aiAssert.h"
+#include "../include/aiTypes.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() {}
+
+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;
+ }
+};
+
+} // Namespace Assimp
+
+
+// --------------------------------------------------------------------------------------
+// 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
+
+
+
+#endif //!! AI_BYTESWAP_H_INC
diff --git a/src/3rdparty/assimp/code/COBLoader.cpp b/src/3rdparty/assimp/code/COBLoader.cpp
new file mode 100644
index 000000000..020de05d1
--- /dev/null
+++ b/src/3rdparty/assimp/code/COBLoader.cpp
@@ -0,0 +1,1278 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file COBLoader.cpp
+ * @brief Implementation of the TrueSpace COB/SCN importer class.
+ */
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_COB_IMPORTER
+#include "COBLoader.h"
+#include "COBScene.h"
+
+#include "StreamReader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+#include "LineSplitter.h"
+#include "TinyFormatter.h"
+
+using namespace Assimp;
+using namespace Assimp::COB;
+using namespace Assimp::Formatter;
+
+#define for_each BOOST_FOREACH
+
+
+static const float units[] = {
+ 1000.f,
+ 100.f,
+ 1.f,
+ 0.001f,
+ 1.f/0.0254f,
+ 1.f/0.3048f,
+ 1.f/0.9144f,
+ 1.f/1609.344f
+};
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+COBImporter::COBImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+COBImporter::~COBImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool COBImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string& extension = GetExtension(pFile);
+ if (extension == "cob" || extension == "scn") {
+ return true;
+ }
+
+ else if ((!extension.length() || checkSig) && pIOHandler) {
+ const char* tokens[] = {"Caligary"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// List all extensions handled by this loader
+void COBImporter::GetExtensionList(std::set<std::string>& app)
+{
+ app.insert("cob");
+ app.insert("scn");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void COBImporter::SetupProperties(const Importer*)
+{
+ // nothing to be done for the moment
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void COBImporter::ThrowException(const std::string& msg)
+{
+ throw DeadlyImportError("COB: "+msg);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void COBImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ COB::Scene scene;
+ boost::scoped_ptr<StreamReaderLE> stream(new StreamReaderLE( pIOHandler->Open(pFile,"rb")) );
+
+ // check header
+ char head[32];
+ stream->CopyAndAdvance(head,32);
+ if (strncmp(head,"Caligari ",9)) {
+ ThrowException("Could not found magic id: `Caligari`");
+ }
+
+ DefaultLogger::get()->info("File format tag: "+std::string(head+9,6));
+ void (COBImporter::* load)(Scene&,StreamReaderLE*)= head[15]=='A'?&COBImporter::ReadAsciiFile:&COBImporter::ReadBinaryFile;
+ if (head[16]!='L') {
+ ThrowException("File is big-endian, which is not supported");
+ }
+
+ // load data into intermediate structures
+ (this->*load)(scene,stream.get());
+ if (scene.nodes.empty()) {
+ ThrowException("No nodes loaded");
+ }
+
+ // sort faces by material indices
+ for_each(boost::shared_ptr< Node >& n,scene.nodes) {
+ if (n->type == Node::TYPE_MESH) {
+ Mesh& mesh = (Mesh&)(*n.get());
+ for_each(Face& f,mesh.faces) {
+ mesh.temp_map[f.material].push_back(&f);
+ }
+ }
+ }
+
+ // count meshes
+ for_each(boost::shared_ptr< Node >& n,scene.nodes) {
+ if (n->type == Node::TYPE_MESH) {
+ Mesh& mesh = (Mesh&)(*n.get());
+ if (mesh.vertex_positions.size() && mesh.texture_coords.size()) {
+ pScene->mNumMeshes += mesh.temp_map.size();
+ }
+ }
+ }
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]();
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes]();
+ pScene->mNumMeshes = 0;
+
+ // count lights and cameras
+ for_each(boost::shared_ptr< Node >& n,scene.nodes) {
+ if (n->type == Node::TYPE_LIGHT) {
+ ++pScene->mNumLights;
+ }
+ else if (n->type == Node::TYPE_CAMERA) {
+ ++pScene->mNumCameras;
+ }
+ }
+
+ if (pScene->mNumLights) {
+ pScene->mLights = new aiLight*[pScene->mNumLights]();
+ }
+ if (pScene->mNumCameras) {
+ pScene->mCameras = new aiCamera*[pScene->mNumCameras]();
+ }
+ pScene->mNumLights = pScene->mNumCameras = 0;
+
+ // resolve parents by their IDs and build the output graph
+ boost::scoped_ptr<Node> root(new Group());
+ for (size_t n = 0; n < scene.nodes.size(); ++n) {
+ const Node& nn = *scene.nodes[n].get();
+ if (nn.parent_id==0) {
+ root->temp_children.push_back(&nn);
+ }
+
+ for (size_t m = n; m < scene.nodes.size(); ++m) {
+ const Node& mm = *scene.nodes[m].get();
+ if (mm.parent_id == nn.id) {
+ nn.temp_children.push_back(&mm);
+ }
+ }
+ }
+
+ pScene->mRootNode = BuildNodes(*root.get(),scene,pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+void ConvertTexture(boost::shared_ptr< Texture > tex, MaterialHelper* out, aiTextureType type)
+{
+ const aiString path( tex->path );
+ out->AddProperty(&path,AI_MATKEY_TEXTURE(type,0));
+ out->AddProperty(&tex->transform,1,AI_MATKEY_UVTRANSFORM(type,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+aiNode* COBImporter::BuildNodes(const Node& root,const Scene& scin,aiScene* fill)
+{
+ aiNode* nd = new aiNode();
+ nd->mName.Set(root.name);
+ nd->mTransformation = root.transform;
+
+ // Note to everybody believing Voodoo is appropriate here:
+ // I know polymorphism, run as fast as you can ;-)
+ if (Node::TYPE_MESH == root.type) {
+ const Mesh& ndmesh = (const Mesh&)(root);
+ if (ndmesh.vertex_positions.size() && ndmesh.texture_coords.size()) {
+
+ typedef std::pair<unsigned int,Mesh::FaceRefList> Entry;
+ for_each(const Entry& reflist,ndmesh.temp_map) {
+ { // create mesh
+ size_t n = 0;
+ for_each(Face* f, reflist.second) {
+ n += f->indices.size();
+ }
+ if (!n) {
+ continue;
+ }
+ aiMesh* outmesh = fill->mMeshes[fill->mNumMeshes++] = new aiMesh();
+ ++nd->mNumMeshes;
+
+ outmesh->mVertices = new aiVector3D[n];
+ outmesh->mTextureCoords[0] = new aiVector3D[n];
+
+ outmesh->mFaces = new aiFace[reflist.second.size()]();
+ for_each(Face* f, reflist.second) {
+ if (f->indices.empty()) {
+ continue;
+ }
+
+ aiFace& fout = outmesh->mFaces[outmesh->mNumFaces++];
+ fout.mIndices = new unsigned int[f->indices.size()];
+
+ for_each(VertexIndex& v, f->indices) {
+ if (v.pos_idx >= ndmesh.vertex_positions.size()) {
+ ThrowException("Position index out of range");
+ }
+ if (v.uv_idx >= ndmesh.texture_coords.size()) {
+ ThrowException("UV index out of range");
+ }
+ outmesh->mVertices[outmesh->mNumVertices] = ndmesh.vertex_positions[ v.pos_idx ];
+ outmesh->mTextureCoords[0][outmesh->mNumVertices] = aiVector3D(
+ ndmesh.texture_coords[ v.uv_idx ].x,
+ ndmesh.texture_coords[ v.uv_idx ].y,
+ 0.f
+ );
+
+ fout.mIndices[fout.mNumIndices++] = outmesh->mNumVertices++;
+ }
+ }
+ outmesh->mMaterialIndex = fill->mNumMaterials;
+ }{ // create material
+ const Material* min = NULL;
+ for_each(const Material& m, scin.materials) {
+ if (m.parent_id == ndmesh.id && m.matnum == reflist.first) {
+ min = &m;
+ break;
+ }
+ }
+ boost::scoped_ptr<const Material> defmat;
+ if (!min) {
+ DefaultLogger::get()->debug(format()<<"Could not resolve material index "
+ <<reflist.first<<" - creating default material for this slot");
+
+ defmat.reset(min=new Material());
+ }
+
+ MaterialHelper* mat = new MaterialHelper();
+ fill->mMaterials[fill->mNumMaterials++] = mat;
+
+ const aiString s(format("#mat_")<<fill->mNumMeshes<<"_"<<min->matnum);
+ mat->AddProperty(&s,AI_MATKEY_NAME);
+
+ if (int tmp = ndmesh.draw_flags & Mesh::WIRED ? 1 : 0) {
+ mat->AddProperty(&tmp,1,AI_MATKEY_ENABLE_WIREFRAME);
+ }
+
+ { int shader;
+ switch(min->shader)
+ {
+ case Material::FLAT:
+ shader = aiShadingMode_Gouraud;
+ break;
+
+ case Material::PHONG:
+ shader = aiShadingMode_Phong;
+ break;
+
+ case Material::METAL:
+ shader = aiShadingMode_CookTorrance;
+ break;
+
+ default:
+ ai_assert(false); // shouldn't be here
+ }
+ mat->AddProperty(&shader,1,AI_MATKEY_SHADING_MODEL);
+ if (shader != aiShadingMode_Gouraud) {
+ mat->AddProperty(&min->exp,1,AI_MATKEY_SHININESS);
+ }
+ }
+
+ mat->AddProperty(&min->ior,1,AI_MATKEY_REFRACTI);
+ mat->AddProperty(&min->rgb,1,AI_MATKEY_COLOR_DIFFUSE);
+
+ aiColor3D c = aiColor3D(min->rgb)*min->ks;
+ mat->AddProperty(&c,1,AI_MATKEY_COLOR_SPECULAR);
+
+ c = aiColor3D(min->rgb)*min->ka;
+ mat->AddProperty(&c,1,AI_MATKEY_COLOR_AMBIENT);
+
+ // convert textures if some exist.
+ if (min->tex_color) {
+ ConvertTexture(min->tex_color,mat,aiTextureType_DIFFUSE);
+ }
+ if (min->tex_env) {
+ ConvertTexture(min->tex_env ,mat,aiTextureType_UNKNOWN);
+ }
+ if (min->tex_bump) {
+ ConvertTexture(min->tex_bump ,mat,aiTextureType_HEIGHT);
+ }
+ }
+ }
+ }
+ }
+ else if (Node::TYPE_LIGHT == root.type) {
+ const Light& ndlight = (const Light&)(root);
+ aiLight* outlight = fill->mLights[fill->mNumLights++] = new aiLight();
+
+ outlight->mName.Set(ndlight.name);
+ outlight->mColorDiffuse = outlight->mColorAmbient = outlight->mColorSpecular = ndlight.color;
+
+ outlight->mAngleOuterCone = AI_DEG_TO_RAD(ndlight.angle);
+ outlight->mAngleInnerCone = AI_DEG_TO_RAD(ndlight.inner_angle);
+
+ // XXX
+ outlight->mType = ndlight.ltype==Light::SPOT ? aiLightSource_SPOT : aiLightSource_DIRECTIONAL;
+ }
+ else if (Node::TYPE_CAMERA == root.type) {
+ const Camera& ndcam = (const Camera&)(root);
+ aiCamera* outcam = fill->mCameras[fill->mNumCameras++] = new aiCamera();
+
+ outcam->mName.Set(ndcam.name);
+ }
+
+ // add meshes
+ if (nd->mNumMeshes) { // mMeshes must be NULL if count is 0
+ nd->mMeshes = new unsigned int[nd->mNumMeshes];
+ for (unsigned int i = 0; i < nd->mNumMeshes;++i) {
+ nd->mMeshes[i] = fill->mNumMeshes-i-1;
+ }
+ }
+
+ // add children recursively
+ nd->mChildren = new aiNode*[root.temp_children.size()]();
+ for_each(const Node* n, root.temp_children) {
+ (nd->mChildren[nd->mNumChildren++] = BuildNodes(*n,scin,fill))->mParent = nd;
+ }
+
+ return nd;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read an ASCII file into the given scene data structure
+void COBImporter::ReadAsciiFile(Scene& out, StreamReaderLE* stream)
+{
+ ChunkInfo ci;
+ for (LineSplitter splitter(*stream);splitter;++splitter) {
+
+ // add all chunks to be recognized here. /else ../ ommitted intentionally.
+ if (splitter.match_start("PolH ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadPolH_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("BitM ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadBitM_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Mat1 ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadMat1_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Grou ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadGrou_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Lght ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadLght_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Came ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadCame_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Bone ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadBone_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Chan ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadChan_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Unit ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadUnit_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("END ")) {
+ // we don't need this, but I guess there is a reason this
+ // chunk has been implemented into COB for.
+ return;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadChunkInfo_Ascii(ChunkInfo& out, const LineSplitter& splitter)
+{
+ const char* all_tokens[8];
+ splitter.get_tokens(all_tokens);
+
+ out.version = (all_tokens[1][1]-'0')*100+(all_tokens[1][3]-'0')*10+(all_tokens[1][4]-'0');
+ out.id = strtol10(all_tokens[3]);
+ out.parent_id = strtol10(all_tokens[5]);
+ out.size = strtol10s(all_tokens[7]);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::UnsupportedChunk_Ascii(LineSplitter& splitter, const ChunkInfo& nfo, const char* name)
+{
+ const std::string error = format("Encountered unsupported chunk: ") << name <<
+ " [version: "<<nfo.version<<", size: "<<nfo.size<<"]";
+
+ // we can recover if the chunk size was specified.
+ if (nfo.size != static_cast<unsigned int>(-1)) {
+ DefaultLogger::get()->error(error);
+
+ // (HACK) - our current position in the stream is the beginning of the
+ // head line of the next chunk. That's fine, but the caller is going
+ // to call ++ on `splitter`, which we need to swallow to avoid
+ // missing the next line.
+ splitter.get_stream().IncPtr(nfo.size);
+ splitter.swallow_next_increment();
+ }
+ else ThrowException(error);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogWarn_Ascii(const LineSplitter& splitter, const format& message) {
+ LogWarn_Ascii(message << " [at line "<< splitter.get_index()<<"]");
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogError_Ascii(const LineSplitter& splitter, const format& message) {
+ LogError_Ascii(message << " [at line "<< splitter.get_index()<<"]");
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogInfo_Ascii(const LineSplitter& splitter, const format& message) {
+ LogInfo_Ascii(message << " [at line "<< splitter.get_index()<<"]");
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogDebug_Ascii(const LineSplitter& splitter, const format& message) {
+ LogDebug_Ascii(message << " [at line "<< splitter.get_index()<<"]");
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogWarn_Ascii(const Formatter::format& message) {
+ DefaultLogger::get()->warn(std::string("COB: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogError_Ascii(const Formatter::format& message) {
+ DefaultLogger::get()->error(std::string("COB: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogInfo_Ascii(const Formatter::format& message) {
+ DefaultLogger::get()->info(std::string("COB: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogDebug_Ascii(const Formatter::format& message) {
+ DefaultLogger::get()->debug(std::string("COB: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadBasicNodeInfo_Ascii(Node& msh, LineSplitter& splitter, const ChunkInfo&)
+{
+ for (;splitter;++splitter) {
+ if (splitter.match_start("Name")) {
+ msh.name = std::string(splitter[1]);
+
+ // make nice names by merging the dupe count
+ std::replace(msh.name.begin(),msh.name.end(),
+ ',','_');
+ }
+ else if (splitter.match_start("Transform")) {
+ for (unsigned int y = 0; y < 4 && ++splitter; ++y) {
+ const char* s = splitter->c_str();
+ for (unsigned int x = 0; x < 4; ++x) {
+ SkipSpaces(&s);
+ msh.transform[y][x] = fast_atof(&s);
+ }
+ }
+ // we need the transform chunk, so we won't return until we have it.
+ return;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+void COBImporter::ReadFloat3Tuple_Ascii(T& fill, const char** in)
+{
+ const char* rgb = *in;
+ for (unsigned int i = 0; i < 3; ++i) {
+ SkipSpaces(&rgb);
+ if (*rgb == ',')++rgb;
+ SkipSpaces(&rgb);
+
+ fill[i] = fast_atof(&rgb);
+ }
+ *in = rgb;
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadMat1_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 8) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Mat1");
+ }
+
+ ++splitter;
+ if (!splitter.match_start("mat# ")) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `mat#` line in `Mat1` chunk "<<nfo.id);
+ return;
+ }
+
+ out.materials.push_back(Material());
+ Material& mat = out.materials.back();
+ mat = nfo;
+
+ mat.matnum = strtol10(splitter[1]);
+ ++splitter;
+
+ if (!splitter.match_start("shader: ")) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `mat#` line in `Mat1` chunk "<<nfo.id);
+ return;
+ }
+ std::string shader = std::string(splitter[1]);
+ shader = shader.substr(0,shader.find_first_of(" \t"));
+
+ if (shader == "metal") {
+ mat.shader = Material::METAL;
+ }
+ else if (shader == "phong") {
+ mat.shader = Material::PHONG;
+ }
+ else if (shader != "flat") {
+ LogWarn_Ascii(splitter,format()<<
+ "Unknown value for `shader` in `Mat1` chunk "<<nfo.id);
+ }
+
+ ++splitter;
+ if (!splitter.match_start("rgb ")) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `rgb` line in `Mat1` chunk "<<nfo.id);
+ }
+
+ const char* rgb = splitter[1];
+ ReadFloat3Tuple_Ascii(mat.rgb,&rgb);
+
+ ++splitter;
+ if (!splitter.match_start("alpha ")) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `alpha` line in `Mat1` chunk "<<nfo.id);
+ }
+
+ const char* tokens[10];
+ splitter.get_tokens(tokens);
+
+ mat.alpha = fast_atof( tokens[1] );
+ mat.ka = fast_atof( tokens[3] );
+ mat.ks = fast_atof( tokens[5] );
+ mat.exp = fast_atof( tokens[7] );
+ mat.ior = fast_atof( tokens[9] );
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadUnit_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 1) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Unit");
+ }
+ ++splitter;
+ if (!splitter.match_start("Units ")) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `Units` line in `Unit` chunk "<<nfo.id);
+ return;
+ }
+
+ // parent chunks preceede their childs, so we should have the
+ // corresponding chunk already.
+ for_each(boost::shared_ptr< Node >& nd, out.nodes) {
+ if (nd->id == nfo.parent_id) {
+ const unsigned int t=strtol10(splitter[1]);
+
+ nd->unit_scale = t>=sizeof(units)/sizeof(units[0])?(
+ LogWarn_Ascii(splitter,format()<<t<<" is not a valid value for `Units` attribute in `Unit chunk` "<<nfo.id)
+ ,1.f):units[t];
+ return;
+ }
+ }
+ LogWarn_Ascii(splitter,format()<<"`Unit` chunk "<<nfo.id<<" is a child of "
+ <<nfo.parent_id<<" which does not exist");
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadChan_Ascii(Scene&, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 8) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Chan");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadLght_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 8) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Lght");
+ }
+
+ out.nodes.push_back(boost::shared_ptr<Light>(new Light()));
+ Light& msh = (Light&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
+
+ if (splitter.match_start("Infinite ")) {
+ msh.ltype = Light::INFINITE;
+ }
+ else if (splitter.match_start("Local ")) {
+ msh.ltype = Light::LOCAL;
+ }
+ else if (splitter.match_start("Spot ")) {
+ msh.ltype = Light::SPOT;
+ }
+ else {
+ LogWarn_Ascii(splitter,format()<<
+ "Unknown kind of light source in `Lght` chunk "<<nfo.id<<" : "<<*splitter);
+ msh.ltype = Light::SPOT;
+ }
+
+ ++splitter;
+ if (!splitter.match_start("color ")) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `color` line in `Lght` chunk "<<nfo.id);
+ }
+
+ const char* rgb = splitter[1];
+ ReadFloat3Tuple_Ascii(msh.color ,&rgb);
+
+ SkipSpaces(&rgb);
+ if (strncmp(rgb,"cone angle",10)) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `cone angle` entity in `color` line in `Lght` chunk "<<nfo.id);
+ }
+ SkipSpaces(rgb+10,&rgb);
+ msh.angle = fast_atof(&rgb);
+
+ SkipSpaces(&rgb);
+ if (strncmp(rgb,"inner angle",11)) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `inner angle` entity in `color` line in `Lght` chunk "<<nfo.id);
+ }
+ SkipSpaces(rgb+11,&rgb);
+ msh.inner_angle = fast_atof(&rgb);
+
+ // skip the rest for we can't handle this kind of physically-based lighting information.
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadCame_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 2) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Came");
+ }
+
+ out.nodes.push_back(boost::shared_ptr<Camera>(new Camera()));
+ Camera& msh = (Camera&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
+
+ // skip the next line, we don't know this differenciation between a
+ // standard camera and a panoramic camera.
+ ++splitter;
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadBone_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 5) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Bone");
+ }
+
+ out.nodes.push_back(boost::shared_ptr<Bone>(new Bone()));
+ Bone& msh = (Bone&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
+
+ // TODO
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadGrou_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 1) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Grou");
+ }
+
+ out.nodes.push_back(boost::shared_ptr<Group>(new Group()));
+ Group& msh = (Group&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadPolH_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 8) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"PolH");
+ }
+
+ out.nodes.push_back(boost::shared_ptr<Mesh>(new Mesh()));
+ Mesh& msh = (Mesh&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
+
+ // the chunk has a fixed order of components, but some are not interesting of us so
+ // we're just looking for keywords in arbitrary order. The end of the chunk is
+ // either the last `Face` or the `DrawFlags` attribute, depending on the format ver.
+ for (;splitter;++splitter) {
+ if (splitter.match_start("World Vertices")) {
+ const unsigned int cnt = strtol10(splitter[2]);
+ msh.vertex_positions.resize(cnt);
+
+ for (unsigned int cur = 0;cur < cnt && ++splitter;++cur) {
+ const char* s = splitter->c_str();
+
+ aiVector3D& v = msh.vertex_positions[cur];
+
+ SkipSpaces(&s);
+ v.x = fast_atof(&s);
+ SkipSpaces(&s);
+ v.y = fast_atof(&s);
+ SkipSpaces(&s);
+ v.z = fast_atof(&s);
+ }
+ }
+ else if (splitter.match_start("Texture Vertices")) {
+ const unsigned int cnt = strtol10(splitter[2]);
+ msh.texture_coords.resize(cnt);
+
+ for (unsigned int cur = 0;cur < cnt && ++splitter;++cur) {
+ const char* s = splitter->c_str();
+
+ aiVector2D& v = msh.texture_coords[cur];
+
+ SkipSpaces(&s);
+ v.x = fast_atof(&s);
+ SkipSpaces(&s);
+ v.y = fast_atof(&s);
+ }
+ }
+ else if (splitter.match_start("Faces")) {
+ const unsigned int cnt = strtol10(splitter[1]);
+ msh.faces.reserve(cnt);
+
+ for (unsigned int cur = 0; cur < cnt && ++splitter ;++cur) {
+ if (splitter.match_start("Hole")) {
+ LogWarn_Ascii(splitter,"Skipping unsupported `Hole` line");
+ continue;
+ }
+
+ if (!splitter.match_start("Face")) {
+ ThrowException("Expected Face line");
+ }
+
+ msh.faces.push_back(Face());
+ Face& face = msh.faces.back();
+
+ face.indices.resize(strtol10(splitter[2]));
+ face.flags = strtol10(splitter[4]);
+ face.material = strtol10(splitter[6]);
+
+ const char* s = (++splitter)->c_str();
+ for (size_t i = 0; i < face.indices.size(); ++i) {
+ if (!SkipSpaces(&s)) {
+ ThrowException("Expected EOL token in Face entry");
+ }
+ if ('<' != *s++) {
+ ThrowException("Expected < token in Face entry");
+ }
+ face.indices[i].pos_idx = strtol10(s,&s);
+ if (',' != *s++) {
+ ThrowException("Expected , token in Face entry");
+ }
+ face.indices[i].uv_idx = strtol10(s,&s);
+ if ('>' != *s++) {
+ ThrowException("Expected < token in Face entry");
+ }
+ }
+ }
+ if (nfo.version <= 4) {
+ break;
+ }
+ }
+ else if (splitter.match_start("DrawFlags")) {
+ msh.draw_flags = strtol10(splitter[1]);
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadBitM_Ascii(Scene&, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 1) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"BitM");
+ }
+/*
+ "\nThumbNailHdrSize %ld"
+ "\nThumbHeader: %02hx 02hx %02hx "
+ "\nColorBufSize %ld"
+ "\nColorBufZipSize %ld"
+ "\nZippedThumbnail: %02hx 02hx %02hx "
+*/
+
+ const unsigned int head = strtol10((++splitter)[1]);
+ if (head != sizeof(Bitmap::BitmapHeader)) {
+ LogWarn_Ascii(splitter,"Unexpected ThumbNailHdrSize, skipping this chunk");
+ return;
+ }
+
+ /*union {
+ Bitmap::BitmapHeader data;
+ char opaq[sizeof Bitmap::BitmapHeader()];
+ };*/
+// ReadHexOctets(opaq,head,(++splitter)[1]);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadString_Binary(std::string& out, StreamReaderLE& reader)
+{
+ out.resize( reader.GetI2());
+ for_each(char& c,out) {
+ c = reader.GetI1();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadBasicNodeInfo_Binary(Node& msh, StreamReaderLE& reader, const ChunkInfo&)
+{
+ const unsigned int dupes = reader.GetI2();
+ ReadString_Binary(msh.name,reader);
+
+ msh.name = format(msh.name)<<'_'<<dupes;
+
+ // skip local axes for the moment
+ reader.IncPtr(48);
+
+ msh.transform = aiMatrix4x4();
+ for (unsigned int y = 0; y < 3; ++y) {
+ for (unsigned int x =0; x < 4; ++x) {
+ msh.transform[y][x] = reader.GetF4();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::UnsupportedChunk_Binary( StreamReaderLE& reader, const ChunkInfo& nfo, const char* name)
+{
+ const std::string error = format("Encountered unsupported chunk: ") << name <<
+ " [version: "<<nfo.version<<", size: "<<nfo.size<<"]";
+
+ // we can recover if the chunk size was specified.
+ if (nfo.size != static_cast<unsigned int>(-1)) {
+ DefaultLogger::get()->error(error);
+ reader.IncPtr(nfo.size);
+ }
+ else ThrowException(error);
+}
+
+// ------------------------------------------------------------------------------------------------
+// tiny utility guard to aid me at staying within chunk boundaries.
+class chunk_guard {
+
+public:
+
+ chunk_guard(const COB::ChunkInfo& nfo, StreamReaderLE& reader)
+ : nfo(nfo)
+ , reader(reader)
+ , cur(reader.GetCurrentPos())
+ {
+ }
+
+ ~chunk_guard() {
+ // don't do anything if the size is not given
+ if (nfo.size != static_cast<unsigned int>(-1)) {
+ reader.IncPtr(static_cast<int>(nfo.size)-reader.GetCurrentPos()+cur);
+ }
+ }
+
+private:
+
+ const COB::ChunkInfo& nfo;
+ StreamReaderLE& reader;
+ long cur;
+};
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadBinaryFile(Scene& out, StreamReaderLE* reader)
+{
+ while (1) {
+ std::string type;
+ type += reader -> GetI1()
+ ,type += reader -> GetI1()
+ ,type += reader -> GetI1()
+ ,type += reader -> GetI1()
+ ;
+
+ ChunkInfo nfo;
+ nfo.version = reader -> GetI2()*10;
+ nfo.version += reader -> GetI2();
+
+ nfo.id = reader->GetI4();
+ nfo.parent_id = reader->GetI4();
+ nfo.size = reader->GetI4();
+
+ if (type == "PolH") {
+ ReadPolH_Binary(out,*reader,nfo);
+ }
+ else if (type == "BitM") {
+ ReadBitM_Binary(out,*reader,nfo);
+ }
+ else if (type == "Grou") {
+ ReadGrou_Binary(out,*reader,nfo);
+ }
+ else if (type == "Lght") {
+ ReadLght_Binary(out,*reader,nfo);
+ }
+ else if (type == "Came") {
+ ReadCame_Binary(out,*reader,nfo);
+ }
+ else if (type == "Mat1") {
+ ReadMat1_Binary(out,*reader,nfo);
+ }
+ /* else if (type == "Bone") {
+ ReadBone_Binary(out,*reader,nfo);
+ }
+ else if (type == "Chan") {
+ ReadChan_Binary(out,*reader,nfo);
+ }*/
+ else if (type == "Unit") {
+ ReadUnit_Binary(out,*reader,nfo);
+ }
+ else if (type == "OLay") {
+ // ignore layer index silently.
+ if (nfo.size != static_cast<unsigned int>(-1) ) {
+ reader->IncPtr(nfo.size);
+ }
+ else return UnsupportedChunk_Binary(*reader,nfo,type.c_str());
+ }
+ else if (type == "END ") {
+ return;
+ }
+ else UnsupportedChunk_Binary(*reader,nfo,type.c_str());
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadPolH_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 8) {
+ return UnsupportedChunk_Binary(reader,nfo,"PolH");
+ }
+ const chunk_guard cn(nfo,reader);
+
+ out.nodes.push_back(boost::shared_ptr<Mesh>(new Mesh()));
+ Mesh& msh = (Mesh&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Binary(msh,reader,nfo);
+
+ msh.vertex_positions.resize(reader.GetI4());
+ for_each(aiVector3D& v,msh.vertex_positions) {
+ v.x = reader.GetF4();
+ v.y = reader.GetF4();
+ v.z = reader.GetF4();
+ }
+
+ msh.texture_coords.resize(reader.GetI4());
+ for_each(aiVector2D& v,msh.texture_coords) {
+ v.x = reader.GetF4();
+ v.y = reader.GetF4();
+ }
+
+ const size_t NNN = reader.GetI4();
+ msh.faces.reserve(NNN);
+ for (size_t i = 0; i < NNN; ++i) {
+ // XXX backface culling flag is 0x10 in flags
+
+ // hole?
+ bool hole;
+ if ((hole = (reader.GetI1() & 0x08) != 0)) {
+ // XXX Basically this should just work fine - then triangulator
+ // should output properly triangulated data even for polygons
+ // with holes. Test data specific to COB is needed to confirm it.
+ if (msh.faces.empty()) {
+ ThrowException(format("A hole is the first entity in the `PolH` chunk with id ") << nfo.id);
+ }
+ }
+ else msh.faces.push_back(Face());
+ Face& f = msh.faces.back();
+
+ const size_t num = reader.GetI2();
+ f.indices.reserve(f.indices.size() + num);
+
+ if (!hole) {
+ f.material = reader.GetI2();
+ f.flags = 0;
+ }
+
+ for (size_t x = 0; x < num; ++x) {
+ f.indices.push_back(VertexIndex());
+
+ VertexIndex& v = f.indices.back();
+ v.pos_idx = reader.GetI4();
+ v.uv_idx = reader.GetI4();
+ }
+
+ if (hole) {
+ std::reverse(f.indices.rbegin(),f.indices.rbegin()+num);
+ }
+ }
+ if (nfo.version>4) {
+ msh.draw_flags = reader.GetI4();
+ }
+ nfo.version>5 && nfo.version<8 ? reader.GetI4() : 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadBitM_Binary(COB::Scene&, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 1) {
+ return UnsupportedChunk_Binary(reader,nfo,"BitM");
+ }
+
+ const chunk_guard cn(nfo,reader);
+
+ const uint32_t len = reader.GetI4();
+ reader.IncPtr(len);
+
+ reader.GetI4();
+ reader.IncPtr(reader.GetI4());
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadMat1_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 8) {
+ return UnsupportedChunk_Binary(reader,nfo,"Mat1");
+ }
+
+ const chunk_guard cn(nfo,reader);
+
+ out.materials.push_back(Material());
+ Material& mat = out.materials.back();
+ mat = nfo;
+
+ mat.matnum = reader.GetI2();
+ switch(reader.GetI1()) {
+ case 'f':
+ mat.type = Material::FLAT;
+ break;
+ case 'p':
+ mat.type = Material::PHONG;
+ break;
+ case 'm':
+ mat.type = Material::METAL;
+ break;
+ default:
+ LogError_Ascii(format("Unrecognized shader type in `Mat1` chunk with id ")<<nfo.id);
+ mat.type = Material::FLAT;
+ }
+
+ switch(reader.GetI1()) {
+ case 'f':
+ mat.autofacet = Material::FACETED;
+ break;
+ case 'a':
+ mat.autofacet = Material::AUTOFACETED;
+ break;
+ case 's':
+ mat.autofacet = Material::SMOOTH;
+ break;
+ default:
+ LogError_Ascii(format("Unrecognized faceting mode in `Mat1` chunk with id ")<<nfo.id);
+ mat.autofacet = Material::FACETED;
+ }
+ mat.autofacet_angle = static_cast<float>(reader.GetI1());
+
+ mat.rgb.r = reader.GetF4();
+ mat.rgb.g = reader.GetF4();
+ mat.rgb.b = reader.GetF4();
+
+ mat.alpha = reader.GetF4();
+ mat.ka = reader.GetF4();
+ mat.ks = reader.GetF4();
+ mat.exp = reader.GetF4();
+ mat.ior = reader.GetF4();
+
+ char id[2];
+ id[0] = reader.GetI1(),id[1] = reader.GetI1();
+
+ if (id[0] == 'e' && id[1] == ':') {
+ mat.tex_env.reset(new Texture());
+
+ reader.GetI1();
+ ReadString_Binary(mat.tex_env->path,reader);
+
+ // advance to next texture-id
+ id[0] = reader.GetI1(),id[1] = reader.GetI1();
+ }
+
+ if (id[0] == 't' && id[1] == ':') {
+ mat.tex_color.reset(new Texture());
+
+ reader.GetI1();
+ ReadString_Binary(mat.tex_color->path,reader);
+
+ mat.tex_color->transform.mTranslation.x = reader.GetF4();
+ mat.tex_color->transform.mTranslation.y = reader.GetF4();
+
+ mat.tex_color->transform.mScaling.x = reader.GetF4();
+ mat.tex_color->transform.mScaling.y = reader.GetF4();
+
+ // advance to next texture-id
+ id[0] = reader.GetI1(),id[1] = reader.GetI1();
+ }
+
+ if (id[0] == 'b' && id[1] == ':') {
+ mat.tex_bump.reset(new Texture());
+
+ reader.GetI1();
+ ReadString_Binary(mat.tex_bump->path,reader);
+
+ mat.tex_bump->transform.mTranslation.x = reader.GetF4();
+ mat.tex_bump->transform.mTranslation.y = reader.GetF4();
+
+ mat.tex_bump->transform.mScaling.x = reader.GetF4();
+ mat.tex_bump->transform.mScaling.y = reader.GetF4();
+
+ // skip amplitude for I don't know its purpose.
+ reader.GetF4();
+ }
+ reader.IncPtr(-2);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadCame_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 2) {
+ return UnsupportedChunk_Binary(reader,nfo,"Came");
+ }
+
+ const chunk_guard cn(nfo,reader);
+
+ out.nodes.push_back(boost::shared_ptr<Camera>(new Camera()));
+ Camera& msh = (Camera&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Binary(msh,reader,nfo);
+
+ // the rest is not interesting for us, so we skip over it.
+ if (nfo.version > 1) {
+ if (reader.GetI2()==512) {
+ reader.IncPtr(42);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadLght_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 2) {
+ return UnsupportedChunk_Binary(reader,nfo,"Lght");
+ }
+
+ const chunk_guard cn(nfo,reader);
+
+ out.nodes.push_back(boost::shared_ptr<Light>(new Light()));
+ Light& msh = (Light&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Binary(msh,reader,nfo);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadGrou_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 2) {
+ return UnsupportedChunk_Binary(reader,nfo,"Grou");
+ }
+
+ const chunk_guard cn(nfo,reader);
+
+ out.nodes.push_back(boost::shared_ptr<Group>(new Group()));
+ Group& msh = (Group&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Binary(msh,reader,nfo);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadUnit_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 1) {
+ return UnsupportedChunk_Binary(reader,nfo,"Unit");
+ }
+
+ const chunk_guard cn(nfo,reader);
+
+ // parent chunks preceede their childs, so we should have the
+ // corresponding chunk already.
+ for_each(boost::shared_ptr< Node >& nd, out.nodes) {
+ if (nd->id == nfo.parent_id) {
+ const unsigned int t=reader.GetI2();
+ nd->unit_scale = t>=sizeof(units)/sizeof(units[0])?(
+ LogWarn_Ascii(format()<<t<<" is not a valid value for `Units` attribute in `Unit chunk` "<<nfo.id)
+ ,1.f):units[t];
+
+ return;
+ }
+ }
+ LogWarn_Ascii(format()<<"`Unit` chunk "<<nfo.id<<" is a child of "
+ <<nfo.parent_id<<" which does not exist");
+}
+
+
+#endif
diff --git a/src/3rdparty/assimp/code/COBLoader.h b/src/3rdparty/assimp/code/COBLoader.h
new file mode 100644
index 000000000..55e484b31
--- /dev/null
+++ b/src/3rdparty/assimp/code/COBLoader.h
@@ -0,0 +1,175 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file COBLoader.h
+ * @brief Declaration of the TrueSpace (*.cob,*.scn) importer class.
+ */
+#ifndef INCLUDED_AI_COB_LOADER_H
+#define INCLUDED_AI_COB_LOADER_H
+
+#include "BaseImporter.h"
+namespace Assimp {
+ class LineSplitter;
+
+ // TinyFormatter.h
+ namespace Formatter {
+ template <typename T,typename TR, typename A> class basic_formatter;
+ typedef class basic_formatter< char, std::char_traits<char>, std::allocator<char> > format;
+ }
+
+ // COBScene.h
+ namespace COB {
+ struct ChunkInfo;
+ struct Node;
+ struct Scene;
+ }
+
+// -------------------------------------------------------------------------------------------
+/** Importer class to load TrueSpace files (cob,scn) up to v6.
+ *
+ * Currently relatively limited, loads only ASCII files and needs more test coverage. */
+// -------------------------------------------------------------------------------------------
+class COBImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+
+ /** Constructor to be privately used by Importer */
+ COBImporter();
+
+ /** Destructor, private as well */
+ ~COBImporter();
+
+public:
+
+ // --------------------
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // --------------------
+ void GetExtensionList(std::set<std::string>& app);
+
+ // --------------------
+ void SetupProperties(const Importer* pImp);
+
+ // --------------------
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+ // -------------------------------------------------------------------
+ /** Prepend 'COB: ' and throw msg.*/
+ static void ThrowException(const std::string& msg);
+
+ // -------------------------------------------------------------------
+ /** @brief Read from an ascii scene/object file
+ * @param out Receives output data.
+ * @param stream Stream to read from. */
+ void ReadAsciiFile(COB::Scene& out, StreamReaderLE* stream);
+
+ // -------------------------------------------------------------------
+ /** @brief Read from a binary scene/object file
+ * @param out Receives output data.
+ * @param stream Stream to read from. */
+ void ReadBinaryFile(COB::Scene& out, StreamReaderLE* stream);
+
+
+private:
+
+ // Conversion to Assimp output format
+
+ aiNode* BuildNodes(const COB::Node& root,const COB::Scene& scin,aiScene* fill);
+
+private:
+
+ // ASCII file support
+
+ void UnsupportedChunk_Ascii(LineSplitter& splitter, const COB::ChunkInfo& nfo, const char* name);
+ void ReadChunkInfo_Ascii(COB::ChunkInfo& out, const LineSplitter& splitter);
+ void ReadBasicNodeInfo_Ascii(COB::Node& msh, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ template <typename T> void ReadFloat3Tuple_Ascii(T& fill, const char** in);
+
+ void ReadPolH_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadBitM_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadMat1_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadGrou_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadBone_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadCame_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadLght_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadUnit_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadChan_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+
+
+ // ASCII file logging stuff to add proper line numbers to messages
+
+ static void LogWarn_Ascii (const LineSplitter& splitter, const Formatter::format& message);
+ static void LogError_Ascii(const LineSplitter& splitter, const Formatter::format& message);
+ static void LogInfo_Ascii (const LineSplitter& splitter, const Formatter::format& message);
+ static void LogDebug_Ascii(const LineSplitter& splitter, const Formatter::format& message);
+
+ static void LogWarn_Ascii (const Formatter::format& message);
+ static void LogError_Ascii (const Formatter::format& message);
+ static void LogInfo_Ascii (const Formatter::format& message);
+ static void LogDebug_Ascii (const Formatter::format& message);
+
+
+ // Binary file support
+
+ void UnsupportedChunk_Binary(StreamReaderLE& reader, const COB::ChunkInfo& nfo, const char* name);
+ void ReadString_Binary(std::string& out, StreamReaderLE& reader);
+ void ReadBasicNodeInfo_Binary(COB::Node& msh, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+
+ void ReadPolH_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+ void ReadBitM_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+ void ReadMat1_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+ void ReadCame_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+ void ReadLght_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+ void ReadGrou_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+ void ReadUnit_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+
+
+}; // !class COBImporter
+
+} // end of namespace Assimp
+#endif // AI_UNREALIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/COBScene.h b/src/3rdparty/assimp/code/COBScene.h
new file mode 100644
index 000000000..794ee1376
--- /dev/null
+++ b/src/3rdparty/assimp/code/COBScene.h
@@ -0,0 +1,272 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file COBScene.h
+* @brief Utilities for the COB importer.
+*/
+#ifndef INCLUDED_AI_COB_SCENE_H
+#define INCLUDED_AI_COB_SCENE_H
+
+#include <boost/shared_ptr.hpp>
+#include "BaseImporter.h"
+
+namespace Assimp {
+ namespace COB {
+
+// ------------------
+/** Represents a single vertex index in a face */
+struct VertexIndex
+{
+ // intentionally uninitialized
+ unsigned int pos_idx,uv_idx;
+};
+
+// ------------------
+/** COB Face data structure */
+struct Face
+{
+ // intentionally uninitialized
+ unsigned int material, flags;
+ std::vector<VertexIndex> indices;
+};
+
+// ------------------
+/** COB chunk header information */
+struct ChunkInfo
+{
+ enum {NO_SIZE=0xffffffff};
+
+ ChunkInfo ()
+ : id (0)
+ , parent_id (0)
+ , version (0)
+ , size (NO_SIZE)
+ {}
+
+ // Id of this chunk, unique within file
+ unsigned int id;
+
+ // and the corresponding parent
+ unsigned int parent_id;
+
+ // version. v1.23 becomes 123
+ unsigned int version;
+
+ // chunk size in bytes, only relevant for binary files
+ // NO_SIZE is also valid.
+ unsigned int size;
+};
+
+// ------------------
+/** A node in the scenegraph */
+struct Node : public ChunkInfo
+{
+ using ChunkInfo::operator=;
+ enum Type {
+ TYPE_MESH,TYPE_GROUP,TYPE_LIGHT,TYPE_CAMERA,TYPE_BONE
+ };
+
+ virtual ~Node() {}
+ Node(Type type) : type(type), unit_scale(1.f){}
+
+ Type type;
+
+ // used during resolving
+ typedef std::deque<const Node*> ChildList;
+ mutable ChildList temp_children;
+
+ // unique name
+ std::string name;
+
+ // local mesh transformation
+ aiMatrix4x4 transform;
+
+ // scaling for this node to get to the metric system
+ float unit_scale;
+};
+
+// ------------------
+/** COB Mesh data structure */
+struct Mesh : public Node
+{
+ using ChunkInfo::operator=;
+ enum DrawFlags {
+ SOLID = 0x1,
+ TRANS = 0x2,
+ WIRED = 0x4,
+ BBOX = 0x8,
+ HIDE = 0x10
+ };
+
+ Mesh()
+ : Node(TYPE_MESH)
+ , draw_flags(SOLID)
+ {}
+
+ // vertex elements
+ std::vector<aiVector2D> texture_coords;
+ std::vector<aiVector3D> vertex_positions;
+
+ // face data
+ std::vector<Face> faces;
+
+ // misc. drawing flags
+ unsigned int draw_flags;
+
+ // used during resolving
+ typedef std::deque<Face*> FaceRefList;
+ typedef std::map< unsigned int,FaceRefList > TempMap;
+ TempMap temp_map;
+};
+
+// ------------------
+/** COB Group data structure */
+struct Group : public Node
+{
+ using ChunkInfo::operator=;
+ Group() : Node(TYPE_GROUP) {}
+};
+
+// ------------------
+/** COB Bone data structure */
+struct Bone : public Node
+{
+ using ChunkInfo::operator=;
+ Bone() : Node(TYPE_BONE) {}
+};
+
+// ------------------
+/** COB Light data structure */
+struct Light : public Node
+{
+ enum LightType {
+ SPOT,LOCAL,INFINITE
+ };
+
+ using ChunkInfo::operator=;
+ Light() : Node(TYPE_LIGHT),angle(),inner_angle(),ltype(SPOT) {}
+
+ aiColor3D color;
+ float angle,inner_angle;
+
+ LightType ltype;
+};
+
+// ------------------
+/** COB Camera data structure */
+struct Camera : public Node
+{
+ using ChunkInfo::operator=;
+ Camera() : Node(TYPE_CAMERA) {}
+};
+
+// ------------------
+/** COB Texture data structure */
+struct Texture
+{
+ std::string path;
+ aiUVTransform transform;
+};
+
+// ------------------
+/** COB Material data structure */
+struct Material : ChunkInfo
+{
+ using ChunkInfo::operator=;
+ enum Shader {
+ FLAT,PHONG,METAL
+ };
+
+ enum AutoFacet {
+ FACETED,AUTOFACETED,SMOOTH
+ };
+
+ Material() : alpha(),exp(),ior(),ka(),ks(1.f),
+ matnum(0xffffffff),
+ shader(FLAT),autofacet(FACETED),
+ autofacet_angle()
+ {}
+
+ std::string type;
+
+ aiColor3D rgb;
+ float alpha, exp, ior,ka,ks;
+
+ unsigned int matnum;
+ Shader shader;
+
+ AutoFacet autofacet;
+ float autofacet_angle;
+
+ boost::shared_ptr<Texture> tex_env,tex_bump,tex_color;
+};
+
+// ------------------
+/** Embedded bitmap, for instance for the thumbnail image */
+struct Bitmap : ChunkInfo
+{
+ Bitmap() : orig_size() {}
+ struct BitmapHeader
+ {
+ };
+
+ BitmapHeader head;
+ size_t orig_size;
+ std::vector<char> buff_zipped;
+};
+
+typedef std::deque< boost::shared_ptr<Node> > NodeList;
+typedef std::vector< Material > MaterialList;
+
+// ------------------
+/** Represents a master COB scene, even if we loaded just a single COB file */
+struct Scene
+{
+ NodeList nodes;
+ MaterialList materials;
+
+ // becomes *0 later
+ Bitmap thumbnail;
+};
+
+ } // end COB
+} // end Assimp
+
+#endif
diff --git a/src/3rdparty/assimp/code/CSMLoader.cpp b/src/3rdparty/assimp/code/CSMLoader.cpp
new file mode 100644
index 000000000..ff3884d59
--- /dev/null
+++ b/src/3rdparty/assimp/code/CSMLoader.cpp
@@ -0,0 +1,281 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2009, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file CSMLoader.cpp
+ * Implementation of the CSM importer class.
+ */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_CSM_IMPORTER
+
+#include "CSMLoader.h"
+#include "SkeletonMeshBuilder.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+CSMImporter::CSMImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+CSMImporter::~CSMImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ // check file extension
+ const std::string extension = GetExtension(pFile);
+
+ if ( extension == "csm")
+ return true;
+
+ if ((checkSig || !extension.length()) && pIOHandler) {
+ const char* tokens[] = {"$Filename"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a string of all file extensions supported
+void CSMImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("csm");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void CSMImporter::SetupProperties(const Importer*)
+{
+ // nothing to be done for the moment
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void CSMImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open CSM file " + pFile + ".");
+ }
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+ const char* buffer = &mBuffer2[0];
+
+ aiAnimation* anim = new aiAnimation();
+ int first = 0, last = 0x00ffffff;
+
+ // now process the file and look out for '$' sections
+ while (1) {
+ SkipSpaces(&buffer);
+ if ('\0' == *buffer)
+ break;
+
+ if ('$' == *buffer) {
+ ++buffer;
+ if (TokenMatchI(buffer,"firstframe",10)) {
+ SkipSpaces(&buffer);
+ first = strtol10s(buffer,&buffer);
+ }
+ else if (TokenMatchI(buffer,"lastframe",9)) {
+ SkipSpaces(&buffer);
+ last = strtol10s(buffer,&buffer);
+ }
+ else if (TokenMatchI(buffer,"rate",4)) {
+ SkipSpaces(&buffer);
+ float d;
+ buffer = fast_atof_move(buffer,d);
+ anim->mTicksPerSecond = d;
+ }
+ else if (TokenMatchI(buffer,"order",5)) {
+ std::vector< aiNodeAnim* > anims_temp;
+ anims_temp.reserve(30);
+ while (1) {
+ SkipSpaces(&buffer);
+ if (IsLineEnd(*buffer) && SkipSpacesAndLineEnd(&buffer) && *buffer == '$')
+ break; // next section
+
+ // Construct a new node animation channel and setup its name
+ anims_temp.push_back(new aiNodeAnim());
+ aiNodeAnim* nda = anims_temp.back();
+
+ char* ot = nda->mNodeName.data;
+ while (!IsSpaceOrNewLine(*buffer))
+ *ot++ = *buffer++;
+
+ *ot = '\0';
+ nda->mNodeName.length = (size_t)(ot-nda->mNodeName.data);
+ }
+
+ anim->mNumChannels = anims_temp.size();
+ if (!anim->mNumChannels)
+ throw DeadlyImportError("CSM: Empty $order section");
+
+ // copy over to the output animation
+ anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
+ ::memcpy(anim->mChannels,&anims_temp[0],sizeof(aiNodeAnim*)*anim->mNumChannels);
+ }
+ else if (TokenMatchI(buffer,"points",6)) {
+ if (!anim->mNumChannels)
+ throw DeadlyImportError("CSM: \'$order\' section is required to appear prior to \'$points\'");
+
+ // If we know how many frames we'll read, we can preallocate some storage
+ unsigned int alloc = 100;
+ if (last != 0x00ffffff)
+ {
+ alloc = last-first;
+ alloc += alloc>>2u; // + 25%
+ for (unsigned int i = 0; i < anim->mNumChannels;++i)
+ anim->mChannels[i]->mPositionKeys = new aiVectorKey[alloc];
+ }
+
+ unsigned int filled = 0;
+
+ // Now read all point data.
+ while (1) {
+ SkipSpaces(&buffer);
+ if (IsLineEnd(*buffer) && (!SkipSpacesAndLineEnd(&buffer) || *buffer == '$')) {
+ break; // next section
+ }
+
+ // read frame
+ const int frame = ::strtol10(buffer,&buffer);
+ last = std::max(frame,last);
+ first = std::min(frame,last);
+ for (unsigned int i = 0; i < anim->mNumChannels;++i) {
+
+ aiNodeAnim* s = anim->mChannels[i];
+ if (s->mNumPositionKeys == alloc) { /* need to reallocate? */
+
+ aiVectorKey* old = s->mPositionKeys;
+ s->mPositionKeys = new aiVectorKey[s->mNumPositionKeys = alloc*2];
+ ::memcpy(s->mPositionKeys,old,sizeof(aiVectorKey)*alloc);
+ delete[] old;
+ }
+
+ // read x,y,z
+ if (!SkipSpacesAndLineEnd(&buffer))
+ throw DeadlyImportError("CSM: Unexpected EOF occured reading sample x coord");
+
+ if (TokenMatchI(buffer, "DROPOUT", 7)) {
+ // seems this is invalid marker data; at least the doc says it's possible
+ DefaultLogger::get()->warn("CSM: Encountered invalid marker data (DROPOUT)");
+ }
+ else {
+ aiVectorKey* sub = s->mPositionKeys + s->mNumPositionKeys;
+ sub->mTime = (double)frame;
+ buffer = fast_atof_move(buffer, (float&)sub->mValue.x);
+
+ if (!SkipSpacesAndLineEnd(&buffer))
+ throw DeadlyImportError("CSM: Unexpected EOF occured reading sample y coord");
+ buffer = fast_atof_move(buffer, (float&)sub->mValue.y);
+
+ if (!SkipSpacesAndLineEnd(&buffer))
+ throw DeadlyImportError("CSM: Unexpected EOF occured reading sample z coord");
+ buffer = fast_atof_move(buffer, (float&)sub->mValue.z);
+
+ ++s->mNumPositionKeys;
+ }
+ }
+
+ // update allocation granularity
+ if (filled == alloc)
+ alloc *= 2;
+
+ ++filled;
+ }
+ // all channels must be complete in order to continue safely.
+ for (unsigned int i = 0; i < anim->mNumChannels;++i) {
+
+ if (!anim->mChannels[i]->mNumPositionKeys)
+ throw DeadlyImportError("CSM: Invalid marker track");
+ }
+ }
+ }
+ else {
+ // advance to the next line
+ SkipLine(&buffer);
+ }
+ }
+
+ // Setup a proper animation duration
+ anim->mDuration = last - std::min( first, 0 );
+
+ // build a dummy root node with the tiny markers as children
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("$CSM_DummyRoot");
+
+ pScene->mRootNode->mNumChildren = anim->mNumChannels;
+ pScene->mRootNode->mChildren = new aiNode* [anim->mNumChannels];
+
+ for (unsigned int i = 0; i < anim->mNumChannels;++i) {
+ aiNodeAnim* na = anim->mChannels[i];
+
+ aiNode* nd = pScene->mRootNode->mChildren[i] = new aiNode();
+ nd->mName = anim->mChannels[i]->mNodeName;
+ nd->mParent = pScene->mRootNode;
+
+ aiMatrix4x4::Translation(na->mPositionKeys[0].mValue, nd->mTransformation);
+ }
+
+ // Store the one and only animation in the scene
+ pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations=1];
+ pScene->mAnimations[0] = anim;
+ anim->mName.Set("$CSM_MasterAnim");
+
+ // mark the scene as incomplete and run SkeletonMeshBuilder on it
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ SkeletonMeshBuilder maker(pScene,pScene->mRootNode,true);
+}
+
+#endif // !! ASSIMP_BUILD_NO_CSM_IMPORTER
diff --git a/src/3rdparty/assimp/code/CSMLoader.h b/src/3rdparty/assimp/code/CSMLoader.h
new file mode 100644
index 000000000..2b5e8e639
--- /dev/null
+++ b/src/3rdparty/assimp/code/CSMLoader.h
@@ -0,0 +1,88 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file CSMLoader.h
+ * Declaration of the CharacterStudio Motion importer class.
+ */
+#ifndef INCLUDED_AI_CSM_LOADER_H
+#define INCLUDED_AI_CSM_LOADER_H
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Importer class to load MOCAPs in CharacterStudio Motion format.
+ *
+ * A very rudimentary loader for the moment. No support for the hierarchy,
+ * every marker is returned as child of root.
+ *
+ * Link to file format specification:
+ * <max_8_dvd>\samples\Motion\Docs\CSM.rtf
+*/
+class CSMImporter : public BaseImporter
+{
+ friend class Importer;
+protected:
+ /** Constructor to be privately used by Importer */
+ CSMImporter();
+
+ /** Destructor, private as well */
+ ~CSMImporter();
+
+public:
+ // -------------------------------------------------------------------
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+}; // end of class CSMImporter
+} // end of namespace Assimp
+#endif // AI_AC3DIMPORTER_H_INC
+
diff --git a/src/3rdparty/assimp/code/CalcTangentsProcess.cpp b/src/3rdparty/assimp/code/CalcTangentsProcess.cpp
new file mode 100644
index 000000000..83ec00a54
--- /dev/null
+++ b/src/3rdparty/assimp/code/CalcTangentsProcess.cpp
@@ -0,0 +1,283 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the post processing step to calculate
+ * tangents and bitangents for all imported meshes
+ */
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "CalcTangentsProcess.h"
+#include "ProcessHelper.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+CalcTangentsProcess::CalcTangentsProcess()
+{
+ this->configMaxAngle = AI_DEG_TO_RAD(45.f);
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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)
+{
+ // get the current value of the property
+ this->configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f);
+ this->configMaxAngle = std::max(std::min(this->configMaxAngle,45.0f),0.0f);
+ this->configMaxAngle = AI_DEG_TO_RAD(this->configMaxAngle);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void CalcTangentsProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->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)DefaultLogger::get()->debug("CalcTangentsProcess finished. Tangents have been calculated");
+ else DefaultLogger::get()->debug("CalcTangentsProcess finished");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Calculates tangents and bitangents 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) // thisimplies 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)))
+ {
+ DefaultLogger::get()->info("Tangents are undefined for line and point meshes");
+ return false;
+ }
+
+ // what we can check, though, is if the mesh has normals and texture coord. That's a requirement
+ if ( pMesh->mNormals == NULL || pMesh->mTextureCoords[0] == NULL)
+ {
+ DefaultLogger::get()->error("Unable to compute tangents: UV0 and normals must be there ");
+ 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[0];
+ 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)
+ {
+ register unsigned int idx = face.mIndices[i];
+ vertexDone [idx] = true;
+ meshTang [idx] = qnan;
+ meshBitang [idx] = 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;
+
+ // tangent points in the direction where to positive X axis of the texture coords would point in model space
+ // bitangents points along the positive Y axis of the texture coords, 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();
+
+ // 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 = cosf(this->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.clear();
+
+ // 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/src/3rdparty/assimp/code/CalcTangentsProcess.h b/src/3rdparty/assimp/code/CalcTangentsProcess.h
new file mode 100644
index 000000000..189710ff8
--- /dev/null
+++ b/src/3rdparty/assimp/code/CalcTangentsProcess.h
@@ -0,0 +1,118 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file 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 CalcTangentsProcess : public BaseProcess
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ CalcTangentsProcess();
+
+ /** Destructor, private as well */
+ ~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;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_CALCTANGENTSPROCESS_H_INC
diff --git a/src/3rdparty/assimp/code/ColladaHelper.h b/src/3rdparty/assimp/code/ColladaHelper.h
new file mode 100644
index 000000000..8c3745c08
--- /dev/null
+++ b/src/3rdparty/assimp/code/ColladaHelper.h
@@ -0,0 +1,603 @@
+/** Helper structures for the Collada loader */
+
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef AI_COLLADAHELPER_H_INC
+#define AI_COLLADAHELPER_H_INC
+
+namespace Assimp {
+namespace Collada {
+
+/** Collada file versions which evolved during the years ... */
+enum FormatVersion
+{
+ FV_1_5_n,
+ FV_1_4_n,
+ FV_1_3_n
+};
+
+
+/** Transformation types that can be applied to a node */
+enum TransformType
+{
+ TF_LOOKAT,
+ TF_ROTATE,
+ TF_TRANSLATE,
+ TF_SCALE,
+ TF_SKEW,
+ TF_MATRIX
+};
+
+/** Different types of input data to a vertex or face */
+enum InputType
+{
+ IT_Invalid,
+ IT_Vertex, // special type for per-index data referring to the <vertices> element carrying the per-vertex data.
+ IT_Position,
+ IT_Normal,
+ IT_Texcoord,
+ IT_Color,
+ IT_Tangent,
+ IT_Bitangent
+};
+
+/** Contains all data for one of the different transformation types */
+struct Transform
+{
+ std::string mID; ///< SID of the transform step, by which anim channels address their target node
+ TransformType mType;
+ float f[16]; ///< Interpretation of data depends on the type of the transformation
+};
+
+/** A collada camera. */
+struct Camera
+{
+ Camera()
+ : mOrtho (false)
+ , mHorFov (10e10f)
+ , mVerFov (10e10f)
+ , mAspect (10e10f)
+ , mZNear (0.1f)
+ , mZFar (1000.f)
+ {}
+
+ // Name of camera
+ std::string mName;
+
+ // True if it is an orthografic camera
+ bool mOrtho;
+
+ //! Horizontal field of view in degrees
+ float mHorFov;
+
+ //! Vertical field of view in degrees
+ float mVerFov;
+
+ //! Screen aspect
+ float mAspect;
+
+ //! Near& far z
+ float mZNear, mZFar;
+};
+
+#define aiLightSource_AMBIENT 0xdeaddead
+
+/** A collada light source. */
+struct Light
+{
+ Light()
+ : mType(0)
+ , mAttConstant (1.f)
+ , mAttLinear (0.f)
+ , mAttQuadratic (0.f)
+ , mFalloffAngle (180.f)
+ , mFalloffExponent (0.f)
+ , mPenumbraAngle (10e10f)
+ , mOuterAngle (10e10f)
+ , mIntensity (1.f)
+ {}
+
+ //! Type of the light source aiLightSourceType + ambient
+ unsigned int mType;
+
+ //! Color of the light
+ aiColor3D mColor;
+
+ //! Light attenuation
+ float mAttConstant,mAttLinear,mAttQuadratic;
+
+ //! Spot light falloff
+ float mFalloffAngle;
+ float mFalloffExponent;
+
+ // -----------------------------------------------------
+ // FCOLLADA extension from here
+
+ //! ... related stuff from maja and max extensions
+ float mPenumbraAngle;
+ float mOuterAngle;
+
+ //! Common light intensity
+ float mIntensity;
+};
+
+/** Short vertex index description */
+struct InputSemanticMapEntry
+{
+ InputSemanticMapEntry()
+ : mSet (0)
+ , mType (IT_Invalid)
+ {}
+
+ //! Index of set, optional
+ unsigned int mSet;
+
+ //! Name of referenced vertex input
+ InputType mType;
+};
+
+/** Table to map from effect to vertex input semantics */
+struct SemanticMappingTable
+{
+ //! Name of material
+ std::string mMatName;
+
+ //! List of semantic map commands, grouped by effect semantic name
+ std::map<std::string, InputSemanticMapEntry> mMap;
+
+ //! For std::find
+ bool operator == (const std::string& s) const {
+ return s == mMatName;
+ }
+};
+
+/** A reference to a mesh inside a node, including materials assigned to the various subgroups.
+ * The ID refers to either a mesh or a controller which specifies the mesh
+ */
+struct MeshInstance
+{
+ ///< ID of the mesh or controller to be instanced
+ std::string mMeshOrController;
+
+ ///< Map of materials by the subgroup ID they're applied to
+ std::map<std::string, SemanticMappingTable> mMaterials;
+};
+
+/** A reference to a camera inside a node*/
+struct CameraInstance
+{
+ ///< ID of the camera
+ std::string mCamera;
+};
+
+/** A reference to a light inside a node*/
+struct LightInstance
+{
+ ///< ID of the camera
+ std::string mLight;
+};
+
+/** A reference to a node inside a node*/
+struct NodeInstance
+{
+ ///< ID of the node
+ std::string mNode;
+};
+
+/** A node in a scene hierarchy */
+struct Node
+{
+ std::string mName;
+ std::string mID;
+ std::string mSID;
+ Node* mParent;
+ std::vector<Node*> mChildren;
+
+ /** Operations in order to calculate the resulting transformation to parent. */
+ std::vector<Transform> mTransforms;
+
+ /** Meshes at this node */
+ std::vector<MeshInstance> mMeshes;
+
+ /** Lights at this node */
+ std::vector<LightInstance> mLights;
+
+ /** Cameras at this node */
+ std::vector<CameraInstance> mCameras;
+
+ /** Node instances at this node */
+ std::vector<NodeInstance> mNodeInstances;
+
+ /** Rootnodes: Name of primary camera, if any */
+ std::string mPrimaryCamera;
+
+ //! Constructor. Begin with a zero parent
+ Node() {
+ mParent = NULL;
+ }
+
+ //! Destructor: delete all children subsequently
+ ~Node() {
+ for ( std::vector<Node*>::iterator it = mChildren.begin(); it != mChildren.end(); ++it)
+ delete *it;
+ }
+};
+
+/** Data source array: either floats or strings */
+struct Data
+{
+ bool mIsStringArray;
+ std::vector<float> mValues;
+ std::vector<std::string> mStrings;
+};
+
+/** Accessor to a data array */
+struct Accessor
+{
+ size_t mCount; // in number of objects
+ size_t mSize; // size of an object, in elements (floats or strings, mostly 1)
+ size_t mOffset; // in number of values
+ size_t mStride; // Stride in number of values
+ std::vector<std::string> mParams; // names of the data streams in the accessors. Empty string tells to ignore.
+ size_t mSubOffset[4]; // Suboffset inside the object for the common 4 elements. For a vector, thats XYZ, for a color RGBA and so on.
+ // For example, SubOffset[0] denotes which of the values inside the object is the vector X component.
+ std::string mSource; // URL of the source array
+ mutable const Data* mData; // Pointer to the source array, if resolved. NULL else
+
+ Accessor()
+ {
+ mCount = 0; mSize = 0; mOffset = 0; mStride = 0; mData = NULL;
+ mSubOffset[0] = mSubOffset[1] = mSubOffset[2] = mSubOffset[3] = 0;
+ }
+};
+
+/** A single face in a mesh */
+struct Face
+{
+ std::vector<size_t> mIndices;
+};
+
+/** An input channel for mesh data, referring to a single accessor */
+struct InputChannel
+{
+ InputType mType; // Type of the data
+ size_t mIndex; // Optional index, if multiple sets of the same data type are given
+ size_t mOffset; // Index offset in the indices array of per-face indices. Don't ask, can't explain that any better.
+ std::string mAccessor; // ID of the accessor where to read the actual values from.
+ mutable const Accessor* mResolved; // Pointer to the accessor, if resolved. NULL else
+
+ InputChannel() { mType = IT_Invalid; mIndex = 0; mOffset = 0; mResolved = NULL; }
+};
+
+/** Subset of a mesh with a certain material */
+struct SubMesh
+{
+ std::string mMaterial; ///< subgroup identifier
+ size_t mNumFaces; ///< number of faces in this submesh
+};
+
+/** Contains data for a single mesh */
+struct Mesh
+{
+ Mesh()
+ {
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
+ mNumUVComponents[i] = 2;
+ }
+
+ // just to check if there's some sophisticated addressing involved...
+ // which we don't support, and therefore should warn about.
+ std::string mVertexID;
+
+ // Vertex data addressed by vertex indices
+ std::vector<InputChannel> mPerVertexData;
+
+ // actual mesh data, assembled on encounter of a <p> element. Verbose format, not indexed
+ std::vector<aiVector3D> mPositions;
+ std::vector<aiVector3D> mNormals;
+ std::vector<aiVector3D> mTangents;
+ std::vector<aiVector3D> mBitangents;
+ std::vector<aiVector3D> mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ std::vector<aiColor4D> mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
+
+ unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+
+ // Faces. Stored are only the number of vertices for each face.
+ // 1 == point, 2 == line, 3 == triangle, 4+ == poly
+ std::vector<size_t> mFaceSize;
+
+ // Position indices for all faces in the sequence given in mFaceSize -
+ // necessary for bone weight assignment
+ std::vector<size_t> mFacePosIndices;
+
+ // Submeshes in this mesh, each with a given material
+ std::vector<SubMesh> mSubMeshes;
+};
+
+/** Which type of primitives the ReadPrimitives() function is going to read */
+enum PrimitiveType
+{
+ Prim_Invalid,
+ Prim_Lines,
+ Prim_LineStrip,
+ Prim_Triangles,
+ Prim_TriStrips,
+ Prim_TriFans,
+ Prim_Polylist,
+ Prim_Polygon
+};
+
+/** A skeleton controller to deform a mesh with the use of joints */
+struct Controller
+{
+ // the URL of the mesh deformed by the controller.
+ std::string mMeshId;
+
+ // accessor URL of the joint names
+ std::string mJointNameSource;
+
+ ///< The bind shape matrix, as array of floats. I'm not sure what this matrix actually describes, but it can't be ignored in all cases
+ float mBindShapeMatrix[16];
+
+ // accessor URL of the joint inverse bind matrices
+ std::string mJointOffsetMatrixSource;
+
+ // input channel: joint names.
+ InputChannel mWeightInputJoints;
+ // input channel: joint weights
+ InputChannel mWeightInputWeights;
+
+ // Number of weights per vertex.
+ std::vector<size_t> mWeightCounts;
+
+ // JointIndex-WeightIndex pairs for all vertices
+ std::vector< std::pair<size_t, size_t> > mWeights;
+};
+
+/** A collada material. Pretty much the only member is a reference to an effect. */
+struct Material
+{
+ std::string mEffect;
+};
+
+/** Type of the effect param */
+enum ParamType
+{
+ Param_Sampler,
+ Param_Surface
+};
+
+/** A param for an effect. Might be of several types, but they all just refer to each other, so I summarize them */
+struct EffectParam
+{
+ ParamType mType;
+ std::string mReference; // to which other thing the param is referring to.
+};
+
+/** Shading type supported by the standard effect spec of Collada */
+enum ShadeType
+{
+ Shade_Invalid,
+ Shade_Constant,
+ Shade_Lambert,
+ Shade_Phong,
+ Shade_Blinn
+};
+
+/** Represents a texture sampler in collada */
+struct Sampler
+{
+ Sampler()
+ : mWrapU (true)
+ , mWrapV (true)
+ , mMirrorU ()
+ , mMirrorV ()
+ , mOp (aiTextureOp_Multiply)
+ , mUVId (0xffffffff)
+ , mWeighting (1.f)
+ , mMixWithPrevious (1.f)
+ {}
+
+ /** Name of image reference
+ */
+ std::string mName;
+
+ /** Wrap U?
+ */
+ bool mWrapU;
+
+ /** Wrap V?
+ */
+ bool mWrapV;
+
+ /** Mirror U?
+ */
+ bool mMirrorU;
+
+ /** Mirror V?
+ */
+ bool mMirrorV;
+
+ /** Blend mode
+ */
+ aiTextureOp mOp;
+
+ /** UV transformation
+ */
+ aiUVTransform mTransform;
+
+ /** Name of source UV channel
+ */
+ std::string mUVChannel;
+
+ /** Resolved UV channel index or 0xffffffff if not known
+ */
+ unsigned int mUVId;
+
+ // OKINO/MAX3D extensions from here
+ // -------------------------------------------------------
+
+ /** Weighting factor
+ */
+ float mWeighting;
+
+ /** Mixing factor from OKINO
+ */
+ float mMixWithPrevious;
+};
+
+/** A collada effect. Can contain about anything according to the Collada spec,
+ but we limit our version to a reasonable subset. */
+struct Effect
+{
+ // Shading mode
+ ShadeType mShadeType;
+
+ // Colors
+ aiColor4D mEmissive, mAmbient, mDiffuse, mSpecular,
+ mTransparent, mReflective;
+
+ // Textures
+ Sampler mTexEmissive, mTexAmbient, mTexDiffuse, mTexSpecular,
+ mTexTransparent, mTexBump, mTexReflective;
+
+ // Scalar factory
+ float mShininess, mRefractIndex, mReflectivity;
+ float mTransparency;
+
+ // local params referring to each other by their SID
+ typedef std::map<std::string, Collada::EffectParam> ParamLibrary;
+ ParamLibrary mParams;
+
+ // MAX3D extensions
+ // ---------------------------------------------------------
+ // Double-sided?
+ bool mDoubleSided, mWireframe, mFaceted;
+
+ Effect()
+ : mShadeType (Shade_Phong)
+ , mEmissive ( 0, 0, 0, 1)
+ , mAmbient ( 0.1f, 0.1f, 0.1f, 1)
+ , mDiffuse ( 0.6f, 0.6f, 0.6f, 1)
+ , mSpecular ( 0.4f, 0.4f, 0.4f, 1)
+ , mTransparent ( 0, 0, 0, 1)
+ , mShininess (10.0f)
+ , mRefractIndex (1.f)
+ , mReflectivity (1.f)
+ , mTransparency (0.f)
+ , mDoubleSided (false)
+ , mWireframe (false)
+ , mFaceted (false)
+ {
+ }
+};
+
+/** An image, meaning texture */
+struct Image
+{
+ std::string mFileName;
+
+ /** If image file name is zero, embedded image data
+ */
+ std::vector<uint8_t> mImageData;
+
+ /** If image file name is zero, file format of
+ * embedded image data.
+ */
+ std::string mEmbeddedFormat;
+
+};
+
+/** An animation channel. */
+struct AnimationChannel
+{
+ /** URL of the data to animate. Could be about anything, but we support only the
+ * "NodeID/TransformID.SubElement" notation
+ */
+ std::string mTarget;
+
+ /** Source URL of the time values. Collada calls them "input". Meh. */
+ std::string mSourceTimes;
+ /** Source URL of the value values. Collada calls them "output". */
+ std::string mSourceValues;
+};
+
+/** An animation. Container for 0-x animation channels or 0-x animations */
+struct Animation
+{
+ /** Anim name */
+ std::string mName;
+
+ /** the animation channels, if any */
+ std::vector<AnimationChannel> mChannels;
+
+ /** the sub-animations, if any */
+ std::vector<Animation*> mSubAnims;
+
+ /** Destructor */
+ ~Animation()
+ {
+ for ( std::vector<Animation*>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it)
+ delete *it;
+ }
+};
+
+/** Description of a collada animation channel which has been determined to affect the current node */
+struct ChannelEntry
+{
+ const Collada::AnimationChannel* mChannel; ///> the source channel
+ std::string mTransformId; // the ID of the transformation step of the node which is influenced
+ size_t mTransformIndex; // Index into the node's transform chain to apply the channel to
+ size_t mSubElement; // starting index inside the transform data
+
+ // resolved data references
+ const Collada::Accessor* mTimeAccessor; ///> Collada accessor to the time values
+ const Collada::Data* mTimeData; ///> Source data array for the time values
+ const Collada::Accessor* mValueAccessor; ///> Collada accessor to the key value values
+ const Collada::Data* mValueData; ///> Source datat array for the key value values
+
+ ChannelEntry() : mChannel(0), mTransformIndex(0), mSubElement(0), mTimeAccessor(0), mTimeData(0), mValueAccessor(0), mValueData(0) {}
+};
+
+} // end of namespace Collada
+} // end of namespace Assimp
+
+#endif // AI_COLLADAHELPER_H_INC
diff --git a/src/3rdparty/assimp/code/ColladaLoader.cpp b/src/3rdparty/assimp/code/ColladaLoader.cpp
new file mode 100644
index 000000000..b95d6bee8
--- /dev/null
+++ b/src/3rdparty/assimp/code/ColladaLoader.cpp
@@ -0,0 +1,1492 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the Collada loader */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_DAE_IMPORTER
+
+#include "../include/aiAnim.h"
+#include "ColladaLoader.h"
+#include "ColladaParser.h"
+
+#include "fast_atof.h"
+#include "ParsingUtils.h"
+#include "SkeletonMeshBuilder.h"
+
+#include "time.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ColladaLoader::ColladaLoader()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ColladaLoader::~ColladaLoader()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ // check file extension
+ std::string extension = GetExtension(pFile);
+
+ if ( extension == "dae")
+ return true;
+
+ // XML - too generic, we need to open the file and search for typical keywords
+ if ( extension == "xml" || !extension.length() || checkSig) {
+ /* If CanRead() is called in order to check whether we
+ * support a specific file extension in general pIOHandler
+ * might be NULL and it's our duty to return true here.
+ */
+ if (!pIOHandler)return true;
+ const char* tokens[] = {"collada"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get file extension list
+void ColladaLoader::GetExtensionList( std::set<std::string>& extensions )
+{
+ extensions.insert("dae");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+ mFileName = pFile;
+
+ // clean all member arrays - just for safety, it should work even if we did not
+ mMeshIndexByID.clear();
+ mMaterialIndexByName.clear();
+ mMeshes.clear();
+ newMats.clear();
+ mLights.clear();
+ mCameras.clear();
+ mTextures.clear();
+
+ // parse the input file
+ ColladaParser parser( pIOHandler, pFile);
+
+ if ( !parser.mRootNode)
+ throw DeadlyImportError( "Collada: File came out empty. Something is wrong here.");
+
+ // reserve some storage to avoid unnecessary reallocs
+ newMats.reserve(parser.mMaterialLibrary.size()*2);
+ mMeshes.reserve(parser.mMeshLibrary.size()*2);
+
+ mCameras.reserve(parser.mCameraLibrary.size());
+ mLights.reserve(parser.mLightLibrary.size());
+
+ // create the materials first, for the meshes to find
+ BuildMaterials( parser, pScene);
+
+ // build the node hierarchy from it
+ pScene->mRootNode = BuildHierarchy( parser, parser.mRootNode);
+
+ // ... then fill the materials with the now adjusted settings
+ FillMaterials(parser, pScene);
+
+ // Convert to Y_UP, if different orientation
+ if ( parser.mUpDirection == ColladaParser::UP_X)
+ pScene->mRootNode->mTransformation *= aiMatrix4x4(
+ 0, -1, 0, 0,
+ 1, 0, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+ else if ( parser.mUpDirection == ColladaParser::UP_Z)
+ pScene->mRootNode->mTransformation *= aiMatrix4x4(
+ 1, 0, 0, 0,
+ 0, 0, 1, 0,
+ 0, -1, 0, 0,
+ 0, 0, 0, 1);
+
+ // store all meshes
+ StoreSceneMeshes( pScene);
+
+ // store all materials
+ StoreSceneMaterials( pScene);
+
+ // store all lights
+ StoreSceneLights( pScene);
+
+ // store all cameras
+ StoreSceneCameras( pScene);
+
+ // store all animations
+ StoreAnimations( pScene, parser);
+
+
+ // If no meshes have been loaded, it's probably just an animated skeleton.
+ if (!pScene->mNumMeshes) {
+
+ SkeletonMeshBuilder hero(pScene);
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively constructs a scene node for the given parser node and returns it.
+aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode)
+{
+ // create a node for it
+ aiNode* node = new aiNode();
+
+ // find a name for the new node. It's more complicated than you might think
+ node->mName.Set( FindNameForNode( pNode));
+
+ // calculate the transformation matrix for it
+ node->mTransformation = pParser.CalculateResultTransform( pNode->mTransforms);
+
+ // now resolve node instances
+ std::vector<const Collada::Node*> instances;
+ ResolveNodeInstances(pParser,pNode,instances);
+
+ // add children. first the *real* ones
+ node->mNumChildren = pNode->mChildren.size()+instances.size();
+ node->mChildren = new aiNode*[node->mNumChildren];
+
+ for ( size_t a = 0; a < pNode->mChildren.size(); a++)
+ {
+ node->mChildren[a] = BuildHierarchy( pParser, pNode->mChildren[a]);
+ node->mChildren[a]->mParent = node;
+ }
+
+ // ... and finally the resolved node instances
+ for ( size_t a = 0; a < instances.size(); a++)
+ {
+ node->mChildren[pNode->mChildren.size() + a] = BuildHierarchy( pParser, instances[a]);
+ node->mChildren[pNode->mChildren.size() + a]->mParent = node;
+ }
+
+ // construct meshes
+ BuildMeshesForNode( pParser, pNode, node);
+
+ // construct cameras
+ BuildCamerasForNode(pParser, pNode, node);
+
+ // construct lights
+ BuildLightsForNode(pParser, pNode, node);
+ return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Resolve node instances
+void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode,
+ std::vector<const Collada::Node*>& resolved)
+{
+ // reserve enough storage
+ resolved.reserve(pNode->mNodeInstances.size());
+
+ // ... and iterate through all nodes to be instanced as children of pNode
+ for (std::vector<Collada::NodeInstance>::const_iterator it = pNode->mNodeInstances.begin(),
+ end = pNode->mNodeInstances.end(); it != end; ++it)
+ {
+ // find the corresponding node in the library
+ const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find((*it).mNode);
+ Collada::Node* nd = itt == pParser.mNodeLibrary.end() ? NULL : (*itt).second;
+
+ // FIX for http://sourceforge.net/tracker/?func=detail&aid=3054873&group_id=226462&atid=1067632
+ // need to check for both name and ID to catch all. To avoid breaking valid files,
+ // the workaround is only enabled when the first attempt to resolve the node has failed.
+ if (!nd) {
+ nd = const_cast<Collada::Node*>(FindNode(pParser.mRootNode,(*it).mNode));
+ }
+ if (!nd)
+ DefaultLogger::get()->error("Collada: Unable to resolve reference to instanced node " + (*it).mNode);
+
+ else {
+ // attach this node to the list of children
+ resolved.push_back(nd);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Resolve UV channels
+void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler,
+ const Collada::SemanticMappingTable& table)
+{
+ std::map<std::string, Collada::InputSemanticMapEntry>::const_iterator it = table.mMap.find(sampler.mUVChannel);
+ if (it != table.mMap.end()) {
+ if (it->second.mType != Collada::IT_Texcoord)
+ DefaultLogger::get()->error("Collada: Unexpected effect input mapping");
+
+ sampler.mUVId = it->second.mSet;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Builds lights for the given node and references them
+void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
+{
+ BOOST_FOREACH( const Collada::LightInstance& lid, pNode->mLights)
+ {
+ // find the referred light
+ ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find( lid.mLight);
+ if ( srcLightIt == pParser.mLightLibrary.end())
+ {
+ DefaultLogger::get()->warn("Collada: Unable to find light for ID \"" + lid.mLight + "\". Skipping.");
+ continue;
+ }
+ const Collada::Light* srcLight = &srcLightIt->second;
+ if (srcLight->mType == aiLightSource_AMBIENT) {
+ DefaultLogger::get()->error("Collada: Skipping ambient light for the moment");
+ continue;
+ }
+
+ // now fill our ai data structure
+ aiLight* out = new aiLight();
+ out->mName = pTarget->mName;
+ out->mType = (aiLightSourceType)srcLight->mType;
+
+ // collada lights point in -Z by default, rest is specified in node transform
+ out->mDirection = aiVector3D(0.f,0.f,-1.f);
+
+ out->mAttenuationConstant = srcLight->mAttConstant;
+ out->mAttenuationLinear = srcLight->mAttLinear;
+ out->mAttenuationQuadratic = srcLight->mAttQuadratic;
+
+ // collada doesn't differenciate between these color types
+ out->mColorDiffuse = out->mColorSpecular = out->mColorAmbient = srcLight->mColor*srcLight->mIntensity;
+
+ // convert falloff angle and falloff exponent in our representation, if given
+ if (out->mType == aiLightSource_SPOT) {
+
+ out->mAngleInnerCone = AI_DEG_TO_RAD( srcLight->mFalloffAngle );
+
+ // ... some extension magic
+ if (srcLight->mOuterAngle == 10e10f)
+ {
+ // ... some deprecation magic
+ if (srcLight->mPenumbraAngle == 10e10f)
+ {
+ // Need to rely on falloff_exponent. I don't know how to interpret it, so I need to guess ....
+ // epsilon chosen to be 0.1
+ out->mAngleOuterCone = AI_DEG_TO_RAD (acos(pow(0.1f,1.f/srcLight->mFalloffExponent))+
+ srcLight->mFalloffAngle);
+ }
+ else {
+ out->mAngleOuterCone = out->mAngleInnerCone + AI_DEG_TO_RAD( srcLight->mPenumbraAngle );
+ if (out->mAngleOuterCone < out->mAngleInnerCone)
+ std::swap(out->mAngleInnerCone,out->mAngleOuterCone);
+ }
+ }
+ else out->mAngleOuterCone = AI_DEG_TO_RAD( srcLight->mOuterAngle );
+ }
+
+ // add to light list
+ mLights.push_back(out);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Builds cameras for the given node and references them
+void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
+{
+ BOOST_FOREACH( const Collada::CameraInstance& cid, pNode->mCameras)
+ {
+ // find the referred light
+ ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find( cid.mCamera);
+ if ( srcCameraIt == pParser.mCameraLibrary.end())
+ {
+ DefaultLogger::get()->warn("Collada: Unable to find camera for ID \"" + cid.mCamera + "\". Skipping.");
+ continue;
+ }
+ const Collada::Camera* srcCamera = &srcCameraIt->second;
+
+ // orthographic cameras not yet supported in Assimp
+ if (srcCamera->mOrtho) {
+ DefaultLogger::get()->warn("Collada: Orthographic cameras are not supported.");
+ }
+
+ // now fill our ai data structure
+ aiCamera* out = new aiCamera();
+ out->mName = pTarget->mName;
+
+ // collada cameras point in -Z by default, rest is specified in node transform
+ out->mLookAt = aiVector3D(0.f,0.f,-1.f);
+
+ // near/far z is already ok
+ out->mClipPlaneFar = srcCamera->mZFar;
+ out->mClipPlaneNear = srcCamera->mZNear;
+
+ // ... but for the rest some values are optional
+ // and we need to compute the others in any combination
+ if (srcCamera->mAspect != 10e10f)
+ out->mAspect = srcCamera->mAspect;
+
+ if (srcCamera->mHorFov != 10e10f) {
+ out->mHorizontalFOV = srcCamera->mHorFov;
+
+ if (srcCamera->mVerFov != 10e10f && srcCamera->mAspect == 10e10f) {
+ out->mAspect = tan(AI_DEG_TO_RAD(srcCamera->mHorFov)) /
+ tan(AI_DEG_TO_RAD(srcCamera->mVerFov));
+ }
+ }
+ else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) {
+ out->mHorizontalFOV = 2.0f * AI_RAD_TO_DEG(atan(srcCamera->mAspect *
+ tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f)));
+ }
+
+ // Collada uses degrees, we use radians
+ out->mHorizontalFOV = AI_DEG_TO_RAD(out->mHorizontalFOV);
+
+ // add to camera list
+ mCameras.push_back(out);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Builds meshes for the given node and references them
+void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
+{
+ // accumulated mesh references by this node
+ std::vector<size_t> newMeshRefs;
+ newMeshRefs.reserve(pNode->mMeshes.size());
+
+ // add a mesh for each subgroup in each collada mesh
+ BOOST_FOREACH( const Collada::MeshInstance& mid, pNode->mMeshes)
+ {
+ const Collada::Mesh* srcMesh = NULL;
+ const Collada::Controller* srcController = NULL;
+
+ // find the referred mesh
+ ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMeshOrController);
+ if ( srcMeshIt == pParser.mMeshLibrary.end())
+ {
+ // if not found in the mesh-library, it might also be a controller referring to a mesh
+ ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find( mid.mMeshOrController);
+ if ( srcContrIt != pParser.mControllerLibrary.end())
+ {
+ srcController = &srcContrIt->second;
+ srcMeshIt = pParser.mMeshLibrary.find( srcController->mMeshId);
+ if ( srcMeshIt != pParser.mMeshLibrary.end())
+ srcMesh = srcMeshIt->second;
+ }
+
+ if ( !srcMesh)
+ {
+ DefaultLogger::get()->warn( boost::str( boost::format( "Collada: Unable to find geometry for ID \"%s\". Skipping.") % mid.mMeshOrController));
+ continue;
+ }
+ } else
+ {
+ // ID found in the mesh library -> direct reference to an unskinned mesh
+ srcMesh = srcMeshIt->second;
+ }
+
+ // build a mesh for each of its subgroups
+ size_t vertexStart = 0, faceStart = 0;
+ for ( size_t sm = 0; sm < srcMesh->mSubMeshes.size(); ++sm)
+ {
+ const Collada::SubMesh& submesh = srcMesh->mSubMeshes[sm];
+ if ( submesh.mNumFaces == 0)
+ continue;
+
+ // find material assigned to this submesh
+ std::string meshMaterial;
+ std::map<std::string, Collada::SemanticMappingTable >::const_iterator meshMatIt = mid.mMaterials.find( submesh.mMaterial);
+
+ const Collada::SemanticMappingTable* table = NULL;
+ if ( meshMatIt != mid.mMaterials.end())
+ {
+ table = &meshMatIt->second;
+ meshMaterial = table->mMatName;
+ }
+ else
+ {
+ DefaultLogger::get()->warn( boost::str( boost::format( "Collada: No material specified for subgroup \"%s\" in geometry \"%s\".") % submesh.mMaterial % mid.mMeshOrController));
+ if ( !mid.mMaterials.empty() )
+ meshMaterial = mid.mMaterials.begin()->second.mMatName;
+ }
+
+ // OK ... here the *real* fun starts ... we have the vertex-input-to-effect-semantic-table
+ // given. The only mapping stuff which we do actually support is the UV channel.
+ std::map<std::string, size_t>::const_iterator matIt = mMaterialIndexByName.find( meshMaterial);
+ unsigned int matIdx;
+ if ( matIt != mMaterialIndexByName.end())
+ matIdx = matIt->second;
+ else
+ matIdx = 0;
+
+ if (table && !table->mMap.empty() ) {
+ std::pair<Collada::Effect*, aiMaterial*>& mat = newMats[matIdx];
+
+ // Iterate through all texture channels assigned to the effect and
+ // check whether we have mapping information for it.
+ ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table);
+ ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table);
+ ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table);
+ ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table);
+ ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent,*table);
+ ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table);
+ }
+
+ // built lookup index of the Mesh-Submesh-Material combination
+ ColladaMeshIndex index( mid.mMeshOrController, sm, meshMaterial);
+
+ // if we already have the mesh at the library, just add its index to the node's array
+ std::map<ColladaMeshIndex, size_t>::const_iterator dstMeshIt = mMeshIndexByID.find( index);
+ if ( dstMeshIt != mMeshIndexByID.end()) {
+ newMeshRefs.push_back( dstMeshIt->second);
+ }
+ else
+ {
+ // else we have to add the mesh to the collection and store its newly assigned index at the node
+ aiMesh* dstMesh = CreateMesh( pParser, srcMesh, submesh, srcController, vertexStart, faceStart);
+
+ // store the mesh, and store its new index in the node
+ newMeshRefs.push_back( mMeshes.size());
+ mMeshIndexByID[index] = mMeshes.size();
+ mMeshes.push_back( dstMesh);
+ vertexStart += dstMesh->mNumVertices; faceStart += submesh.mNumFaces;
+
+ // assign the material index
+ dstMesh->mMaterialIndex = matIdx;
+ }
+ }
+ }
+
+ // now place all mesh references we gathered in the target node
+ pTarget->mNumMeshes = newMeshRefs.size();
+ if ( newMeshRefs.size())
+ {
+ pTarget->mMeshes = new unsigned int[pTarget->mNumMeshes];
+ std::copy( newMeshRefs.begin(), newMeshRefs.end(), pTarget->mMeshes);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh
+aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
+ const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace)
+{
+ aiMesh* dstMesh = new aiMesh;
+
+ // count the vertices addressed by its faces
+ const size_t numVertices = std::accumulate( pSrcMesh->mFaceSize.begin() + pStartFace,
+ pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, 0);
+
+ // copy positions
+ dstMesh->mNumVertices = numVertices;
+ dstMesh->mVertices = new aiVector3D[numVertices];
+ std::copy( pSrcMesh->mPositions.begin() + pStartVertex, pSrcMesh->mPositions.begin() +
+ pStartVertex + numVertices, dstMesh->mVertices);
+
+ // normals, if given. HACK: (thom) Due to the unclear Collada spec we never
+ // know if we have the same number of normals as there are positions. So we
+ // also ignore any vertex attribute if it has a different count
+ if ( pSrcMesh->mNormals.size() >= pStartVertex + numVertices)
+ {
+ dstMesh->mNormals = new aiVector3D[numVertices];
+ std::copy( pSrcMesh->mNormals.begin() + pStartVertex, pSrcMesh->mNormals.begin() +
+ pStartVertex + numVertices, dstMesh->mNormals);
+ }
+
+ // tangents, if given.
+ if ( pSrcMesh->mTangents.size() >= pStartVertex + numVertices)
+ {
+ dstMesh->mTangents = new aiVector3D[numVertices];
+ std::copy( pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() +
+ pStartVertex + numVertices, dstMesh->mTangents);
+ }
+
+ // bitangents, if given.
+ if ( pSrcMesh->mBitangents.size() >= pStartVertex + numVertices)
+ {
+ dstMesh->mBitangents = new aiVector3D[numVertices];
+ std::copy( pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() +
+ pStartVertex + numVertices, dstMesh->mBitangents);
+ }
+
+ // same for texturecoords, as many as we have
+ // empty slots are not allowed, need to pack and adjust UV indexes accordingly
+ for ( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
+ {
+ if ( pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices)
+ {
+ dstMesh->mTextureCoords[real] = new aiVector3D[numVertices];
+ for ( size_t b = 0; b < numVertices; ++b)
+ dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex+b];
+
+ dstMesh->mNumUVComponents[real] = pSrcMesh->mNumUVComponents[a];
+ ++real;
+ }
+ }
+
+ // same for vertex colors, as many as we have. again the same packing to avoid empty slots
+ for ( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
+ {
+ if ( pSrcMesh->mColors[a].size() >= pStartVertex + numVertices)
+ {
+ dstMesh->mColors[real] = new aiColor4D[numVertices];
+ std::copy( pSrcMesh->mColors[a].begin() + pStartVertex, pSrcMesh->mColors[a].begin() + pStartVertex + numVertices,dstMesh->mColors[real]);
+ ++real;
+ }
+ }
+
+ // create faces. Due to the fact that each face uses unique vertices, we can simply count up on each vertex
+ size_t vertex = 0;
+ dstMesh->mNumFaces = pSubMesh.mNumFaces;
+ dstMesh->mFaces = new aiFace[dstMesh->mNumFaces];
+ for ( size_t a = 0; a < dstMesh->mNumFaces; ++a)
+ {
+ size_t s = pSrcMesh->mFaceSize[ pStartFace + a];
+ aiFace& face = dstMesh->mFaces[a];
+ face.mNumIndices = s;
+ face.mIndices = new unsigned int[s];
+ for ( size_t b = 0; b < s; ++b)
+ face.mIndices[b] = vertex++;
+ }
+
+ // create bones if given
+ if ( pSrcController)
+ {
+ // refuse if the vertex count does not match
+// if ( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices)
+// throw DeadlyImportError( "Joint Controller vertex count does not match mesh vertex count");
+
+ // resolve references - joint names
+ const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource);
+ const Collada::Data& jointNames = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointNamesAcc.mSource);
+ // joint offset matrices
+ const Collada::Accessor& jointMatrixAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointOffsetMatrixSource);
+ const Collada::Data& jointMatrices = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointMatrixAcc.mSource);
+ // joint vertex_weight name list - should refer to the same list as the joint names above. If not, report and reconsider
+ const Collada::Accessor& weightNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputJoints.mAccessor);
+ if ( &weightNamesAcc != &jointNamesAcc)
+ throw DeadlyImportError( "Temporary implementational lazyness. If you read this, please report to the author.");
+ // vertex weights
+ const Collada::Accessor& weightsAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor);
+ const Collada::Data& weights = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightsAcc.mSource);
+
+ if ( !jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray)
+ throw DeadlyImportError( "Data type mismatch while resolving mesh joints");
+ // sanity check: we rely on the vertex weights always coming as pairs of BoneIndex-WeightIndex
+ if ( pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1)
+ throw DeadlyImportError( "Unsupported vertex_weight adresssing scheme.");
+
+ // create containers to collect the weights for each bone
+ size_t numBones = jointNames.mStrings.size();
+ std::vector<std::vector<aiVertexWeight> > dstBones( numBones);
+
+ // build a temporary array of pointers to the start of each vertex's weights
+ typedef std::vector< std::pair<size_t, size_t> > IndexPairVector;
+ std::vector<IndexPairVector::const_iterator> weightStartPerVertex( pSrcController->mWeightCounts.size());
+ IndexPairVector::const_iterator pit = pSrcController->mWeights.begin();
+ for ( size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a)
+ {
+ weightStartPerVertex[a] = pit;
+ pit += pSrcController->mWeightCounts[a];
+ }
+
+ // now for each vertex put the corresponding vertex weights into each bone's weight collection
+ for ( size_t a = pStartVertex; a < pStartVertex + numVertices; ++a)
+ {
+ // which position index was responsible for this vertex? that's also the index by which
+ // the controller assigns the vertex weights
+ size_t orgIndex = pSrcMesh->mFacePosIndices[a];
+ // find the vertex weights for this vertex
+ IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex];
+ size_t pairCount = pSrcController->mWeightCounts[orgIndex];
+
+ for ( size_t b = 0; b < pairCount; ++b, ++iit)
+ {
+ size_t jointIndex = iit->first;
+ size_t vertexIndex = iit->second;
+
+ float weight = ReadFloat( weightsAcc, weights, vertexIndex, 0);
+
+ // one day I gonna kill that XSI Collada exporter
+ if ( weight > 0.0f)
+ {
+ aiVertexWeight w;
+ w.mVertexId = a - pStartVertex;
+ w.mWeight = weight;
+ dstBones[jointIndex].push_back( w);
+ }
+ }
+ }
+
+ // count the number of bones which influence vertices of the current submesh
+ size_t numRemainingBones = 0;
+ for ( std::vector<std::vector<aiVertexWeight> >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it)
+ if ( it->size() > 0)
+ numRemainingBones++;
+
+ // create bone array and copy bone weights one by one
+ dstMesh->mNumBones = numRemainingBones;
+ dstMesh->mBones = new aiBone*[numRemainingBones];
+ size_t boneCount = 0;
+ for ( size_t a = 0; a < numBones; ++a)
+ {
+ // omit bones without weights
+ if ( dstBones[a].size() == 0)
+ continue;
+
+ // create bone with its weights
+ aiBone* bone = new aiBone;
+ bone->mName = ReadString( jointNamesAcc, jointNames, a);
+ bone->mOffsetMatrix.a1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 0);
+ bone->mOffsetMatrix.a2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 1);
+ bone->mOffsetMatrix.a3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 2);
+ bone->mOffsetMatrix.a4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 3);
+ bone->mOffsetMatrix.b1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 4);
+ bone->mOffsetMatrix.b2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 5);
+ bone->mOffsetMatrix.b3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 6);
+ bone->mOffsetMatrix.b4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 7);
+ bone->mOffsetMatrix.c1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 8);
+ bone->mOffsetMatrix.c2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 9);
+ bone->mOffsetMatrix.c3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 10);
+ bone->mOffsetMatrix.c4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 11);
+ bone->mNumWeights = dstBones[a].size();
+ bone->mWeights = new aiVertexWeight[bone->mNumWeights];
+ std::copy( dstBones[a].begin(), dstBones[a].end(), bone->mWeights);
+
+ // apply bind shape matrix to offset matrix
+ aiMatrix4x4 bindShapeMatrix;
+ bindShapeMatrix.a1 = pSrcController->mBindShapeMatrix[0];
+ bindShapeMatrix.a2 = pSrcController->mBindShapeMatrix[1];
+ bindShapeMatrix.a3 = pSrcController->mBindShapeMatrix[2];
+ bindShapeMatrix.a4 = pSrcController->mBindShapeMatrix[3];
+ bindShapeMatrix.b1 = pSrcController->mBindShapeMatrix[4];
+ bindShapeMatrix.b2 = pSrcController->mBindShapeMatrix[5];
+ bindShapeMatrix.b3 = pSrcController->mBindShapeMatrix[6];
+ bindShapeMatrix.b4 = pSrcController->mBindShapeMatrix[7];
+ bindShapeMatrix.c1 = pSrcController->mBindShapeMatrix[8];
+ bindShapeMatrix.c2 = pSrcController->mBindShapeMatrix[9];
+ bindShapeMatrix.c3 = pSrcController->mBindShapeMatrix[10];
+ bindShapeMatrix.c4 = pSrcController->mBindShapeMatrix[11];
+ bindShapeMatrix.d1 = pSrcController->mBindShapeMatrix[12];
+ bindShapeMatrix.d2 = pSrcController->mBindShapeMatrix[13];
+ bindShapeMatrix.d3 = pSrcController->mBindShapeMatrix[14];
+ bindShapeMatrix.d4 = pSrcController->mBindShapeMatrix[15];
+ bone->mOffsetMatrix *= bindShapeMatrix;
+
+ // HACK: (thom) Some exporters address the bone nodes by SID, others address them by ID or even name.
+ // Therefore I added a little name replacement here: I search for the bone's node by either name, ID or SID,
+ // and replace the bone's name by the node's name so that the user can use the standard
+ // find-by-name method to associate nodes with bones.
+ const Collada::Node* bnode = FindNode( pParser.mRootNode, bone->mName.data);
+ if ( !bnode)
+ bnode = FindNodeBySID( pParser.mRootNode, bone->mName.data);
+
+ // assign the name that we would have assigned for the source node
+ if ( bnode)
+ bone->mName.Set( FindNameForNode( bnode));
+ else
+ DefaultLogger::get()->warn( boost::str( boost::format( "ColladaLoader::CreateMesh(): could not find corresponding node for joint \"%s\".") % bone->mName.data));
+
+ // and insert bone
+ dstMesh->mBones[boneCount++] = bone;
+ }
+ }
+
+ return dstMesh;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Stores all meshes in the given scene
+void ColladaLoader::StoreSceneMeshes( aiScene* pScene)
+{
+ pScene->mNumMeshes = mMeshes.size();
+ if ( mMeshes.size() > 0)
+ {
+ pScene->mMeshes = new aiMesh*[mMeshes.size()];
+ std::copy( mMeshes.begin(), mMeshes.end(), pScene->mMeshes);
+ mMeshes.clear();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Stores all cameras in the given scene
+void ColladaLoader::StoreSceneCameras( aiScene* pScene)
+{
+ pScene->mNumCameras = mCameras.size();
+ if ( mCameras.size() > 0)
+ {
+ pScene->mCameras = new aiCamera*[mCameras.size()];
+ std::copy( mCameras.begin(), mCameras.end(), pScene->mCameras);
+ mCameras.clear();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Stores all lights in the given scene
+void ColladaLoader::StoreSceneLights( aiScene* pScene)
+{
+ pScene->mNumLights = mLights.size();
+ if ( mLights.size() > 0)
+ {
+ pScene->mLights = new aiLight*[mLights.size()];
+ std::copy( mLights.begin(), mLights.end(), pScene->mLights);
+ mLights.clear();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Stores all textures in the given scene
+void ColladaLoader::StoreSceneTextures( aiScene* pScene)
+{
+ pScene->mNumTextures = mTextures.size();
+ if ( mTextures.size() > 0)
+ {
+ pScene->mTextures = new aiTexture*[mTextures.size()];
+ std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures);
+ mTextures.clear();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Stores all materials in the given scene
+void ColladaLoader::StoreSceneMaterials( aiScene* pScene)
+{
+ pScene->mNumMaterials = newMats.size();
+
+ if (newMats.size() > 0) {
+ pScene->mMaterials = new aiMaterial*[newMats.size()];
+ for (unsigned int i = 0; i < newMats.size();++i)
+ pScene->mMaterials[i] = newMats[i].second;
+
+ newMats.clear();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Stores all animations
+void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser)
+{
+ // recursivly collect all animations from the collada scene
+ StoreAnimations( pScene, pParser, &pParser.mAnims, "");
+
+ // catch special case: many animations with the same length, each affecting only a single node.
+ // we need to unite all those single-node-anims to a proper combined animation
+ for ( size_t a = 0; a < mAnims.size(); ++a)
+ {
+ aiAnimation* templateAnim = mAnims[a];
+ if ( templateAnim->mNumChannels == 1)
+ {
+ // search for other single-channel-anims with the same duration
+ std::vector<size_t> collectedAnimIndices;
+ for ( size_t b = a+1; b < mAnims.size(); ++b)
+ {
+ aiAnimation* other = mAnims[b];
+ if ( other->mNumChannels == 1 && other->mDuration == templateAnim->mDuration && other->mTicksPerSecond == templateAnim->mTicksPerSecond )
+ collectedAnimIndices.push_back( b);
+ }
+
+ // if there are other animations which fit the template anim, combine all channels into a single anim
+ if ( !collectedAnimIndices.empty() )
+ {
+ aiAnimation* combinedAnim = new aiAnimation();
+ combinedAnim->mName = aiString( std::string( "combinedAnim_") + char( '0' + a));
+ combinedAnim->mDuration = templateAnim->mDuration;
+ combinedAnim->mTicksPerSecond = templateAnim->mTicksPerSecond;
+ combinedAnim->mNumChannels = collectedAnimIndices.size() + 1;
+ combinedAnim->mChannels = new aiNodeAnim*[combinedAnim->mNumChannels];
+ // add the template anim as first channel by moving its aiNodeAnim to the combined animation
+ combinedAnim->mChannels[0] = templateAnim->mChannels[0];
+ templateAnim->mChannels[0] = NULL;
+ delete templateAnim;
+ // combined animation replaces template animation in the anim array
+ mAnims[a] = combinedAnim;
+
+ // move the memory of all other anims to the combined anim and erase them from the source anims
+ for ( size_t b = 0; b < collectedAnimIndices.size(); ++b)
+ {
+ aiAnimation* srcAnimation = mAnims[collectedAnimIndices[b]];
+ combinedAnim->mChannels[1 + b] = srcAnimation->mChannels[0];
+ srcAnimation->mChannels[0] = NULL;
+ delete srcAnimation;
+ }
+
+ // in a second go, delete all the single-channel-anims that we've stripped from their channels
+ // back to front to preserve indices - you know, removing an element from a vector moves all elements behind the removed one
+ while ( !collectedAnimIndices.empty() )
+ {
+ mAnims.erase( mAnims.begin() + collectedAnimIndices.back());
+ collectedAnimIndices.pop_back();
+ }
+ }
+ }
+ }
+
+ // now store all anims in the scene
+ if ( !mAnims.empty())
+ {
+ pScene->mNumAnimations = mAnims.size();
+ pScene->mAnimations = new aiAnimation*[mAnims.size()];
+ std::copy( mAnims.begin(), mAnims.end(), pScene->mAnimations);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructs the animations for the given source anim
+void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string pPrefix)
+{
+ std::string animName = pPrefix.empty() ? pSrcAnim->mName : pPrefix + "_" + pSrcAnim->mName;
+
+ // create nested animations, if given
+ for ( std::vector<Collada::Animation*>::const_iterator it = pSrcAnim->mSubAnims.begin(); it != pSrcAnim->mSubAnims.end(); ++it)
+ StoreAnimations( pScene, pParser, *it, animName);
+
+ // create animation channels, if any
+ if ( !pSrcAnim->mChannels.empty())
+ CreateAnimation( pScene, pParser, pSrcAnim, animName);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructs the animation for the given source anim
+void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName)
+{
+ // collect a list of animatable nodes
+ std::vector<const aiNode*> nodes;
+ CollectNodes( pScene->mRootNode, nodes);
+
+ std::vector<aiNodeAnim*> anims;
+ for ( std::vector<const aiNode*>::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit)
+ {
+ // find all the collada anim channels which refer to the current node
+ std::vector<Collada::ChannelEntry> entries;
+ std::string nodeName = (*nit)->mName.data;
+
+ // find the collada node corresponding to the aiNode
+ const Collada::Node* srcNode = FindNode( pParser.mRootNode, nodeName);
+// ai_assert( srcNode != NULL);
+ if ( !srcNode)
+ continue;
+
+ // now check all channels if they affect the current node
+ for ( std::vector<Collada::AnimationChannel>::const_iterator cit = pSrcAnim->mChannels.begin();
+ cit != pSrcAnim->mChannels.end(); ++cit)
+ {
+ const Collada::AnimationChannel& srcChannel = *cit;
+ Collada::ChannelEntry entry;
+
+ // we except the animation target to be of type "nodeName/transformID.subElement". Ignore all others
+ // find the slash that separates the node name - there should be only one
+ std::string::size_type slashPos = srcChannel.mTarget.find( '/');
+ if ( slashPos == std::string::npos)
+ continue;
+ if ( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos)
+ continue;
+ std::string targetID = srcChannel.mTarget.substr( 0, slashPos);
+ if ( targetID != srcNode->mID)
+ continue;
+
+ // find the dot that separates the transformID - there should be only one or zero
+ std::string::size_type dotPos = srcChannel.mTarget.find( '.');
+ if ( dotPos != std::string::npos)
+ {
+ if ( srcChannel.mTarget.find( '.', dotPos+1) != std::string::npos)
+ continue;
+
+ entry.mTransformId = srcChannel.mTarget.substr( slashPos+1, dotPos - slashPos - 1);
+
+ std::string subElement = srcChannel.mTarget.substr( dotPos+1);
+ if ( subElement == "ANGLE")
+ entry.mSubElement = 3; // last number in an Axis-Angle-Transform is the angle
+ else if ( subElement == "X")
+ entry.mSubElement = 0;
+ else if ( subElement == "Y")
+ entry.mSubElement = 1;
+ else if ( subElement == "Z")
+ entry.mSubElement = 2;
+ else
+ DefaultLogger::get()->warn( boost::str( boost::format( "Unknown anim subelement \"%s\". Ignoring") % subElement));
+ } else
+ {
+ // no subelement following, transformId is remaining string
+ entry.mTransformId = srcChannel.mTarget.substr( slashPos+1);
+ }
+
+ // determine which transform step is affected by this channel
+ entry.mTransformIndex = 0xffffffff;
+ for ( size_t a = 0; a < srcNode->mTransforms.size(); ++a)
+ if ( srcNode->mTransforms[a].mID == entry.mTransformId)
+ entry.mTransformIndex = a;
+
+ if ( entry.mTransformIndex == 0xffffffff)
+ continue;
+
+ entry.mChannel = &(*cit);
+ entries.push_back( entry);
+ }
+
+ // if there's no channel affecting the current node, we skip it
+ if ( entries.empty())
+ continue;
+
+ // resolve the data pointers for all anim channels. Find the minimum time while we're at it
+ float startTime = 1e20f, endTime = -1e20f;
+ for ( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
+ {
+ Collada::ChannelEntry& e = *it;
+ e.mTimeAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceTimes);
+ e.mTimeData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mTimeAccessor->mSource);
+ e.mValueAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceValues);
+ e.mValueData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mValueAccessor->mSource);
+
+ // time count and value count must match
+ if ( e.mTimeAccessor->mCount != e.mValueAccessor->mCount)
+ throw DeadlyImportError( boost::str( boost::format( "Time count / value count mismatch in animation channel \"%s\".") % e.mChannel->mTarget));
+
+ // find bounding times
+ startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0));
+ endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0));
+ }
+
+ // create a local transformation chain of the node's transforms
+ std::vector<Collada::Transform> transforms = srcNode->mTransforms;
+
+ // now for every unique point in time, find or interpolate the key values for that time
+ // and apply them to the transform chain. Then the node's present transformation can be calculated.
+ float time = startTime;
+ std::vector<aiMatrix4x4> resultTrafos;
+ while ( 1)
+ {
+ for ( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
+ {
+ Collada::ChannelEntry& e = *it;
+
+ // find the keyframe behind the current point in time
+ size_t pos = 0;
+ float postTime = 0.f;
+ while ( 1)
+ {
+ if ( pos >= e.mTimeAccessor->mCount)
+ break;
+ postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0);
+ if ( postTime >= time)
+ break;
+ ++pos;
+ }
+
+ pos = std::min( pos, e.mTimeAccessor->mCount-1);
+
+ // read values from there
+ float temp[16];
+ for ( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
+ temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c);
+
+ // if not exactly at the key time, interpolate with previous value set
+ if ( postTime > time && pos > 0)
+ {
+ float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0);
+ float factor = (time - postTime) / (preTime - postTime);
+
+ for ( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
+ {
+ float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c);
+ temp[c] += (v - temp[c]) * factor;
+ }
+ }
+
+ // Apply values to current transformation
+ std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement);
+ }
+
+ // Calculate resulting transformation
+ aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms);
+
+ // out of lazyness: we store the time in matrix.d4
+ mat.d4 = time;
+ resultTrafos.push_back( mat);
+
+ // find next point in time to evaluate. That's the closest frame larger than the current in any channel
+ float nextTime = 1e20f;
+ for ( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
+ {
+ Collada::ChannelEntry& e = *it;
+
+ // find the next time value larger than the current
+ size_t pos = 0;
+ while ( pos < e.mTimeAccessor->mCount)
+ {
+ float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0);
+ if ( t > time)
+ {
+ nextTime = std::min( nextTime, t);
+ break;
+ }
+ ++pos;
+ }
+ }
+
+ // no more keys on any channel after the current time -> we're done
+ if ( nextTime > 1e19)
+ break;
+
+ // else construct next keyframe at this following time point
+ time = nextTime;
+ }
+
+ // there should be some keyframes
+ ai_assert( resultTrafos.size() > 0);
+
+ // build an animation channel for the given node out of these trafo keys
+ aiNodeAnim* dstAnim = new aiNodeAnim;
+ dstAnim->mNodeName = nodeName;
+ dstAnim->mNumPositionKeys = resultTrafos.size();
+ dstAnim->mNumRotationKeys= resultTrafos.size();
+ dstAnim->mNumScalingKeys = resultTrafos.size();
+ dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()];
+ dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()];
+ dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()];
+
+ for ( size_t a = 0; a < resultTrafos.size(); ++a)
+ {
+ const aiMatrix4x4& mat = resultTrafos[a];
+ double time = double( mat.d4); // remember? time is stored in mat.d4
+
+ dstAnim->mPositionKeys[a].mTime = time;
+ dstAnim->mRotationKeys[a].mTime = time;
+ dstAnim->mScalingKeys[a].mTime = time;
+ mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue);
+ }
+
+ anims.push_back( dstAnim);
+ }
+
+ if ( !anims.empty())
+ {
+ aiAnimation* anim = new aiAnimation;
+ anim->mName.Set( pName);
+ anim->mNumChannels = anims.size();
+ anim->mChannels = new aiNodeAnim*[anims.size()];
+ std::copy( anims.begin(), anims.end(), anim->mChannels);
+ anim->mDuration = 0.0f;
+ for ( size_t a = 0; a < anims.size(); ++a)
+ {
+ anim->mDuration = std::max( anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys-1].mTime);
+ anim->mDuration = std::max( anim->mDuration, anims[a]->mRotationKeys[anims[a]->mNumRotationKeys-1].mTime);
+ anim->mDuration = std::max( anim->mDuration, anims[a]->mScalingKeys[anims[a]->mNumScalingKeys-1].mTime);
+ }
+ anim->mTicksPerSecond = 1;
+ mAnims.push_back( anim);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Add a texture to a material structure
+void ColladaLoader::AddTexture ( Assimp::MaterialHelper& mat, const ColladaParser& pParser,
+ const Collada::Effect& effect,
+ const Collada::Sampler& sampler,
+ aiTextureType type, unsigned int idx)
+{
+ // first of all, basic file name
+ const aiString name = FindFilenameForEffectTexture( pParser, effect, sampler.mName );
+ mat.AddProperty( &name, _AI_MATKEY_TEXTURE_BASE, type, idx );
+
+ // mapping mode
+ int map = aiTextureMapMode_Clamp;
+ if (sampler.mWrapU)
+ map = aiTextureMapMode_Wrap;
+ if (sampler.mWrapU && sampler.mMirrorU)
+ map = aiTextureMapMode_Mirror;
+
+ mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_U_BASE, type, idx);
+
+ map = aiTextureMapMode_Clamp;
+ if (sampler.mWrapV)
+ map = aiTextureMapMode_Wrap;
+ if (sampler.mWrapV && sampler.mMirrorV)
+ map = aiTextureMapMode_Mirror;
+
+ mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_V_BASE, type, idx);
+
+ // UV transformation
+ mat.AddProperty(&sampler.mTransform, 1,
+ _AI_MATKEY_UVTRANSFORM_BASE, type, idx);
+
+ // Blend mode
+ mat.AddProperty((int*)&sampler.mOp , 1,
+ _AI_MATKEY_TEXBLEND_BASE, type, idx);
+
+ // Blend factor
+ mat.AddProperty((float*)&sampler.mWeighting , 1,
+ _AI_MATKEY_TEXBLEND_BASE, type, idx);
+
+ // UV source index ... if we didn't resolve the mapping it is actually just
+ // a guess but it works in most cases. We search for the frst occurence of a
+ // number in the channel name. We assume it is the zero-based index into the
+ // UV channel array of all corresponding meshes. It could also be one-based
+ // for some exporters, but we won't care of it unless someone complains about.
+ if (sampler.mUVId != 0xffffffff)
+ map = sampler.mUVId;
+ else {
+ map = -1;
+ for (std::string::const_iterator it = sampler.mUVChannel.begin();it != sampler.mUVChannel.end(); ++it){
+ if (IsNumeric(*it)) {
+ map = strtol10(&(*it));
+ break;
+ }
+ }
+ if (-1 == map) {
+ DefaultLogger::get()->warn("Collada: unable to determine UV channel for texture");
+ map = 0;
+ }
+ }
+ mat.AddProperty(&map,1,_AI_MATKEY_UVWSRC_BASE,type,idx);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Fills materials from the collada material definitions
+void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene*)
+{
+ for (std::vector<std::pair<Collada::Effect*, aiMaterial*> >::iterator it = newMats.begin(),
+ end = newMats.end(); it != end; ++it)
+ {
+ MaterialHelper& mat = (MaterialHelper&)*it->second;
+ Collada::Effect& effect = *it->first;
+
+ // resolve shading mode
+ int shadeMode;
+ if (effect.mFaceted) /* fixme */
+ shadeMode = aiShadingMode_Flat;
+ else {
+ switch( effect.mShadeType)
+ {
+ case Collada::Shade_Constant:
+ shadeMode = aiShadingMode_NoShading;
+ break;
+ case Collada::Shade_Lambert:
+ shadeMode = aiShadingMode_Gouraud;
+ break;
+ case Collada::Shade_Blinn:
+ shadeMode = aiShadingMode_Blinn;
+ break;
+ case Collada::Shade_Phong:
+ shadeMode = aiShadingMode_Phong;
+ break;
+
+ default:
+ DefaultLogger::get()->warn("Collada: Unrecognized shading mode, using gouraud shading");
+ shadeMode = aiShadingMode_Gouraud;
+ break;
+ }
+ }
+ mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ // double-sided?
+ shadeMode = effect.mDoubleSided;
+ mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_TWOSIDED);
+
+ // wireframe?
+ shadeMode = effect.mWireframe;
+ mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME);
+
+ // add material colors
+ mat.AddProperty( &effect.mAmbient, 1,AI_MATKEY_COLOR_AMBIENT);
+ mat.AddProperty( &effect.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mat.AddProperty( &effect.mSpecular, 1,AI_MATKEY_COLOR_SPECULAR);
+ mat.AddProperty( &effect.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
+ mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT);
+ mat.AddProperty( &effect.mReflective, 1, AI_MATKEY_COLOR_REFLECTIVE);
+
+ // scalar properties
+ mat.AddProperty( &effect.mShininess, 1, AI_MATKEY_SHININESS);
+ mat.AddProperty( &effect.mReflectivity, 1, AI_MATKEY_REFLECTIVITY);
+ mat.AddProperty( &effect.mRefractIndex, 1, AI_MATKEY_REFRACTI);
+
+ // transparency, a very hard one. seemingly not all files are following the
+ // specification here .. but we can trick.
+ if (effect.mTransparency > 0.f && effect.mTransparency < 1.f) {
+ effect.mTransparency = 1.f- effect.mTransparency;
+ mat.AddProperty( &effect.mTransparency, 1, AI_MATKEY_OPACITY );
+ mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT );
+ }
+
+ // add textures, if given
+ if ( !effect.mTexAmbient.mName.empty())
+ /* It is merely a lightmap */
+ AddTexture( mat, pParser, effect, effect.mTexAmbient, aiTextureType_LIGHTMAP);
+
+ if ( !effect.mTexEmissive.mName.empty())
+ AddTexture( mat, pParser, effect, effect.mTexEmissive, aiTextureType_EMISSIVE);
+
+ if ( !effect.mTexSpecular.mName.empty())
+ AddTexture( mat, pParser, effect, effect.mTexSpecular, aiTextureType_SPECULAR);
+
+ if ( !effect.mTexDiffuse.mName.empty())
+ AddTexture( mat, pParser, effect, effect.mTexDiffuse, aiTextureType_DIFFUSE);
+
+ if ( !effect.mTexBump.mName.empty())
+ AddTexture( mat, pParser, effect, effect.mTexBump, aiTextureType_HEIGHT);
+
+ if ( !effect.mTexTransparent.mName.empty())
+ AddTexture( mat, pParser, effect, effect.mTexTransparent, aiTextureType_OPACITY);
+
+ if ( !effect.mTexReflective.mName.empty())
+ AddTexture( mat, pParser, effect, effect.mTexReflective, aiTextureType_REFLECTION);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructs materials from the collada material definitions
+void ColladaLoader::BuildMaterials( const ColladaParser& pParser, aiScene*)
+{
+ newMats.reserve(pParser.mMaterialLibrary.size());
+
+ for ( ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin(); matIt != pParser.mMaterialLibrary.end(); ++matIt)
+ {
+ const Collada::Material& material = matIt->second;
+ // a material is only a reference to an effect
+ ColladaParser::EffectLibrary::const_iterator effIt = pParser.mEffectLibrary.find( material.mEffect);
+ if ( effIt == pParser.mEffectLibrary.end())
+ continue;
+ const Collada::Effect& effect = effIt->second;
+
+ // create material
+ Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
+ aiString name( matIt->first);
+ mat->AddProperty(&name,AI_MATKEY_NAME);
+
+ // MEGA SUPER MONSTER HACK by Alex ... It's all my fault, yes.
+ // We store the reference to the effect in the material and
+ // return ... we'll add the actual material properties later
+ // after we processed all meshes. During mesh processing,
+ // we evaluate vertex input mappings. Afterwards we should be
+ // able to correctly setup source UV channels for textures.
+
+ // ... moved to ColladaLoader::FillMaterials()
+ // *duck*
+
+ // store the material
+ mMaterialIndexByName[matIt->first] = newMats.size();
+ newMats.push_back( std::pair<Collada::Effect*, aiMaterial*>(const_cast<Collada::Effect*>(&effect),mat) );
+ }
+ // ScenePreprocessor generates a default material automatically if none is there.
+ // All further code here in this loader works well without a valid material so
+ // we can safely let it to ScenePreprocessor.
+#if 0
+ if ( newMats.size() == 0)
+ {
+ Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
+ aiString name( AI_DEFAULT_MATERIAL_NAME );
+ mat->AddProperty( &name, AI_MATKEY_NAME);
+
+ const int shadeMode = aiShadingMode_Phong;
+ mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
+ aiColor4D colAmbient( 0.2f, 0.2f, 0.2f, 1.0f), colDiffuse( 0.8f, 0.8f, 0.8f, 1.0f), colSpecular( 0.5f, 0.5f, 0.5f, 0.5f);
+ mat->AddProperty( &colAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
+ mat->AddProperty( &colDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mat->AddProperty( &colSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
+ const float specExp = 5.0f;
+ mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS);
+ }
+#endif
+}
+
+// ------------------------------------------------------------------------------------------------
+// Resolves the texture name for the given effect texture entry
+aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pParser,
+ const Collada::Effect& pEffect, const std::string& pName)
+{
+ // recurse through the param references until we end up at an image
+ std::string name = pName;
+ while ( 1)
+ {
+ // the given string is a param entry. Find it
+ Collada::Effect::ParamLibrary::const_iterator it = pEffect.mParams.find( name);
+ // if not found, we're at the end of the recursion. The resulting string should be the image ID
+ if ( it == pEffect.mParams.end())
+ break;
+
+ // else recurse on
+ name = it->second.mReference;
+ }
+
+ // find the image referred by this name in the image library of the scene
+ ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find( name);
+ if ( imIt == pParser.mImageLibrary.end())
+ {
+ throw DeadlyImportError( boost::str( boost::format(
+ "Collada: Unable to resolve effect texture entry \"%s\", ended up at ID \"%s\".") % pName % name));
+ }
+
+ aiString result;
+
+ // if this is an embedded texture image setup an aiTexture for it
+ if (imIt->second.mFileName.empty())
+ {
+ if (imIt->second.mImageData.empty()) {
+ throw DeadlyImportError("Collada: Invalid texture, no data or file reference given");
+ }
+
+ aiTexture* tex = new aiTexture();
+
+ // setup format hint
+ if (imIt->second.mEmbeddedFormat.length() > 3) {
+ DefaultLogger::get()->warn("Collada: texture format hint is too long, truncating to 3 characters");
+ }
+ strncpy(tex->achFormatHint,imIt->second.mEmbeddedFormat.c_str(),3);
+
+ // and copy texture data
+ tex->mHeight = 0;
+ tex->mWidth = imIt->second.mImageData.size();
+ tex->pcData = (aiTexel*)new char[tex->mWidth];
+ memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth);
+
+ // setup texture reference string
+ result.data[0] = '*';
+ result.length = 1 + ASSIMP_itoa10(result.data+1,MAXLEN-1,mTextures.size());
+
+ // and add this texture to the list
+ mTextures.push_back(tex);
+ }
+ else
+ {
+ result.Set( imIt->second.mFileName );
+ ConvertPath(result);
+ }
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a path read from a collada file to the usual representation
+void ColladaLoader::ConvertPath (aiString& ss)
+{
+ // TODO: collada spec, p 22. Handle URI correctly.
+ // For the moment we're just stripping the file:// away to make it work.
+ // Windoes doesn't seem to be able to find stuff like
+ // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg'
+ if (0 == strncmp(ss.data,"file://",7))
+ {
+ ss.length -= 7;
+ memmove(ss.data,ss.data+7,ss.length);
+ ss.data[ss.length] = '\0';
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a float value from an accessor and its data array.
+float ColladaLoader::ReadFloat( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const
+{
+ // FIXME: (thom) Test for data type here in every access? For the moment, I leave this to the caller
+ size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset;
+ ai_assert( pos < pData.mValues.size());
+ return pData.mValues[pos];
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a string value from an accessor and its data array.
+const std::string& ColladaLoader::ReadString( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const
+{
+ size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset;
+ ai_assert( pos < pData.mStrings.size());
+ return pData.mStrings[pos];
+}
+
+// ------------------------------------------------------------------------------------------------
+// Collects all nodes into the given array
+void ColladaLoader::CollectNodes( const aiNode* pNode, std::vector<const aiNode*>& poNodes) const
+{
+ poNodes.push_back( pNode);
+
+ for ( size_t a = 0; a < pNode->mNumChildren; ++a)
+ CollectNodes( pNode->mChildren[a], poNodes);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Finds a node in the collada scene by the given name
+const Collada::Node* ColladaLoader::FindNode( const Collada::Node* pNode, const std::string& pName) const
+{
+ if ( pNode->mName == pName || pNode->mID == pName)
+ return pNode;
+
+ for ( size_t a = 0; a < pNode->mChildren.size(); ++a)
+ {
+ const Collada::Node* node = FindNode( pNode->mChildren[a], pName);
+ if ( node)
+ return node;
+ }
+
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Finds a node in the collada scene by the given SID
+const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const
+{
+ if ( pNode->mSID == pSID)
+ return pNode;
+
+ for ( size_t a = 0; a < pNode->mChildren.size(); ++a)
+ {
+ const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID);
+ if ( node)
+ return node;
+ }
+
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Finds a proper name for a node derived from the collada-node's properties
+std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode) const
+{
+ // now setup the name of the node. We take the name if not empty, otherwise the collada ID
+ // FIX: Workaround for XSI calling the instanced visual scene 'untitled' by default.
+ if (!pNode->mName.empty() && pNode->mName != "untitled")
+ return pNode->mName;
+ else if (!pNode->mID.empty())
+ return pNode->mID;
+ else if (!pNode->mSID.empty())
+ return pNode->mSID;
+ else
+ {
+ // No need to worry. Unnamed nodes are no problem at all, except
+ // if cameras or lights need to be assigned to them.
+ return boost::str( boost::format( "$ColladaAutoName$_%d") % clock());
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER
diff --git a/src/3rdparty/assimp/code/ColladaLoader.h b/src/3rdparty/assimp/code/ColladaLoader.h
new file mode 100644
index 000000000..72439eb90
--- /dev/null
+++ b/src/3rdparty/assimp/code/ColladaLoader.h
@@ -0,0 +1,241 @@
+/** Defines the collada loader class */
+
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef AI_COLLADALOADER_H_INC
+#define AI_COLLADALOADER_H_INC
+
+#include "BaseImporter.h"
+#include "ColladaParser.h"
+
+namespace Assimp
+{
+
+struct ColladaMeshIndex
+{
+ std::string mMeshID;
+ size_t mSubMesh;
+ std::string mMaterial;
+ ColladaMeshIndex( const std::string& pMeshID, size_t pSubMesh, const std::string& pMaterial)
+ : mMeshID( pMeshID), mSubMesh( pSubMesh), mMaterial( pMaterial)
+ { }
+
+ bool operator < (const ColladaMeshIndex& p) const
+ {
+ if ( mMeshID == p.mMeshID)
+ {
+ if ( mSubMesh == p.mSubMesh)
+ return mMaterial < p.mMaterial;
+ else
+ return mSubMesh < p.mSubMesh;
+ } else
+ {
+ return mMeshID < p.mMeshID;
+ }
+ }
+};
+
+/** Loader class to read Collada scenes. Collada is over-engineered to death, with every new iteration bringing
+ * more useless stuff, so I limited the data to what I think is useful for games.
+*/
+class ColladaLoader : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ ColladaLoader();
+
+ /** Destructor, private as well */
+ ~ColladaLoader();
+
+public:
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details. */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+
+protected:
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList( std::set<std::string>& extensions);
+
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+
+ /** Recursively constructs a scene node for the given parser node and returns it. */
+ aiNode* BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode);
+
+ /** Resolve node instances */
+ void ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode,
+ std::vector<const Collada::Node*>& resolved);
+
+ /** Builds meshes for the given node and references them */
+ void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode,
+ aiNode* pTarget);
+
+ /** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */
+ aiMesh* CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
+ const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace);
+
+ /** Builds cameras for the given node and references them */
+ void BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode,
+ aiNode* pTarget);
+
+ /** Builds lights for the given node and references them */
+ void BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode,
+ aiNode* pTarget);
+
+ /** Stores all meshes in the given scene */
+ void StoreSceneMeshes( aiScene* pScene);
+
+ /** Stores all materials in the given scene */
+ void StoreSceneMaterials( aiScene* pScene);
+
+ /** Stores all lights in the given scene */
+ void StoreSceneLights( aiScene* pScene);
+
+ /** Stores all cameras in the given scene */
+ void StoreSceneCameras( aiScene* pScene);
+
+ /** Stores all textures in the given scene */
+ void StoreSceneTextures( aiScene* pScene);
+
+ /** Stores all animations
+ * @param pScene target scene to store the anims
+ */
+ void StoreAnimations( aiScene* pScene, const ColladaParser& pParser);
+
+ /** Stores all animations for the given source anim and its nested child animations
+ * @param pScene target scene to store the anims
+ * @param pSrcAnim the source animation to process
+ * @param pPrefix Prefix to the name in case of nested animations
+ */
+ void StoreAnimations( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string pPrefix);
+
+ /** Constructs the animation for the given source anim */
+ void CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName);
+
+ /** Constructs materials from the collada material definitions */
+ void BuildMaterials( const ColladaParser& pParser, aiScene* pScene);
+
+ /** Fill materials from the collada material definitions */
+ void FillMaterials( const ColladaParser& pParser, aiScene* pScene);
+
+ /** Resolve UV channel mappings*/
+ void ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler,
+ const Collada::SemanticMappingTable& table);
+
+ /** Add a texture and all of its sampling properties to a material*/
+ void AddTexture ( Assimp::MaterialHelper& mat, const ColladaParser& pParser,
+ const Collada::Effect& effect,
+ const Collada::Sampler& sampler,
+ aiTextureType type, unsigned int idx = 0);
+
+ /** Resolves the texture name for the given effect texture entry */
+ aiString FindFilenameForEffectTexture( const ColladaParser& pParser,
+ const Collada::Effect& pEffect, const std::string& pName);
+
+ /** Converts a path read from a collada file to the usual representation */
+ void ConvertPath( aiString& ss);
+
+ /** Reads a float value from an accessor and its data array.
+ * @param pAccessor The accessor to use for reading
+ * @param pData The data array to read from
+ * @param pIndex The index of the element to retrieve
+ * @param pOffset Offset into the element, for multipart elements such as vectors or matrices
+ * @return the specified value
+ */
+ float ReadFloat( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const;
+
+ /** Reads a string value from an accessor and its data array.
+ * @param pAccessor The accessor to use for reading
+ * @param pData The data array to read from
+ * @param pIndex The index of the element to retrieve
+ * @return the specified value
+ */
+ const std::string& ReadString( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const;
+
+ /** Recursively collects all nodes into the given array */
+ void CollectNodes( const aiNode* pNode, std::vector<const aiNode*>& poNodes) const;
+
+ /** Finds a node in the collada scene by the given name */
+ const Collada::Node* FindNode( const Collada::Node* pNode, const std::string& pName) const;
+ /** Finds a node in the collada scene by the given SID */
+ const Collada::Node* FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const;
+
+ /** Finds a proper name for a node derived from the collada-node's properties */
+ std::string FindNameForNode( const Collada::Node* pNode) const;
+
+protected:
+ /** Filename, for a verbose error message */
+ std::string mFileName;
+
+ /** Which mesh-material compound was stored under which mesh ID */
+ std::map<ColladaMeshIndex, size_t> mMeshIndexByID;
+
+ /** Which material was stored under which index in the scene */
+ std::map<std::string, size_t> mMaterialIndexByName;
+
+ /** Accumulated meshes for the target scene */
+ std::vector<aiMesh*> mMeshes;
+
+ /** Temporary material list */
+ std::vector<std::pair<Collada::Effect*, aiMaterial*> > newMats;
+
+ /** Temporary camera list */
+ std::vector<aiCamera*> mCameras;
+
+ /** Temporary light list */
+ std::vector<aiLight*> mLights;
+
+ /** Temporary texture list */
+ std::vector<aiTexture*> mTextures;
+
+ /** Accumulated animations for the target scene */
+ std::vector<aiAnimation*> mAnims;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_COLLADALOADER_H_INC
diff --git a/src/3rdparty/assimp/code/ColladaParser.cpp b/src/3rdparty/assimp/code/ColladaParser.cpp
new file mode 100644
index 000000000..b34c485e3
--- /dev/null
+++ b/src/3rdparty/assimp/code/ColladaParser.cpp
@@ -0,0 +1,2797 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file ColladaParser.cpp
+ * @brief Implementation of the Collada parser helper
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_DAE_IMPORTER
+
+#include "ColladaParser.h"
+#include "fast_atof.h"
+#include "ParsingUtils.h"
+
+using namespace Assimp;
+using namespace Assimp::Collada;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile)
+ : mFileName( pFile)
+{
+ mRootNode = NULL;
+ mUnitSize = 1.0f;
+ mUpDirection = UP_Z;
+
+ // We assume the newest file format by default
+ mFormat = FV_1_5_n;
+
+ // open the file
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open file " + pFile + ".");
+
+ // generate a XML reader for it
+ boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper( new CIrrXML_IOStreamReader( file.get()));
+ mReader = irr::io::createIrrXMLReader( mIOWrapper.get());
+ if ( !mReader)
+ ThrowException( "Collada: Unable to open file.");
+
+ // start reading
+ ReadContents();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ColladaParser::~ColladaParser()
+{
+ delete mReader;
+ for ( NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it)
+ delete it->second;
+ for ( MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it)
+ delete it->second;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read bool from text contents of current element
+bool ColladaParser::ReadBoolFromTextContent()
+{
+ const char* cur = GetTextContent();
+ return (!ASSIMP_strincmp(cur,"true",4) || '0' != *cur);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read float from text contents of current element
+float ColladaParser::ReadFloatFromTextContent()
+{
+ const char* cur = GetTextContent();
+ return fast_atof(cur);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the contents of the file
+void ColladaParser::ReadContents()
+{
+ while ( mReader->read())
+ {
+ // handle the root element "COLLADA"
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "COLLADA"))
+ {
+ // check for 'version' attribute
+ const int attrib = TestAttribute("version");
+ if (attrib != -1) {
+ const char* version = mReader->getAttributeValue(attrib);
+
+ if (!::strncmp(version,"1.5",3)) {
+ mFormat = FV_1_5_n;
+ DefaultLogger::get()->debug("Collada schema version is 1.5.n");
+ }
+ else if (!::strncmp(version,"1.4",3)) {
+ mFormat = FV_1_4_n;
+ DefaultLogger::get()->debug("Collada schema version is 1.4.n");
+ }
+ else if (!::strncmp(version,"1.3",3)) {
+ mFormat = FV_1_3_n;
+ DefaultLogger::get()->debug("Collada schema version is 1.3.n");
+ }
+ }
+
+ ReadStructure();
+ } else
+ {
+ DefaultLogger::get()->debug( boost::str( boost::format( "Ignoring global element \"%s\".") % mReader->getNodeName()));
+ SkipElement();
+ }
+ } else
+ {
+ // skip everything else silently
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the structure of the file
+void ColladaParser::ReadStructure()
+{
+ while ( mReader->read())
+ {
+ // beginning of elements
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "asset"))
+ ReadAssetInfo();
+ else if ( IsElement( "library_animations"))
+ ReadAnimationLibrary();
+ else if ( IsElement( "library_controllers"))
+ ReadControllerLibrary();
+ else if ( IsElement( "library_images"))
+ ReadImageLibrary();
+ else if ( IsElement( "library_materials"))
+ ReadMaterialLibrary();
+ else if ( IsElement( "library_effects"))
+ ReadEffectLibrary();
+ else if ( IsElement( "library_geometries"))
+ ReadGeometryLibrary();
+ else if ( IsElement( "library_visual_scenes"))
+ ReadSceneLibrary();
+ else if ( IsElement( "library_lights"))
+ ReadLightLibrary();
+ else if ( IsElement( "library_cameras"))
+ ReadCameraLibrary();
+ else if ( IsElement( "library_nodes"))
+ ReadSceneNode(NULL); /* some hacking to reuse this piece of code */
+ else if ( IsElement( "scene"))
+ ReadScene();
+ else
+ SkipElement();
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads asset informations such as coordinate system informations and legal blah
+void ColladaParser::ReadAssetInfo()
+{
+ if ( mReader->isEmptyElement())
+ return;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "unit"))
+ {
+ // read unit data from the element's attributes
+ const int attrIndex = TestAttribute( "meter");
+ if (attrIndex == -1) {
+ mUnitSize = 1.f;
+ }
+ else {
+ mUnitSize = mReader->getAttributeValueAsFloat( attrIndex);
+ }
+
+ // consume the trailing stuff
+ if ( !mReader->isEmptyElement())
+ SkipElement();
+ }
+ else if ( IsElement( "up_axis"))
+ {
+ // read content, strip whitespace, compare
+ const char* content = GetTextContent();
+ if ( strncmp( content, "X_UP", 4) == 0)
+ mUpDirection = UP_X;
+ else if ( strncmp( content, "Y_UP", 4) == 0)
+ mUpDirection = UP_Y;
+ else
+ mUpDirection = UP_Z;
+
+ // check element end
+ TestClosing( "up_axis");
+ } else
+ {
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "asset") != 0)
+ ThrowException( "Expected end of \"asset\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the animation library
+void ColladaParser::ReadAnimationLibrary()
+{
+ if (mReader->isEmptyElement())
+ return;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "animation"))
+ {
+ // delegate the reading. Depending on the inner elements it will be a container or a anim channel
+ ReadAnimation( &mAnims);
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "library_animations") != 0)
+ ThrowException( "Expected end of \"library_animations\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an animation into the given parent structure
+void ColladaParser::ReadAnimation( Collada::Animation* pParent)
+{
+ if ( mReader->isEmptyElement())
+ return;
+
+ // an <animation> element may be a container for grouping sub-elements or an animation channel
+ // this is the channel collection by ID, in case it has channels
+ typedef std::map<std::string, AnimationChannel> ChannelMap;
+ ChannelMap channels;
+ // this is the anim container in case we're a container
+ Animation* anim = NULL;
+
+ // optional name given as an attribute
+ std::string animName;
+ int indexName = TestAttribute( "name");
+ int indexID = TestAttribute( "id");
+ if ( indexName >= 0)
+ animName = mReader->getAttributeValue( indexName);
+ else if ( indexID >= 0)
+ animName = mReader->getAttributeValue( indexID);
+ else
+ animName = "animation";
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ // we have subanimations
+ if ( IsElement( "animation"))
+ {
+ // create container from our element
+ if ( !anim)
+ {
+ anim = new Animation;
+ anim->mName = animName;
+ pParent->mSubAnims.push_back( anim);
+ }
+
+ // recurse into the subelement
+ ReadAnimation( anim);
+ }
+ else if ( IsElement( "source"))
+ {
+ // possible animation data - we'll never know. Better store it
+ ReadSource();
+ }
+ else if ( IsElement( "sampler"))
+ {
+ // read the ID to assign the corresponding collada channel afterwards.
+ int indexID = GetAttribute( "id");
+ std::string id = mReader->getAttributeValue( indexID);
+ ChannelMap::iterator newChannel = channels.insert( std::make_pair( id, AnimationChannel())).first;
+
+ // have it read into a channel
+ ReadAnimationSampler( newChannel->second);
+ }
+ else if ( IsElement( "channel"))
+ {
+ // the binding element whose whole purpose is to provide the target to animate
+ // Thanks, Collada! A directly posted information would have been too simple, I guess.
+ // Better add another indirection to that! Can't have enough of those.
+ int indexTarget = GetAttribute( "target");
+ int indexSource = GetAttribute( "source");
+ const char* sourceId = mReader->getAttributeValue( indexSource);
+ if ( sourceId[0] == '#')
+ sourceId++;
+ ChannelMap::iterator cit = channels.find( sourceId);
+ if ( cit != channels.end())
+ cit->second.mTarget = mReader->getAttributeValue( indexTarget);
+
+ if ( !mReader->isEmptyElement())
+ SkipElement();
+ }
+ else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "animation") != 0)
+ ThrowException( "Expected end of \"animation\" element.");
+
+ break;
+ }
+ }
+
+ // it turned out to have channels - add them
+ if ( !channels.empty())
+ {
+ // special filtering for stupid exporters packing each channel into a separate animation
+ if ( channels.size() == 1)
+ {
+ pParent->mChannels.push_back( channels.begin()->second);
+ } else
+ {
+ // else create the animation, if not done yet, and store the channels
+ if ( !anim)
+ {
+ anim = new Animation;
+ anim->mName = animName;
+ pParent->mSubAnims.push_back( anim);
+ }
+ for ( ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it)
+ anim->mChannels.push_back( it->second);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an animation sampler into the given anim channel
+void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel)
+{
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "input"))
+ {
+ int indexSemantic = GetAttribute( "semantic");
+ const char* semantic = mReader->getAttributeValue( indexSemantic);
+ int indexSource = GetAttribute( "source");
+ const char* source = mReader->getAttributeValue( indexSource);
+ if ( source[0] != '#')
+ ThrowException( "Unsupported URL format");
+ source++;
+
+ if ( strcmp( semantic, "INPUT") == 0)
+ pChannel.mSourceTimes = source;
+ else if ( strcmp( semantic, "OUTPUT") == 0)
+ pChannel.mSourceValues = source;
+
+ if ( !mReader->isEmptyElement())
+ SkipElement();
+ }
+ else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "sampler") != 0)
+ ThrowException( "Expected end of \"sampler\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the skeleton controller library
+void ColladaParser::ReadControllerLibrary()
+{
+ if (mReader->isEmptyElement())
+ return;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "controller"))
+ {
+ // read ID. Ask the spec if it's neccessary or optional... you might be surprised.
+ int attrID = GetAttribute( "id");
+ std::string id = mReader->getAttributeValue( attrID);
+
+ // create an entry and store it in the library under its ID
+ mControllerLibrary[id] = Controller();
+
+ // read on from there
+ ReadController( mControllerLibrary[id]);
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "library_controllers") != 0)
+ ThrowException( "Expected end of \"library_controllers\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a controller into the given mesh structure
+void ColladaParser::ReadController( Collada::Controller& pController)
+{
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ // two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other
+ if ( IsElement( "morph"))
+ {
+ // should skip everything inside, so there's no danger of catching elements inbetween
+ SkipElement();
+ }
+ else if ( IsElement( "skin"))
+ {
+ // read the mesh it refers to. According to the spec this could also be another
+ // controller, but I refuse to implement every idea they've come up with
+ int sourceIndex = GetAttribute( "source");
+ pController.mMeshId = mReader->getAttributeValue( sourceIndex) + 1;
+ }
+ else if ( IsElement( "bind_shape_matrix"))
+ {
+ // content is 16 floats to define a matrix... it seems to be important for some models
+ const char* content = GetTextContent();
+
+ // read the 16 floats
+ for ( unsigned int a = 0; a < 16; a++)
+ {
+ // read a number
+ content = fast_atof_move( content, pController.mBindShapeMatrix[a]);
+ // skip whitespace after it
+ SkipSpacesAndLineEnd( &content);
+ }
+
+ TestClosing( "bind_shape_matrix");
+ }
+ else if ( IsElement( "source"))
+ {
+ // data array - we have specialists to handle this
+ ReadSource();
+ }
+ else if ( IsElement( "joints"))
+ {
+ ReadControllerJoints( pController);
+ }
+ else if ( IsElement( "vertex_weights"))
+ {
+ ReadControllerWeights( pController);
+ }
+ else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "controller") == 0)
+ break;
+ else if ( strcmp( mReader->getNodeName(), "skin") != 0)
+ ThrowException( "Expected end of \"controller\" element.");
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the joint definitions for the given controller
+void ColladaParser::ReadControllerJoints( Collada::Controller& pController)
+{
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ // Input channels for joint data. Two possible semantics: "JOINT" and "INV_BIND_MATRIX"
+ if ( IsElement( "input"))
+ {
+ int indexSemantic = GetAttribute( "semantic");
+ const char* attrSemantic = mReader->getAttributeValue( indexSemantic);
+ int indexSource = GetAttribute( "source");
+ const char* attrSource = mReader->getAttributeValue( indexSource);
+
+ // local URLS always start with a '#'. We don't support global URLs
+ if ( attrSource[0] != '#')
+ ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\"") % attrSource));
+ attrSource++;
+
+ // parse source URL to corresponding source
+ if ( strcmp( attrSemantic, "JOINT") == 0)
+ pController.mJointNameSource = attrSource;
+ else if ( strcmp( attrSemantic, "INV_BIND_MATRIX") == 0)
+ pController.mJointOffsetMatrixSource = attrSource;
+ else
+ ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in joint data") % attrSemantic));
+
+ // skip inner data, if present
+ if ( !mReader->isEmptyElement())
+ SkipElement();
+ }
+ else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "joints") != 0)
+ ThrowException( "Expected end of \"joints\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the joint weights for the given controller
+void ColladaParser::ReadControllerWeights( Collada::Controller& pController)
+{
+ // read vertex count from attributes and resize the array accordingly
+ int indexCount = GetAttribute( "count");
+ size_t vertexCount = (size_t) mReader->getAttributeValueAsInt( indexCount);
+ pController.mWeightCounts.resize( vertexCount);
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ // Input channels for weight data. Two possible semantics: "JOINT" and "WEIGHT"
+ if ( IsElement( "input"))
+ {
+ InputChannel channel;
+
+ int indexSemantic = GetAttribute( "semantic");
+ const char* attrSemantic = mReader->getAttributeValue( indexSemantic);
+ int indexSource = GetAttribute( "source");
+ const char* attrSource = mReader->getAttributeValue( indexSource);
+ int indexOffset = TestAttribute( "offset");
+ if ( indexOffset >= 0)
+ channel.mOffset = mReader->getAttributeValueAsInt( indexOffset);
+
+ // local URLS always start with a '#'. We don't support global URLs
+ if ( attrSource[0] != '#')
+ ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\"") % attrSource));
+ channel.mAccessor = attrSource + 1;
+
+ // parse source URL to corresponding source
+ if ( strcmp( attrSemantic, "JOINT") == 0)
+ pController.mWeightInputJoints = channel;
+ else if ( strcmp( attrSemantic, "WEIGHT") == 0)
+ pController.mWeightInputWeights = channel;
+ else
+ ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in vertex_weight data") % attrSemantic));
+
+ // skip inner data, if present
+ if ( !mReader->isEmptyElement())
+ SkipElement();
+ }
+ else if ( IsElement( "vcount"))
+ {
+ // read weight count per vertex
+ const char* text = GetTextContent();
+ size_t numWeights = 0;
+ for ( std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it)
+ {
+ if ( *text == 0)
+ ThrowException( "Out of data while reading vcount");
+
+ *it = strtol10( text, &text);
+ numWeights += *it;
+ SkipSpacesAndLineEnd( &text);
+ }
+
+ TestClosing( "vcount");
+
+ // reserve weight count
+ pController.mWeights.resize( numWeights);
+ }
+ else if ( IsElement( "v"))
+ {
+ // read JointIndex - WeightIndex pairs
+ const char* text = GetTextContent();
+
+ for ( std::vector< std::pair<size_t, size_t> >::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it)
+ {
+ if ( *text == 0)
+ ThrowException( "Out of data while reading vertex_weights");
+ it->first = strtol10( text, &text);
+ SkipSpacesAndLineEnd( &text);
+ if ( *text == 0)
+ ThrowException( "Out of data while reading vertex_weights");
+ it->second = strtol10( text, &text);
+ SkipSpacesAndLineEnd( &text);
+ }
+
+ TestClosing( "v");
+ }
+ else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "vertex_weights") != 0)
+ ThrowException( "Expected end of \"vertex_weights\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the image library contents
+void ColladaParser::ReadImageLibrary()
+{
+ if ( mReader->isEmptyElement())
+ return;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+ if ( IsElement( "image"))
+ {
+ // read ID. Another entry which is "optional" by design but obligatory in reality
+ int attrID = GetAttribute( "id");
+ std::string id = mReader->getAttributeValue( attrID);
+
+ // create an entry and store it in the library under its ID
+ mImageLibrary[id] = Image();
+
+ // read on from there
+ ReadImage( mImageLibrary[id]);
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+ if ( strcmp( mReader->getNodeName(), "library_images") != 0)
+ ThrowException( "Expected end of \"library_images\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an image entry into the given image
+void ColladaParser::ReadImage( Collada::Image& pImage)
+{
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT){
+ // Need to run different code paths here, depending on the Collada XSD version
+ if (IsElement("image")) {
+ SkipElement();
+ }
+ else if ( IsElement( "init_from"))
+ {
+ if (mFormat == FV_1_4_n)
+ {
+ // FIX: C4D exporter writes empty <init_from/> tags
+ if (!mReader->isEmptyElement()) {
+ // element content is filename - hopefully
+ const char* sz = TestTextContent();
+ if (sz)pImage.mFileName = sz;
+ TestClosing( "init_from");
+ }
+ if (!pImage.mFileName.length()) {
+ pImage.mFileName = "unknown_texture";
+ }
+ }
+ else if (mFormat == FV_1_5_n)
+ {
+ // make sure we skip over mip and array initializations, which
+ // we don't support, but which could confuse the loader if
+ // they're not skipped.
+ int attrib = TestAttribute("array_index");
+ if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
+ DefaultLogger::get()->warn("Collada: Ignoring texture array index");
+ continue;
+ }
+
+ attrib = TestAttribute("mip_index");
+ if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
+ DefaultLogger::get()->warn("Collada: Ignoring MIP map layer");
+ continue;
+ }
+
+ // TODO: correctly jump over cube and volume maps?
+ }
+ }
+ else if (mFormat == FV_1_5_n)
+ {
+ if ( IsElement( "ref"))
+ {
+ // element content is filename - hopefully
+ const char* sz = TestTextContent();
+ if (sz)pImage.mFileName = sz;
+ TestClosing( "ref");
+ }
+ else if ( IsElement( "hex") && !pImage.mFileName.length())
+ {
+ // embedded image. get format
+ const int attrib = TestAttribute("format");
+ if (-1 == attrib)
+ DefaultLogger::get()->warn("Collada: Unknown image file format");
+ else pImage.mEmbeddedFormat = mReader->getAttributeValue(attrib);
+
+ const char* data = GetTextContent();
+
+ // hexadecimal-encoded binary octets. First of all, find the
+ // required buffer size to reserve enough storage.
+ const char* cur = data;
+ while (!IsSpaceOrNewLine(*cur)) cur++;
+
+ const unsigned int size = (unsigned int)(cur-data) * 2;
+ pImage.mImageData.resize(size);
+ for (unsigned int i = 0; i < size;++i)
+ pImage.mImageData[i] = HexOctetToDecimal(data+(i<<1));
+
+ TestClosing( "hex");
+ }
+ }
+ else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+ if ( strcmp( mReader->getNodeName(), "image") == 0)
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the material library
+void ColladaParser::ReadMaterialLibrary()
+{
+ if ( mReader->isEmptyElement())
+ return;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "material"))
+ {
+ // read ID. By now you propably know my opinion about this "specification"
+ int attrID = GetAttribute( "id");
+ std::string id = mReader->getAttributeValue( attrID);
+
+ // create an entry and store it in the library under its ID
+ ReadMaterial(mMaterialLibrary[id] = Material());
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "library_materials") != 0)
+ ThrowException( "Expected end of \"library_materials\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the light library
+void ColladaParser::ReadLightLibrary()
+{
+ if ( mReader->isEmptyElement())
+ return;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+ if ( IsElement( "light"))
+ {
+ // read ID. By now you propably know my opinion about this "specification"
+ int attrID = GetAttribute( "id");
+ std::string id = mReader->getAttributeValue( attrID);
+
+ // create an entry and store it in the library under its ID
+ ReadLight(mLightLibrary[id] = Light());
+
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+ if ( strcmp( mReader->getNodeName(), "library_lights") != 0)
+ ThrowException( "Expected end of \"library_lights\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the camera library
+void ColladaParser::ReadCameraLibrary()
+{
+ if ( mReader->isEmptyElement())
+ return;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+ if ( IsElement( "camera"))
+ {
+ // read ID. By now you propably know my opinion about this "specification"
+ int attrID = GetAttribute( "id");
+ std::string id = mReader->getAttributeValue( attrID);
+
+ // create an entry and store it in the library under its ID
+ Camera& cam = mCameraLibrary[id];
+ attrID = TestAttribute( "name");
+ if (attrID != -1)
+ cam.mName = mReader->getAttributeValue( attrID);
+
+ ReadCamera(cam);
+
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+ if ( strcmp( mReader->getNodeName(), "library_cameras") != 0)
+ ThrowException( "Expected end of \"library_cameras\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a material entry into the given material
+void ColladaParser::ReadMaterial( Collada::Material& pMaterial)
+{
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+ if (IsElement("material")) {
+ SkipElement();
+ }
+ else if ( IsElement( "instance_effect"))
+ {
+ // referred effect by URL
+ int attrUrl = GetAttribute( "url");
+ const char* url = mReader->getAttributeValue( attrUrl);
+ if ( url[0] != '#')
+ ThrowException( "Unknown reference format");
+
+ pMaterial.mEffect = url+1;
+
+ SkipElement();
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+ if ( strcmp( mReader->getNodeName(), "material") != 0)
+ ThrowException( "Expected end of \"material\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a light entry into the given light
+void ColladaParser::ReadLight( Collada::Light& pLight)
+{
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+ if (IsElement("light")) {
+ SkipElement();
+ }
+ else if (IsElement("spot")) {
+ pLight.mType = aiLightSource_SPOT;
+ }
+ else if (IsElement("ambient")) {
+ pLight.mType = aiLightSource_AMBIENT;
+ }
+ else if (IsElement("directional")) {
+ pLight.mType = aiLightSource_DIRECTIONAL;
+ }
+ else if (IsElement("point")) {
+ pLight.mType = aiLightSource_POINT;
+ }
+ else if (IsElement("color")) {
+ // text content contains 3 floats
+ const char* content = GetTextContent();
+
+ content = fast_atof_move( content, (float&)pLight.mColor.r);
+ SkipSpacesAndLineEnd( &content);
+
+ content = fast_atof_move( content, (float&)pLight.mColor.g);
+ SkipSpacesAndLineEnd( &content);
+
+ content = fast_atof_move( content, (float&)pLight.mColor.b);
+ SkipSpacesAndLineEnd( &content);
+
+ TestClosing( "color");
+ }
+ else if (IsElement("constant_attenuation")) {
+ pLight.mAttConstant = ReadFloatFromTextContent();
+ TestClosing("constant_attenuation");
+ }
+ else if (IsElement("linear_attenuation")) {
+ pLight.mAttLinear = ReadFloatFromTextContent();
+ TestClosing("linear_attenuation");
+ }
+ else if (IsElement("quadratic_attenuation")) {
+ pLight.mAttQuadratic = ReadFloatFromTextContent();
+ TestClosing("quadratic_attenuation");
+ }
+ else if (IsElement("falloff_angle")) {
+ pLight.mFalloffAngle = ReadFloatFromTextContent();
+ TestClosing("falloff_angle");
+ }
+ else if (IsElement("falloff_exponent")) {
+ pLight.mFalloffExponent = ReadFloatFromTextContent();
+ TestClosing("falloff_exponent");
+ }
+ // FCOLLADA extensions
+ // -------------------------------------------------------
+ else if (IsElement("outer_cone")) {
+ pLight.mOuterAngle = ReadFloatFromTextContent();
+ TestClosing("outer_cone");
+ }
+ // ... and this one is even deprecated
+ else if (IsElement("penumbra_angle")) {
+ pLight.mPenumbraAngle = ReadFloatFromTextContent();
+ TestClosing("penumbra_angle");
+ }
+ else if (IsElement("intensity")) {
+ pLight.mIntensity = ReadFloatFromTextContent();
+ TestClosing("intensity");
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+ if ( strcmp( mReader->getNodeName(), "light") == 0)
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a camera entry into the given light
+void ColladaParser::ReadCamera( Collada::Camera& pCamera)
+{
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+ if (IsElement("camera")) {
+ SkipElement();
+ }
+ else if (IsElement("orthographic")) {
+ pCamera.mOrtho = true;
+ }
+ else if (IsElement("xfov") || IsElement("xmag")) {
+ pCamera.mHorFov = ReadFloatFromTextContent();
+ TestClosing((pCamera.mOrtho ? "xmag" : "xfov"));
+ }
+ else if (IsElement("yfov") || IsElement("ymag")) {
+ pCamera.mVerFov = ReadFloatFromTextContent();
+ TestClosing((pCamera.mOrtho ? "ymag" : "yfov"));
+ }
+ else if (IsElement("aspect_ratio")) {
+ pCamera.mAspect = ReadFloatFromTextContent();
+ TestClosing("aspect_ratio");
+ }
+ else if (IsElement("znear")) {
+ pCamera.mZNear = ReadFloatFromTextContent();
+ TestClosing("znear");
+ }
+ else if (IsElement("zfar")) {
+ pCamera.mZFar = ReadFloatFromTextContent();
+ TestClosing("zfar");
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+ if ( strcmp( mReader->getNodeName(), "camera") == 0)
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the effect library
+void ColladaParser::ReadEffectLibrary()
+{
+ if (mReader->isEmptyElement()) {
+ return;
+ }
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+ if ( IsElement( "effect"))
+ {
+ int attrID = GetAttribute( "id");
+ std::string id = mReader->getAttributeValue( attrID);
+
+ // create an entry and store it in the library under its ID
+ mEffectLibrary[id] = Effect();
+ // read on from there
+ ReadEffect( mEffectLibrary[id]);
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+ if ( strcmp( mReader->getNodeName(), "library_effects") != 0)
+ ThrowException( "Expected end of \"library_effects\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect entry into the given effect
+void ColladaParser::ReadEffect( Collada::Effect& pEffect)
+{
+ // for the moment we don't support any other type of effect.
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "profile_COMMON"))
+ ReadEffectProfileCommon( pEffect);
+ else
+ SkipElement();
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "effect") != 0)
+ ThrowException( "Expected end of \"effect\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an COMMON effect profile
+void ColladaParser::ReadEffectProfileCommon( Collada::Effect& pEffect)
+{
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "newparam")) {
+ // save ID
+ int attrSID = GetAttribute( "sid");
+ std::string sid = mReader->getAttributeValue( attrSID);
+ pEffect.mParams[sid] = EffectParam();
+ ReadEffectParam( pEffect.mParams[sid]);
+ }
+ else if ( IsElement( "technique") || IsElement( "extra"))
+ {
+ // just syntactic sugar
+ }
+
+ /* Shading modes */
+ else if ( IsElement( "phong"))
+ pEffect.mShadeType = Shade_Phong;
+ else if ( IsElement( "constant"))
+ pEffect.mShadeType = Shade_Constant;
+ else if ( IsElement( "lambert"))
+ pEffect.mShadeType = Shade_Lambert;
+ else if ( IsElement( "blinn"))
+ pEffect.mShadeType = Shade_Blinn;
+
+ /* Color + texture properties */
+ else if ( IsElement( "emission"))
+ ReadEffectColor( pEffect.mEmissive, pEffect.mTexEmissive);
+ else if ( IsElement( "ambient"))
+ ReadEffectColor( pEffect.mAmbient, pEffect.mTexAmbient);
+ else if ( IsElement( "diffuse"))
+ ReadEffectColor( pEffect.mDiffuse, pEffect.mTexDiffuse);
+ else if ( IsElement( "specular"))
+ ReadEffectColor( pEffect.mSpecular, pEffect.mTexSpecular);
+ else if ( IsElement( "reflective")) {
+ ReadEffectColor( pEffect.mReflective, pEffect.mTexReflective);
+ }
+ else if ( IsElement( "transparent")) {
+ ReadEffectColor( pEffect.mTransparent,pEffect.mTexTransparent);
+ }
+ else if ( IsElement( "shininess"))
+ ReadEffectFloat( pEffect.mShininess);
+ else if ( IsElement( "reflectivity"))
+ ReadEffectFloat( pEffect.mReflectivity);
+
+ /* Single scalar properties */
+ else if ( IsElement( "transparency"))
+ ReadEffectFloat( pEffect.mTransparency);
+ else if ( IsElement( "index_of_refraction"))
+ ReadEffectFloat( pEffect.mRefractIndex);
+
+ // GOOGLEEARTH/OKINO extensions
+ // -------------------------------------------------------
+ else if ( IsElement( "double_sided"))
+ pEffect.mDoubleSided = ReadBoolFromTextContent();
+
+ // FCOLLADA extensions
+ // -------------------------------------------------------
+ else if ( IsElement( "bump")) {
+ aiColor4D dummy;
+ ReadEffectColor( dummy,pEffect.mTexBump);
+ }
+
+ // MAX3D extensions
+ // -------------------------------------------------------
+ else if ( IsElement( "wireframe")) {
+ pEffect.mWireframe = ReadBoolFromTextContent();
+ TestClosing( "wireframe");
+ }
+ else if ( IsElement( "faceted")) {
+ pEffect.mFaceted = ReadBoolFromTextContent();
+ TestClosing( "faceted");
+ }
+ else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+ if ( strcmp( mReader->getNodeName(), "profile_COMMON") == 0)
+ {
+ break;
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read texture wrapping + UV transform settings from a profile==Maya chunk
+void ColladaParser::ReadSamplerProperties( Sampler& out )
+{
+ if (mReader->isEmptyElement()) {
+ return;
+ }
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+
+ // MAYA extensions
+ // -------------------------------------------------------
+ if ( IsElement( "wrapU")) {
+ out.mWrapU = ReadBoolFromTextContent();
+ TestClosing( "wrapU");
+ }
+ else if ( IsElement( "wrapV")) {
+ out.mWrapU = ReadBoolFromTextContent();
+ TestClosing( "wrapV");
+ }
+ else if ( IsElement( "mirrorU")) {
+ out.mMirrorU = ReadBoolFromTextContent();
+ TestClosing( "mirrorU");
+ }
+ else if ( IsElement( "mirrorV")) {
+ out.mMirrorU = ReadBoolFromTextContent();
+ TestClosing( "mirrorV");
+ }
+ else if ( IsElement( "repeatU")) {
+ out.mTransform.mScaling.x = ReadFloatFromTextContent();
+ TestClosing( "repeatU");
+ }
+ else if ( IsElement( "repeatV")) {
+ out.mTransform.mScaling.y = ReadFloatFromTextContent();
+ TestClosing( "repeatV");
+ }
+ else if ( IsElement( "offsetU")) {
+ out.mTransform.mTranslation.x = ReadFloatFromTextContent();
+ TestClosing( "offsetU");
+ }
+ else if ( IsElement( "offsetV")) {
+ out.mTransform.mTranslation.x = ReadFloatFromTextContent();
+ TestClosing( "offsetV");
+ }
+ else if ( IsElement( "rotateUV")) {
+ out.mTransform.mRotation = ReadFloatFromTextContent();
+ TestClosing( "rotateUV");
+ }
+ else if ( IsElement( "blend_mode")) {
+
+ const char* sz = GetTextContent();
+ // http://www.feelingsoftware.com/content/view/55/72/lang,en/
+ // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE
+ if (0 == ASSIMP_strincmp(sz,"ADD",3))
+ out.mOp = aiTextureOp_Add;
+
+ else if (0 == ASSIMP_strincmp(sz,"SUBTRACT",8))
+ out.mOp = aiTextureOp_Subtract;
+
+ else if (0 == ASSIMP_strincmp(sz,"MULTIPLY",8))
+ out.mOp = aiTextureOp_Multiply;
+
+ else {
+ DefaultLogger::get()->warn("Collada: Unsupported MAYA texture blend mode");
+ }
+ TestClosing( "blend_mode");
+ }
+ // OKINO extensions
+ // -------------------------------------------------------
+ else if ( IsElement( "weighting")) {
+ out.mWeighting = ReadFloatFromTextContent();
+ TestClosing( "weighting");
+ }
+ else if ( IsElement( "mix_with_previous_layer")) {
+ out.mMixWithPrevious = ReadFloatFromTextContent();
+ TestClosing( "mix_with_previous_layer");
+ }
+ // MAX3D extensions
+ // -------------------------------------------------------
+ else if ( IsElement( "amount")) {
+ out.mWeighting = ReadFloatFromTextContent();
+ TestClosing( "amount");
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+ if ( strcmp( mReader->getNodeName(), "technique") == 0)
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect entry containing a color or a texture defining that color
+void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler)
+{
+ if (mReader->isEmptyElement())
+ return;
+
+ // Save current element name
+ const std::string curElem = mReader->getNodeName();
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+ if ( IsElement( "color"))
+ {
+ // text content contains 4 floats
+ const char* content = GetTextContent();
+
+ content = fast_atof_move( content, (float&)pColor.r);
+ SkipSpacesAndLineEnd( &content);
+
+ content = fast_atof_move( content, (float&)pColor.g);
+ SkipSpacesAndLineEnd( &content);
+
+ content = fast_atof_move( content, (float&)pColor.b);
+ SkipSpacesAndLineEnd( &content);
+
+ content = fast_atof_move( content, (float&)pColor.a);
+ SkipSpacesAndLineEnd( &content);
+ TestClosing( "color");
+ }
+ else if ( IsElement( "texture"))
+ {
+ // get name of source textur/sampler
+ int attrTex = GetAttribute( "texture");
+ pSampler.mName = mReader->getAttributeValue( attrTex);
+
+ // get name of UV source channel
+ attrTex = GetAttribute( "texcoord");
+ pSampler.mUVChannel = mReader->getAttributeValue( attrTex);
+ //SkipElement();
+ }
+ else if ( IsElement( "technique"))
+ {
+ const int _profile = GetAttribute( "profile");
+ const char* profile = mReader->getAttributeValue( _profile );
+
+ // Some extensions are quite useful ... ReadSamplerProperties processes
+ // several extensions in MAYA, OKINO and MAX3D profiles.
+ if (!::strcmp(profile,"MAYA") || !::strcmp(profile,"MAX3D") || !::strcmp(profile,"OKINO"))
+ {
+ // get more information on this sampler
+ ReadSamplerProperties(pSampler);
+ }
+ else SkipElement();
+ }
+ else if ( !IsElement( "extra"))
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
+ if (mReader->getNodeName() == curElem)
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect entry containing a float
+void ColladaParser::ReadEffectFloat( float& pFloat)
+{
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT){
+ if ( IsElement( "float"))
+ {
+ // text content contains a single floats
+ const char* content = GetTextContent();
+ content = fast_atof_move( content, pFloat);
+ SkipSpacesAndLineEnd( &content);
+
+ TestClosing( "float");
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect parameter specification of any kind
+void ColladaParser::ReadEffectParam( Collada::EffectParam& pParam)
+{
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+ if ( IsElement( "surface"))
+ {
+ // image ID given inside <init_from> tags
+ TestOpening( "init_from");
+ const char* content = GetTextContent();
+ pParam.mType = Param_Surface;
+ pParam.mReference = content;
+ TestClosing( "init_from");
+
+ // don't care for remaining stuff
+ SkipElement( "surface");
+ }
+ else if ( IsElement( "sampler2D"))
+ {
+ // surface ID is given inside <source> tags
+ TestOpening( "source");
+ const char* content = GetTextContent();
+ pParam.mType = Param_Sampler;
+ pParam.mReference = content;
+ TestClosing( "source");
+
+ // don't care for remaining stuff
+ SkipElement( "sampler2D");
+ } else
+ {
+ // ignore unknown element
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the geometry library contents
+void ColladaParser::ReadGeometryLibrary()
+{
+ if ( mReader->isEmptyElement())
+ return;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "geometry"))
+ {
+ // read ID. Another entry which is "optional" by design but obligatory in reality
+ int indexID = GetAttribute( "id");
+ std::string id = mReader->getAttributeValue( indexID);
+
+ // TODO: (thom) support SIDs
+ // ai_assert( TestAttribute( "sid") == -1);
+
+ // create a mesh and store it in the library under its ID
+ Mesh* mesh = new Mesh;
+ mMeshLibrary[id] = mesh;
+
+ // read on from there
+ ReadGeometry( mesh);
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "library_geometries") != 0)
+ ThrowException( "Expected end of \"library_geometries\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a geometry from the geometry library.
+void ColladaParser::ReadGeometry( Collada::Mesh* pMesh)
+{
+ if ( mReader->isEmptyElement())
+ return;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "mesh"))
+ {
+ // read on from there
+ ReadMesh( pMesh);
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "geometry") != 0)
+ ThrowException( "Expected end of \"geometry\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a mesh from the geometry library
+void ColladaParser::ReadMesh( Mesh* pMesh)
+{
+ if ( mReader->isEmptyElement())
+ return;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "source"))
+ {
+ // we have professionals dealing with this
+ ReadSource();
+ }
+ else if ( IsElement( "vertices"))
+ {
+ // read per-vertex mesh data
+ ReadVertexData( pMesh);
+ }
+ else if ( IsElement( "triangles") || IsElement( "lines") || IsElement( "linestrips")
+ || IsElement( "polygons") || IsElement( "polylist") || IsElement( "trifans") || IsElement( "tristrips"))
+ {
+ // read per-index mesh data and faces setup
+ ReadIndexData( pMesh);
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "technique_common") == 0)
+ {
+ // end of another meaningless element - read over it
+ }
+ else if ( strcmp( mReader->getNodeName(), "mesh") == 0)
+ {
+ // end of <mesh> element - we're done here
+ break;
+ } else
+ {
+ // everything else should be punished
+ ThrowException( "Expected end of \"mesh\" element.");
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a source element
+void ColladaParser::ReadSource()
+{
+ int indexID = GetAttribute( "id");
+ std::string sourceID = mReader->getAttributeValue( indexID);
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "float_array") || IsElement( "IDREF_array") || IsElement( "Name_array"))
+ {
+ ReadDataArray();
+ }
+ else if ( IsElement( "technique_common"))
+ {
+ // ignore
+ }
+ else if ( IsElement( "accessor"))
+ {
+ ReadAccessor( sourceID);
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "source") == 0)
+ {
+ // end of <source> - we're done
+ break;
+ }
+ else if ( strcmp( mReader->getNodeName(), "technique_common") == 0)
+ {
+ // end of another meaningless element - read over it
+ } else
+ {
+ // everything else should be punished
+ ThrowException( "Expected end of \"source\" element.");
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a data array holding a number of floats, and stores it in the global library
+void ColladaParser::ReadDataArray()
+{
+ std::string elmName = mReader->getNodeName();
+ bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array");
+
+ // read attributes
+ int indexID = GetAttribute( "id");
+ std::string id = mReader->getAttributeValue( indexID);
+ int indexCount = GetAttribute( "count");
+ unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( indexCount);
+ const char* content = TestTextContent();
+ if (content) { // some exporters write empty data arrays, silently skip over them
+
+ // read values and store inside an array in the data library
+ mDataLibrary[id] = Data();
+ Data& data = mDataLibrary[id];
+ data.mIsStringArray = isStringArray;
+
+ if ( isStringArray)
+ {
+ data.mStrings.reserve( count);
+ std::string s;
+
+ for ( unsigned int a = 0; a < count; a++)
+ {
+ if ( *content == 0)
+ ThrowException( "Expected more values while reading IDREF_array contents.");
+
+ s.clear();
+ while ( !IsSpaceOrNewLine( *content))
+ s += *content++;
+ data.mStrings.push_back( s);
+
+ SkipSpacesAndLineEnd( &content);
+ }
+ } else
+ {
+ data.mValues.reserve( count);
+
+ for ( unsigned int a = 0; a < count; a++)
+ {
+ if ( *content == 0)
+ ThrowException( "Expected more values while reading float_array contents.");
+
+ float value;
+ // read a number
+ content = fast_atof_move( content, value);
+ data.mValues.push_back( value);
+ // skip whitespace after it
+ SkipSpacesAndLineEnd( &content);
+ }
+ }
+ }
+
+ // test for closing tag
+ TestClosing( elmName.c_str());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an accessor and stores it in the global library
+void ColladaParser::ReadAccessor( const std::string& pID)
+{
+ // read accessor attributes
+ int attrSource = GetAttribute( "source");
+ const char* source = mReader->getAttributeValue( attrSource);
+ if ( source[0] != '#')
+ ThrowException( boost::str( boost::format( "Unknown reference format in url \"%s\".") % source));
+ int attrCount = GetAttribute( "count");
+ unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( attrCount);
+ int attrOffset = TestAttribute( "offset");
+ unsigned int offset = 0;
+ if ( attrOffset > -1)
+ offset = (unsigned int) mReader->getAttributeValueAsInt( attrOffset);
+ int attrStride = TestAttribute( "stride");
+ unsigned int stride = 1;
+ if ( attrStride > -1)
+ stride = (unsigned int) mReader->getAttributeValueAsInt( attrStride);
+
+ // store in the library under the given ID
+ mAccessorLibrary[pID] = Accessor();
+ Accessor& acc = mAccessorLibrary[pID];
+ acc.mCount = count;
+ acc.mOffset = offset;
+ acc.mStride = stride;
+ acc.mSource = source+1; // ignore the leading '#'
+ acc.mSize = 0; // gets incremented with every param
+
+ // and read the components
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "param"))
+ {
+ // read data param
+ int attrName = TestAttribute( "name");
+ std::string name;
+ if ( attrName > -1)
+ {
+ name = mReader->getAttributeValue( attrName);
+
+ // analyse for common type components and store it's sub-offset in the corresponding field
+
+ /* Cartesian coordinates */
+ if ( name == "X") acc.mSubOffset[0] = acc.mParams.size();
+ else if ( name == "Y") acc.mSubOffset[1] = acc.mParams.size();
+ else if ( name == "Z") acc.mSubOffset[2] = acc.mParams.size();
+
+ /* RGBA colors */
+ else if ( name == "R") acc.mSubOffset[0] = acc.mParams.size();
+ else if ( name == "G") acc.mSubOffset[1] = acc.mParams.size();
+ else if ( name == "B") acc.mSubOffset[2] = acc.mParams.size();
+ else if ( name == "A") acc.mSubOffset[3] = acc.mParams.size();
+
+ /* UVWQ (STPQ) texture coordinates */
+ else if ( name == "S") acc.mSubOffset[0] = acc.mParams.size();
+ else if ( name == "T") acc.mSubOffset[1] = acc.mParams.size();
+ else if ( name == "P") acc.mSubOffset[2] = acc.mParams.size();
+ // else if ( name == "Q") acc.mSubOffset[3] = acc.mParams.size();
+ /* 4D uv coordinates are not supported in Assimp */
+
+ /* Generic extra data, interpreted as UV data, too*/
+ else if ( name == "U") acc.mSubOffset[0] = acc.mParams.size();
+ else if ( name == "V") acc.mSubOffset[1] = acc.mParams.size();
+ //else
+ // DefaultLogger::get()->warn( boost::str( boost::format( "Unknown accessor parameter \"%s\". Ignoring data channel.") % name));
+ }
+
+ // read data type
+ int attrType = TestAttribute( "type");
+ if ( attrType > -1)
+ {
+ // for the moment we only distinguish between a 4x4 matrix and anything else.
+ // TODO: (thom) I don't have a spec here at work. Check if there are other multi-value types
+ // which should be tested for here.
+ std::string type = mReader->getAttributeValue( attrType);
+ if ( type == "float4x4")
+ acc.mSize += 16;
+ else
+ acc.mSize += 1;
+ }
+
+ acc.mParams.push_back( name);
+
+ // skip remaining stuff of this element, if any
+ SkipElement();
+ } else
+ {
+ ThrowException( "Unexpected sub element in tag \"accessor\".");
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "accessor") != 0)
+ ThrowException( "Expected end of \"accessor\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads input declarations of per-vertex mesh data into the given mesh
+void ColladaParser::ReadVertexData( Mesh* pMesh)
+{
+ // extract the ID of the <vertices> element. Not that we care, but to catch strange referencing schemes we should warn about
+ int attrID= GetAttribute( "id");
+ pMesh->mVertexID = mReader->getAttributeValue( attrID);
+
+ // a number of <input> elements
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "input"))
+ {
+ ReadInputChannel( pMesh->mPerVertexData);
+ } else
+ {
+ ThrowException( "Unexpected sub element in tag \"vertices\".");
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "vertices") != 0)
+ ThrowException( "Expected end of \"vertices\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads input declarations of per-index mesh data into the given mesh
+void ColladaParser::ReadIndexData( Mesh* pMesh)
+{
+ std::vector<size_t> vcount;
+ std::vector<InputChannel> perIndexData;
+
+ // read primitive count from the attribute
+ int attrCount = GetAttribute( "count");
+ size_t numPrimitives = (size_t) mReader->getAttributeValueAsInt( attrCount);
+
+ // material subgroup
+ int attrMaterial = TestAttribute( "material");
+ SubMesh subgroup;
+ if ( attrMaterial > -1)
+ subgroup.mMaterial = mReader->getAttributeValue( attrMaterial);
+ subgroup.mNumFaces = numPrimitives;
+ pMesh->mSubMeshes.push_back( subgroup);
+
+ // distinguish between polys and triangles
+ std::string elementName = mReader->getNodeName();
+ PrimitiveType primType = Prim_Invalid;
+ if ( IsElement( "lines"))
+ primType = Prim_Lines;
+ else if ( IsElement( "linestrips"))
+ primType = Prim_LineStrip;
+ else if ( IsElement( "polygons"))
+ primType = Prim_Polygon;
+ else if ( IsElement( "polylist"))
+ primType = Prim_Polylist;
+ else if ( IsElement( "triangles"))
+ primType = Prim_Triangles;
+ else if ( IsElement( "trifans"))
+ primType = Prim_TriFans;
+ else if ( IsElement( "tristrips"))
+ primType = Prim_TriStrips;
+
+ ai_assert( primType != Prim_Invalid);
+
+ // also a number of <input> elements, but in addition a <p> primitive collection and propably index counts for all primitives
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "input"))
+ {
+ ReadInputChannel( perIndexData);
+ }
+ else if ( IsElement( "vcount"))
+ {
+ if ( !mReader->isEmptyElement())
+ {
+ // case <polylist> - specifies the number of indices for each polygon
+ const char* content = GetTextContent();
+ vcount.reserve( numPrimitives);
+ for ( unsigned int a = 0; a < numPrimitives; a++)
+ {
+ if ( *content == 0)
+ ThrowException( "Expected more values while reading vcount contents.");
+ // read a number
+ vcount.push_back( (size_t) strtol10( content, &content));
+ // skip whitespace after it
+ SkipSpacesAndLineEnd( &content);
+ }
+
+ TestClosing( "vcount");
+ }
+ }
+ else if ( IsElement( "p"))
+ {
+ if ( !mReader->isEmptyElement())
+ {
+ // now here the actual fun starts - these are the indices to construct the mesh data from
+ ReadPrimitives( pMesh, perIndexData, numPrimitives, vcount, primType);
+ }
+ } else
+ {
+ ThrowException( "Unexpected sub element in tag \"vertices\".");
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( mReader->getNodeName() != elementName)
+ ThrowException( boost::str( boost::format( "Expected end of \"%s\" element.") % elementName));
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a single input channel element and stores it in the given array, if valid
+void ColladaParser::ReadInputChannel( std::vector<InputChannel>& poChannels)
+{
+ InputChannel channel;
+
+ // read semantic
+ int attrSemantic = GetAttribute( "semantic");
+ std::string semantic = mReader->getAttributeValue( attrSemantic);
+ channel.mType = GetTypeForSemantic( semantic);
+
+ // read source
+ int attrSource = GetAttribute( "source");
+ const char* source = mReader->getAttributeValue( attrSource);
+ if ( source[0] != '#')
+ ThrowException( boost::str( boost::format( "Unknown reference format in url \"%s\".") % source));
+ channel.mAccessor = source+1; // skipping the leading #, hopefully the remaining text is the accessor ID only
+
+ // read index offset, if per-index <input>
+ int attrOffset = TestAttribute( "offset");
+ if ( attrOffset > -1)
+ channel.mOffset = mReader->getAttributeValueAsInt( attrOffset);
+
+ // read set if texture coordinates
+ if (channel.mType == IT_Texcoord || channel.mType == IT_Color){
+ int attrSet = TestAttribute("set");
+ if (attrSet > -1){
+ attrSet = mReader->getAttributeValueAsInt( attrSet);
+ if (attrSet < 0)
+ ThrowException( boost::str( boost::format( "Invalid index \"%i\" for set attribute") % (attrSet)));
+
+ channel.mIndex = attrSet;
+ }
+ }
+
+ // store, if valid type
+ if ( channel.mType != IT_Invalid)
+ poChannels.push_back( channel);
+
+ // skip remaining stuff of this element, if any
+ SkipElement();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a <p> primitive index list and assembles the mesh data into the given mesh
+void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels,
+ size_t pNumPrimitives, const std::vector<size_t>& pVCount, PrimitiveType pPrimType)
+{
+ // determine number of indices coming per vertex
+ // find the offset index for all per-vertex channels
+ size_t numOffsets = 1;
+ size_t perVertexOffset = 0xffffffff; // invalid value
+ BOOST_FOREACH( const InputChannel& channel, pPerIndexChannels)
+ {
+ numOffsets = std::max( numOffsets, channel.mOffset+1);
+ if ( channel.mType == IT_Vertex)
+ perVertexOffset = channel.mOffset;
+ }
+
+ // determine the expected number of indices
+ size_t expectedPointCount = 0;
+ switch( pPrimType)
+ {
+ case Prim_Polylist:
+ {
+ BOOST_FOREACH( size_t i, pVCount)
+ expectedPointCount += i;
+ break;
+ }
+ case Prim_Lines:
+ expectedPointCount = 2 * pNumPrimitives;
+ break;
+ case Prim_Triangles:
+ expectedPointCount = 3 * pNumPrimitives;
+ break;
+ default:
+ // other primitive types don't state the index count upfront... we need to guess
+ break;
+ }
+
+ // and read all indices into a temporary array
+ std::vector<size_t> indices;
+ if ( expectedPointCount > 0)
+ indices.reserve( expectedPointCount * numOffsets);
+
+ const char* content = GetTextContent();
+ while ( *content != 0)
+ {
+ // read a value.
+ // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways.
+ int value = std::max( 0, strtol10s( content, &content));
+ indices.push_back( size_t( value));
+ // skip whitespace after it
+ SkipSpacesAndLineEnd( &content);
+ }
+
+ // complain if the index count doesn't fit
+ if ( expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets)
+ ThrowException( "Expected different index count in <p> element.");
+ else if ( expectedPointCount == 0 && (indices.size() % numOffsets) != 0)
+ ThrowException( "Expected different index count in <p> element.");
+
+ // find the data for all sources
+ for ( std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
+ {
+ InputChannel& input = *it;
+ if ( input.mResolved)
+ continue;
+
+ // find accessor
+ input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
+ // resolve accessor's data pointer as well, if neccessary
+ const Accessor* acc = input.mResolved;
+ if ( !acc->mData)
+ acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
+ }
+ // and the same for the per-index channels
+ for ( std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
+ {
+ InputChannel& input = *it;
+ if ( input.mResolved)
+ continue;
+
+ // ignore vertex pointer, it doesn't refer to an accessor
+ if ( input.mType == IT_Vertex)
+ {
+ // warn if the vertex channel does not refer to the <vertices> element in the same mesh
+ if ( input.mAccessor != pMesh->mVertexID)
+ ThrowException( "Unsupported vertex referencing scheme.");
+ continue;
+ }
+
+ // find accessor
+ input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
+ // resolve accessor's data pointer as well, if neccessary
+ const Accessor* acc = input.mResolved;
+ if ( !acc->mData)
+ acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
+ }
+
+
+ // now assemble vertex data according to those indices
+ std::vector<size_t>::const_iterator idx = indices.begin();
+
+ // For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
+ size_t numPrimitives = pNumPrimitives;
+ if ( pPrimType == Prim_TriFans || pPrimType == Prim_Polygon)
+ numPrimitives = 1;
+
+ pMesh->mFaceSize.reserve( numPrimitives);
+ pMesh->mFacePosIndices.reserve( indices.size() / numOffsets);
+
+ for ( size_t a = 0; a < numPrimitives; a++)
+ {
+ // determine number of points for this primitive
+ size_t numPoints = 0;
+ switch( pPrimType)
+ {
+ case Prim_Lines:
+ numPoints = 2;
+ break;
+ case Prim_Triangles:
+ numPoints = 3;
+ break;
+ case Prim_Polylist:
+ numPoints = pVCount[a];
+ break;
+ case Prim_TriFans:
+ case Prim_Polygon:
+ numPoints = indices.size() / numOffsets;
+ break;
+ default:
+ // LineStrip and TriStrip not supported due to expected index unmangling
+ ThrowException( "Unsupported primitive type.");
+ break;
+ }
+
+ // store the face size to later reconstruct the face from
+ pMesh->mFaceSize.push_back( numPoints);
+
+ // gather that number of vertices
+ for ( size_t b = 0; b < numPoints; b++)
+ {
+ // read all indices for this vertex. Yes, in a hacky local array
+ assert( numOffsets < 20 && perVertexOffset < 20);
+ size_t vindex[20];
+ for ( size_t offsets = 0; offsets < numOffsets; ++offsets)
+ vindex[offsets] = *idx++;
+
+ // extract per-vertex channels using the global per-vertex offset
+ for ( std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
+ ExtractDataObjectFromChannel( *it, vindex[perVertexOffset], pMesh);
+ // and extract per-index channels using there specified offset
+ for ( std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
+ ExtractDataObjectFromChannel( *it, vindex[it->mOffset], pMesh);
+
+ // store the vertex-data index for later assignment of bone vertex weights
+ pMesh->mFacePosIndices.push_back( vindex[perVertexOffset]);
+ }
+ }
+
+
+ // if I ever get my hands on that guy who invented this steaming pile of indirection...
+ TestClosing( "p");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Extracts a single object from an input channel and stores it in the appropriate mesh data array
+void ColladaParser::ExtractDataObjectFromChannel( const InputChannel& pInput, size_t pLocalIndex, Mesh* pMesh)
+{
+ // ignore vertex referrer - we handle them that separate
+ if ( pInput.mType == IT_Vertex)
+ return;
+
+ const Accessor& acc = *pInput.mResolved;
+ if ( pLocalIndex >= acc.mCount)
+ ThrowException( boost::str( boost::format( "Invalid data index (%d/%d) in primitive specification") % pLocalIndex % acc.mCount));
+
+ // get a pointer to the start of the data object referred to by the accessor and the local index
+ const float* dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex* acc.mStride;
+
+ // assemble according to the accessors component sub-offset list. We don't care, yet,
+ // what kind of object exactly we're extracting here
+ float obj[4];
+ for ( size_t c = 0; c < 4; ++c)
+ obj[c] = dataObject[acc.mSubOffset[c]];
+
+ // now we reinterpret it according to the type we're reading here
+ switch( pInput.mType)
+ {
+ case IT_Position: // ignore all position streams except 0 - there can be only one position
+ if ( pInput.mIndex == 0)
+ pMesh->mPositions.push_back( aiVector3D( obj[0], obj[1], obj[2]));
+ else
+ DefaultLogger::get()->error("Collada: just one vertex position stream supported");
+ break;
+ case IT_Normal:
+ // pad to current vertex count if necessary
+ if ( pMesh->mNormals.size() < pMesh->mPositions.size()-1)
+ pMesh->mNormals.insert( pMesh->mNormals.end(), pMesh->mPositions.size() - pMesh->mNormals.size() - 1, aiVector3D( 0, 1, 0));
+
+ // ignore all normal streams except 0 - there can be only one normal
+ if ( pInput.mIndex == 0)
+ pMesh->mNormals.push_back( aiVector3D( obj[0], obj[1], obj[2]));
+ else
+ DefaultLogger::get()->error("Collada: just one vertex normal stream supported");
+ break;
+ case IT_Tangent:
+ // pad to current vertex count if necessary
+ if ( pMesh->mTangents.size() < pMesh->mPositions.size()-1)
+ pMesh->mTangents.insert( pMesh->mTangents.end(), pMesh->mPositions.size() - pMesh->mTangents.size() - 1, aiVector3D( 1, 0, 0));
+
+ // ignore all tangent streams except 0 - there can be only one tangent
+ if ( pInput.mIndex == 0)
+ pMesh->mTangents.push_back( aiVector3D( obj[0], obj[1], obj[2]));
+ else
+ DefaultLogger::get()->error("Collada: just one vertex tangent stream supported");
+ break;
+ case IT_Bitangent:
+ // pad to current vertex count if necessary
+ if ( pMesh->mBitangents.size() < pMesh->mPositions.size()-1)
+ pMesh->mBitangents.insert( pMesh->mBitangents.end(), pMesh->mPositions.size() - pMesh->mBitangents.size() - 1, aiVector3D( 0, 0, 1));
+
+ // ignore all bitangent streams except 0 - there can be only one bitangent
+ if ( pInput.mIndex == 0)
+ pMesh->mBitangents.push_back( aiVector3D( obj[0], obj[1], obj[2]));
+ else
+ DefaultLogger::get()->error("Collada: just one vertex bitangent stream supported");
+ break;
+ case IT_Texcoord:
+ // up to 4 texture coord sets are fine, ignore the others
+ if ( pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS)
+ {
+ // pad to current vertex count if necessary
+ if ( pMesh->mTexCoords[pInput.mIndex].size() < pMesh->mPositions.size()-1)
+ pMesh->mTexCoords[pInput.mIndex].insert( pMesh->mTexCoords[pInput.mIndex].end(),
+ pMesh->mPositions.size() - pMesh->mTexCoords[pInput.mIndex].size() - 1, aiVector3D( 0, 0, 0));
+
+ pMesh->mTexCoords[pInput.mIndex].push_back( aiVector3D( obj[0], obj[1], obj[2]));
+ if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) /* hack ... consider cleaner solution */
+ pMesh->mNumUVComponents[pInput.mIndex]=3;
+ } else
+ {
+ DefaultLogger::get()->error("Collada: too many texture coordinate sets. Skipping.");
+ }
+ break;
+ case IT_Color:
+ // up to 4 color sets are fine, ignore the others
+ if ( pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS)
+ {
+ // pad to current vertex count if necessary
+ if ( pMesh->mColors[pInput.mIndex].size() < pMesh->mPositions.size()-1)
+ pMesh->mColors[pInput.mIndex].insert( pMesh->mColors[pInput.mIndex].end(),
+ pMesh->mPositions.size() - pMesh->mColors[pInput.mIndex].size() - 1, aiColor4D( 0, 0, 0, 1));
+
+ pMesh->mColors[pInput.mIndex].push_back( aiColor4D( obj[0], obj[1], obj[2], obj[3]));
+ } else
+ {
+ DefaultLogger::get()->error("Collada: too many vertex color sets. Skipping.");
+ }
+
+ break;
+ default:
+ // IT_Invalid and IT_Vertex
+ ai_assert(false && "shouldn't ever get here");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the library of node hierarchies and scene parts
+void ColladaParser::ReadSceneLibrary()
+{
+ if ( mReader->isEmptyElement())
+ return;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ // a visual scene - generate root node under its ID and let ReadNode() do the recursive work
+ if ( IsElement( "visual_scene"))
+ {
+ // read ID. Is optional according to the spec, but how on earth should a scene_instance refer to it then?
+ int indexID = GetAttribute( "id");
+ const char* attrID = mReader->getAttributeValue( indexID);
+
+ // read name if given.
+ int indexName = TestAttribute( "name");
+ const char* attrName = "unnamed";
+ if ( indexName > -1)
+ attrName = mReader->getAttributeValue( indexName);
+
+ // create a node and store it in the library under its ID
+ Node* node = new Node;
+ node->mID = attrID;
+ node->mName = attrName;
+ mNodeLibrary[node->mID] = node;
+
+ ReadSceneNode( node);
+ } else
+ {
+ // ignore the rest
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "library_visual_scenes") == 0)
+ //ThrowException( "Expected end of \"library_visual_scenes\" element.");
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a scene node's contents including children and stores it in the given node
+void ColladaParser::ReadSceneNode( Node* pNode)
+{
+ // quit immediately on <bla/> elements
+ if ( mReader->isEmptyElement())
+ return;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "node"))
+ {
+ Node* child = new Node;
+ int attrID = TestAttribute( "id");
+ if ( attrID > -1)
+ child->mID = mReader->getAttributeValue( attrID);
+ int attrSID = TestAttribute( "sid");
+ if ( attrSID > -1)
+ child->mSID = mReader->getAttributeValue( attrSID);
+
+ int attrName = TestAttribute( "name");
+ if ( attrName > -1)
+ child->mName = mReader->getAttributeValue( attrName);
+
+ // TODO: (thom) support SIDs
+ // assert( TestAttribute( "sid") == -1);
+
+ if (pNode)
+ {
+ pNode->mChildren.push_back( child);
+ child->mParent = pNode;
+ }
+ else
+ {
+ // no parent node given, probably called from <library_nodes> element.
+ // create new node in node library
+ mNodeLibrary[child->mID] = child;
+ }
+
+ // read on recursively from there
+ ReadSceneNode( child);
+ continue;
+ }
+ // For any further stuff we need a valid node to work on
+ else if (!pNode)
+ continue;
+
+ if ( IsElement( "lookat"))
+ ReadNodeTransformation( pNode, TF_LOOKAT);
+ else if ( IsElement( "matrix"))
+ ReadNodeTransformation( pNode, TF_MATRIX);
+ else if ( IsElement( "rotate"))
+ ReadNodeTransformation( pNode, TF_ROTATE);
+ else if ( IsElement( "scale"))
+ ReadNodeTransformation( pNode, TF_SCALE);
+ else if ( IsElement( "skew"))
+ ReadNodeTransformation( pNode, TF_SKEW);
+ else if ( IsElement( "translate"))
+ ReadNodeTransformation( pNode, TF_TRANSLATE);
+ else if ( IsElement( "render") && pNode->mParent == NULL && 0 == pNode->mPrimaryCamera.length())
+ {
+ // ... scene evaluation or, in other words, postprocessing pipeline,
+ // or, again in other words, a turing-complete description how to
+ // render a Collada scene. The only thing that is interesting for
+ // us is the primary camera.
+ int attrId = TestAttribute("camera_node");
+ if (-1 != attrId)
+ {
+ const char* s = mReader->getAttributeValue(attrId);
+ if (s[0] != '#')
+ DefaultLogger::get()->error("Collada: Unresolved reference format of camera");
+ else
+ pNode->mPrimaryCamera = s+1;
+ }
+ }
+ else if ( IsElement( "instance_node"))
+ {
+ // find the node in the library
+ int attrID = TestAttribute( "url");
+ if ( attrID != -1)
+ {
+ const char* s = mReader->getAttributeValue(attrID);
+ if (s[0] != '#')
+ DefaultLogger::get()->error("Collada: Unresolved reference format of node");
+ else
+ {
+ pNode->mNodeInstances.push_back(NodeInstance());
+ pNode->mNodeInstances.back().mNode = s+1;
+ }
+ }
+ }
+ else if ( IsElement( "instance_geometry") || IsElement( "instance_controller"))
+ {
+ // Reference to a mesh or controller, with possible material associations
+ ReadNodeGeometry( pNode);
+ }
+ else if ( IsElement( "instance_light"))
+ {
+ // Reference to a light, name given in 'url' attribute
+ int attrID = TestAttribute("url");
+ if (-1 == attrID)
+ DefaultLogger::get()->warn("Collada: Expected url attribute in <instance_light> element");
+ else
+ {
+ const char* url = mReader->getAttributeValue( attrID);
+ if ( url[0] != '#')
+ ThrowException( "Unknown reference format in <instance_light> element");
+
+ pNode->mLights.push_back(LightInstance());
+ pNode->mLights.back().mLight = url+1;
+ }
+ }
+ else if ( IsElement( "instance_camera"))
+ {
+ // Reference to a camera, name given in 'url' attribute
+ int attrID = TestAttribute("url");
+ if (-1 == attrID)
+ DefaultLogger::get()->warn("Collada: Expected url attribute in <instance_camera> element");
+ else
+ {
+ const char* url = mReader->getAttributeValue( attrID);
+ if ( url[0] != '#')
+ ThrowException( "Unknown reference format in <instance_camera> element");
+
+ pNode->mCameras.push_back(CameraInstance());
+ pNode->mCameras.back().mCamera = url+1;
+ }
+ }
+ else
+ {
+ // skip everything else for the moment
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a node transformation entry of the given type and adds it to the given node's transformation list.
+void ColladaParser::ReadNodeTransformation( Node* pNode, TransformType pType)
+{
+ if ( mReader->isEmptyElement())
+ return;
+
+ std::string tagName = mReader->getNodeName();
+
+ Transform tf;
+ tf.mType = pType;
+
+ // read SID
+ int indexSID = TestAttribute( "sid");
+ if ( indexSID >= 0)
+ tf.mID = mReader->getAttributeValue( indexSID);
+
+ // how many parameters to read per transformation type
+ static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 };
+ const char* content = GetTextContent();
+
+ // read as many parameters and store in the transformation
+ for ( unsigned int a = 0; a < sNumParameters[pType]; a++)
+ {
+ // read a number
+ content = fast_atof_move( content, tf.f[a]);
+ // skip whitespace after it
+ SkipSpacesAndLineEnd( &content);
+ }
+
+ // place the transformation at the queue of the node
+ pNode->mTransforms.push_back( tf);
+
+ // and consume the closing tag
+ TestClosing( tagName.c_str());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Processes bind_vertex_input and bind elements
+void ColladaParser::ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl)
+{
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+ if ( IsElement( "bind_vertex_input"))
+ {
+ Collada::InputSemanticMapEntry vn;
+
+ // effect semantic
+ int n = GetAttribute("semantic");
+ std::string s = mReader->getAttributeValue(n);
+
+ // input semantic
+ n = GetAttribute("input_semantic");
+ vn.mType = GetTypeForSemantic( mReader->getAttributeValue(n) );
+
+ // index of input set
+ n = TestAttribute("input_set");
+ if (-1 != n)
+ vn.mSet = mReader->getAttributeValueAsInt(n);
+
+ tbl.mMap[s] = vn;
+ }
+ else if ( IsElement( "bind")) {
+ DefaultLogger::get()->warn("Collada: Found unsupported <bind> element");
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+ if ( strcmp( mReader->getNodeName(), "instance_material") == 0)
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a mesh reference in a node and adds it to the node's mesh list
+void ColladaParser::ReadNodeGeometry( Node* pNode)
+{
+ // referred mesh is given as an attribute of the <instance_geometry> element
+ int attrUrl = GetAttribute( "url");
+ const char* url = mReader->getAttributeValue( attrUrl);
+ if ( url[0] != '#')
+ ThrowException( "Unknown reference format");
+
+ Collada::MeshInstance instance;
+ instance.mMeshOrController = url+1; // skipping the leading #
+
+ if ( !mReader->isEmptyElement())
+ {
+ // read material associations. Ignore additional elements inbetween
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "instance_material"))
+ {
+ // read ID of the geometry subgroup and the target material
+ int attrGroup = GetAttribute( "symbol");
+ std::string group = mReader->getAttributeValue( attrGroup);
+ int attrMaterial = GetAttribute( "target");
+ const char* urlMat = mReader->getAttributeValue( attrMaterial);
+ Collada::SemanticMappingTable s;
+ if ( urlMat[0] == '#')
+ urlMat++;
+
+ s.mMatName = urlMat;
+
+ // resolve further material details + THIS UGLY AND NASTY semantic mapping stuff
+ if ( !mReader->isEmptyElement())
+ ReadMaterialVertexInputBinding(s);
+
+ // store the association
+ instance.mMaterials[group] = s;
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( strcmp( mReader->getNodeName(), "instance_geometry") == 0
+ || strcmp( mReader->getNodeName(), "instance_controller") == 0)
+ break;
+ }
+ }
+ }
+
+ // store it
+ pNode->mMeshes.push_back( instance);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the collada scene
+void ColladaParser::ReadScene()
+{
+ if ( mReader->isEmptyElement())
+ return;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+ if ( IsElement( "instance_visual_scene"))
+ {
+ // should be the first and only occurence
+ if ( mRootNode)
+ ThrowException( "Invalid scene containing multiple root nodes");
+
+ // read the url of the scene to instance. Should be of format "#some_name"
+ int urlIndex = GetAttribute( "url");
+ const char* url = mReader->getAttributeValue( urlIndex);
+ if ( url[0] != '#')
+ ThrowException( "Unknown reference format");
+
+ // find the referred scene, skip the leading #
+ NodeLibrary::const_iterator sit = mNodeLibrary.find( url+1);
+ if ( sit == mNodeLibrary.end())
+ ThrowException( "Unable to resolve visual_scene reference \"" + std::string(url) + "\".");
+ mRootNode = sit->second;
+ } else {
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Aborts the file reading with an exception
+void ColladaParser::ThrowException( const std::string& pError) const
+{
+ throw DeadlyImportError( boost::str( boost::format( "Collada: %s - %s") % mFileName % pError));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skips all data until the end node of the current element
+void ColladaParser::SkipElement()
+{
+ // nothing to skip if it's an <element />
+ if ( mReader->isEmptyElement())
+ return;
+
+ // reroute
+ SkipElement( mReader->getNodeName());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skips all data until the end node of the given element
+void ColladaParser::SkipElement( const char* pElement)
+{
+ // copy the current node's name because it'a pointer to the reader's internal buffer,
+ // which is going to change with the upcoming parsing
+ std::string element = pElement;
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ if ( mReader->getNodeName() == element)
+ break;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tests for an opening element of the given name, throws an exception if not found
+void ColladaParser::TestOpening( const char* pName)
+{
+ // read element start
+ if ( !mReader->read())
+ ThrowException( boost::str( boost::format( "Unexpected end of file while beginning of \"%s\" element.") % pName));
+ // whitespace in front is ok, just read again if found
+ if ( mReader->getNodeType() == irr::io::EXN_TEXT)
+ if ( !mReader->read())
+ ThrowException( boost::str( boost::format( "Unexpected end of file while reading beginning of \"%s\" element.") % pName));
+
+ if ( mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp( mReader->getNodeName(), pName) != 0)
+ ThrowException( boost::str( boost::format( "Expected start of \"%s\" element.") % pName));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tests for the closing tag of the given element, throws an exception if not found
+void ColladaParser::TestClosing( const char* pName)
+{
+ // check if we're already on the closing tag and return right away
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp( mReader->getNodeName(), pName) == 0)
+ return;
+
+ // if not, read some more
+ if ( !mReader->read())
+ ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of \"%s\" element.") % pName));
+ // whitespace in front is ok, just read again if found
+ if ( mReader->getNodeType() == irr::io::EXN_TEXT)
+ if ( !mReader->read())
+ ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of \"%s\" element.") % pName));
+
+ // but this has the be the closing tag, or we're lost
+ if ( mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp( mReader->getNodeName(), pName) != 0)
+ ThrowException( boost::str( boost::format( "Expected end of \"%s\" element.") % pName));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes
+int ColladaParser::GetAttribute( const char* pAttr) const
+{
+ int index = TestAttribute( pAttr);
+ if ( index != -1)
+ return index;
+
+ // attribute not found -> throw an exception
+ ThrowException( boost::str( boost::format( "Expected attribute \"%s\" at element \"%s\".") % pAttr % mReader->getNodeName()));
+ return -1;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tests the present element for the presence of one attribute, returns its index or throws an exception if not found
+int ColladaParser::TestAttribute( const char* pAttr) const
+{
+ for ( int a = 0; a < mReader->getAttributeCount(); a++)
+ if ( strcmp( mReader->getAttributeName( a), pAttr) == 0)
+ return a;
+
+ return -1;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the text contents of an element, throws an exception if not given. Skips leading whitespace.
+const char* ColladaParser::GetTextContent()
+{
+ const char* sz = TestTextContent();
+ if (!sz) {
+ ThrowException( "Invalid contents in element \"n\".");
+ }
+ return sz;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the text contents of an element, returns NULL if not given. Skips leading whitespace.
+const char* ColladaParser::TestTextContent()
+{
+ // present node should be the beginning of an element
+ if ( mReader->getNodeType() != irr::io::EXN_ELEMENT || mReader->isEmptyElement())
+ ThrowException( "Expected opening element");
+
+ // read contents of the element
+ if ( !mReader->read())
+ ThrowException( "Unexpected end of file while reading n element.");
+ if ( mReader->getNodeType() != irr::io::EXN_TEXT)
+ return NULL;
+
+ // skip leading whitespace
+ const char* text = mReader->getNodeData();
+ SkipSpacesAndLineEnd( &text);
+
+ return text;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Calculates the resulting transformation fromm all the given transform steps
+aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vector<Transform>& pTransforms) const
+{
+ aiMatrix4x4 res;
+
+ for ( std::vector<Transform>::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it)
+ {
+ const Transform& tf = *it;
+ switch( tf.mType)
+ {
+ case TF_LOOKAT:
+ {
+ aiVector3D pos( tf.f[0], tf.f[1], tf.f[2]);
+ aiVector3D dstPos( tf.f[3], tf.f[4], tf.f[5]);
+ aiVector3D up = aiVector3D( tf.f[6], tf.f[7], tf.f[8]).Normalize();
+ aiVector3D dir = aiVector3D( dstPos - pos).Normalize();
+ aiVector3D right = (dir ^ up).Normalize();
+
+ res *= aiMatrix4x4(
+ right.x, up.x, -dir.x, pos.x,
+ right.y, up.y, -dir.y, pos.y,
+ right.z, up.z, -dir.z, pos.z,
+ 0, 0, 0, 1);
+ break;
+ }
+ case TF_ROTATE:
+ {
+ aiMatrix4x4 rot;
+ float angle = tf.f[3] * float( AI_MATH_PI) / 180.0f;
+ aiVector3D axis( tf.f[0], tf.f[1], tf.f[2]);
+ aiMatrix4x4::Rotation( angle, axis, rot);
+ res *= rot;
+ break;
+ }
+ case TF_TRANSLATE:
+ {
+ aiMatrix4x4 trans;
+ aiMatrix4x4::Translation( aiVector3D( tf.f[0], tf.f[1], tf.f[2]), trans);
+ res *= trans;
+ break;
+ }
+ case TF_SCALE:
+ {
+ aiMatrix4x4 scale( tf.f[0], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[1], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[2], 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+ res *= scale;
+ break;
+ }
+ case TF_SKEW:
+ // TODO: (thom)
+ ai_assert( false);
+ break;
+ case TF_MATRIX:
+ {
+ aiMatrix4x4 mat( tf.f[0], tf.f[1], tf.f[2], tf.f[3], tf.f[4], tf.f[5], tf.f[6], tf.f[7],
+ tf.f[8], tf.f[9], tf.f[10], tf.f[11], tf.f[12], tf.f[13], tf.f[14], tf.f[15]);
+ res *= mat;
+ break;
+ }
+ default:
+ assert( false);
+ break;
+ }
+ }
+
+ return res;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Determines the input data type for the given semantic string
+Collada::InputType ColladaParser::GetTypeForSemantic( const std::string& pSemantic)
+{
+ if ( pSemantic == "POSITION")
+ return IT_Position;
+ else if ( pSemantic == "TEXCOORD")
+ return IT_Texcoord;
+ else if ( pSemantic == "NORMAL")
+ return IT_Normal;
+ else if ( pSemantic == "COLOR")
+ return IT_Color;
+ else if ( pSemantic == "VERTEX")
+ return IT_Vertex;
+ else if ( pSemantic == "BINORMAL" || pSemantic == "TEXBINORMAL")
+ return IT_Bitangent;
+ else if ( pSemantic == "TANGENT" || pSemantic == "TEXTANGENT")
+ return IT_Tangent;
+
+ DefaultLogger::get()->warn( boost::str( boost::format( "Unknown vertex input type \"%s\". Ignoring.") % pSemantic));
+ return IT_Invalid;
+}
+
+#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER
diff --git a/src/3rdparty/assimp/code/ColladaParser.h b/src/3rdparty/assimp/code/ColladaParser.h
new file mode 100644
index 000000000..cba184748
--- /dev/null
+++ b/src/3rdparty/assimp/code/ColladaParser.h
@@ -0,0 +1,341 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file ColladaParser.h
+ * @brief Defines the parser helper class for the collada loader
+ */
+
+#ifndef AI_COLLADAPARSER_H_INC
+#define AI_COLLADAPARSER_H_INC
+
+#include "irrXMLWrapper.h"
+#include "ColladaHelper.h"
+
+namespace Assimp
+{
+
+// ------------------------------------------------------------------------------------------
+/** Parser helper class for the Collada loader.
+ *
+ * Does all the XML reading and builds internal data structures from it,
+ * but leaves the resolving of all the references to the loader.
+*/
+class ColladaParser
+{
+ friend class ColladaLoader;
+
+protected:
+ /** Constructor from XML file */
+ ColladaParser( IOSystem* pIOHandler, const std::string& pFile);
+
+ /** Destructor */
+ ~ColladaParser();
+
+ /** Reads the contents of the file */
+ void ReadContents();
+
+ /** Reads the structure of the file */
+ void ReadStructure();
+
+ /** Reads asset informations such as coordinate system informations and legal blah */
+ void ReadAssetInfo();
+
+ /** Reads the animation library */
+ void ReadAnimationLibrary();
+
+ /** Reads an animation into the given parent structure */
+ void ReadAnimation( Collada::Animation* pParent);
+
+ /** Reads an animation sampler into the given anim channel */
+ void ReadAnimationSampler( Collada::AnimationChannel& pChannel);
+
+ /** Reads the skeleton controller library */
+ void ReadControllerLibrary();
+
+ /** Reads a controller into the given mesh structure */
+ void ReadController( Collada::Controller& pController);
+
+ /** Reads the joint definitions for the given controller */
+ void ReadControllerJoints( Collada::Controller& pController);
+
+ /** Reads the joint weights for the given controller */
+ void ReadControllerWeights( Collada::Controller& pController);
+
+ /** Reads the image library contents */
+ void ReadImageLibrary();
+
+ /** Reads an image entry into the given image */
+ void ReadImage( Collada::Image& pImage);
+
+ /** Reads the material library */
+ void ReadMaterialLibrary();
+
+ /** Reads a material entry into the given material */
+ void ReadMaterial( Collada::Material& pMaterial);
+
+ /** Reads the camera library */
+ void ReadCameraLibrary();
+
+ /** Reads a camera entry into the given camera */
+ void ReadCamera( Collada::Camera& pCamera);
+
+ /** Reads the light library */
+ void ReadLightLibrary();
+
+ /** Reads a light entry into the given light */
+ void ReadLight( Collada::Light& pLight);
+
+ /** Reads the effect library */
+ void ReadEffectLibrary();
+
+ /** Reads an effect entry into the given effect*/
+ void ReadEffect( Collada::Effect& pEffect);
+
+ /** Reads an COMMON effect profile */
+ void ReadEffectProfileCommon( Collada::Effect& pEffect);
+
+ /** Read sampler properties */
+ void ReadSamplerProperties( Collada::Sampler& pSampler);
+
+ /** Reads an effect entry containing a color or a texture defining that color */
+ void ReadEffectColor( aiColor4D& pColor, Collada::Sampler& pSampler);
+
+ /** Reads an effect entry containing a float */
+ void ReadEffectFloat( float& pFloat);
+
+ /** Reads an effect parameter specification of any kind */
+ void ReadEffectParam( Collada::EffectParam& pParam);
+
+ /** Reads the geometry library contents */
+ void ReadGeometryLibrary();
+
+ /** Reads a geometry from the geometry library. */
+ void ReadGeometry( Collada::Mesh* pMesh);
+
+ /** Reads a mesh from the geometry library */
+ void ReadMesh( Collada::Mesh* pMesh);
+
+ /** Reads a source element - a combination of raw data and an accessor defining
+ * things that should not be redefinable. Yes, that's another rant.
+ */
+ void ReadSource();
+
+ /** Reads a data array holding a number of elements, and stores it in the global library.
+ * Currently supported are array of floats and arrays of strings.
+ */
+ void ReadDataArray();
+
+ /** Reads an accessor and stores it in the global library under the given ID -
+ * accessors use the ID of the parent <source> element
+ */
+ void ReadAccessor( const std::string& pID);
+
+ /** Reads input declarations of per-vertex mesh data into the given mesh */
+ void ReadVertexData( Collada::Mesh* pMesh);
+
+ /** Reads input declarations of per-index mesh data into the given mesh */
+ void ReadIndexData( Collada::Mesh* pMesh);
+
+ /** Reads a single input channel element and stores it in the given array, if valid */
+ void ReadInputChannel( std::vector<Collada::InputChannel>& poChannels);
+
+ /** Reads a <p> primitive index list and assembles the mesh data into the given mesh */
+ void ReadPrimitives( Collada::Mesh* pMesh, std::vector<Collada::InputChannel>& pPerIndexChannels,
+ size_t pNumPrimitives, const std::vector<size_t>& pVCount, Collada::PrimitiveType pPrimType);
+
+ /** Extracts a single object from an input channel and stores it in the appropriate mesh data array */
+ void ExtractDataObjectFromChannel( const Collada::InputChannel& pInput, size_t pLocalIndex, Collada::Mesh* pMesh);
+
+ /** Reads the library of node hierarchies and scene parts */
+ void ReadSceneLibrary();
+
+ /** Reads a scene node's contents including children and stores it in the given node */
+ void ReadSceneNode( Collada::Node* pNode);
+
+ /** Reads a node transformation entry of the given type and adds it to the given node's transformation list. */
+ void ReadNodeTransformation( Collada::Node* pNode, Collada::TransformType pType);
+
+ /** Reads a mesh reference in a node and adds it to the node's mesh list */
+ void ReadNodeGeometry( Collada::Node* pNode);
+
+ /** Reads the collada scene */
+ void ReadScene();
+
+ // Processes bind_vertex_input and bind elements
+ void ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl);
+
+protected:
+ /** Aborts the file reading with an exception */
+ void ThrowException( const std::string& pError) const;
+
+ /** Skips all data until the end node of the current element */
+ void SkipElement();
+
+ /** Skips all data until the end node of the given element */
+ void SkipElement( const char* pElement);
+
+ /** Compares the current xml element name to the given string and returns true if equal */
+ bool IsElement( const char* pName) const;
+
+ /** Tests for the opening tag of the given element, throws an exception if not found */
+ void TestOpening( const char* pName);
+
+ /** Tests for the closing tag of the given element, throws an exception if not found */
+ void TestClosing( const char* pName);
+
+ /** Checks the present element for the presence of the attribute, returns its index
+ or throws an exception if not found */
+ int GetAttribute( const char* pAttr) const;
+
+ /** Returns the index of the named attribute or -1 if not found. Does not throw,
+ therefore useful for optional attributes */
+ int TestAttribute( const char* pAttr) const;
+
+ /** Reads the text contents of an element, throws an exception if not given.
+ Skips leading whitespace. */
+ const char* GetTextContent();
+
+ /** Reads the text contents of an element, returns NULL if not given.
+ Skips leading whitespace. */
+ const char* TestTextContent();
+
+ /** Reads a single bool from current text content */
+ bool ReadBoolFromTextContent();
+
+ /** Reads a single float from current text content */
+ float ReadFloatFromTextContent();
+
+ /** Calculates the resulting transformation from all the given transform steps */
+ aiMatrix4x4 CalculateResultTransform( const std::vector<Collada::Transform>& pTransforms) const;
+
+ /** Determines the input data type for the given semantic string */
+ Collada::InputType GetTypeForSemantic( const std::string& pSemantic);
+
+ /** Finds the item in the given library by its reference, throws if not found */
+ template <typename Type> const Type& ResolveLibraryReference(
+ const std::map<std::string, Type>& pLibrary, const std::string& pURL) const;
+
+protected:
+ /** Filename, for a verbose error message */
+ std::string mFileName;
+
+ /** XML reader, member for everyday use */
+ irr::io::IrrXMLReader* mReader;
+
+ /** All data arrays found in the file by ID. Might be referred to by actually
+ everyone. Collada, you are a steaming pile of indirection. */
+ typedef std::map<std::string, Collada::Data> DataLibrary;
+ DataLibrary mDataLibrary;
+
+ /** Same for accessors which define how the data in a data array is accessed. */
+ typedef std::map<std::string, Collada::Accessor> AccessorLibrary;
+ AccessorLibrary mAccessorLibrary;
+
+ /** Mesh library: mesh by ID */
+ typedef std::map<std::string, Collada::Mesh*> MeshLibrary;
+ MeshLibrary mMeshLibrary;
+
+ /** node library: root node of the hierarchy part by ID */
+ typedef std::map<std::string, Collada::Node*> NodeLibrary;
+ NodeLibrary mNodeLibrary;
+
+ /** Image library: stores texture properties by ID */
+ typedef std::map<std::string, Collada::Image> ImageLibrary;
+ ImageLibrary mImageLibrary;
+
+ /** Effect library: surface attributes by ID */
+ typedef std::map<std::string, Collada::Effect> EffectLibrary;
+ EffectLibrary mEffectLibrary;
+
+ /** Material library: surface material by ID */
+ typedef std::map<std::string, Collada::Material> MaterialLibrary;
+ MaterialLibrary mMaterialLibrary;
+
+ /** Light library: surface light by ID */
+ typedef std::map<std::string, Collada::Light> LightLibrary;
+ LightLibrary mLightLibrary;
+
+ /** Camera library: surface material by ID */
+ typedef std::map<std::string, Collada::Camera> CameraLibrary;
+ CameraLibrary mCameraLibrary;
+
+ /** Controller library: joint controllers by ID */
+ typedef std::map<std::string, Collada::Controller> ControllerLibrary;
+ ControllerLibrary mControllerLibrary;
+
+ /** Pointer to the root node. Don't delete, it just points to one of
+ the nodes in the node library. */
+ Collada::Node* mRootNode;
+
+ /** Root animation container */
+ Collada::Animation mAnims;
+
+ /** Size unit: how large compared to a meter */
+ float mUnitSize;
+
+ /** Which is the up vector */
+ enum { UP_X, UP_Y, UP_Z } mUpDirection;
+
+ /** Collada file format version */
+ Collada::FormatVersion mFormat;
+};
+
+// ------------------------------------------------------------------------------------------------
+// Check for element match
+inline bool ColladaParser::IsElement( const char* pName) const
+{
+ ai_assert( mReader->getNodeType() == irr::io::EXN_ELEMENT);
+ return ::strcmp( mReader->getNodeName(), pName) == 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Finds the item in the given library by its reference, throws if not found
+template <typename Type>
+const Type& ColladaParser::ResolveLibraryReference( const std::map<std::string, Type>& pLibrary, const std::string& pURL) const
+{
+ typename std::map<std::string, Type>::const_iterator it = pLibrary.find( pURL);
+ if ( it == pLibrary.end())
+ ThrowException( boost::str( boost::format( "Unable to resolve library reference \"%s\".") % pURL));
+ return it->second;
+}
+
+} // end of namespace Assimp
+
+#endif // AI_COLLADAPARSER_H_INC
diff --git a/src/3rdparty/assimp/code/ComputeUVMappingProcess.cpp b/src/3rdparty/assimp/code/ComputeUVMappingProcess.cpp
new file mode 100644
index 000000000..a56849177
--- /dev/null
+++ b/src/3rdparty/assimp/code/ComputeUVMappingProcess.cpp
@@ -0,0 +1,508 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file GenUVCoords step */
+
+
+#include "AssimpPCH.h"
+#include "ComputeUVMappingProcess.h"
+#include "ProcessHelper.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+
+namespace {
+
+ const static aiVector3D base_axis_y(0.f,1.f,0.f);
+ const static aiVector3D base_axis_x(1.f,0.f,0.f);
+ const static aiVector3D base_axis_z(0.f,0.f,1.f);
+ const static float angle_epsilon = 0.95f;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 float b = planeNormal * (planePos - ray.pos);
+ float h = ray.dir * planeNormal;
+ if ((h < 10e-5f && h > -10e-5f) || (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;
+
+ DefaultLogger::get()->error("Unable to compute UV coordinates, no free UV slot found");
+ return 0xffffffff;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 float LOWER_LIMIT = 0.1f;
+ const static float UPPER_LIMIT = 0.9f;
+
+ const static float LOWER_EPSILON = 10e-3f;
+ const static float UPPER_EPSILON = 1.f-10e-3f;
+
+ 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.f;
+
+ // 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.f;
+
+ // 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.f;
+ else if (!round_to_zero && out[face.mIndices[n]].x <= LOWER_EPSILON)
+ out[face.mIndices[n]].x = 1.f;
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+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((atan2 (diff.z, diff.y) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
+ (asin (diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.f);
+ }
+ }
+ 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((atan2 (diff.x, diff.z) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
+ (asin (diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.f);
+ }
+ }
+ 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((atan2 (diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
+ (asin (diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.f);
+ }
+ }
+ // 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((atan2 (diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
+ (asin (diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.f);
+ }
+ }
+
+
+ // 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 float 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 = (atan2 ( pos.z - center.z, pos.y - center.y) +(float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI;
+ }
+ }
+ else if (axis * base_axis_y >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ const float 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 = (atan2 ( pos.x - center.x, pos.z - center.z) +(float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI;
+ }
+ }
+ else if (axis * base_axis_z >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ const float 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 = (atan2 ( pos.y - center.y, pos.x - center.x) +(float)AI_MATH_PI ) / (float)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 float 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 = (atan2 ( pos.x - center.x, pos.z - center.z) +(float)AI_MATH_PI ) / (float)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)
+{
+ float 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);
+ }
+ }
+ 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);
+ }
+ }
+ 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);
+ }
+ }
+ // 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);
+ }
+ }
+
+ // shouldn't be necessary to remove UV seams ...
+}
+
+// ------------------------------------------------------------------------------------------------
+void ComputeUVMappingProcess::ComputeBoxMapping(aiMesh*, aiVector3D*)
+{
+ DefaultLogger::get()->error("Mapping type currently not implemented");
+}
+
+// ------------------------------------------------------------------------------------------------
+void ComputeUVMappingProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->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())
+ {
+ sprintf(buffer, "Found non-UV mapped texture (%s,%i). Mapping type: %s",
+ TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex,
+ MappingTypeToString(mapping));
+
+ DefaultLogger::get()->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;
+
+ // 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;
+ if ( mesh->mMaterialIndex != i || ( outIdx = FindEmptyUVChannel(mesh) ) == 0xffffffff ||
+ !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)
+ {
+ DefaultLogger::get()->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;
+ ((MaterialHelper*)mat)->AddProperty(&idx,1,AI_MATKEY_UVWSRC(prop->mSemantic,prop->mIndex));
+ }
+ }
+ }
+ }
+ DefaultLogger::get()->debug("GenUVCoordsProcess finished");
+}
diff --git a/src/3rdparty/assimp/code/ComputeUVMappingProcess.h b/src/3rdparty/assimp/code/ComputeUVMappingProcess.h
new file mode 100644
index 000000000..412c641ab
--- /dev/null
+++ b/src/3rdparty/assimp/code/ComputeUVMappingProcess.h
@@ -0,0 +1,149 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiMesh.h"
+
+class ComputeUVMappingTest;
+namespace Assimp
+ {
+
+// ---------------------------------------------------------------------------
+/** ComputeUVMappingProcess - converts special mappings, such as spherical,
+ * cylindrical or boxed to proper UV coordinates for rendering.
+*/
+class ASSIMP_API ComputeUVMappingProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::ComputeUVMappingTest; // grant the unit test full access to us
+
+protected:
+ /** Constructor to be privately used by Importer */
+ ComputeUVMappingProcess();
+
+ /** Destructor, private as well */
+ ~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
+ {
+ 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/src/3rdparty/assimp/code/ConvertToLHProcess.cpp b/src/3rdparty/assimp/code/ConvertToLHProcess.cpp
new file mode 100644
index 000000000..f4c72245a
--- /dev/null
+++ b/src/3rdparty/assimp/code/ConvertToLHProcess.cpp
@@ -0,0 +1,318 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file 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 "AssimpPCH.h"
+#include "ConvertToLHProcess.h"
+
+using namespace Assimp;
+
+#ifndef ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MakeLeftHandedProcess::MakeLeftHandedProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MakeLeftHandedProcess::~MakeLeftHandedProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// 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);
+ DefaultLogger::get()->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);
+ }
+ }
+ DefaultLogger::get()->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)
+{
+ // 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 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)
+{
+ MaterialHelper* mat = (MaterialHelper*)_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)
+{
+ DefaultLogger::get()->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]);
+ DefaultLogger::get()->debug("FlipUVsProcess finished");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts a single material
+void FlipUVsProcess::ProcessMaterial (aiMaterial* _mat)
+{
+ MaterialHelper* mat = (MaterialHelper*)_mat;
+ for (unsigned int a = 0; a < mat->mNumProperties;++a) {
+ aiMaterialProperty* prop = mat->mProperties[a];
+
+ // 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)
+{
+ // mirror texture y coordinate
+ for ( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) {
+ if ( !pMesh->HasTextureCoords( a))
+ break;
+
+ for ( unsigned int b = 0; b < pMesh->mNumVertices; b++)
+ pMesh->mTextureCoords[a][b].y = 1.0f - pMesh->mTextureCoords[a][b].y;
+ }
+}
+
+#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)
+{
+ DefaultLogger::get()->debug("FlipWindingOrderProcess begin");
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ ProcessMesh(pScene->mMeshes[i]);
+ DefaultLogger::get()->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]);
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_FLIPWINDING_PROCESS
diff --git a/src/3rdparty/assimp/code/ConvertToLHProcess.h b/src/3rdparty/assimp/code/ConvertToLHProcess.h
new file mode 100644
index 000000000..25bf178bc
--- /dev/null
+++ b/src/3rdparty/assimp/code/ConvertToLHProcess.h
@@ -0,0 +1,169 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiTypes.h"
+#include "BaseProcess.h"
+
+struct aiMesh;
+struct aiNodeAnim;
+
+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 ASSIMP_API MakeLeftHandedProcess : public BaseProcess
+{
+ friend class Importer;
+
+public:
+ /** Constructor to be privately used by Importer */
+ MakeLeftHandedProcess();
+
+ /** Destructor, private as well */
+ ~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 ASSIMP_API 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 ASSIMP_API 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/src/3rdparty/assimp/code/DXFLoader.cpp b/src/3rdparty/assimp/code/DXFLoader.cpp
new file mode 100644
index 000000000..e372d603f
--- /dev/null
+++ b/src/3rdparty/assimp/code/DXFLoader.cpp
@@ -0,0 +1,611 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file DXFLoader.cpp
+ * @brief Implementation of the DXF importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
+
+#include "DXFLoader.h"
+#include "ParsingUtils.h"
+#include "ConvertToLHProcess.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+
+// AutoCAD Binary DXF<CR><LF><SUB><NULL>
+#define AI_DXF_BINARY_IDENT ("AutoCAD Binary DXF\r\n\x1a\0")
+#define AI_DXF_BINARY_IDENT_LEN (24)
+
+// color indices for DXF - 16 are supported
+static aiColor4D g_aclrDxfIndexColors[] =
+{
+ aiColor4D (0.6f, 0.6f, 0.6f, 1.0f),
+ aiColor4D (1.0f, 0.0f, 0.0f, 1.0f), // red
+ aiColor4D (0.0f, 1.0f, 0.0f, 1.0f), // green
+ aiColor4D (0.0f, 0.0f, 1.0f, 1.0f), // blue
+ aiColor4D (0.3f, 1.0f, 0.3f, 1.0f), // light green
+ aiColor4D (0.3f, 0.3f, 1.0f, 1.0f), // light blue
+ aiColor4D (1.0f, 0.3f, 0.3f, 1.0f), // light red
+ aiColor4D (1.0f, 0.0f, 1.0f, 1.0f), // pink
+ aiColor4D (1.0f, 0.6f, 0.0f, 1.0f), // orange
+ aiColor4D (0.6f, 0.3f, 0.0f, 1.0f), // dark orange
+ aiColor4D (1.0f, 1.0f, 0.0f, 1.0f), // yellow
+ aiColor4D (0.3f, 0.3f, 0.3f, 1.0f), // dark gray
+ aiColor4D (0.8f, 0.8f, 0.8f, 1.0f), // light gray
+ aiColor4D (0.0f, 00.f, 0.0f, 1.0f), // black
+ aiColor4D (1.0f, 1.0f, 1.0f, 1.0f), // white
+ aiColor4D (0.6f, 0.0f, 1.0f, 1.0f) // violet
+};
+#define AI_DXF_NUM_INDEX_COLORS (sizeof(g_aclrDxfIndexColors)/sizeof(g_aclrDxfIndexColors[0]))
+
+// invalid/unassigned color value
+aiColor4D g_clrInvalid = aiColor4D(get_qnan(),0.f,0.f,1.f);
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+DXFImporter::DXFImporter() : buffer(0), groupCode(0), bRepeat(false), mDefaultLayer(false)
+{
+ memset(cursor,0,sizeof(cursor));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+DXFImporter::~DXFImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool DXFImporter::CanRead( const std::string& pFile, IOSystem*, bool) const
+{
+ return SimpleExtensionCheck(pFile,"dxf");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all supported file extensions
+void DXFImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("dxf");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a copy of the next data line, skip strange data
+bool DXFImporter::GetNextLine()
+{
+ if (!SkipLine(&buffer))
+ return false;
+ if (!SkipSpaces(&buffer))
+ return GetNextLine();
+ else if (*buffer == '{') {
+ // some strange meta data ...
+ while (true)
+ {
+ if (!SkipLine(&buffer))
+ return false;
+
+ if (SkipSpaces(&buffer) && *buffer == '}')
+ break;
+ }
+ return GetNextLine();
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the next token in the file
+bool DXFImporter::GetNextToken()
+{
+ if (bRepeat) {
+ bRepeat = false;
+ return true;
+ }
+
+ SkipSpaces(&buffer);
+ groupCode = strtol10s(buffer,&buffer);
+ if (!GetNextLine())
+ return false;
+
+ // copy the data line to a separate buffer
+ char* m = cursor, *end = &cursor[4096];
+ while (!IsSpaceOrNewLine( *buffer ) && m < end)
+ *m++ = *buffer++;
+
+ *m = '\0';
+ GetNextLine();
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void DXFImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open DXF file " + pFile + "");
+ }
+
+ // read the contents of the file in a buffer
+ std::vector<char> buffer2;
+ TextFileToBuffer(file.get(),buffer2);
+ buffer = &buffer2[0];
+
+ bRepeat = false;
+ mDefaultLayer = NULL;
+
+ // check whether this is a binaray DXF file - we can't read binary DXF files :-(
+ if (!strncmp(AI_DXF_BINARY_IDENT,buffer,AI_DXF_BINARY_IDENT_LEN))
+ throw DeadlyImportError("DXF: Binary files are not supported at the moment");
+
+ // now get all lines of the file
+ while (GetNextToken()) {
+
+ if (2 == groupCode) {
+
+ // ENTITIES and BLOCKS sections - skip the whole rest, no need to waste our time with them
+ if (!::strcmp(cursor,"ENTITIES") || !::strcmp(cursor,"BLOCKS")) {
+ if (!ParseEntities())
+ break;
+ else bRepeat = true;
+ }
+
+ // other sections - skip them to make sure there will be no name conflicts
+ else {
+ while ( GetNextToken()) {
+ if (!::strcmp(cursor,"ENDSEC"))
+ break;
+ }
+ }
+ }
+ // print comment strings
+ else if (999 == groupCode) {
+ DefaultLogger::get()->info(std::string( cursor ));
+ }
+ else if (!groupCode && !::strcmp(cursor,"EOF"))
+ break;
+ }
+
+ // find out how many valud layers we have
+ for (std::vector<LayerInfo>::const_iterator it = mLayers.begin(),end = mLayers.end(); it != end;++it) {
+ if (!(*it).vPositions.empty())
+ ++pScene->mNumMeshes;
+ }
+
+ if (!pScene->mNumMeshes)
+ throw DeadlyImportError("DXF: this file contains no 3d data");
+
+ pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ];
+ unsigned int m = 0;
+ for (std::vector<LayerInfo>::const_iterator it = mLayers.begin(),end = mLayers.end();it != end;++it) {
+ if ((*it).vPositions.empty()) {
+ continue;
+ }
+ // generate the output mesh
+ aiMesh* pMesh = pScene->mMeshes[m++] = new aiMesh();
+ const std::vector<aiVector3D>& vPositions = (*it).vPositions;
+ const std::vector<aiColor4D>& vColors = (*it).vColors;
+
+ // check whether we need vertex colors here
+ aiColor4D* clrOut = NULL;
+ const aiColor4D* clr = NULL;
+ for (std::vector<aiColor4D>::const_iterator it2 = (*it).vColors.begin(), end2 = (*it).vColors.end();it2 != end2; ++it2) {
+
+ if ((*it2).r == (*it2).r) /* qnan? */ {
+ clrOut = pMesh->mColors[0] = new aiColor4D[vPositions.size()];
+ for (unsigned int i = 0; i < vPositions.size();++i)
+ clrOut[i] = aiColor4D(0.6f,0.6f,0.6f,1.0f);
+
+ clr = &vColors[0];
+ break;
+ }
+ }
+
+ pMesh->mNumFaces = (unsigned int)vPositions.size() / 4u;
+ pMesh->mFaces = new aiFace[pMesh->mNumFaces];
+
+ aiVector3D* vpOut = pMesh->mVertices = new aiVector3D[vPositions.size()];
+ const aiVector3D* vp = &vPositions[0];
+
+ for (unsigned int i = 0; i < pMesh->mNumFaces;++i) {
+ aiFace& face = pMesh->mFaces[i];
+
+ // check whether we need four, three or two indices here
+ if (vp[1] == vp[2]) {
+ face.mNumIndices = 2;
+ }
+ else if (vp[3] == vp[2]) {
+ face.mNumIndices = 3;
+ }
+ else face.mNumIndices = 4;
+ face.mIndices = new unsigned int[face.mNumIndices];
+
+ for (unsigned int a = 0; a < face.mNumIndices;++a) {
+ *vpOut++ = vp[a];
+ if (clr) {
+ if (is_not_qnan( clr[a].r )) {
+ *clrOut = clr[a];
+ }
+ ++clrOut;
+ }
+ face.mIndices[a] = pMesh->mNumVertices++;
+ }
+ vp += 4;
+ }
+ }
+
+ // generate the output scene graph
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("<DXF_ROOT>");
+
+ if (1 == pScene->mNumMeshes) {
+ pScene->mRootNode->mMeshes = new unsigned int[ pScene->mRootNode->mNumMeshes = 1 ];
+ pScene->mRootNode->mMeshes[0] = 0;
+ }
+ else
+ {
+ pScene->mRootNode->mChildren = new aiNode*[ pScene->mRootNode->mNumChildren = pScene->mNumMeshes ];
+ for (m = 0; m < pScene->mRootNode->mNumChildren;++m) {
+ aiNode* p = pScene->mRootNode->mChildren[m] = new aiNode();
+ p->mName.length = ::strlen( mLayers[m].name );
+ strcpy(p->mName.data, mLayers[m].name);
+
+ p->mMeshes = new unsigned int[p->mNumMeshes = 1];
+ p->mMeshes[0] = m;
+ p->mParent = pScene->mRootNode;
+ }
+ }
+
+ // generate a default material
+ MaterialHelper* pcMat = new MaterialHelper();
+ aiString s;
+ s.Set(AI_DEFAULT_MATERIAL_NAME);
+ pcMat->AddProperty(&s, AI_MATKEY_NAME);
+
+ aiColor4D clrDiffuse(0.6f,0.6f,0.6f,1.0f);
+ pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_DIFFUSE);
+
+ clrDiffuse = aiColor4D(1.0f,1.0f,1.0f,1.0f);
+ pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_SPECULAR);
+
+ clrDiffuse = aiColor4D(0.05f,0.05f,0.05f,1.0f);
+ pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_AMBIENT);
+
+ pScene->mNumMaterials = 1;
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = pcMat;
+
+ // flip winding order to be ccw
+ FlipWindingOrderProcess flipper;
+ flipper.Execute(pScene);
+
+ // --- everything destructs automatically ---
+}
+
+// ------------------------------------------------------------------------------------------------
+bool DXFImporter::ParseEntities()
+{
+ while (GetNextToken()) {
+ if (!groupCode) {
+ if (!::strcmp(cursor,"3DFACE") || !::strcmp(cursor,"LINE") || !::strcmp(cursor,"3DLINE")){
+ //http://sourceforge.net/tracker/index.php?func=detail&aid=2970566&group_id=226462&atid=1067632
+ Parse3DFace();
+ bRepeat = true;
+ }
+ if (!::strcmp(cursor,"POLYLINE") || !::strcmp(cursor,"LWPOLYLINE")){
+ ParsePolyLine();
+ bRepeat = true;
+ }
+ if (!::strcmp(cursor,"ENDSEC")) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::SetLayer(LayerInfo*& out)
+{
+ for (std::vector<LayerInfo>::iterator it = mLayers.begin(),end = mLayers.end();it != end;++it) {
+ if (!::strcmp( (*it).name, cursor )) {
+ out = &(*it);
+ break;
+ }
+ }
+ if (!out) {
+ // we don't have this layer yet
+ mLayers.push_back(LayerInfo());
+ out = &mLayers.back();
+ ::strcpy(out->name,cursor);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::SetDefaultLayer(LayerInfo*& out)
+{
+ if (!mDefaultLayer) {
+ mLayers.push_back(LayerInfo());
+ mDefaultLayer = &mLayers.back();
+ }
+ out = mDefaultLayer;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool DXFImporter::ParsePolyLine()
+{
+ bool ret = false;
+ LayerInfo* out = NULL;
+
+ std::vector<aiVector3D> positions;
+ std::vector<aiColor4D> colors;
+ std::vector<unsigned int> indices;
+ unsigned int flags = 0;
+
+ while (GetNextToken()) {
+ switch (groupCode)
+ {
+ case 0:
+ {
+ if (!::strcmp(cursor,"VERTEX")) {
+ aiVector3D v;aiColor4D clr(g_clrInvalid);
+ unsigned int idx[4] = {0xffffffff,0xffffffff,0xffffffff,0xffffffff};
+ ParsePolyLineVertex(v, clr, idx);
+ if (0xffffffff == idx[0]) {
+ positions.push_back(v);
+ colors.push_back(clr);
+ }
+ else {
+ // check whether we have a fourth coordinate
+ if (0xffffffff == idx[3]) {
+ idx[3] = idx[2];
+ }
+
+ indices.reserve(indices.size()+4);
+ for (unsigned int m = 0; m < 4;++m)
+ indices.push_back(idx[m]);
+ }
+ bRepeat = true;
+ }
+ else if (!::strcmp(cursor,"ENDSEQ")) {
+ ret = true;
+ }
+ break;
+ }
+
+ // flags --- important that we know whether it is a polyface mesh
+ case 70:
+ {
+ if (!flags) {
+ flags = strtol10(cursor);
+ }
+ break;
+ };
+
+ // optional number of vertices
+ case 71:
+ {
+ positions.reserve(strtol10(cursor));
+ break;
+ }
+
+ // optional number of faces
+ case 72:
+ {
+ indices.reserve(strtol10(cursor));
+ break;
+ }
+
+ // 8 specifies the layer
+ case 8:
+ {
+ SetLayer(out);
+ break;
+ }
+ }
+ }
+ if (!(flags & 64)) {
+ DefaultLogger::get()->warn("DXF: Only polyface meshes are currently supported");
+ return ret;
+ }
+
+ if (positions.size() < 3 || indices.size() < 3) {
+ DefaultLogger::get()->warn("DXF: Unable to parse POLYLINE element - not enough vertices");
+ return ret;
+ }
+
+ // use a default layer if necessary
+ if (!out) {
+ SetDefaultLayer(out);
+ }
+
+ flags = (unsigned int)(out->vPositions.size()+indices.size());
+ out->vPositions.reserve(flags);
+ out->vColors.reserve(flags);
+
+ // generate unique vertices
+ for (std::vector<unsigned int>::const_iterator it = indices.begin(), end = indices.end();it != end; ++it) {
+ unsigned int idx = *it;
+ if (idx > positions.size() || !idx) {
+ DefaultLogger::get()->error("DXF: Polyface mesh index os out of range");
+ idx = (unsigned int) positions.size();
+ }
+ out->vPositions.push_back(positions[idx-1]); // indices are one-based.
+ out->vColors.push_back(colors[idx-1]); // indices are one-based.
+ }
+
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool DXFImporter::ParsePolyLineVertex(aiVector3D& out,aiColor4D& clr, unsigned int* outIdx)
+{
+ bool ret = false;
+ while (GetNextToken()) {
+ switch (groupCode)
+ {
+ case 0: ret = true;
+ break;
+
+ // todo - handle the correct layer for the vertex. At the moment it is assumed that all vertices of
+ // a polyline are placed on the same global layer.
+
+ // x position of the first corner
+ case 10: out.x = fast_atof(cursor);break;
+
+ // y position of the first corner
+ case 20: out.y = -fast_atof(cursor);break;
+
+ // z position of the first corner
+ case 30: out.z = fast_atof(cursor);break;
+
+ // POLYFACE vertex indices
+ case 71: outIdx[0] = strtol10(cursor);break;
+ case 72: outIdx[1] = strtol10(cursor);break;
+ case 73: outIdx[2] = strtol10(cursor);break;
+ // case 74: outIdx[3] = strtol10(cursor);break;
+
+ // color
+ case 62: clr = g_aclrDxfIndexColors[strtol10(cursor) % AI_DXF_NUM_INDEX_COLORS]; break;
+ };
+ if (ret) {
+ break;
+ }
+ }
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool DXFImporter::Parse3DFace()
+{
+ bool ret = false;
+ LayerInfo* out = NULL;
+
+ aiVector3D vip[4]; // -- vectors are initialized to zero
+ aiColor4D clr(g_clrInvalid);
+
+ // this is also used for for parsing line entities
+ bool bThird = false;
+
+ while (GetNextToken()) {
+ switch (groupCode) {
+ case 0:
+ ret = true;
+ break;
+
+ // 8 specifies the layer
+ case 8: {
+ SetLayer(out);
+ break;
+ }
+
+ // x position of the first corner
+ case 10: vip[0].x = fast_atof(cursor);break;
+
+ // y position of the first corner
+ case 20: vip[0].y = -fast_atof(cursor);break;
+
+ // z position of the first corner
+ case 30: vip[0].z = fast_atof(cursor);break;
+
+ // x position of the second corner
+ case 11: vip[1].x = fast_atof(cursor);break;
+
+ // y position of the second corner
+ case 21: vip[1].y = -fast_atof(cursor);break;
+
+ // z position of the second corner
+ case 31: vip[1].z = fast_atof(cursor);break;
+
+ // x position of the third corner
+ case 12: vip[2].x = fast_atof(cursor);
+ bThird = true;break;
+
+ // y position of the third corner
+ case 22: vip[2].y = -fast_atof(cursor);
+ bThird = true;break;
+
+ // z position of the third corner
+ case 32: vip[2].z = fast_atof(cursor);
+ bThird = true;break;
+
+ // x position of the fourth corner
+ case 13: vip[3].x = fast_atof(cursor);
+ bThird = true;break;
+
+ // y position of the fourth corner
+ case 23: vip[3].y = -fast_atof(cursor);
+ bThird = true;break;
+
+ // z position of the fourth corner
+ case 33: vip[3].z = fast_atof(cursor);
+ bThird = true;break;
+
+ // color
+ case 62: clr = g_aclrDxfIndexColors[strtol10(cursor) % AI_DXF_NUM_INDEX_COLORS]; break;
+ };
+ if (ret)
+ break;
+ }
+
+ if (!bThird)
+ vip[2] = vip[1];
+
+ // use a default layer if necessary
+ if (!out) {
+ SetDefaultLayer(out);
+ }
+ // add the faces to the face list for this layer
+ out->vPositions.push_back(vip[0]);
+ out->vPositions.push_back(vip[1]);
+ out->vPositions.push_back(vip[2]);
+ out->vPositions.push_back(vip[3]); // might be equal to the third
+
+ for (unsigned int i = 0; i < 4;++i)
+ out->vColors.push_back(clr);
+ return ret;
+}
+
+#endif // !! ASSIMP_BUILD_NO_DXF_IMPORTER
+
diff --git a/src/3rdparty/assimp/code/DXFLoader.h b/src/3rdparty/assimp/code/DXFLoader.h
new file mode 100644
index 000000000..9ae16ed59
--- /dev/null
+++ b/src/3rdparty/assimp/code/DXFLoader.h
@@ -0,0 +1,184 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file DXFLoader.h
+ * @brief Declaration of the .dxf importer class.
+ */
+#ifndef AI_DXFLOADER_H_INCLUDED
+#define AI_DXFLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** DXF importer class
+*/
+class DXFImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ DXFImporter();
+
+ /** Destructor, private as well */
+ ~DXFImporter();
+
+
+ // describes a single layer in the DXF file
+ struct LayerInfo
+ {
+ LayerInfo()
+ {
+ name[0] = '\0';
+ }
+
+ char name[4096];
+
+ // face buffer - order is x,y,z v1,v2,v3,v4
+ // if v2 = v3: line
+ // elsif v3 = v2: triangle
+ // else: polygon
+ std::vector<aiVector3D> vPositions;
+ std::vector<aiColor4D> vColors;
+ };
+
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details. */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /** Get the next line from the file.
+ * @return false if the end of the file was reached
+ */
+ bool GetNextLine();
+
+ // -------------------------------------------------------------------
+ /** Get the next token (group code + data line) from the file.
+ * @return false if the end of the file was reached
+ */
+ bool GetNextToken();
+
+ // -------------------------------------------------------------------
+ /** Parses the ENTITIES section in the file
+ * @return false if the end of the file was reached
+ */
+ bool ParseEntities();
+
+ // -------------------------------------------------------------------
+ /** Parses a 3DFACE section in the file
+ * @return false if the end of the file was reached
+ */
+ bool Parse3DFace();
+
+ // -------------------------------------------------------------------
+ /** Parses a POLYLINE section in the file
+ * @return false if the end of the file was reached
+ */
+ bool ParsePolyLine();
+
+ // -------------------------------------------------------------------
+ /** Sets the current layer - cursor must point to the name of it.
+ * @param out Receives a handle to the layer
+ */
+ void SetLayer(LayerInfo*& out);
+
+ // -------------------------------------------------------------------
+ /** Creates a default layer.
+ * @param out Receives a handle to the default layer
+ */
+ void SetDefaultLayer(LayerInfo*& out);
+
+ // -------------------------------------------------------------------
+ /** Parses a VERTEX element in a POLYLINE/POLYFACE
+ * @param out Receives the output vertex.
+ * @param clr Receives the output vertex color - won't be modified
+ * if it is not existing.
+ * @param outIdx Receives the output vertex indices, if present.
+ * Wont't be modified otherwise. Size must be at least 4.
+ * @return false if the end of the file was reached
+ */
+ bool ParsePolyLineVertex(aiVector3D& out, aiColor4D& clr,
+ unsigned int* outIdx);
+
+private:
+
+ // points to the next section
+ const char* buffer;
+
+ // specifies the current group code
+ int groupCode;
+
+ // contains the current data line
+ char cursor[4096];
+
+ // specifies whether the next call to GetNextToken()
+ // should return the current token a second time
+ bool bRepeat;
+
+ // list of all loaded layers
+ std::vector<LayerInfo> mLayers;
+ LayerInfo* mDefaultLayer;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/DefaultIOStream.cpp b/src/3rdparty/assimp/code/DefaultIOStream.cpp
new file mode 100644
index 000000000..20febb060
--- /dev/null
+++ b/src/3rdparty/assimp/code/DefaultIOStream.cpp
@@ -0,0 +1,141 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file DefaultIOStream.cpp
+ * @brief Default File I/O implementation for #Importer
+ */
+
+#include "AssimpPCH.h"
+
+#include "DefaultIOStream.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <QtGlobal>
+
+using namespace Assimp;
+
+// ----------------------------------------------------------------------------------
+DefaultIOStream::~DefaultIOStream()
+{
+ if (mFile) {
+ ::fclose(mFile);
+ }
+}
+
+// ----------------------------------------------------------------------------------
+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
+ Q_ASSERT(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 (0xffffffff == cachedSize) {
+
+ // TODO: Is that really faster if we're already owning a handle to the file?
+#if defined _WIN32 && !defined __GNUC__
+ struct __stat64 fileStat;
+ int err = _stat64( mFilename.c_str(), &fileStat );
+ if (0 != err)
+ return 0;
+ cachedSize = (size_t) (fileStat.st_size);
+#else
+ struct stat fileStat;
+ int err = stat(mFilename.c_str(), &fileStat );
+ if (0 != err)
+ return 0;
+ cachedSize = (size_t) (fileStat.st_size);
+#endif
+ }
+ return cachedSize;
+}
+
+// ----------------------------------------------------------------------------------
+void DefaultIOStream::Flush()
+{
+ if (mFile) {
+ ::fflush(mFile);
+ }
+}
+
+// ----------------------------------------------------------------------------------
diff --git a/src/3rdparty/assimp/code/DefaultIOStream.h b/src/3rdparty/assimp/code/DefaultIOStream.h
new file mode 100644
index 000000000..1fdaa33be
--- /dev/null
+++ b/src/3rdparty/assimp/code/DefaultIOStream.h
@@ -0,0 +1,133 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Default file I/O using fXXX()-family of functions */
+#ifndef AI_DEFAULTIOSTREAM_H_INC
+#define AI_DEFAULTIOSTREAM_H_INC
+
+#include <stdio.h>
+#include "../include/IOStream.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 DefaultIOStream : public IOStream
+{
+ friend class DefaultIOSystem;
+
+protected:
+ DefaultIOStream ();
+ 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 datastructure, using clib
+ FILE* mFile;
+ //! Filename
+ std::string mFilename;
+
+ //! Cached file size
+ mutable size_t cachedSize;
+};
+
+
+// ----------------------------------------------------------------------------------
+inline DefaultIOStream::DefaultIOStream () :
+ mFile (NULL),
+ mFilename (""),
+ cachedSize (0xffffffff)
+{
+ // empty
+}
+
+
+// ----------------------------------------------------------------------------------
+inline DefaultIOStream::DefaultIOStream (FILE* pFile,
+ const std::string &strFilename) :
+ mFile(pFile),
+ mFilename(strFilename),
+ cachedSize (0xffffffff)
+{
+ // empty
+}
+// ----------------------------------------------------------------------------------
+
+} // ns assimp
+
+#endif //!!AI_DEFAULTIOSTREAM_H_INC
+
diff --git a/src/3rdparty/assimp/code/DefaultIOSystem.cpp b/src/3rdparty/assimp/code/DefaultIOSystem.cpp
new file mode 100644
index 000000000..f09f710b9
--- /dev/null
+++ b/src/3rdparty/assimp/code/DefaultIOSystem.cpp
@@ -0,0 +1,167 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file Default implementation of IOSystem using the standard C file functions */
+
+#include "AssimpPCH.h"
+
+#include <stdlib.h>
+#include "DefaultIOSystem.h"
+#include "DefaultIOStream.h"
+
+#ifdef __unix__
+#include <sys/param.h>
+#include <stdlib.h>
+#endif
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor.
+DefaultIOSystem::DefaultIOSystem()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor.
+DefaultIOSystem::~DefaultIOSystem()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tests for the existence of a file at the given path.
+bool DefaultIOSystem::Exists( const char* pFile) const
+{
+ FILE* file = ::fopen( pFile, "rb");
+ if ( !file)
+ return false;
+
+ ::fclose( file);
+ 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 = ::fopen( strFile, strMode);
+ if ( NULL == file)
+ return NULL;
+
+ 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);
+}
+
+// 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
+
+// ------------------------------------------------------------------------------------------------
+// Convert a relative path into an absolute path
+inline void MakeAbsolutePath (const char* in, char* _out)
+{
+ ai_assert(in && _out);
+ char* ret;
+#ifdef _WIN32
+ ret = ::_fullpath(_out, in,PATHLIMIT);
+#else
+ // use realpath
+ ret = realpath(in, _out);
+#endif
+ if (!ret) {
+ // preserve the input path, maybe someone else is able to fix
+ // the path before it is accessed (e.g. our file system filter)
+ DefaultLogger::get()->warn("Invalid path: "+std::string(in));
+ strcpy(_out,in);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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);
+}
+
+#undef PATHLIMIT
diff --git a/src/3rdparty/assimp/code/DefaultIOSystem.h b/src/3rdparty/assimp/code/DefaultIOSystem.h
new file mode 100644
index 000000000..3c3601754
--- /dev/null
+++ b/src/3rdparty/assimp/code/DefaultIOSystem.h
@@ -0,0 +1,83 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Default implementation of IOSystem using the standard C file functions */
+#ifndef AI_DEFAULTIOSYSTEM_H_INC
+#define AI_DEFAULTIOSYSTEM_H_INC
+
+#include "../include/IOSystem.h"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Default implementation of IOSystem using the standard C file functions */
+class DefaultIOSystem : public IOSystem
+{
+public:
+ /** Constructor. */
+ DefaultIOSystem();
+
+ /** Destructor. */
+ ~DefaultIOSystem();
+
+ // -------------------------------------------------------------------
+ /** 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;
+};
+
+} //!ns Assimp
+
+#endif //AI_DEFAULTIOSYSTEM_H_INC
diff --git a/src/3rdparty/assimp/code/DefaultLogger.cpp b/src/3rdparty/assimp/code/DefaultLogger.cpp
new file mode 100644
index 000000000..74cf5d2ad
--- /dev/null
+++ b/src/3rdparty/assimp/code/DefaultLogger.cpp
@@ -0,0 +1,423 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file DefaultLogger.cpp
+ * @brief Implementation of DefaultLogger (and Logger)
+ */
+
+#include "AssimpPCH.h"
+#include "DefaultIOSystem.h"
+
+// Default log streams
+#include "Win32DebugLogStream.h"
+#include "StdOStreamLogStream.h"
+#include "FileLogStream.h"
+
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+# include <boost/thread/thread.hpp>
+# include <boost/thread/mutex.hpp>
+
+boost::mutex loggerMutex;
+#endif
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------
+NullLogger DefaultLogger::s_pNullLogger;
+Logger *DefaultLogger::m_pLogger = &DefaultLogger::s_pNullLogger;
+
+// ----------------------------------------------------------------------------------
+// Represents a logstream + 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 NULL;
+#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) : NULL);
+ 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
+ boost::mutex::scoped_lock 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 std::string &message) {
+
+ // SECURITY FIX: otherwise it's easy to produce overruns ...
+ if (message.length()>MAX_LOG_MESSAGE_LENGTH) {
+ ai_assert(false);
+ return;
+ }
+ return OnDebug(message.c_str());
+}
+
+// ----------------------------------------------------------------------------------
+void Logger::info(const std::string &message) {
+
+ // SECURITY FIX: otherwise it's easy to produce overruns ...
+ if (message.length()>MAX_LOG_MESSAGE_LENGTH) {
+ ai_assert(false);
+ return;
+ }
+ return OnInfo(message.c_str());
+}
+
+// ----------------------------------------------------------------------------------
+void Logger::warn(const std::string &message) {
+
+ // SECURITY FIX: otherwise it's easy to produce overruns ...
+ if (message.length()>MAX_LOG_MESSAGE_LENGTH) {
+ ai_assert(false);
+ return;
+ }
+ return OnWarn(message.c_str());
+}
+
+// ----------------------------------------------------------------------------------
+void Logger::error(const std::string &message) {
+
+ // SECURITY FIX: otherwise it's easy to produce overruns ...
+ if (message.length()>MAX_LOG_MESSAGE_LENGTH) {
+ ai_assert(false);
+ return;
+ }
+ return OnError(message.c_str());
+}
+
+// ----------------------------------------------------------------------------------
+void DefaultLogger::set( Logger *logger )
+{
+ // enter the mutex here to avoid concurrency problems
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+ boost::mutex::scoped_lock lock(loggerMutex);
+#endif
+
+ if (!logger)logger = &s_pNullLogger;
+ if (m_pLogger && !isNullLogger() )
+ delete m_pLogger;
+
+ DefaultLogger::m_pLogger = logger;
+}
+
+// ----------------------------------------------------------------------------------
+bool DefaultLogger::isNullLogger()
+{
+ return m_pLogger == &s_pNullLogger;
+}
+
+// ----------------------------------------------------------------------------------
+// Singleton getter
+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
+ boost::mutex::scoped_lock 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;
+
+ char msg[MAX_LOG_MESSAGE_LENGTH*2];
+ ::sprintf(msg,"Debug, T%i: %s", GetThreadID(), message );
+
+ WriteToStreams( msg, Logger::DEBUGGING );
+}
+
+// ----------------------------------------------------------------------------------
+// Logs an info
+void DefaultLogger::OnInfo( const char* message )
+{
+ char msg[MAX_LOG_MESSAGE_LENGTH*2];
+ ::sprintf(msg,"Info, T%i: %s", GetThreadID(), message );
+
+ WriteToStreams( msg , Logger::INFO );
+}
+
+// ----------------------------------------------------------------------------------
+// Logs a warning
+void DefaultLogger::OnWarn( const char* message )
+{
+ char msg[MAX_LOG_MESSAGE_LENGTH*2];
+ ::sprintf(msg,"Warn, T%i: %s", GetThreadID(), message );
+
+ WriteToStreams( msg, Logger::WARN );
+}
+
+// ----------------------------------------------------------------------------------
+// Logs an error
+void DefaultLogger::OnError( const char* message )
+{
+ char msg[MAX_LOG_MESSAGE_LENGTH*2];
+ ::sprintf(msg,"Error, T%i: %s", GetThreadID(), message );
+
+ WriteToStreams( msg, Logger::ERR );
+}
+
+// ----------------------------------------------------------------------------------
+// Attachs a new stream
+bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity )
+{
+ if (!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;
+}
+
+// ----------------------------------------------------------------------------------
+// Detatch a stream
+bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity )
+{
+ if (!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;
+ if ( (*it)->m_uiErrorSeverity == 0 )
+ {
+ // don't delete the underlying stream 'cause the caller gains ownership again
+ (**it).m_pStream = NULL;
+ delete *it;
+ m_StreamArray.erase( it );
+ break;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// ----------------------------------------------------------------------------------
+// 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(NULL != 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 boost::threads
+#ifdef WIN32
+ return (unsigned int)::GetCurrentThreadId();
+#else
+ return 0; // not supported
+#endif
+}
+
+// ----------------------------------------------------------------------------------
+
+} // !namespace Assimp
diff --git a/src/3rdparty/assimp/code/DefaultProgressHandler.h b/src/3rdparty/assimp/code/DefaultProgressHandler.h
new file mode 100644
index 000000000..efa784bb1
--- /dev/null
+++ b/src/3rdparty/assimp/code/DefaultProgressHandler.h
@@ -0,0 +1,64 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file ProgressHandler.h
+ * @brief Abstract base class 'ProgressHandler'.
+ */
+#ifndef INCLUDED_AI_DEFAULTPROGRESSHANDLER_H
+#define INCLUDED_AI_DEFAULTPROGRESSHANDLER_H
+
+#include "../include/ProgressHandler.h"
+namespace Assimp {
+
+// ------------------------------------------------------------------------------------
+/** @brief Internal default implementation of the #ProgressHandler interface. */
+class ASSIMP_API DefaultProgressHandler
+ : public ProgressHandler {
+
+
+ virtual bool Update(float /* percentage */) {
+ return false;
+ }
+
+
+}; // !class DefaultProgressHandler
+} // Namespace Assimp
+
+#endif
diff --git a/src/3rdparty/assimp/code/Exceptional.h b/src/3rdparty/assimp/code/Exceptional.h
new file mode 100644
index 000000000..ab9c28756
--- /dev/null
+++ b/src/3rdparty/assimp/code/Exceptional.h
@@ -0,0 +1,122 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef INCLUDED_EXCEPTIONAL_H
+#define INCLUDED_EXCEPTIONAL_H
+
+#include <stdexcept>
+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 ASSIMP_API DeadlyImportError
+ : public runtime_error
+{
+public:
+ /** Constructor with arguments */
+ explicit DeadlyImportError( const std::string& pErrorText)
+ : runtime_error(pErrorText)
+ {
+ }
+
+private:
+};
+
+#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/src/3rdparty/assimp/code/FileLogStream.h b/src/3rdparty/assimp/code/FileLogStream.h
new file mode 100644
index 000000000..15b8da7be
--- /dev/null
+++ b/src/3rdparty/assimp/code/FileLogStream.h
@@ -0,0 +1,64 @@
+#ifndef ASSIMP_FILELOGSTREAM_H_INC
+#define ASSIMP_FILELOGSTREAM_H_INC
+
+#include "../include/LogStream.h"
+#include "../include/IOStream.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/src/3rdparty/assimp/code/FileSystemFilter.h b/src/3rdparty/assimp/code/FileSystemFilter.h
new file mode 100644
index 000000000..cc3b4a687
--- /dev/null
+++ b/src/3rdparty/assimp/code/FileSystemFilter.h
@@ -0,0 +1,244 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FileSystemFilter.h
+ * Implements a filter system to filter calls to Exists() and Open()
+ * in order to improve the sucess rate of file opening ...
+ */
+#ifndef AI_FILESYSTEMFILTER_H_INC
+#define AI_FILESYSTEMFILTER_H_INC
+
+#include "../include/IOSystem.h"
+#include "fast_atof.h"
+#include "ParsingUtils.h"
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** File system filter
+ */
+class FileSystemFilter : public IOSystem
+{
+public:
+ /** Constructor. */
+ FileSystemFilter(const std::string& file, IOSystem* old)
+ : wrapped (old)
+ , src_file (file)
+ {
+ ai_assert(NULL != wrapped);
+
+ // Determine base directory
+ base = src_file;
+ std::string::size_type ss2;
+ if (std::string::npos != (ss2 = base.find_last_of("\\/"))) {
+ base.erase(ss2,base.length()-ss2);
+ }
+ else {
+ base = "";
+ // return;
+ }
+
+ // make sure the directory is terminated properly
+ char s;
+
+ if (base.length() == 0) {
+ base = ".";
+ base += getOsSeparator();
+ }
+ else if ((s = *(base.end()-1)) != '\\' && s != '/')
+ base += getOsSeparator();
+
+ DefaultLogger::get()->info("Import root directory is \'" + base + "\'");
+ }
+
+ /** Destructor. */
+ ~FileSystemFilter()
+ {
+ // haha
+ }
+
+ // -------------------------------------------------------------------
+ /** Tests for the existence of a file at the given path. */
+ bool Exists( const char* pFile) const
+ {
+ std::string tmp = pFile;
+
+ // Currently this IOSystem is also used to open THE ONE FILE.
+ if (tmp != src_file) {
+ BuildPath(tmp);
+ Cleanup(tmp);
+ }
+
+ return wrapped->Exists(tmp);
+ }
+
+ // -------------------------------------------------------------------
+ /** Returns the directory separator. */
+ char getOsSeparator() const
+ {
+ return wrapped->getOsSeparator();
+ }
+
+ // -------------------------------------------------------------------
+ /** Open a new file with a given path. */
+ IOStream* Open( const char* pFile, const char* pMode = "rb")
+ {
+ ai_assert(pFile);
+ ai_assert(pMode);
+
+ // First try the unchanged path
+ IOStream* s = wrapped->Open(pFile,pMode);
+
+ if (!s) {
+ std::string tmp = pFile;
+
+ // Try to convert between absolute and relative paths
+ BuildPath(tmp);
+ s = wrapped->Open(tmp,pMode);
+
+ if (!s) {
+ // Finally, look for typical issues with paths
+ // and try to correct them. This is our last
+ // resort.
+ Cleanup(tmp);
+ s = wrapped->Open(tmp,pMode);
+ }
+ }
+
+ return s;
+ }
+
+ // -------------------------------------------------------------------
+ /** Closes the given file and releases all resources associated with it. */
+ void Close( IOStream* pFile)
+ {
+ return wrapped->Close(pFile);
+ }
+
+ // -------------------------------------------------------------------
+ /** Compare two paths */
+ bool ComparePaths (const char* one, const char* second) const
+ {
+ return wrapped->ComparePaths (one,second);
+ }
+
+private:
+ IOSystem* wrapped;
+ std::string src_file, base;
+
+ // -------------------------------------------------------------------
+ /** Build a valid path from a given relative or absolute path.
+ */
+ void BuildPath (std::string& in) const
+ {
+ // if we can already access the file, great.
+ if (in.length() < 3 || wrapped->Exists(in.c_str())) {
+ return;
+ }
+
+ // Determine whether this is a relative path (Windows-specific - most assets are packaged on Windows).
+ if (in[1] != ':') {
+
+ // append base path and try
+ in = base + in;
+ if (wrapped->Exists(in.c_str())) {
+ return;
+ }
+ }
+
+ // hopefully the underyling file system has another few tricks to access this file ...
+ }
+
+ // -------------------------------------------------------------------
+ /** Cleanup the given path
+ */
+ void Cleanup (std::string& in) const
+ {
+ char last = 0;
+
+ // Remove a very common issue when we're parsing file names: spaces at the
+ // beginning of the path.
+ std::string::iterator it = in.begin();
+ while (IsSpaceOrNewLine( *it ))++it;
+ if (it != in.begin())
+ in.erase(in.begin(),it+1);
+
+ const char sep = 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 = sep;
+
+ // 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
+ uint32_t tmp;
+ if ( 0xffffffff != (tmp = HexOctetToDecimal(&*it))) {
+ *it = (char)tmp;
+ it = in.erase(it+1,it+2);
+ --it;
+ }
+ }
+
+ last = *it;
+ }
+ }
+};
+
+} //!ns Assimp
+
+#endif //AI_DEFAULTIOSYSTEM_H_INC
diff --git a/src/3rdparty/assimp/code/FindDegenerates.cpp b/src/3rdparty/assimp/code/FindDegenerates.cpp
new file mode 100644
index 000000000..3cdd325dd
--- /dev/null
+++ b/src/3rdparty/assimp/code/FindDegenerates.cpp
@@ -0,0 +1,216 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file FindDegenerates.cpp
+ * @brief Implementation of the FindDegenerates post-process step.
+*/
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "ProcessHelper.h"
+#include "FindDegenerates.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FindDegeneratesProcess::FindDegeneratesProcess()
+: configRemoveDegenerates (false)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// 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
+ configRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FindDegeneratesProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("FindDegeneratesProcess begin");
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i){
+ ExecuteOnMesh( pScene->mMeshes[i]);
+ }
+ DefaultLogger::get()->debug("FindDegeneratesProcess finished");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported mesh
+void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh)
+{
+ mesh->mPrimitiveTypes = 0;
+
+ std::vector<bool> remove_me;
+ if (configRemoveDegenerates)
+ 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 (register 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 (register 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 attemps to access this data.
+ face.mIndices[face.mNumIndices] = 0xdeadbeef;
+
+ if (first)
+ {
+ ++deg;
+ first = false;
+ }
+
+ if (configRemoveDegenerates) {
+ remove_me[a] = true;
+ goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
+ }
+ }
+ }
+ }
+
+ // 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 (configRemoveDegenerates && 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 = NULL;
+ }
+ }
+ else {
+ // Otherwise delete it if we don't need this face
+ delete[] face_src.mIndices;
+ face_src.mIndices = NULL;
+ face_src.mNumIndices = 0;
+ }
+ }
+ // Just leave the rest of the array unreferenced, we don't care for now
+ mesh->mNumFaces = n;
+ if (!mesh->mNumFaces) {
+ // WTF!?
+ // OK ... for completeness and because I'm not yet tired,
+ // let's write code that willl hopefully never be called
+ // (famous last words)
+
+ // OK ... bad idea.
+ throw DeadlyImportError("Mesh is empty after removal of degenerated primitives ... WTF!?");
+ }
+ }
+
+ if (deg && !DefaultLogger::isNullLogger())
+ {
+ char s[64];
+ ASSIMP_itoa10(s,deg);
+ DefaultLogger::get()->warn(std::string("Found ") + s + " degenerated primitives");
+ }
+}
diff --git a/src/3rdparty/assimp/code/FindDegenerates.h b/src/3rdparty/assimp/code/FindDegenerates.h
new file mode 100644
index 000000000..daa5cf93d
--- /dev/null
+++ b/src/3rdparty/assimp/code/FindDegenerates.h
@@ -0,0 +1,110 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiMesh.h"
+
+class FindDegeneratesProcessTest;
+namespace Assimp {
+
+
+// ---------------------------------------------------------------------------
+/** FindDegeneratesProcess: Searches a mesh for degenerated triangles.
+*/
+class ASSIMP_API FindDegeneratesProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::FindDegeneratesProcessTest; // grant the unit test full access to us
+
+protected:
+ /** Constructor to be privately used by Importer */
+ FindDegeneratesProcess();
+
+ /** Destructor, private as well */
+ ~FindDegeneratesProcess();
+
+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);
+
+ // -------------------------------------------------------------------
+ // Execute step on a given mesh
+ void ExecuteOnMesh( aiMesh* mesh);
+
+
+ // -------------------------------------------------------------------
+ /** @brief Enable the instant removal of degenerated primitives
+ * @param d hm ... difficult to guess what this means, hu!?
+ */
+ void EnableInstantRemoval(bool d) {
+ configRemoveDegenerates = d;
+ }
+
+ // -------------------------------------------------------------------
+ /** @brief Check whether instant removal is currently enabled
+ * @return ...
+ */
+ bool IsInstantRemoval() const {
+ return configRemoveDegenerates;
+ }
+
+private:
+
+ //! Configuration option: remove degenerates faces immediately
+ bool configRemoveDegenerates;
+};
+}
+
+#endif // !! AI_FINDDEGENERATESPROCESS_H_INC
diff --git a/src/3rdparty/assimp/code/FindInstancesProcess.cpp b/src/3rdparty/assimp/code/FindInstancesProcess.cpp
new file mode 100644
index 000000000..3ce59dd8d
--- /dev/null
+++ b/src/3rdparty/assimp/code/FindInstancesProcess.cpp
@@ -0,0 +1,294 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file FindInstancesProcess.cpp
+ * @brief Implementation of the aiProcess_FindInstances postprocessing step
+*/
+
+#include "AssimpPCH.h"
+#include "FindInstancesProcess.h"
+
+#include <QtGlobal>
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+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 ||
+ aha->mNumWeights != oha->mNumWeights) {
+ 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)
+{
+ DefaultLogger::get()->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.
+ boost::scoped_array<uint64_t> hashes (new uint64_t[pScene->mNumMeshes]);
+ boost::scoped_array<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;
+
+ Q_ASSERT(4 == AI_MAX_NUMBER_OF_COLOR_SETS);
+
+ // as in JIV: manually unrolled as continue wouldn't work as desired in inner loops
+ if (orig->mTextureCoords[0]) {
+ if (!CompareArrays(orig->mTextureCoords[0],inst->mTextureCoords[0],orig->mNumVertices,uvEpsilon))
+ continue;
+ if (orig->mTextureCoords[1]) {
+ if (!CompareArrays(orig->mTextureCoords[1],inst->mTextureCoords[1],orig->mNumVertices,uvEpsilon))
+ continue;
+ if (orig->mTextureCoords[2]) {
+ if (!CompareArrays(orig->mTextureCoords[2],inst->mTextureCoords[2],orig->mNumVertices,uvEpsilon))
+ continue;
+ if (orig->mTextureCoords[3]) {
+ if (!CompareArrays(orig->mTextureCoords[3],inst->mTextureCoords[3],orig->mNumVertices,uvEpsilon))
+ continue;
+ }
+ }
+ }
+ }
+
+ Q_ASSERT(4 == AI_MAX_NUMBER_OF_COLOR_SETS);
+
+ // and the same nasty stuff for vertex colors ...
+ if (orig->mColors[0]) {
+ if (!CompareArrays(orig->mColors[0],inst->mColors[0],orig->mNumVertices,uvEpsilon))
+ continue;
+ if (orig->mTextureCoords[1]) {
+ if (!CompareArrays(orig->mColors[1],inst->mColors[1],orig->mNumVertices,uvEpsilon))
+ continue;
+ if (orig->mTextureCoords[2]) {
+ if (!CompareArrays(orig->mColors[2],inst->mColors[2],orig->mNumVertices,uvEpsilon))
+ continue;
+ if (orig->mTextureCoords[3]) {
+ if (!CompareArrays(orig->mColors[3],inst->mColors[3],orig->mNumVertices,uvEpsilon))
+ 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.
+ boost::scoped_array<unsigned int> ftbl_orig(new unsigned int[orig->mNumVertices]);
+ boost::scoped_array<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 nodegraph with our nice lookup table
+ UpdateMeshIndices(pScene->mRootNode,remapping.get());
+
+ // write to log
+ if (!DefaultLogger::isNullLogger()) {
+
+ char buffer[512];
+ ::sprintf(buffer,"FindInstancesProcess finished. Found %i instances",pScene->mNumMeshes-numMeshesOut);
+ DefaultLogger::get()->info(buffer);
+ }
+ pScene->mNumMeshes = numMeshesOut;
+ }
+ else DefaultLogger::get()->debug("FindInstancesProcess finished. No instanced meshes found");
+ }
+}
diff --git a/src/3rdparty/assimp/code/FindInstancesProcess.h b/src/3rdparty/assimp/code/FindInstancesProcess.h
new file mode 100644
index 000000000..3b329a153
--- /dev/null
+++ b/src/3rdparty/assimp/code/FindInstancesProcess.h
@@ -0,0 +1,140 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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. It isn't absolutely unique.
+ * @param in Input mesh
+ * @return Hash.
+ */
+inline uint64_t GetMeshHash(aiMesh* in)
+{
+ ai_assert(NULL != 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 aray
+ * @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 ASSIMP_API FindInstancesProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::FindInstancesProcessTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ FindInstancesProcess();
+
+ /** Destructor, private as well */
+ ~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/src/3rdparty/assimp/code/FindInvalidDataProcess.cpp b/src/3rdparty/assimp/code/FindInvalidDataProcess.cpp
new file mode 100644
index 000000000..eceb4d807
--- /dev/null
+++ b/src/3rdparty/assimp/code/FindInvalidDataProcess.cpp
@@ -0,0 +1,419 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Defines a post processing step to search an importer's output
+ for data that is obviously invalid */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
+
+// internal headers
+#include "FindInvalidDataProcess.h"
+#include "ProcessHelper.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FindInvalidDataProcess::FindInvalidDataProcess() : configEpsilon(0.0f)
+{
+ // 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));
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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) {
+
+ register unsigned int ref = node->mMeshes[a];
+ if (0xffffffff != (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)
+{
+ DefaultLogger::get()->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] = 0xffffffff;
+ 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;
+ }
+
+ DefaultLogger::get()->info("FindInvalidDataProcess finished. Found issues ...");
+ }
+ else DefaultLogger::get()->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 NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+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 NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+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) {
+ DefaultLogger::get()->error(std::string("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, float epsilon);
+
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE bool EpsilonCompare(float n, float s, float epsilon) {
+ return fabs(n-s)>epsilon;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <>
+bool EpsilonCompare<aiVectorKey>(const aiVectorKey& n, const aiVectorKey& s, float 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, float 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, float epsilon)
+{
+ if (num <= 1) {
+ return true;
+ }
+
+ if (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)
+{
+ int i = 0;
+
+ // ScenePreprocessor's work ...
+ ai_assert((0 != anim->mPositionKeys && 0 != anim->mRotationKeys && 0 != anim->mScalingKeys));
+
+ // Check whether all values in a tracks are identical - in this case
+ // we can remove al keys except one.
+ // POSITIONS
+ 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)
+ DefaultLogger::get()->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 ? true : false));
+
+ // 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)) {
+ DefaultLogger::get()->error("Deleting mesh: Unable to continue without vertex positions");
+ return 2;
+ }
+
+ // process texture coordinates
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS && pMesh->mTextureCoords[i];++i) {
+ if (ProcessArray(pMesh->mTextureCoords[i],pMesh->mNumVertices,"uvcoords",dirtyMask)) {
+
+ // 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;
+ }
+ 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/src/3rdparty/assimp/code/FindInvalidDataProcess.h b/src/3rdparty/assimp/code/FindInvalidDataProcess.h
new file mode 100644
index 000000000..0cdf15877
--- /dev/null
+++ b/src/3rdparty/assimp/code/FindInvalidDataProcess.h
@@ -0,0 +1,111 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiTypes.h"
+
+struct aiMesh;
+class FindInvalidDataProcessTest;
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** The FindInvalidData postprocessing 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
+{
+ friend class Importer;
+ friend class ::FindInvalidDataProcessTest;
+
+protected:
+
+ /** Constructor to be privately used by Importer */
+ FindInvalidDataProcess();
+
+ /** Destructor, private as well */
+ ~FindInvalidDataProcess();
+
+public:
+
+ // -------------------------------------------------------------------
+ //
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ // Setup import settings
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ // Run the step
+ void Execute( aiScene* pScene);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Executes the postprocessing 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 postprocessing step on the given animation
+ * @param anim The animation to process. */
+ void ProcessAnimation (aiAnimation* anim);
+
+ // -------------------------------------------------------------------
+ /** Executes the postprocessing step on the given anim channel
+ * @param anim The animation channel to process.*/
+ void ProcessAnimationChannel (aiNodeAnim* anim);
+
+private:
+ float configEpsilon;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_AI_FINDINVALIDDATA_H_INC
diff --git a/src/3rdparty/assimp/code/FixNormalsStep.cpp b/src/3rdparty/assimp/code/FixNormalsStep.cpp
new file mode 100644
index 000000000..f83ec9b39
--- /dev/null
+++ b/src/3rdparty/assimp/code/FixNormalsStep.cpp
@@ -0,0 +1,180 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the post processing step to invert
+ * all normals in meshes with infacing normals.
+ */
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "FixNormalsStep.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+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)
+{
+ DefaultLogger::get()->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)
+ DefaultLogger::get()->debug("FixInfacingNormalsProcess finished. Found issues.");
+ else DefaultLogger::get()->debug("FixInfacingNormalsProcess finished. No changes to the scene.");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Apply the step to the mesh
+bool FixInfacingNormalsProcess::ProcessMesh( aiMesh* pcMesh, unsigned int index)
+{
+ ai_assert(NULL != 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 umodified 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 * sqrtf( fDelta1_yz ))return false;
+ if (fDelta1_y < 0.05f * sqrtf( fDelta1_z * fDelta1_x ))return false;
+ if (fDelta1_z < 0.05f * sqrtf( fDelta1_y * fDelta1_x ))return false;
+
+ // now compare the volumes of the bounding boxes
+ if (::fabsf(fDelta0_x * fDelta1_yz) <
+ ::fabsf(fDelta1_x * fDelta1_y * fDelta1_z))
+ {
+ if (!DefaultLogger::isNullLogger())
+ {
+ char buffer[128]; // should be sufficiently large
+ ::sprintf(buffer,"Mesh %i: Normals are facing inwards (or the mesh is planar)",index);
+ DefaultLogger::get()->info(buffer);
+ }
+
+ // 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/src/3rdparty/assimp/code/FixNormalsStep.h b/src/3rdparty/assimp/code/FixNormalsStep.h
new file mode 100644
index 000000000..972001e88
--- /dev/null
+++ b/src/3rdparty/assimp/code/FixNormalsStep.h
@@ -0,0 +1,96 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file 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 deteermine whether the normal
+ * vectors of an object are facing inwards. In this case they will be
+ * flipped.
+ */
+ class ASSIMP_API FixInfacingNormalsProcess : public BaseProcess
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ FixInfacingNormalsProcess();
+
+ /** Destructor, private as well */
+ ~FixInfacingNormalsProcess();
+
+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:
+
+ // -------------------------------------------------------------------
+ /** 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/src/3rdparty/assimp/code/GenFaceNormalsProcess.cpp b/src/3rdparty/assimp/code/GenFaceNormalsProcess.cpp
new file mode 100644
index 000000000..f4baca6ae
--- /dev/null
+++ b/src/3rdparty/assimp/code/GenFaceNormalsProcess.cpp
@@ -0,0 +1,138 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the post processing step to generate face
+* normals for all imported faces.
+*/
+
+#include "AssimpPCH.h"
+#include "GenFaceNormalsProcess.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
+{
+ return (pFlags & aiProcess_GenNormals) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void GenFaceNormalsProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->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) {
+ DefaultLogger::get()->info("GenFaceNormalsProcess finished. "
+ "Face normals have been calculated");
+ }
+ else DefaultLogger::get()->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) {
+ 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))) {
+ DefaultLogger::get()->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]] = 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]];
+ aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).Normalize();
+
+ for (unsigned int i = 0;i < face.mNumIndices;++i) {
+ pMesh->mNormals[face.mIndices[i]] = vNor;
+ }
+ }
+ return true;
+}
diff --git a/src/3rdparty/assimp/code/GenFaceNormalsProcess.h b/src/3rdparty/assimp/code/GenFaceNormalsProcess.h
new file mode 100644
index 000000000..87ca6f22d
--- /dev/null
+++ b/src/3rdparty/assimp/code/GenFaceNormalsProcess.h
@@ -0,0 +1,88 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiMesh.h"
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The GenFaceNormalsProcess computes face normals for all faces of all meshes
+*/
+class ASSIMP_API GenFaceNormalsProcess : public BaseProcess
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ GenFaceNormalsProcess();
+
+ /** Destructor, private as well */
+ ~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);
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_GENFACENORMALPROCESS_H_INC
diff --git a/src/3rdparty/assimp/code/GenVertexNormalsProcess.cpp b/src/3rdparty/assimp/code/GenVertexNormalsProcess.cpp
new file mode 100644
index 000000000..e8be8709f
--- /dev/null
+++ b/src/3rdparty/assimp/code/GenVertexNormalsProcess.cpp
@@ -0,0 +1,228 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the post processing step to generate face
+* normals for all imported faces.
+*/
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "GenVertexNormalsProcess.h"
+#include "ProcessHelper.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+GenVertexNormalsProcess::GenVertexNormalsProcess()
+{
+ this->configMaxAngle = AI_DEG_TO_RAD(175.f);
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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
+{
+ 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,175.f);
+ configMaxAngle = AI_DEG_TO_RAD(std::max(std::min(configMaxAngle,175.0f),0.0f));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void GenVertexNormalsProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->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) {
+ DefaultLogger::get()->info("GenVertexNormalsProcess finished. "
+ "Vertex normals have been calculated");
+ }
+ else DefaultLogger::get()->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)
+ 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)))
+ {
+ DefaultLogger::get()->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<float>::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]] = qnan;
+ continue;
+ }
+
+ aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
+ aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
+ aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]];
+ aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).Normalize();
+
+ 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;
+ float posEpsilon = 1e-5f;
+ 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;
+ 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.Normalize();
+
+ // Write the smoothed normal back to all affected normals
+ for (unsigned int a = 0; a < verticesFound.size(); ++a)
+ {
+ register 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 float fLimit = ::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 pcNor;
+ for (unsigned int a = 0; a < verticesFound.size(); ++a) {
+ const aiVector3D& v = pMesh->mNormals[verticesFound[a]];
+
+ // check whether the angle between the two normals is not too large
+ // HACK: if v.x is qnan the dot product will become qnan, too
+ // therefore the comparison against fLimit should be false
+ // in every case.
+ if (v * pMesh->mNormals[i] < fLimit)
+ continue;
+
+ pcNor += v;
+ }
+ pcNew[i] = pcNor.Normalize();
+ }
+ }
+
+ delete[] pMesh->mNormals;
+ pMesh->mNormals = pcNew;
+
+ return true;
+}
diff --git a/src/3rdparty/assimp/code/GenVertexNormalsProcess.h b/src/3rdparty/assimp/code/GenVertexNormalsProcess.h
new file mode 100644
index 000000000..294cb7ef8
--- /dev/null
+++ b/src/3rdparty/assimp/code/GenVertexNormalsProcess.h
@@ -0,0 +1,118 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiMesh.h"
+
+class GenNormalsTest;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** The GenFaceNormalsProcess computes vertex normals for all vertizes
+*/
+class ASSIMP_API GenVertexNormalsProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::GenNormalsTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ GenVertexNormalsProcess();
+
+ /** Destructor, private as well */
+ ~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(float f)
+ {
+ configMaxAngle =f;
+ }
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** 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*/
+ float configMaxAngle;
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_GENVERTEXNORMALPROCESS_H_INC
+
diff --git a/src/3rdparty/assimp/code/GenericProperty.h b/src/3rdparty/assimp/code/GenericProperty.h
new file mode 100644
index 000000000..ccc504a66
--- /dev/null
+++ b/src/3rdparty/assimp/code/GenericProperty.h
@@ -0,0 +1,112 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef AI_GENERIC_PROPERTY_H_INCLUDED
+#define AI_GENERIC_PROPERTY_H_INCLUDED
+
+#include "./../include/assimp.hpp"
+#include "Hash.h"
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline void SetGenericProperty(std::map< unsigned int, T >& list,
+ const char* szName, const T& value, bool* bWasExisting = NULL)
+{
+ ai_assert(NULL != 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;
+ }
+ (*it).second = value;
+ if (bWasExisting)
+ *bWasExisting = true;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline const T& GetGenericProperty(const std::map< unsigned int, T >& list,
+ const char* szName, const T& errorReturn)
+{
+ ai_assert(NULL != 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 = NULL)
+{
+ ai_assert(NULL != 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;
+}
+
+
+#endif // !! AI_GENERIC_PROPERTY_H_INCLUDED
diff --git a/src/3rdparty/assimp/code/HMPFileData.h b/src/3rdparty/assimp/code/HMPFileData.h
new file mode 100644
index 000000000..2d372a063
--- /dev/null
+++ b/src/3rdparty/assimp/code/HMPFileData.h
@@ -0,0 +1,134 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+//!
+//! @file Data structures for the 3D Game Studio Heightmap format (HMP)
+//!
+
+namespace Assimp {
+namespace HMP {
+
+#include "./../include/Compiler/pushpack1.h"
+
+// to make it easier for us, we test the magic word against both "endianesses"
+#define AI_HMP_MAGIC_NUMBER_BE_4 AI_MAKE_MAGIC("HMP4")
+#define AI_HMP_MAGIC_NUMBER_LE_4 AI_MAKE_MAGIC("4PMH")
+
+#define AI_HMP_MAGIC_NUMBER_BE_5 AI_MAKE_MAGIC("HMP5")
+#define AI_HMP_MAGIC_NUMBER_LE_5 AI_MAKE_MAGIC("5PMH")
+
+#define AI_HMP_MAGIC_NUMBER_BE_7 AI_MAKE_MAGIC("HMP7")
+#define AI_HMP_MAGIC_NUMBER_LE_7 AI_MAKE_MAGIC("7PMH")
+
+// ---------------------------------------------------------------------------
+/** Data structure for the header of a HMP5 file.
+ * This is also used by HMP4 and HMP7, but with modifications
+*/
+struct Header_HMP5
+{
+ int8_t ident[4]; // "HMP5"
+ int32_t version;
+
+ // ignored
+ float scale[3];
+ float scale_origin[3];
+ float boundingradius;
+
+ //! Size of one triangle in x direction
+ float ftrisize_x;
+ //! Size of one triangle in y direction
+ float ftrisize_y;
+ //! Number of vertices in x direction
+ float fnumverts_x;
+
+ //! Number of skins in the file
+ int32_t numskins;
+
+ // can ignore this?
+ int32_t skinwidth;
+ int32_t skinheight;
+
+ //!Number of vertices in the file
+ int32_t numverts;
+
+ // ignored and zero
+ int32_t numtris;
+
+ //! only one supported ...
+ int32_t numframes;
+
+ //! Always 0 ...
+ int32_t num_stverts;
+ int32_t flags;
+ float size;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** Data structure for a terrain vertex in a HMP4 file
+*/
+struct Vertex_HMP4
+{
+ uint16_t p_pos[3];
+ uint8_t normals162index;
+ uint8_t pad;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** Data structure for a terrain vertex in a HMP5 file
+*/
+struct Vertex_HMP5
+{
+ uint16_t z;
+ uint8_t normals162index;
+ uint8_t pad;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** Data structure for a terrain vertex in a HMP7 file
+*/
+struct Vertex_HMP7
+{
+ uint16_t z;
+ int8_t normal_x,normal_y;
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+} //! namespace HMP
+} //! namespace Assimp
diff --git a/src/3rdparty/assimp/code/HMPLoader.cpp b/src/3rdparty/assimp/code/HMPLoader.cpp
new file mode 100644
index 000000000..dc4dea152
--- /dev/null
+++ b/src/3rdparty/assimp/code/HMPLoader.cpp
@@ -0,0 +1,497 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the MDL importer class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_HMP_IMPORTER
+
+// internal headers
+#include "MaterialSystem.h"
+#include "HMPLoader.h"
+#include "MD2FileData.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+HMPImporter::HMPImporter()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+HMPImporter::~HMPImporter()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool HMPImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const
+{
+ const std::string extension = GetExtension(pFile);
+ if (extension == "hmp" )
+ return true;
+
+ // if check for extension is not enough, check for the magic tokens
+ if (!extension.length() || cs) {
+ uint32_t tokens[3];
+ tokens[0] = AI_HMP_MAGIC_NUMBER_LE_4;
+ tokens[1] = AI_HMP_MAGIC_NUMBER_LE_5;
+ tokens[2] = AI_HMP_MAGIC_NUMBER_LE_7;
+ return CheckMagicToken(pIOHandler,pFile,tokens,3,0);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get list of all file extensions that are handled by this loader
+void HMPImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("hmp");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void HMPImporter::InternReadFile( const std::string& pFile,
+ aiScene* _pScene, IOSystem* _pIOHandler)
+{
+ pScene = _pScene;
+ pIOHandler = _pIOHandler;
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open HMP file " + pFile + ".");
+
+ // Check whether the HMP file is large enough to contain
+ // at least the file header
+ const size_t fileSize = file->FileSize();
+ if ( fileSize < 50)
+ throw DeadlyImportError( "HMP File is too small.");
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ std::vector<uint8_t> buffer(fileSize);
+ mBuffer = &buffer[0];
+ file->Read( (void*)mBuffer, 1, fileSize);
+ iFileSize = (unsigned int)fileSize;
+
+ // Determine the file subtype and call the appropriate member function
+ const uint32_t iMagic = *((uint32_t*)this->mBuffer);
+
+ // HMP4 format
+ if (AI_HMP_MAGIC_NUMBER_LE_4 == iMagic ||
+ AI_HMP_MAGIC_NUMBER_BE_4 == iMagic)
+ {
+ DefaultLogger::get()->debug("HMP subtype: 3D GameStudio A4, magic word is HMP4");
+ InternReadFile_HMP4();
+ }
+ // HMP5 format
+ else if (AI_HMP_MAGIC_NUMBER_LE_5 == iMagic ||
+ AI_HMP_MAGIC_NUMBER_BE_5 == iMagic)
+ {
+ DefaultLogger::get()->debug("HMP subtype: 3D GameStudio A5, magic word is HMP5");
+ InternReadFile_HMP5();
+ }
+ // HMP7 format
+ else if (AI_HMP_MAGIC_NUMBER_LE_7 == iMagic ||
+ AI_HMP_MAGIC_NUMBER_BE_7 == iMagic)
+ {
+ DefaultLogger::get()->debug("HMP subtype: 3D GameStudio A7, magic word is HMP7");
+ InternReadFile_HMP7();
+ }
+ else
+ {
+ // Print the magic word to the logger
+ char szBuffer[5];
+ szBuffer[0] = ((char*)&iMagic)[0];
+ szBuffer[1] = ((char*)&iMagic)[1];
+ szBuffer[2] = ((char*)&iMagic)[2];
+ szBuffer[3] = ((char*)&iMagic)[3];
+ szBuffer[4] = '\0';
+
+ // We're definitely unable to load this file
+ throw DeadlyImportError( "Unknown HMP subformat " + pFile +
+ ". Magic word (" + szBuffer + ") is not known");
+ }
+
+ // Set the AI_SCENE_FLAGS_TERRAIN bit
+ pScene->mFlags |= AI_SCENE_FLAGS_TERRAIN;
+
+ // File buffer destructs automatically now
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::ValidateHeader_HMP457( )
+{
+ const HMP::Header_HMP5* const pcHeader = (const HMP::Header_HMP5*)mBuffer;
+
+ if (120 > iFileSize)
+ {
+ throw DeadlyImportError("HMP file is too small (header size is "
+ "120 bytes, this file is smaller)");
+ }
+
+ if (!pcHeader->ftrisize_x || !pcHeader->ftrisize_y)
+ throw DeadlyImportError("Size of triangles in either x or y direction is zero");
+
+ if (pcHeader->fnumverts_x < 1.0f || (pcHeader->numverts/pcHeader->fnumverts_x) < 1.0f)
+ throw DeadlyImportError("Number of triangles in either x or y direction is zero");
+
+ if (!pcHeader->numframes)
+ throw DeadlyImportError("There are no frames. At least one should be there");
+
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::InternReadFile_HMP4( )
+{
+ throw DeadlyImportError("HMP4 is currently not supported");
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::InternReadFile_HMP5( )
+{
+ // read the file header and skip everything to byte 84
+ const HMP::Header_HMP5* pcHeader = (const HMP::Header_HMP5*)mBuffer;
+ const unsigned char* szCurrent = (const unsigned char*)(mBuffer+84);
+ ValidateHeader_HMP457();
+
+ // generate an output mesh
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes = new aiMesh*[1];
+ aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
+
+ pcMesh->mMaterialIndex = 0;
+ pcMesh->mVertices = new aiVector3D[pcHeader->numverts];
+ pcMesh->mNormals = new aiVector3D[pcHeader->numverts];
+
+ const unsigned int height = (unsigned int)(pcHeader->numverts / pcHeader->fnumverts_x);
+ const unsigned int width = (unsigned int)pcHeader->fnumverts_x;
+
+ // generate/load a material for the terrain
+ CreateMaterial(szCurrent,&szCurrent);
+
+ // goto offset 120, I don't know why ...
+ // (fixme) is this the frame header? I assume yes since it starts with 2.
+ szCurrent += 36;
+ SizeCheck(szCurrent + sizeof(const HMP::Vertex_HMP7)*height*width);
+
+ // now load all vertices from the file
+ aiVector3D* pcVertOut = pcMesh->mVertices;
+ aiVector3D* pcNorOut = pcMesh->mNormals;
+ const HMP::Vertex_HMP5* src = (const HMP::Vertex_HMP5*) szCurrent;
+ for (unsigned int y = 0; y < height;++y)
+ {
+ for (unsigned int x = 0; x < width;++x)
+ {
+ pcVertOut->x = x * pcHeader->ftrisize_x;
+ pcVertOut->y = y * pcHeader->ftrisize_y;
+ pcVertOut->z = (((float)src->z / 0xffff)-0.5f) * pcHeader->ftrisize_x * 8.0f;
+ MD2::LookupNormalIndex(src->normals162index, *pcNorOut );
+ ++pcVertOut;++pcNorOut;++src;
+ }
+ }
+
+ // generate texture coordinates if necessary
+ if (pcHeader->numskins)
+ GenerateTextureCoords(width,height);
+
+ // now build a list of faces
+ CreateOutputFaceList(width,height);
+
+ // there is no nodegraph in HMP files. Simply assign the one mesh
+ // (no, not the one ring) to the root node
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("terrain_root");
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::InternReadFile_HMP7( )
+{
+ // read the file header and skip everything to byte 84
+ const HMP::Header_HMP5* const pcHeader = (const HMP::Header_HMP5*)mBuffer;
+ const unsigned char* szCurrent = (const unsigned char*)(mBuffer+84);
+ ValidateHeader_HMP457();
+
+ // generate an output mesh
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes = new aiMesh*[1];
+ aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
+
+ pcMesh->mMaterialIndex = 0;
+ pcMesh->mVertices = new aiVector3D[pcHeader->numverts];
+ pcMesh->mNormals = new aiVector3D[pcHeader->numverts];
+
+ const unsigned int height = (unsigned int)(pcHeader->numverts / pcHeader->fnumverts_x);
+ const unsigned int width = (unsigned int)pcHeader->fnumverts_x;
+
+ // generate/load a material for the terrain
+ CreateMaterial(szCurrent,&szCurrent);
+
+ // goto offset 120, I don't know why ...
+ // (fixme) is this the frame header? I assume yes since it starts with 2.
+ szCurrent += 36;
+
+ SizeCheck(szCurrent + sizeof(const HMP::Vertex_HMP7)*height*width);
+
+ // now load all vertices from the file
+ aiVector3D* pcVertOut = pcMesh->mVertices;
+ aiVector3D* pcNorOut = pcMesh->mNormals;
+ const HMP::Vertex_HMP7* src = (const HMP::Vertex_HMP7*) szCurrent;
+ for (unsigned int y = 0; y < height;++y)
+ {
+ for (unsigned int x = 0; x < width;++x)
+ {
+ pcVertOut->x = x * pcHeader->ftrisize_x;
+ pcVertOut->y = y * pcHeader->ftrisize_y;
+
+ // FIXME: What exctly is the correct scaling factor to use?
+ // possibly pcHeader->scale_origin[2] in combination with a
+ // signed interpretation of src->z?
+ pcVertOut->z = (((float)src->z / 0xffff)-0.5f) * pcHeader->ftrisize_x * 8.0f;
+
+ pcNorOut->x = ((float)src->normal_x / 0x80 ); // * pcHeader->scale_origin[0];
+ pcNorOut->y = ((float)src->normal_y / 0x80 ); // * pcHeader->scale_origin[1];
+ pcNorOut->z = 1.0f;
+ pcNorOut->Normalize();
+
+ ++pcVertOut;++pcNorOut;++src;
+ }
+ }
+
+ // generate texture coordinates if necessary
+ if (pcHeader->numskins)GenerateTextureCoords(width,height);
+
+ // now build a list of faces
+ CreateOutputFaceList(width,height);
+
+ // there is no nodegraph in HMP files. Simply assign the one mesh
+ // (no, not the One Ring) to the root node
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("terrain_root");
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::CreateMaterial(const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut)
+{
+ aiMesh* const pcMesh = pScene->mMeshes[0];
+ const HMP::Header_HMP5* const pcHeader = (const HMP::Header_HMP5*)mBuffer;
+
+ // we don't need to generate texture coordinates if
+ // we have no textures in the file ...
+ if (pcHeader->numskins)
+ {
+ pcMesh->mTextureCoords[0] = new aiVector3D[pcHeader->numverts];
+ pcMesh->mNumUVComponents[0] = 2;
+
+ // now read the first skin and skip all others
+ ReadFirstSkin(pcHeader->numskins,szCurrent,&szCurrent);
+ }
+ else
+ {
+ // generate a default material
+ const int iMode = (int)aiShadingMode_Gouraud;
+ MaterialHelper* pcHelper = new MaterialHelper();
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.6f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ aiString szName;
+ szName.Set(AI_DEFAULT_MATERIAL_NAME);
+ pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
+
+ // add the material to the scene
+ pScene->mNumMaterials = 1;
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = pcHelper;
+ }
+ *szCurrentOut = szCurrent;
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::CreateOutputFaceList(unsigned int width,unsigned int height)
+{
+ aiMesh* const pcMesh = this->pScene->mMeshes[0];
+
+ // Allocate enough storage
+ pcMesh->mNumFaces = (width-1) * (height-1);
+ pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+
+ pcMesh->mNumVertices = pcMesh->mNumFaces*4;
+ aiVector3D* pcVertices = new aiVector3D[pcMesh->mNumVertices];
+ aiVector3D* pcNormals = new aiVector3D[pcMesh->mNumVertices];
+
+ aiFace* pcFaceOut(pcMesh->mFaces);
+ aiVector3D* pcVertOut = pcVertices;
+ aiVector3D* pcNorOut = pcNormals;
+
+ aiVector3D* pcUVs = pcMesh->mTextureCoords[0] ? new aiVector3D[pcMesh->mNumVertices] : NULL;
+ aiVector3D* pcUVOut(pcUVs);
+
+ // Build the terrain square
+ unsigned int iCurrent = 0;
+ for (unsigned int y = 0; y < height-1;++y) {
+ for (unsigned int x = 0; x < width-1;++x,++pcFaceOut) {
+ pcFaceOut->mNumIndices = 4;
+ pcFaceOut->mIndices = new unsigned int[4];
+
+ *pcVertOut++ = pcMesh->mVertices[y*width+x];
+ *pcVertOut++ = pcMesh->mVertices[(y+1)*width+x];
+ *pcVertOut++ = pcMesh->mVertices[(y+1)*width+x+1];
+ *pcVertOut++ = pcMesh->mVertices[y*width+x+1];
+
+
+ *pcNorOut++ = pcMesh->mNormals[y*width+x];
+ *pcNorOut++ = pcMesh->mNormals[(y+1)*width+x];
+ *pcNorOut++ = pcMesh->mNormals[(y+1)*width+x+1];
+ *pcNorOut++ = pcMesh->mNormals[y*width+x+1];
+
+ if (pcMesh->mTextureCoords[0])
+ {
+ *pcUVOut++ = pcMesh->mTextureCoords[0][y*width+x];
+ *pcUVOut++ = pcMesh->mTextureCoords[0][(y+1)*width+x];
+ *pcUVOut++ = pcMesh->mTextureCoords[0][(y+1)*width+x+1];
+ *pcUVOut++ = pcMesh->mTextureCoords[0][y*width+x+1];
+ }
+
+ for (unsigned int i = 0; i < 4;++i)
+ pcFaceOut->mIndices[i] = iCurrent++;
+ }
+ }
+ delete[] pcMesh->mVertices;
+ pcMesh->mVertices = pcVertices;
+
+ delete[] pcMesh->mNormals;
+ pcMesh->mNormals = pcNormals;
+
+ if (pcMesh->mTextureCoords[0])
+ {
+ delete[] pcMesh->mTextureCoords[0];
+ pcMesh->mTextureCoords[0] = pcUVs;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::ReadFirstSkin(unsigned int iNumSkins, const unsigned char* szCursor,
+ const unsigned char** szCursorOut)
+{
+ ai_assert(0 != iNumSkins && NULL != szCursor);
+
+ // read the type of the skin ...
+ // sometimes we need to skip 12 bytes here, I don't know why ...
+ uint32_t iType = *((uint32_t*)szCursor);szCursor += sizeof(uint32_t);
+ if (0 == iType)
+ {
+ szCursor += sizeof(uint32_t) * 2;
+ iType = *((uint32_t*)szCursor);szCursor += sizeof(uint32_t);
+ if (!iType)
+ throw DeadlyImportError("Unable to read HMP7 skin chunk");
+
+ }
+ // read width and height
+ uint32_t iWidth = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
+ uint32_t iHeight = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
+
+ // allocate an output material
+ MaterialHelper* pcMat = new MaterialHelper();
+
+ // read the skin, this works exactly as for MDL7
+ ParseSkinLump_3DGS_MDL7(szCursor,&szCursor,
+ pcMat,iType,iWidth,iHeight);
+
+ // now we need to skip any other skins ...
+ for (unsigned int i = 1; i< iNumSkins;++i)
+ {
+ iType = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
+ iWidth = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
+ iHeight = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
+
+ SkipSkinLump_3DGS_MDL7(szCursor,&szCursor,iType,iWidth,iHeight);
+ SizeCheck(szCursor);
+ }
+
+ // setup the material ...
+ pScene->mNumMaterials = 1;
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = pcMat;
+
+ *szCursorOut = szCursor;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Generate proepr texture coords
+void HMPImporter::GenerateTextureCoords(
+ const unsigned int width, const unsigned int height)
+{
+ ai_assert(NULL != pScene->mMeshes && NULL != pScene->mMeshes[0] &&
+ NULL != pScene->mMeshes[0]->mTextureCoords[0]);
+
+ aiVector3D* uv = pScene->mMeshes[0]->mTextureCoords[0];
+
+ const float fY = (1.0f / height) + (1.0f / height) / (height-1);
+ const float fX = (1.0f / width) + (1.0f / width) / (width-1);
+
+ for (unsigned int y = 0; y < height;++y) {
+ for (unsigned int x = 0; x < width;++x,++uv) {
+ uv->y = fY*y;
+ uv->x = fX*x;
+ uv->z = 0.0f;
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_HMP_IMPORTER
diff --git a/src/3rdparty/assimp/code/HMPLoader.h b/src/3rdparty/assimp/code/HMPLoader.h
new file mode 100644
index 000000000..fdf84baaf
--- /dev/null
+++ b/src/3rdparty/assimp/code/HMPLoader.h
@@ -0,0 +1,159 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/** @file HMPLoader.h
+ * @brief Declaration of the HMP importer class
+ */
+
+#ifndef AI_HMPLOADER_H_INCLUDED
+#define AI_HMPLOADER_H_INCLUDED
+
+// public ASSIMP headers
+#include "../include/aiTypes.h"
+#include "../include/aiTexture.h"
+#include "../include/aiMaterial.h"
+
+// internal headers
+#include "BaseImporter.h"
+#include "MDLLoader.h"
+#include "HMPFileData.h"
+
+namespace Assimp {
+using namespace HMP;
+
+// ---------------------------------------------------------------------------
+/** Used to load 3D GameStudio HMP files (terrains)
+*/
+class HMPImporter : public MDLImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ HMPImporter();
+
+ /** Destructor, private as well */
+ ~HMPImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Import a HMP4 file
+ */
+ void InternReadFile_HMP4( );
+
+ // -------------------------------------------------------------------
+ /** Import a HMP5 file
+ */
+ void InternReadFile_HMP5( );
+
+ // -------------------------------------------------------------------
+ /** Import a HMP7 file
+ */
+ void InternReadFile_HMP7( );
+
+ // -------------------------------------------------------------------
+ /** Validate a HMP 5,4,7 file header
+ */
+ void ValidateHeader_HMP457( );
+
+ // -------------------------------------------------------------------
+ /** Try to load one material from the file, if this fails create
+ * a default material
+ */
+ void CreateMaterial(const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut);
+
+ // -------------------------------------------------------------------
+ /** Build a list of output faces and vertices. The function
+ * triangulates the height map read from the file
+ * \param width Width of the height field
+ * \param width Height of the height field
+ */
+ void CreateOutputFaceList(unsigned int width,unsigned int height);
+
+ // -------------------------------------------------------------------
+ /** Generate planar texture coordinates for a terrain
+ * \param width Width of the terrain, in vertices
+ * \param height Height of the terrain, in vertices
+ */
+ void GenerateTextureCoords(const unsigned int width,
+ const unsigned int height);
+
+ // -------------------------------------------------------------------
+ /** Read the first skin from the file and skip all others ...
+ * \param iNumSkins Number of skins in the file
+ * \param szCursor Position of the first skin (offset 84)
+ */
+ void ReadFirstSkin(unsigned int iNumSkins, const unsigned char* szCursor,
+ const unsigned char** szCursorOut);
+
+private:
+
+};
+
+} // end of namespace Assimp
+
+#endif // AI_HMPIMPORTER_H_INC
+
diff --git a/src/3rdparty/assimp/code/HalfLifeFileData.h b/src/3rdparty/assimp/code/HalfLifeFileData.h
new file mode 100644
index 000000000..b12adf6ad
--- /dev/null
+++ b/src/3rdparty/assimp/code/HalfLifeFileData.h
@@ -0,0 +1,150 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+//
+//! @file Definition of in-memory structures for the HL2 MDL file format
+// and for the HalfLife text format (SMD)
+//
+// The specification has been taken from various sources on the internet.
+
+
+#ifndef AI_MDLFILEHELPER2_H_INC
+#define AI_MDLFILEHELPER2_H_INC
+
+#include "./../include/Compiler/pushpack1.h"
+
+#include "MDLFileData.h"
+
+namespace Assimp {
+namespace MDL {
+
+// magic bytes used in Half Life 2 MDL models
+#define AI_MDL_MAGIC_NUMBER_BE_HL2a AI_MAKE_MAGIC("IDST")
+#define AI_MDL_MAGIC_NUMBER_LE_HL2a AI_MAKE_MAGIC("TSDI")
+#define AI_MDL_MAGIC_NUMBER_BE_HL2b AI_MAKE_MAGIC("IDSQ")
+#define AI_MDL_MAGIC_NUMBER_LE_HL2b AI_MAKE_MAGIC("QSDI")
+
+// ---------------------------------------------------------------------------
+/** \struct Header_HL2
+ * \brief Data structure for the HL2 main header
+ */
+// ---------------------------------------------------------------------------
+struct Header_HL2
+{
+ //! magic number: "IDST"/"IDSQ"
+ char ident[4];
+
+ //! Version number
+ int32_t version;
+
+ //! Original file name in pak ?
+ char name[64];
+
+ //! Length of file name/length of file?
+ int32_t length;
+
+ //! For viewer, ignored
+ aiVector3D eyeposition;
+ aiVector3D min;
+ aiVector3D max;
+
+ //! AABB of the model
+ aiVector3D bbmin;
+ aiVector3D bbmax;
+
+ // File flags
+ int32_t flags;
+
+ //! NUmber of bones contained in the file
+ int32_t numbones;
+ int32_t boneindex;
+
+ //! Number of bone controllers for bone animation
+ int32_t numbonecontrollers;
+ int32_t bonecontrollerindex;
+
+ //! More bounding boxes ...
+ int32_t numhitboxes;
+ int32_t hitboxindex;
+
+ //! Animation sequences in the file
+ int32_t numseq;
+ int32_t seqindex;
+
+ //! Loaded sequences. Ignored
+ int32_t numseqgroups;
+ int32_t seqgroupindex;
+
+ //! Raw texture data
+ int32_t numtextures;
+ int32_t textureindex;
+ int32_t texturedataindex;
+
+ //! Number of skins (=textures?)
+ int32_t numskinref;
+ int32_t numskinfamilies;
+ int32_t skinindex;
+
+ //! Number of parts
+ int32_t numbodyparts;
+ int32_t bodypartindex;
+
+ //! attachable points for gameplay and physics
+ int32_t numattachments;
+ int32_t attachmentindex;
+
+ //! Table of sound effects associated with the model
+ int32_t soundtable;
+ int32_t soundindex;
+ int32_t soundgroups;
+ int32_t soundgroupindex;
+
+ //! Number of animation transitions
+ int32_t numtransitions;
+ int32_t transitionindex;
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+}
+} // end namespaces
+
+#endif // ! AI_MDLFILEHELPER2_H_INC
diff --git a/src/3rdparty/assimp/code/Hash.h b/src/3rdparty/assimp/code/Hash.h
new file mode 100644
index 000000000..9ba79c512
--- /dev/null
+++ b/src/3rdparty/assimp/code/Hash.h
@@ -0,0 +1,108 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef AI_HASH_H_INCLUDED
+#define AI_HASH_H_INCLUDED
+
+// ------------------------------------------------------------------------------------------------
+// hashing function taken from
+// http://www.azillionmonkeys.com/qed/hash.html
+// (incremental version of the hashing function)
+// (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 unsigned int SuperFastHash (const char * data, unsigned int len = 0, unsigned int hash = 0) {
+unsigned int tmp;
+int rem;
+
+ if (!data) return 0;
+ if (!len)len = (unsigned int)::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/src/3rdparty/assimp/code/IFF.h b/src/3rdparty/assimp/code/IFF.h
new file mode 100644
index 000000000..91d56b347
--- /dev/null
+++ b/src/3rdparty/assimp/code/IFF.h
@@ -0,0 +1,102 @@
+
+
+// Definitions for the Interchange File Format (IFF)
+// Alexander Gessler, 2006
+// Adapted to Assimp August 2008
+
+#ifndef AI_IFF_H_INCLUDED
+#define AI_IFF_H_INCLUDED
+
+#include "ByteSwap.h"
+
+namespace Assimp {
+namespace IFF {
+
+#include "./../include/Compiler/pushpack1.h"
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Describes an IFF chunk header
+/////////////////////////////////////////////////////////////////////////////////
+struct ChunkHeader
+{
+ //! Type of the chunk header - FourCC
+ uint32_t type;
+
+ //! Length of the chunk data, in bytes
+ uint32_t length;
+} PACK_STRUCT;
+
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Describes an IFF sub chunk header
+/////////////////////////////////////////////////////////////////////////////////
+struct SubChunkHeader
+{
+ //! Type of the chunk header - FourCC
+ uint32_t type;
+
+ //! Length of the chunk data, in bytes
+ uint16_t length;
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+
+#define AI_IFF_FOURCC(a,b,c,d) ((uint32_t) (((uint8_t)a << 24u) | \
+ ((uint8_t)b << 16u) | ((uint8_t)c << 8u) | ((uint8_t)d)))
+
+
+#define AI_IFF_FOURCC_FORM AI_IFF_FOURCC('F','O','R','M')
+
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Load a chunk header
+//! @param outFile Pointer to the file data - points to the chunk data afterwards
+//! @return Pointer to the chunk header
+/////////////////////////////////////////////////////////////////////////////////
+inline ChunkHeader* LoadChunk(uint8_t*& outFile)
+{
+ ChunkHeader* head = (ChunkHeader*) outFile;
+ AI_LSWAP4(head->length);
+ AI_LSWAP4(head->type);
+ outFile += sizeof(ChunkHeader);
+ return head;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Load a sub chunk header
+//! @param outFile Pointer to the file data - points to the chunk data afterwards
+//! @return Pointer to the sub chunk header
+/////////////////////////////////////////////////////////////////////////////////
+inline SubChunkHeader* LoadSubChunk(uint8_t*& outFile)
+{
+ SubChunkHeader* head = (SubChunkHeader*) outFile;
+ AI_LSWAP2(head->length);
+ AI_LSWAP4(head->type);
+ outFile += sizeof(SubChunkHeader);
+ return head;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Read the file header and return the type of the file and its size
+//! @param outFile Pointer to the file data. The buffer must at
+//! least be 12 bytes large.
+//! @param fileType Receives the type of the file
+//! @return 0 if everything was OK, otherwise an error message
+/////////////////////////////////////////////////////////////////////////////////
+inline const char* ReadHeader(uint8_t* outFile,uint32_t& fileType)
+{
+ ChunkHeader* head = LoadChunk(outFile);
+ if (AI_IFF_FOURCC_FORM != head->type)
+ {
+ return "The file is not an IFF file: FORM chunk is missing";
+ }
+ fileType = *((uint32_t*)(head+1));
+ AI_LSWAP4(fileType);
+ return 0;
+}
+
+
+}}
+
+#endif // !! AI_IFF_H_INCLUDED
diff --git a/src/3rdparty/assimp/code/IRRLoader.cpp b/src/3rdparty/assimp/code/IRRLoader.cpp
new file mode 100644
index 000000000..c83c7876c
--- /dev/null
+++ b/src/3rdparty/assimp/code/IRRLoader.cpp
@@ -0,0 +1,1462 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file IRRLoader.cpp
+ * @brief Implementation of the Irr importer class
+ */
+
+#include "AssimpPCH.h"
+
+#include "IRRLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+#include "GenericProperty.h"
+
+#include "SceneCombiner.h"
+#include "StandardShapes.h"
+
+
+// We need boost::common_factor to compute the lcm/gcd of a number
+#include <boost/math/common_factor_rt.hpp>
+
+using namespace Assimp;
+using namespace irr;
+using namespace irr::io;
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+IRRImporter::IRRImporter() : fps(24.0), configSpeedFlag(false)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+IRRImporter::~IRRImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool IRRImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ /* NOTE: A simple check for the file extension is not enough
+ * here. Irrmesh and irr are easy, but xml is too generic
+ * and could be collada, too. So we need to open the file and
+ * search for typical tokens.
+ */
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "irr")return true;
+ else if (extension == "xml" || checkSig)
+ {
+ /* If CanRead() is called in order to check whether we
+ * support a specific file extension in general pIOHandler
+ * might be NULL and it's our duty to return true here.
+ */
+ if (!pIOHandler)return true;
+ const char* tokens[] = {"irr_scene"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("irr");
+ extensions.insert("xml");
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::SetupProperties(const Importer* pImp)
+{
+ // read the output frame rate of all node animation channels
+ fps = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_IRR_ANIM_FPS,100);
+ if (fps < 10.) {
+ DefaultLogger::get()->error("IRR: Invalid FPS configuration");
+ fps = 100;
+ }
+
+ // AI_CONFIG_FAVOUR_SPEED
+ configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a mesh tha consists of a single squad (a side of a skybox)
+aiMesh* IRRImporter::BuildSingleQuadMesh(const SkyboxVertex& v1,
+ const SkyboxVertex& v2,
+ const SkyboxVertex& v3,
+ const SkyboxVertex& v4)
+{
+ // allocate and prepare the mesh
+ aiMesh* out = new aiMesh();
+
+ out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
+ out->mNumFaces = 1;
+
+ // build the face
+ out->mFaces = new aiFace[1];
+ aiFace& face = out->mFaces[0];
+
+ face.mNumIndices = 4;
+ face.mIndices = new unsigned int[4];
+ for (unsigned int i = 0; i < 4;++i)
+ face.mIndices[i] = i;
+
+ out->mNumVertices = 4;
+
+ // copy vertex positions
+ aiVector3D* vec = out->mVertices = new aiVector3D[4];
+ *vec++ = v1.position;
+ *vec++ = v2.position;
+ *vec++ = v3.position;
+ *vec = v4.position;
+
+ // copy vertex normals
+ vec = out->mNormals = new aiVector3D[4];
+ *vec++ = v1.normal;
+ *vec++ = v2.normal;
+ *vec++ = v3.normal;
+ *vec = v4.normal;
+
+ // copy texture coordinates
+ vec = out->mTextureCoords[0] = new aiVector3D[4];
+ *vec++ = v1.uv;
+ *vec++ = v2.uv;
+ *vec++ = v3.uv;
+ *vec = v4.uv;
+ return out;
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::BuildSkybox(std::vector<aiMesh*>& meshes, std::vector<aiMaterial*> materials)
+{
+ // Update the material of the skybox - replace the name and disable shading for skyboxes.
+ for (unsigned int i = 0; i < 6;++i) {
+ MaterialHelper* out = ( MaterialHelper* ) (*(materials.end()-(6-i)));
+
+ aiString s;
+ s.length = ::sprintf( s.data, "SkyboxSide_%i",i );
+ out->AddProperty(&s,AI_MATKEY_NAME);
+
+ int shading = aiShadingMode_NoShading;
+ out->AddProperty(&shading,1,AI_MATKEY_SHADING_MODEL);
+ }
+
+ // Skyboxes are much more difficult. They are represented
+ // by six single planes with different textures, so we'll
+ // need to build six meshes.
+
+ const float l = 10.f; // the size used by Irrlicht
+
+ // FRONT SIDE
+ meshes.push_back( BuildSingleQuadMesh(
+ SkyboxVertex(-l,-l,-l, 0, 0, 1, 1.f,1.f),
+ SkyboxVertex( l,-l,-l, 0, 0, 1, 0.f,1.f),
+ SkyboxVertex( l, l,-l, 0, 0, 1, 0.f,0.f),
+ SkyboxVertex(-l, l,-l, 0, 0, 1, 1.f,0.f)) );
+ meshes.back()->mMaterialIndex = materials.size()-6u;
+
+ // LEFT SIDE
+ meshes.push_back( BuildSingleQuadMesh(
+ SkyboxVertex( l,-l,-l, -1, 0, 0, 1.f,1.f),
+ SkyboxVertex( l,-l, l, -1, 0, 0, 0.f,1.f),
+ SkyboxVertex( l, l, l, -1, 0, 0, 0.f,0.f),
+ SkyboxVertex( l, l,-l, -1, 0, 0, 1.f,0.f)) );
+ meshes.back()->mMaterialIndex = materials.size()-5u;
+
+ // BACK SIDE
+ meshes.push_back( BuildSingleQuadMesh(
+ SkyboxVertex( l,-l, l, 0, 0, -1, 1.f,1.f),
+ SkyboxVertex(-l,-l, l, 0, 0, -1, 0.f,1.f),
+ SkyboxVertex(-l, l, l, 0, 0, -1, 0.f,0.f),
+ SkyboxVertex( l, l, l, 0, 0, -1, 1.f,0.f)) );
+ meshes.back()->mMaterialIndex = materials.size()-4u;
+
+ // RIGHT SIDE
+ meshes.push_back( BuildSingleQuadMesh(
+ SkyboxVertex(-l,-l, l, 1, 0, 0, 1.f,1.f),
+ SkyboxVertex(-l,-l,-l, 1, 0, 0, 0.f,1.f),
+ SkyboxVertex(-l, l,-l, 1, 0, 0, 0.f,0.f),
+ SkyboxVertex(-l, l, l, 1, 0, 0, 1.f,0.f)) );
+ meshes.back()->mMaterialIndex = materials.size()-3u;
+
+ // TOP SIDE
+ meshes.push_back( BuildSingleQuadMesh(
+ SkyboxVertex( l, l,-l, 0, -1, 0, 1.f,1.f),
+ SkyboxVertex( l, l, l, 0, -1, 0, 0.f,1.f),
+ SkyboxVertex(-l, l, l, 0, -1, 0, 0.f,0.f),
+ SkyboxVertex(-l, l,-l, 0, -1, 0, 1.f,0.f)) );
+ meshes.back()->mMaterialIndex = materials.size()-2u;
+
+ // BOTTOM SIDE
+ meshes.push_back( BuildSingleQuadMesh(
+ SkyboxVertex( l,-l, l, 0, 1, 0, 0.f,0.f),
+ SkyboxVertex( l,-l,-l, 0, 1, 0, 1.f,0.f),
+ SkyboxVertex(-l,-l,-l, 0, 1, 0, 1.f,1.f),
+ SkyboxVertex(-l,-l, l, 0, 1, 0, 0.f,1.f)) );
+ meshes.back()->mMaterialIndex = materials.size()-1u;
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::CopyMaterial(std::vector<aiMaterial*>& materials,
+ std::vector< std::pair<aiMaterial*, unsigned int> >& inmaterials,
+ unsigned int& defMatIdx,
+ aiMesh* mesh)
+{
+ if (inmaterials.empty()) {
+ // Do we have a default material? If not we need to create one
+ if (0xffffffff == defMatIdx)
+ {
+ defMatIdx = (unsigned int)materials.size();
+ MaterialHelper* mat = new MaterialHelper();
+
+ aiString s;
+ s.Set(AI_DEFAULT_MATERIAL_NAME);
+ mat->AddProperty(&s,AI_MATKEY_NAME);
+
+ aiColor3D c(0.6f,0.6f,0.6f);
+ mat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE);
+ }
+ mesh->mMaterialIndex = defMatIdx;
+ return;
+ }
+ else if (inmaterials.size() > 1) {
+ DefaultLogger::get()->info("IRR: Skipping additional materials");
+ }
+
+ mesh->mMaterialIndex = (unsigned int)materials.size();
+ materials.push_back(inmaterials[0].first);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+inline int ClampSpline(int idx, int size)
+{
+ return ( idx<0 ? size+idx : ( idx>=size ? idx-size : idx ) );
+}
+
+// ------------------------------------------------------------------------------------------------
+inline void FindSuitableMultiple(int& angle)
+{
+ if (angle < 3)angle = 3;
+ else if (angle < 10) angle = 10;
+ else if (angle < 20) angle = 20;
+ else if (angle < 30) angle = 30;
+ else
+ {
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::ComputeAnimations(Node* root, aiNode* real, std::vector<aiNodeAnim*>& anims)
+{
+ ai_assert(NULL != root && NULL != real);
+
+ // XXX totally WIP - doesn't produce proper results, need to evaluate
+ // whether there's any use for Irrlicht's proprietary scene format
+ // outside Irrlicht ...
+
+ if (root->animators.empty()) {
+ return;
+ }
+ unsigned int total = 0;
+ for (std::list<Animator>::iterator it = root->animators.begin();it != root->animators.end(); ++it) {
+ if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) {
+ DefaultLogger::get()->warn("IRR: Skipping unknown or unsupported animator");
+ continue;
+ }
+ ++total;
+ }
+ if (!total)return;
+ else if (1 == total) {
+ DefaultLogger::get()->warn("IRR: Adding dummy nodes to simulate multiple animators");
+ }
+
+ // NOTE: 1 tick == i millisecond
+
+ unsigned int cur = 0;
+ for (std::list<Animator>::iterator it = root->animators.begin();
+ it != root->animators.end(); ++it)
+ {
+ if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER)continue;
+
+ Animator& in = *it ;
+ aiNodeAnim* anim = new aiNodeAnim();
+
+ if (cur != total-1) {
+ // Build a new name - a prefix instead of a suffix because it is
+ // easier to check against
+ anim->mNodeName.length = ::sprintf(anim->mNodeName.data,
+ "$INST_DUMMY_%i_%s",total-1,
+ (root->name.length() ? root->name.c_str() : ""));
+
+ // we'll also need to insert a dummy in the node hierarchy.
+ aiNode* dummy = new aiNode();
+
+ for (unsigned int i = 0; i < real->mParent->mNumChildren;++i)
+ if (real->mParent->mChildren[i] == real)
+ real->mParent->mChildren[i] = dummy;
+
+ dummy->mParent = real->mParent;
+ dummy->mName = anim->mNodeName;
+
+ dummy->mNumChildren = 1;
+ dummy->mChildren = new aiNode*[dummy->mNumChildren];
+ dummy->mChildren[0] = real;
+
+ // the transformation matrix of the dummy node is the identity
+
+ real->mParent = dummy;
+ }
+ else anim->mNodeName.Set(root->name);
+ ++cur;
+
+ switch (in.type) {
+ case Animator::ROTATION:
+ {
+ // -----------------------------------------------------
+ // find out how long a full rotation will take
+ // This is the least common multiple of 360.f and all
+ // three euler angles. Although we'll surely find a
+ // possible multiple (haha) it could be somewhat large
+ // for our purposes. So we need to modify the angles
+ // here in order to get good results.
+ // -----------------------------------------------------
+ int angles[3];
+ angles[0] = (int)(in.direction.x*100);
+ angles[1] = (int)(in.direction.y*100);
+ angles[2] = (int)(in.direction.z*100);
+
+ angles[0] %= 360;
+ angles[1] %= 360;
+ angles[2] %= 360;
+
+ if ((angles[0]*angles[1]) && (angles[1]*angles[2]))
+ {
+ FindSuitableMultiple(angles[0]);
+ FindSuitableMultiple(angles[1]);
+ FindSuitableMultiple(angles[2]);
+ }
+
+ int lcm = 360;
+
+ if (angles[0])
+ lcm = boost::math::lcm(lcm,angles[0]);
+
+ if (angles[1])
+ lcm = boost::math::lcm(lcm,angles[1]);
+
+ if (angles[2])
+ lcm = boost::math::lcm(lcm,angles[2]);
+
+ if (360 == lcm)
+ break;
+
+#if 0
+ // This can be a division through zero, but we don't care
+ float f1 = (float)lcm / angles[0];
+ float f2 = (float)lcm / angles[1];
+ float f3 = (float)lcm / angles[2];
+#endif
+
+ // find out how many time units we'll need for the finest
+ // track (in seconds) - this defines the number of output
+ // keys (fps * seconds)
+ float max = 0.f;
+ if (angles[0])
+ max = (float)lcm / angles[0];
+ if (angles[1])
+ max = std::max(max, (float)lcm / angles[1]);
+ if (angles[2])
+ max = std::max(max, (float)lcm / angles[2]);
+
+ anim->mNumRotationKeys = (unsigned int)(max*fps);
+ anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
+
+ // begin with a zero angle
+ aiVector3D angle;
+ for (unsigned int i = 0; i < anim->mNumRotationKeys;++i)
+ {
+ // build the quaternion for the given euler angles
+ aiQuatKey& q = anim->mRotationKeys[i];
+
+ q.mValue = aiQuaternion(angle.x, angle.y, angle.z);
+ q.mTime = (double)i;
+
+ // increase the angle
+ angle += in.direction;
+ }
+
+ // This animation is repeated and repeated ...
+ anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
+ }
+ break;
+
+ case Animator::FLY_CIRCLE:
+ {
+ // -----------------------------------------------------
+ // Find out how much time we'll need to perform a
+ // full circle.
+ // -----------------------------------------------------
+ const double seconds = (1. / in.speed) / 1000.;
+ const double tdelta = 1000. / fps;
+
+ anim->mNumPositionKeys = (unsigned int) (fps * seconds);
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+ // from Irrlicht, what else should we do than copying it?
+ aiVector3D vecU,vecV;
+ if (in.direction.y) {
+ vecV = aiVector3D(50,0,0) ^ in.direction;
+ }
+ else vecV = aiVector3D(0,50,00) ^ in.direction;
+ vecV.Normalize();
+ vecU = (vecV ^ in.direction).Normalize();
+
+ // build the output keys
+ for (unsigned int i = 0; i < anim->mNumPositionKeys;++i) {
+ aiVectorKey& key = anim->mPositionKeys[i];
+ key.mTime = i * tdelta;
+
+ const float t = (float) ( in.speed * key.mTime );
+ key.mValue = in.circleCenter + in.circleRadius * ((vecU*::cos(t)) + (vecV*::sin(t)));
+ }
+
+ // This animation is repeated and repeated ...
+ anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
+ }
+ break;
+
+ case Animator::FLY_STRAIGHT:
+ {
+ anim->mPostState = anim->mPreState = (in.loop ? aiAnimBehaviour_REPEAT : aiAnimBehaviour_CONSTANT);
+ const double seconds = in.timeForWay / 1000.;
+ const double tdelta = 1000. / fps;
+
+ anim->mNumPositionKeys = (unsigned int) (fps * seconds);
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+ aiVector3D diff = in.direction - in.circleCenter;
+ const float lengthOfWay = diff.Length();
+ diff.Normalize();
+
+ const double timeFactor = lengthOfWay / in.timeForWay;
+
+ // build the output keys
+ for (unsigned int i = 0; i < anim->mNumPositionKeys;++i) {
+ aiVectorKey& key = anim->mPositionKeys[i];
+ key.mTime = i * tdelta;
+ key.mValue = in.circleCenter + diff * float(timeFactor * key.mTime);
+ }
+ }
+ break;
+
+ case Animator::FOLLOW_SPLINE:
+ {
+ // repeat outside the defined time range
+ anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
+ const int size = (int)in.splineKeys.size();
+ if (!size) {
+ // We have no point in the spline. That's bad. Really bad.
+ DefaultLogger::get()->warn("IRR: Spline animators with no points defined");
+
+ delete anim;anim = NULL;
+ break;
+ }
+ else if (size == 1) {
+ // We have just one point in the spline so we don't need the full calculation
+ anim->mNumPositionKeys = 1;
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+ anim->mPositionKeys[0].mValue = in.splineKeys[0].mValue;
+ anim->mPositionKeys[0].mTime = 0.f;
+ break;
+ }
+
+ unsigned int ticksPerFull = 15;
+ anim->mNumPositionKeys = (unsigned int) ( ticksPerFull * fps );
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+ for (unsigned int i = 0; i < anim->mNumPositionKeys;++i)
+ {
+ aiVectorKey& key = anim->mPositionKeys[i];
+
+ const float dt = (i * in.speed * 0.001f );
+ const float u = dt - floor(dt);
+ const int idx = (int)floor(dt) % size;
+
+ // get the 4 current points to evaluate the spline
+ const aiVector3D& p0 = in.splineKeys[ ClampSpline( idx - 1, size ) ].mValue;
+ const aiVector3D& p1 = in.splineKeys[ ClampSpline( idx + 0, size ) ].mValue;
+ const aiVector3D& p2 = in.splineKeys[ ClampSpline( idx + 1, size ) ].mValue;
+ const aiVector3D& p3 = in.splineKeys[ ClampSpline( idx + 2, size ) ].mValue;
+
+ // compute polynomials
+ const float u2 = u*u;
+ const float u3 = u2*2;
+
+ const float h1 = 2.0f * u3 - 3.0f * u2 + 1.0f;
+ const float h2 = -2.0f * u3 + 3.0f * u3;
+ const float h3 = u3 - 2.0f * u3;
+ const float h4 = u3 - u2;
+
+ // compute the spline tangents
+ const aiVector3D t1 = ( p2 - p0 ) * in.tightness;
+ aiVector3D t2 = ( p3 - p1 ) * in.tightness;
+
+ // and use them to get the interpolated point
+ t2 = (h1 * p1 + p2 * h2 + t1 * h3 + h4 * t2);
+
+ // build a simple translation matrix from it
+ key.mValue = t2.x;
+ key.mTime = (double) i;
+ }
+ }
+ break;
+ default:
+ // UNKNOWN , OTHER
+ break;
+ };
+ if (anim) {
+ anims.push_back(anim);
+ ++total;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// This function is maybe more generic than we'd need it here
+void SetupMapping (MaterialHelper* mat, aiTextureMapping mode, const aiVector3D& axis = aiVector3D(0.f,0.f,-1.f))
+{
+ // Check whether there are texture properties defined - setup
+ // the desired texture mapping mode for all of them and ignore
+ // all UV settings we might encounter. WE HAVE NO UVS!
+
+ std::vector<aiMaterialProperty*> p;
+ p.reserve(mat->mNumProperties+1);
+
+ for (unsigned int i = 0; i < mat->mNumProperties;++i)
+ {
+ aiMaterialProperty* prop = mat->mProperties[i];
+ if (!::strcmp( prop->mKey.data, "$tex.file")) {
+ // Setup the mapping key
+ aiMaterialProperty* m = new aiMaterialProperty();
+ m->mKey.Set("$tex.mapping");
+ m->mIndex = prop->mIndex;
+ m->mSemantic = prop->mSemantic;
+ m->mType = aiPTI_Integer;
+
+ m->mDataLength = 4;
+ m->mData = new char[4];
+ *((int*)m->mData) = mode;
+
+ p.push_back(prop);
+ p.push_back(m);
+
+ // Setup the mapping axis
+ if (mode == aiTextureMapping_CYLINDER || mode == aiTextureMapping_PLANE || mode == aiTextureMapping_SPHERE) {
+ m = new aiMaterialProperty();
+ m->mKey.Set("$tex.mapaxis");
+ m->mIndex = prop->mIndex;
+ m->mSemantic = prop->mSemantic;
+ m->mType = aiPTI_Float;
+
+ m->mDataLength = 12;
+ m->mData = new char[12];
+ *((aiVector3D*)m->mData) = axis;
+ p.push_back(m);
+ }
+ }
+ else if (! ::strcmp( prop->mKey.data, "$tex.uvwsrc")) {
+ delete mat->mProperties[i];
+ }
+ else p.push_back(prop);
+ }
+
+ if (p.empty())return;
+
+ // rebuild the output array
+ if (p.size() > mat->mNumAllocated) {
+ delete[] mat->mProperties;
+ mat->mProperties = new aiMaterialProperty*[p.size()*2];
+
+ mat->mNumAllocated = p.size()*2;
+ }
+ mat->mNumProperties = (unsigned int)p.size();
+ ::memcpy(mat->mProperties,&p[0],sizeof(void*)*mat->mNumProperties);
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
+ BatchLoader& batch,
+ std::vector<aiMesh*>& meshes,
+ std::vector<aiNodeAnim*>& anims,
+ std::vector<AttachmentInfo>& attach,
+ std::vector<aiMaterial*>& materials,
+ unsigned int& defMatIdx)
+{
+ unsigned int oldMeshSize = (unsigned int)meshes.size();
+ //unsigned int meshTrafoAssign = 0;
+
+ // Now determine the type of the node
+ switch (root->type)
+ {
+ case Node::ANIMMESH:
+ case Node::MESH:
+ {
+ if (!root->meshPath.length())
+ break;
+
+ // Get the loaded mesh from the scene and add it to
+ // the list of all scenes to be attached to the
+ // graph we're currently building
+ aiScene* scene = batch.GetImport(root->id);
+ if (!scene) {
+ DefaultLogger::get()->error("IRR: Unable to load external file: " + root->meshPath);
+ break;
+ }
+ attach.push_back(AttachmentInfo(scene,rootOut));
+
+ // Now combine the material we've loaded for this mesh
+ // with the real materials we got from the file. As we
+ // don't execute any pp-steps on the file, the numbers
+ // should be equal. If they are not, we can impossibly
+ // do this ...
+ if (root->materials.size() != (unsigned int)scene->mNumMaterials) {
+ DefaultLogger::get()->warn("IRR: Failed to match imported materials "
+ "with the materials found in the IRR scene file");
+
+ break;
+ }
+ for (unsigned int i = 0; i < scene->mNumMaterials;++i) {
+ // Delete the old material, we don't need it anymore
+ delete scene->mMaterials[i];
+
+ std::pair<aiMaterial*, unsigned int>& src = root->materials[i];
+ scene->mMaterials[i] = src.first;
+ }
+
+ // NOTE: Each mesh should have exactly one material assigned,
+ // but we do it in a separate loop if this behaviour changes
+ // in future.
+ for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
+ // Process material flags
+ aiMesh* mesh = scene->mMeshes[i];
+
+
+ // If "trans_vertex_alpha" mode is enabled, search all vertex colors
+ // and check whether they have a common alpha value. This is quite
+ // often the case so we can simply extract it to a shared oacity
+ // value.
+ std::pair<aiMaterial*, unsigned int>& src = root->materials[mesh->mMaterialIndex];
+ MaterialHelper* mat = (MaterialHelper*)src.first;
+
+ if (mesh->HasVertexColors(0) && src.second & AI_IRRMESH_MAT_trans_vertex_alpha)
+ {
+ bool bdo = true;
+ for (unsigned int a = 1; a < mesh->mNumVertices;++a) {
+
+ if (mesh->mColors[0][a].a != mesh->mColors[0][a-1].a) {
+ bdo = false;
+ break;
+ }
+ }
+ if (bdo) {
+ DefaultLogger::get()->info("IRR: Replacing mesh vertex alpha with common opacity");
+
+ for (unsigned int a = 0; a < mesh->mNumVertices;++a)
+ mesh->mColors[0][a].a = 1.f;
+
+ mat->AddProperty(& mesh->mColors[0][0].a, 1, AI_MATKEY_OPACITY);
+ }
+ }
+
+ // If we have a second texture coordinate set and a second texture
+ // (either lightmap, normalmap, 2layered material) we need to
+ // setup the correct UV index for it. The texture can either
+ // be diffuse (lightmap & 2layer) or a normal map (normal & parallax)
+ if (mesh->HasTextureCoords(1)) {
+
+ int idx = 1;
+ if (src.second & (AI_IRRMESH_MAT_solid_2layer | AI_IRRMESH_MAT_lightmap)) {
+ mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_DIFFUSE(0));
+ }
+ else if (src.second & AI_IRRMESH_MAT_normalmap_solid) {
+ mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_NORMALS(0));
+ }
+ }
+ }
+ }
+ break;
+
+ case Node::LIGHT:
+ case Node::CAMERA:
+
+ // We're already finished with lights and cameras
+ break;
+
+
+ case Node::SPHERE:
+ {
+ // Generate the sphere model. Our input parameter to
+ // the sphere generation algorithm is the number of
+ // subdivisions of each triangle - but here we have
+ // the number of poylgons on a specific axis. Just
+ // use some hardcoded limits to approximate this ...
+ unsigned int mul = root->spherePolyCountX*root->spherePolyCountY;
+ if (mul < 100)mul = 2;
+ else if (mul < 300)mul = 3;
+ else mul = 4;
+
+ meshes.push_back(StandardShapes::MakeMesh(mul,
+ &StandardShapes::MakeSphere));
+
+ // Adjust scaling
+ root->scaling *= root->sphereRadius/2;
+
+ // Copy one output material
+ CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
+
+ // Now adjust this output material - if there is a first texture
+ // set, setup spherical UV mapping around the Y axis.
+ SetupMapping ( (MaterialHelper*) materials.back(), aiTextureMapping_SPHERE);
+ }
+ break;
+
+ case Node::CUBE:
+ {
+ // Generate an unit cube first
+ meshes.push_back(StandardShapes::MakeMesh(
+ &StandardShapes::MakeHexahedron));
+
+ // Adjust scaling
+ root->scaling *= root->sphereRadius;
+
+ // Copy one output material
+ CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
+
+ // Now adjust this output material - if there is a first texture
+ // set, setup cubic UV mapping
+ SetupMapping ( (MaterialHelper*) materials.back(), aiTextureMapping_BOX );
+ }
+ break;
+
+
+ case Node::SKYBOX:
+ {
+ // A skybox is defined by six materials
+ if (root->materials.size() < 6) {
+ DefaultLogger::get()->error("IRR: There should be six materials for a skybox");
+ break;
+ }
+
+ // copy those materials and generate 6 meshes for our new skybox
+ materials.reserve(materials.size() + 6);
+ for (unsigned int i = 0; i < 6;++i)
+ materials.insert(materials.end(),root->materials[i].first);
+
+ BuildSkybox(meshes,materials);
+
+ // *************************************************************
+ // Skyboxes will require a different code path for rendering,
+ // so there must be a way for the user to add special support
+ // for IRR skyboxes. We add a 'IRR.SkyBox_' prefix to the node.
+ // *************************************************************
+ root->name = "IRR.SkyBox_" + root->name;
+ DefaultLogger::get()->info("IRR: Loading skybox, this will "
+ "require special handling to be displayed correctly");
+ }
+ break;
+
+ case Node::TERRAIN:
+ {
+ // to support terrains, we'd need to have a texture decoder
+ DefaultLogger::get()->error("IRR: Unsupported node - TERRAIN");
+ }
+ break;
+ default:
+ // DUMMY
+ break;
+ };
+
+ // Check whether we added a mesh (or more than one ...). In this case
+ // we'll also need to attach it to the node
+ if (oldMeshSize != (unsigned int) meshes.size()) {
+
+ rootOut->mNumMeshes = (unsigned int)meshes.size() - oldMeshSize;
+ rootOut->mMeshes = new unsigned int[rootOut->mNumMeshes];
+
+ for (unsigned int a = 0; a < rootOut->mNumMeshes;++a) {
+ rootOut->mMeshes[a] = oldMeshSize+a;
+ }
+ }
+
+ // Setup the name of this node
+ rootOut->mName.Set(root->name);
+
+ // Now compute the final local transformation matrix of the
+ // node from the given translation, rotation and scaling values.
+ // (the rotation is given in Euler angles, XYZ order)
+ //std::swap((float&)root->rotation.z,(float&)root->rotation.y);
+ rootOut->mTransformation.FromEulerAnglesXYZ(AI_DEG_TO_RAD(root->rotation) );
+
+ // apply scaling
+ aiMatrix4x4& mat = rootOut->mTransformation;
+ mat.a1 *= root->scaling.x;
+ mat.b1 *= root->scaling.x;
+ mat.c1 *= root->scaling.x;
+ mat.a2 *= root->scaling.y;
+ mat.b2 *= root->scaling.y;
+ mat.c2 *= root->scaling.y;
+ mat.a3 *= root->scaling.z;
+ mat.b3 *= root->scaling.z;
+ mat.c3 *= root->scaling.z;
+
+ // apply translation
+ mat.a4 += root->position.x;
+ mat.b4 += root->position.y;
+ mat.c4 += root->position.z;
+
+ // now compute animations for the node
+ ComputeAnimations(root,rootOut, anims);
+
+ // Add all children recursively. First allocate enough storage
+ // for them, then call us again
+ rootOut->mNumChildren = (unsigned int)root->children.size();
+ if (rootOut->mNumChildren) {
+
+ rootOut->mChildren = new aiNode*[rootOut->mNumChildren];
+ for (unsigned int i = 0; i < rootOut->mNumChildren;++i) {
+
+ aiNode* node = rootOut->mChildren[i] = new aiNode();
+ node->mParent = rootOut;
+ GenerateGraph(root->children[i],node,scene,batch,meshes,
+ anims,attach,materials,defMatIdx);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void IRRImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open IRR file " + pFile + "");
+
+ // Construct the irrXML parser
+ CIrrXML_IOStreamReader st(file.get());
+ reader = createIrrXMLReader((IFileReadCallBack*) &st);
+
+ // The root node of the scene
+ Node* root = new Node(Node::DUMMY);
+ root->parent = NULL;
+ root->name = "<IRRSceneRoot>";
+
+ // Current node parent
+ Node* curParent = root;
+
+ // Scenegraph node we're currently working on
+ Node* curNode = NULL;
+
+ // List of output cameras
+ std::vector<aiCamera*> cameras;
+
+ // List of output lights
+ std::vector<aiLight*> lights;
+
+ // Batch loader used to load external models
+ BatchLoader batch(pIOHandler);
+// batch.SetBasePath(pFile);
+
+ cameras.reserve(5);
+ lights.reserve(5);
+
+ bool inMaterials = false, inAnimator = false;
+ unsigned int guessedAnimCnt = 0, guessedMeshCnt = 0, guessedMatCnt = 0;
+
+ // Parse the XML file
+ while (reader->read()) {
+ switch (reader->getNodeType()) {
+ case EXN_ELEMENT:
+
+ if (!ASSIMP_stricmp(reader->getNodeName(),"node")) {
+ // ***********************************************************************
+ /* What we're going to do with the node depends
+ * on its type:
+ *
+ * "mesh" - Load a mesh from an external file
+ * "cube" - Generate a cube
+ * "skybox" - Generate a skybox
+ * "light" - A light source
+ * "sphere" - Generate a sphere mesh
+ * "animatedMesh" - Load an animated mesh from an external file
+ * and join its animation channels with ours.
+ * "empty" - A dummy node
+ * "camera" - A camera
+ * "terrain" - a terrain node (data comes from a heightmap)
+ * "billboard", ""
+ *
+ * Each of these nodes can be animated and all can have multiple
+ * materials assigned (except lights, cameras and dummies, of course).
+ */
+ // ***********************************************************************
+ const char* sz = reader->getAttributeValueSafe("type");
+ Node* nd;
+ if (!ASSIMP_stricmp(sz,"mesh") || !ASSIMP_stricmp(sz,"octTree")) {
+ // OctTree's and meshes are treated equally
+ nd = new Node(Node::MESH);
+ }
+ else if (!ASSIMP_stricmp(sz,"cube")) {
+ nd = new Node(Node::CUBE);
+ ++guessedMeshCnt;
+ // meshes.push_back(StandardShapes::MakeMesh(&StandardShapes::MakeHexahedron));
+ }
+ else if (!ASSIMP_stricmp(sz,"skybox")) {
+ nd = new Node(Node::SKYBOX);
+ guessedMeshCnt += 6;
+ }
+ else if (!ASSIMP_stricmp(sz,"camera")) {
+ nd = new Node(Node::CAMERA);
+
+ // Setup a temporary name for the camera
+ aiCamera* cam = new aiCamera();
+ cam->mName.Set( nd->name );
+ cameras.push_back(cam);
+ }
+ else if (!ASSIMP_stricmp(sz,"light")) {
+ nd = new Node(Node::LIGHT);
+
+ // Setup a temporary name for the light
+ aiLight* cam = new aiLight();
+ cam->mName.Set( nd->name );
+ lights.push_back(cam);
+ }
+ else if (!ASSIMP_stricmp(sz,"sphere")) {
+ nd = new Node(Node::SPHERE);
+ ++guessedMeshCnt;
+ }
+ else if (!ASSIMP_stricmp(sz,"animatedMesh")) {
+ nd = new Node(Node::ANIMMESH);
+ }
+ else if (!ASSIMP_stricmp(sz,"empty")) {
+ nd = new Node(Node::DUMMY);
+ }
+ else if (!ASSIMP_stricmp(sz,"terrain")) {
+ nd = new Node(Node::TERRAIN);
+ }
+ else if (!ASSIMP_stricmp(sz,"billBoard")) {
+ // We don't support billboards, so ignore them
+ DefaultLogger::get()->error("IRR: Billboards are not supported by Assimp");
+ nd = new Node(Node::DUMMY);
+ }
+ else {
+ DefaultLogger::get()->warn("IRR: Found unknown node: " + std::string(sz));
+
+ /* We skip the contents of nodes we don't know.
+ * We parse the transformation and all animators
+ * and skip the rest.
+ */
+ nd = new Node(Node::DUMMY);
+ }
+
+ /* Attach the newly created node to the scenegraph
+ */
+ curNode = nd;
+ nd->parent = curParent;
+ curParent->children.push_back(nd);
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"materials")) {
+ inMaterials = true;
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"animators")) {
+ inAnimator = true;
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"attributes")) {
+ /* We should have a valid node here
+ * FIX: no ... the scene root node is also contained in an attributes block
+ */
+ if (!curNode) {
+#if 0
+ DefaultLogger::get()->error("IRR: Encountered <attributes> element, but "
+ "there is no node active");
+#endif
+ continue;
+ }
+
+ Animator* curAnim = NULL;
+
+ // Materials can occur for nearly any type of node
+ if (inMaterials && curNode->type != Node::DUMMY) {
+ /* This is a material description - parse it!
+ */
+ curNode->materials.push_back(std::pair< aiMaterial*, unsigned int > () );
+ std::pair< aiMaterial*, unsigned int >& p = curNode->materials.back();
+
+ p.first = ParseMaterial(p.second);
+
+ ++guessedMatCnt;
+ continue;
+ }
+ else if (inAnimator) {
+ /* This is an animation path - add a new animator
+ * to the list.
+ */
+ curNode->animators.push_back(Animator());
+ curAnim = & curNode->animators.back();
+
+ ++guessedAnimCnt;
+ }
+
+ /* Parse all elements in the attributes block
+ * and process them.
+ */
+ while (reader->read()) {
+ if (reader->getNodeType() == EXN_ELEMENT) {
+ if (!ASSIMP_stricmp(reader->getNodeName(),"vector3d")) {
+ VectorProperty prop;
+ ReadVectorProperty(prop);
+
+ if (inAnimator) {
+ if (curAnim->type == Animator::ROTATION && prop.name == "Rotation") {
+ // We store the rotation euler angles in 'direction'
+ curAnim->direction = prop.value;
+ }
+ else if (curAnim->type == Animator::FOLLOW_SPLINE) {
+ // Check whether the vector follows the PointN naming scheme,
+ // here N is the ONE-based index of the point
+ if (prop.name.length() >= 6 && prop.name.substr(0,5) == "Point") {
+ // Add a new key to the list
+ curAnim->splineKeys.push_back(aiVectorKey());
+ aiVectorKey& key = curAnim->splineKeys.back();
+
+ // and parse its properties
+ key.mValue = prop.value;
+ key.mTime = strtol10(&prop.name[5]);
+ }
+ }
+ else if (curAnim->type == Animator::FLY_CIRCLE) {
+ if (prop.name == "Center") {
+ curAnim->circleCenter = prop.value;
+ }
+ else if (prop.name == "Direction") {
+ curAnim->direction = prop.value;
+
+ // From Irrlicht's source - a workaround for backward compatibility with Irrlicht 1.1
+ if (curAnim->direction == aiVector3D()) {
+ curAnim->direction = aiVector3D(0.f,1.f,0.f);
+ }
+ else curAnim->direction.Normalize();
+ }
+ }
+ else if (curAnim->type == Animator::FLY_STRAIGHT) {
+ if (prop.name == "Start") {
+ // We reuse the field here
+ curAnim->circleCenter = prop.value;
+ }
+ else if (prop.name == "End") {
+ // We reuse the field here
+ curAnim->direction = prop.value;
+ }
+ }
+ }
+ else {
+ if (prop.name == "Position") {
+ curNode->position = prop.value;
+ }
+ else if (prop.name == "Rotation") {
+ curNode->rotation = prop.value;
+ }
+ else if (prop.name == "Scale") {
+ curNode->scaling = prop.value;
+ }
+ else if (Node::CAMERA == curNode->type)
+ {
+ aiCamera* cam = cameras.back();
+ if (prop.name == "Target") {
+ cam->mLookAt = prop.value;
+ }
+ else if (prop.name == "UpVector") {
+ cam->mUp = prop.value;
+ }
+ }
+ }
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"bool")) {
+ BoolProperty prop;
+ ReadBoolProperty(prop);
+
+ if (inAnimator && curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop") {
+ curAnim->loop = prop.value;
+ }
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"float")) {
+ FloatProperty prop;
+ ReadFloatProperty(prop);
+
+ if (inAnimator) {
+ // The speed property exists for several animators
+ if (prop.name == "Speed") {
+ curAnim->speed = prop.value;
+ }
+ else if (curAnim->type == Animator::FLY_CIRCLE && prop.name == "Radius") {
+ curAnim->circleRadius = prop.value;
+ }
+ else if (curAnim->type == Animator::FOLLOW_SPLINE && prop.name == "Tightness") {
+ curAnim->tightness = prop.value;
+ }
+ }
+ else {
+ if (prop.name == "FramesPerSecond" && Node::ANIMMESH == curNode->type) {
+ curNode->framesPerSecond = prop.value;
+ }
+ else if (Node::CAMERA == curNode->type) {
+ /* This is the vertical, not the horizontal FOV.
+ * We need to compute the right FOV from the
+ * screen aspect which we don't know yet.
+ */
+ if (prop.name == "Fovy") {
+ cameras.back()->mHorizontalFOV = prop.value;
+ }
+ else if (prop.name == "Aspect") {
+ cameras.back()->mAspect = prop.value;
+ }
+ else if (prop.name == "ZNear") {
+ cameras.back()->mClipPlaneNear = prop.value;
+ }
+ else if (prop.name == "ZFar") {
+ cameras.back()->mClipPlaneFar = prop.value;
+ }
+ }
+ else if (Node::LIGHT == curNode->type) {
+ /* Additional light information
+ */
+ if (prop.name == "Attenuation") {
+ lights.back()->mAttenuationLinear = prop.value;
+ }
+ else if (prop.name == "OuterCone") {
+ lights.back()->mAngleOuterCone = AI_DEG_TO_RAD( prop.value );
+ }
+ else if (prop.name == "InnerCone") {
+ lights.back()->mAngleInnerCone = AI_DEG_TO_RAD( prop.value );
+ }
+ }
+ // radius of the sphere to be generated -
+ // or alternatively, size of the cube
+ else if ((Node::SPHERE == curNode->type && prop.name == "Radius")
+ || (Node::CUBE == curNode->type && prop.name == "Size" )) {
+
+ curNode->sphereRadius = prop.value;
+ }
+ }
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"int")) {
+ IntProperty prop;
+ ReadIntProperty(prop);
+
+ if (inAnimator) {
+ if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") {
+ curAnim->timeForWay = prop.value;
+ }
+ }
+ else {
+ // sphere polgon numbers in each direction
+ if (Node::SPHERE == curNode->type) {
+
+ if (prop.name == "PolyCountX") {
+ curNode->spherePolyCountX = prop.value;
+ }
+ else if (prop.name == "PolyCountY") {
+ curNode->spherePolyCountY = prop.value;
+ }
+ }
+ }
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"string") ||!ASSIMP_stricmp(reader->getNodeName(),"enum")) {
+ StringProperty prop;
+ ReadStringProperty(prop);
+ if (prop.value.length()) {
+ if (prop.name == "Name") {
+ curNode->name = prop.value;
+
+ /* If we're either a camera or a light source
+ * we need to update the name in the aiLight/
+ * aiCamera structure, too.
+ */
+ if (Node::CAMERA == curNode->type) {
+ cameras.back()->mName.Set(prop.value);
+ }
+ else if (Node::LIGHT == curNode->type) {
+ lights.back()->mName.Set(prop.value);
+ }
+ }
+ else if (Node::LIGHT == curNode->type && "LightType" == prop.name)
+ {
+ if (prop.value == "Spot")
+ lights.back()->mType = aiLightSource_SPOT;
+ else if (prop.value == "Point")
+ lights.back()->mType = aiLightSource_POINT;
+ else if (prop.value == "Directional")
+ lights.back()->mType = aiLightSource_DIRECTIONAL;
+ else
+ {
+ // We won't pass the validation with aiLightSourceType_UNDEFINED,
+ // so we remove the light and replace it with a silly dummy node
+ delete lights.back();
+ lights.pop_back();
+ curNode->type = Node::DUMMY;
+
+ DefaultLogger::get()->error("Ignoring light of unknown type: " + prop.value);
+ }
+ }
+ else if ((prop.name == "Mesh" && Node::MESH == curNode->type) ||
+ Node::ANIMMESH == curNode->type)
+ {
+ /* This is the file name of the mesh - either
+ * animated or not. We need to make sure we setup
+ * the correct postprocessing settings here.
+ */
+ unsigned int pp = 0;
+ BatchLoader::PropertyMap map;
+
+ /* If the mesh is a static one remove all animations from the impor data
+ */
+ if (Node::ANIMMESH != curNode->type) {
+ pp |= aiProcess_RemoveComponent;
+ SetGenericProperty<int>(map.ints,AI_CONFIG_PP_RVC_FLAGS,
+ aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
+ }
+
+ /* TODO: maybe implement the protection against recursive
+ * loading calls directly in BatchLoader? The current
+ * implementation is not absolutely safe. A LWS and an IRR
+ * file referencing each other *could* cause the system to
+ * recurse forever.
+ */
+
+ const std::string extension = GetExtension(prop.value);
+ if ("irr" == extension) {
+ DefaultLogger::get()->error("IRR: Can't load another IRR file recursively");
+ }
+ else
+ {
+ curNode->id = batch.AddLoadRequest(prop.value,pp,&map);
+ curNode->meshPath = prop.value;
+ }
+ }
+ else if (inAnimator && prop.name == "Type")
+ {
+ // type of the animator
+ if (prop.value == "rotation") {
+ curAnim->type = Animator::ROTATION;
+ }
+ else if (prop.value == "flyCircle") {
+ curAnim->type = Animator::FLY_CIRCLE;
+ }
+ else if (prop.value == "flyStraight") {
+ curAnim->type = Animator::FLY_CIRCLE;
+ }
+ else if (prop.value == "followSpline") {
+ curAnim->type = Animator::FOLLOW_SPLINE;
+ }
+ else {
+ DefaultLogger::get()->warn("IRR: Ignoring unknown animator: "
+ + prop.value);
+
+ curAnim->type = Animator::UNKNOWN;
+ }
+ }
+ }
+ }
+ }
+ else if (reader->getNodeType() == EXN_ELEMENT_END && !ASSIMP_stricmp(reader->getNodeName(),"attributes")) {
+ break;
+ }
+ }
+ }
+ break;
+
+ case EXN_ELEMENT_END:
+
+ // If we reached the end of a node, we need to continue processing its parent
+ if (!ASSIMP_stricmp(reader->getNodeName(),"node")) {
+ if (!curNode) {
+ // currently is no node set. We need to go
+ // back in the node hierarchy
+ if (!curParent) {
+ curParent = root;
+ DefaultLogger::get()->error("IRR: Too many closing <node> elements");
+ }
+ else curParent = curParent->parent;
+ }
+ else curNode = NULL;
+ }
+ // clear all flags
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"materials")) {
+ inMaterials = false;
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"animators")) {
+ inAnimator = false;
+ }
+ break;
+
+ default:
+ // GCC complains that not all enumeration values are handled
+ break;
+ }
+ }
+
+ /* Now iterate through all cameras and compute their final (horizontal) FOV
+ */
+ for (std::vector<aiCamera*>::iterator it = cameras.begin(), end = cameras.end();it != end; ++it) {
+ aiCamera* cam = *it;
+
+ // screen aspect could be missing
+ if (cam->mAspect) {
+ cam->mHorizontalFOV *= cam->mAspect;
+ }
+ else DefaultLogger::get()->warn("IRR: Camera aspect is not given, can't compute horizontal FOV");
+ }
+
+ batch.LoadAll();
+
+ /* Allocate a tempoary scene data structure
+ */
+ aiScene* tempScene = new aiScene();
+ tempScene->mRootNode = new aiNode();
+ tempScene->mRootNode->mName.Set("<IRRRoot>");
+
+ /* Copy the cameras to the output array
+ */
+ if (!cameras.empty()) {
+ tempScene->mNumCameras = (unsigned int)cameras.size();
+ tempScene->mCameras = new aiCamera*[tempScene->mNumCameras];
+ ::memcpy(tempScene->mCameras,&cameras[0],sizeof(void*)*tempScene->mNumCameras);
+ }
+
+ /* Copy the light sources to the output array
+ */
+ if (!lights.empty()) {
+ tempScene->mNumLights = (unsigned int)lights.size();
+ tempScene->mLights = new aiLight*[tempScene->mNumLights];
+ ::memcpy(tempScene->mLights,&lights[0],sizeof(void*)*tempScene->mNumLights);
+ }
+
+ // temporary data
+ std::vector< aiNodeAnim*> anims;
+ std::vector< aiMaterial*> materials;
+ std::vector< AttachmentInfo > attach;
+ std::vector<aiMesh*> meshes;
+
+ // try to guess how much storage we'll need
+ anims.reserve (guessedAnimCnt + (guessedAnimCnt >> 2));
+ meshes.reserve (guessedMeshCnt + (guessedMeshCnt >> 2));
+ materials.reserve (guessedMatCnt + (guessedMatCnt >> 2));
+
+ /* Now process our scenegraph recursively: generate final
+ * meshes and generate animation channels for all nodes.
+ */
+ unsigned int defMatIdx = 0xffffffff;
+ GenerateGraph(root,tempScene->mRootNode, tempScene,
+ batch, meshes, anims, attach, materials, defMatIdx);
+
+ if (!anims.empty())
+ {
+ tempScene->mNumAnimations = 1;
+ tempScene->mAnimations = new aiAnimation*[tempScene->mNumAnimations];
+ aiAnimation* an = tempScene->mAnimations[0] = new aiAnimation();
+
+ // ***********************************************************
+ // This is only the global animation channel of the scene.
+ // If there are animated models, they will have separate
+ // animation channels in the scene. To display IRR scenes
+ // correctly, users will need to combine the global anim
+ // channel with all the local animations they want to play
+ // ***********************************************************
+ an->mName.Set("Irr_GlobalAnimChannel");
+
+ // copy all node animation channels to the global channel
+ an->mNumChannels = (unsigned int)anims.size();
+ an->mChannels = new aiNodeAnim*[an->mNumChannels];
+ ::memcpy(an->mChannels, & anims [0], sizeof(void*)*an->mNumChannels);
+ }
+ if (!meshes.empty()) {
+ // copy all meshes to the temporary scene
+ tempScene->mNumMeshes = (unsigned int)meshes.size();
+ tempScene->mMeshes = new aiMesh*[tempScene->mNumMeshes];
+ ::memcpy(tempScene->mMeshes,&meshes[0],tempScene->mNumMeshes*
+ sizeof(void*));
+ }
+
+ /* Copy all materials to the output array
+ */
+ if (!materials.empty()) {
+ tempScene->mNumMaterials = (unsigned int)materials.size();
+ tempScene->mMaterials = new aiMaterial*[tempScene->mNumMaterials];
+ ::memcpy(tempScene->mMaterials,&materials[0],sizeof(void*)*
+ tempScene->mNumMaterials);
+ }
+
+ /* Now merge all sub scenes and attach them to the correct
+ * attachment points in the scenegraph.
+ */
+ SceneCombiner::MergeScenes(&pScene,tempScene,attach,
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) : 0));
+
+
+ /* If we have no meshes | no materials now set the INCOMPLETE
+ * scene flag. This is necessary if we failed to load all
+ * models from external files
+ */
+ if (!pScene->mNumMeshes || !pScene->mNumMaterials) {
+ DefaultLogger::get()->warn("IRR: No meshes loaded, setting AI_SCENE_FLAGS_INCOMPLETE");
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ }
+
+ /* Finished ... everything destructs automatically and all
+ * temporary scenes have already been deleted by MergeScenes()
+ */
+ return;
+}
diff --git a/src/3rdparty/assimp/code/IRRLoader.h b/src/3rdparty/assimp/code/IRRLoader.h
new file mode 100644
index 000000000..b7b3ea642
--- /dev/null
+++ b/src/3rdparty/assimp/code/IRRLoader.h
@@ -0,0 +1,314 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file IRRLoader.h
+ * @brief Declaration of the .irrMesh (Irrlight Engine Mesh Format)
+ * importer class.
+ */
+#ifndef AI_IRRLOADER_H_INCLUDED
+#define AI_IRRLOADER_H_INCLUDED
+
+#include "IRRShared.h"
+#include "SceneCombiner.h"
+
+namespace Assimp {
+
+
+// ---------------------------------------------------------------------------
+/** Irr importer class.
+ *
+ * Irr is the native scene file format of the Irrlight engine and its editor
+ * irrEdit. As IrrEdit itself is capable of importing quite many file formats,
+ * it might be a good file format for data exchange.
+ */
+class IRRImporter : public BaseImporter, public IrrlichtBase
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ IRRImporter();
+
+ /** Destructor, private as well */
+ ~IRRImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /**
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /**
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /**
+ */
+ void SetupProperties(const Importer* pImp);
+
+private:
+
+ /** Data structure for a scenegraph node animator
+ */
+ struct Animator
+ {
+ // Type of the animator
+ enum AT
+ {
+ UNKNOWN = 0x0,
+ ROTATION = 0x1,
+ FLY_CIRCLE = 0x2,
+ FLY_STRAIGHT = 0x3,
+ FOLLOW_SPLINE = 0x4,
+ OTHER = 0x5
+
+ } type;
+
+ Animator(AT t = UNKNOWN)
+ : type (t)
+ , speed (0.001f)
+ , direction (0.f,1.f,0.f)
+ , circleRadius (1.f)
+ , tightness (0.5f)
+ , loop (true)
+ , timeForWay (100)
+ {
+ }
+
+
+ // common parameters
+ float speed;
+ aiVector3D direction;
+
+ // FLY_CIRCLE
+ aiVector3D circleCenter;
+ float circleRadius;
+
+ // FOLLOW_SPLINE
+ float tightness;
+ std::vector<aiVectorKey> splineKeys;
+
+ // ROTATION (angles given in direction)
+
+ // FLY STRAIGHT
+ // circleCenter = start, direction = end
+ bool loop;
+ int timeForWay;
+ };
+
+ /** Data structure for a scenegraph node in an IRR file
+ */
+ struct Node
+ {
+ // Type of the node
+ enum ET
+ {
+ LIGHT,
+ CUBE,
+ MESH,
+ SKYBOX,
+ DUMMY,
+ CAMERA,
+ TERRAIN,
+ SPHERE,
+ ANIMMESH
+ } type;
+
+ Node(ET t)
+ : type (t)
+ , scaling (1.f,1.f,1.f) // assume uniform scaling by default
+ , parent (0)
+ , framesPerSecond (0.f)
+ , id (0)
+ , sphereRadius (1.f)
+ , spherePolyCountX (100)
+ , spherePolyCountY (100)
+ {
+
+ // Generate a default name for the node
+ char buffer[128];
+ static int cnt;
+ ::sprintf(buffer,"IrrNode_%i",cnt++);
+ name = std::string(buffer);
+
+ // reserve space for up to 5 materials
+ materials.reserve(5);
+
+ // reserve space for up to 5 children
+ children.reserve(5);
+ }
+
+ // Transformation of the node
+ aiVector3D position, rotation, scaling;
+
+ // Name of the node
+ std::string name;
+
+ // List of all child nodes
+ std::vector<Node*> children;
+
+ // Parent node
+ Node* parent;
+
+ // Animated meshes: frames per second
+ // 0.f if not specified
+ float framesPerSecond;
+
+ // Meshes: path to the mesh to be loaded
+ std::string meshPath;
+ unsigned int id;
+
+ // Meshes: List of materials to be assigned
+ // along with their corresponding material flags
+ std::vector< std::pair<aiMaterial*, unsigned int> > materials;
+
+ // Spheres: radius of the sphere to be generates
+ float sphereRadius;
+
+ // Spheres: Number of polygons in the x,y direction
+ unsigned int spherePolyCountX,spherePolyCountY;
+
+ // List of all animators assigned to the node
+ std::list<Animator> animators;
+ };
+
+ /** Data structure for a vertex in an IRR skybox
+ */
+ struct SkyboxVertex
+ {
+ SkyboxVertex()
+ {}
+
+ //! Construction from single vertex components
+ SkyboxVertex(float px, float py, float pz,
+ float nx, float ny, float nz,
+ float uvx, float uvy)
+
+ : position (px,py,pz)
+ , normal (nx,ny,nz)
+ , uv (uvx,uvy,0.f)
+ {}
+
+ aiVector3D position, normal, uv;
+ };
+
+
+ // -------------------------------------------------------------------
+ /** Fill the scenegraph recursively
+ */
+ void GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
+ BatchLoader& batch,
+ std::vector<aiMesh*>& meshes,
+ std::vector<aiNodeAnim*>& anims,
+ std::vector<AttachmentInfo>& attach,
+ std::vector<aiMaterial*>& materials,
+ unsigned int& defaultMatIdx);
+
+
+ // -------------------------------------------------------------------
+ /** Generate a mesh that consists of just a single quad
+ */
+ aiMesh* BuildSingleQuadMesh(const SkyboxVertex& v1,
+ const SkyboxVertex& v2,
+ const SkyboxVertex& v3,
+ const SkyboxVertex& v4);
+
+
+ // -------------------------------------------------------------------
+ /** Build a skybox
+ *
+ * @param meshes Receives 6 output meshes
+ * @param materials The last 6 materials are assigned to the newly
+ * created meshes. The names of the materials are adjusted.
+ */
+ void BuildSkybox(std::vector<aiMesh*>& meshes,
+ std::vector<aiMaterial*> materials);
+
+
+ // -------------------------------------------------------------------
+ /** Copy a material for a mesh to the output material list
+ *
+ * @param materials Receives an output material
+ * @param inmaterials List of input materials
+ * @param defMatIdx Default material index - 0xffffffff if not there
+ * @param mesh Mesh to work on
+ */
+ void CopyMaterial(std::vector<aiMaterial*>& materials,
+ std::vector< std::pair<aiMaterial*, unsigned int> >& inmaterials,
+ unsigned int& defMatIdx,
+ aiMesh* mesh);
+
+
+ // -------------------------------------------------------------------
+ /** Compute animations for a specific node
+ *
+ * @param root Node to be processed
+ * @param anims The list of output animations
+ */
+ void ComputeAnimations(Node* root, aiNode* real,
+ std::vector<aiNodeAnim*>& anims);
+
+
+private:
+
+ /** Configuration option: desired output FPS */
+ double fps;
+
+ /** Configuration option: speed flag was set? */
+ bool configSpeedFlag;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_IRRIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/IRRMeshLoader.cpp b/src/3rdparty/assimp/code/IRRMeshLoader.cpp
new file mode 100644
index 000000000..67b37e287
--- /dev/null
+++ b/src/3rdparty/assimp/code/IRRMeshLoader.cpp
@@ -0,0 +1,504 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the IrrMesh importer class */
+
+#include "AssimpPCH.h"
+
+#include "IRRMeshLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+using namespace irr;
+using namespace irr::io;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+IRRMeshImporter::IRRMeshImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+IRRMeshImporter::~IRRMeshImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool IRRMeshImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ /* NOTE: A simple check for the file extension is not enough
+ * here. Irrmesh and irr are easy, but xml is too generic
+ * and could be collada, too. So we need to open the file and
+ * search for typical tokens.
+ */
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "irrmesh")return true;
+ else if (extension == "xml" || checkSig)
+ {
+ /* If CanRead() is called to check whether the loader
+ * supports a specific file extension in general we
+ * must return true here.
+ */
+ if (!pIOHandler)return true;
+ const char* tokens[] = {"irrmesh"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all file extensions which are handled by this class
+void IRRMeshImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("xml");
+ extensions.insert("irrmesh");
+}
+
+static inline void deleteAndSetNull(aiMaterial*& rPtr) {
+ delete rPtr;
+ rPtr = 0;
+}
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void IRRMeshImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open IRRMESH file " + pFile + "");
+
+ // Construct the irrXML parser
+ CIrrXML_IOStreamReader st(file.get());
+ reader = createIrrXMLReader((IFileReadCallBack*) &st);
+
+ // final data
+ std::vector<aiMaterial*> materials;
+ std::vector<aiMesh*> meshes;
+ materials.reserve (5);
+ meshes.reserve (5);
+
+ // temporary data - current mesh buffer
+ aiMaterial* curMat = NULL;
+ aiMesh* curMesh = NULL;
+ unsigned int curMatFlags;
+
+ std::vector<aiVector3D> curVertices,curNormals,curTangents,curBitangents;
+ std::vector<aiColor4D> curColors;
+ std::vector<aiVector3D> curUVs,curUV2s;
+
+ // some temporary variables
+ int textMeaning = 0;
+ int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents
+ bool useColors = false;
+
+ // Parse the XML file
+ while (reader->read()) {
+ switch (reader->getNodeType()) {
+ case EXN_ELEMENT:
+
+ if (!ASSIMP_stricmp(reader->getNodeName(),"buffer") && (curMat || curMesh)) {
+ // end of previous buffer. A material and a mesh should be there
+ if ( !curMat || !curMesh) {
+ DefaultLogger::get()->error("IRRMESH: A buffer must contain a mesh and a material");
+ deleteAndSetNull(curMat);
+ delete curMesh;
+ }
+ else {
+ materials.push_back(curMat);
+ meshes.push_back(curMesh);
+ }
+ curMat = NULL;
+ curMesh = NULL;
+
+ curVertices.clear();
+ curColors.clear();
+ curNormals.clear();
+ curUV2s.clear();
+ curUVs.clear();
+ curTangents.clear();
+ curBitangents.clear();
+ }
+
+
+ if (!ASSIMP_stricmp(reader->getNodeName(),"material")) {
+ if (curMat) {
+ DefaultLogger::get()->warn("IRRMESH: Only one material description per buffer, please");
+ deleteAndSetNull(curMat);
+ }
+ curMat = ParseMaterial(curMatFlags);
+ }
+ /* no else here! */ if (!ASSIMP_stricmp(reader->getNodeName(),"vertices"))
+ {
+ int num = reader->getAttributeValueAsInt("vertexCount");
+
+ if (!num) {
+ // This is possible ... remove the mesh from the list and skip further reading
+ DefaultLogger::get()->warn("IRRMESH: Found mesh with zero vertices");
+
+ deleteAndSetNull(curMat);
+
+ curMesh = NULL;
+ textMeaning = 0;
+ continue;
+ }
+
+ curVertices.reserve (num);
+ curNormals.reserve (num);
+ curColors.reserve (num);
+ curUVs.reserve (num);
+
+ // Determine the file format
+ const char* t = reader->getAttributeValueSafe("type");
+ if (!ASSIMP_stricmp("2tcoords", t)) {
+ curUV2s.reserve (num);
+ vertexFormat = 1;
+
+ if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) {
+ // *********************************************************
+ // We have a second texture! So use this UV channel
+ // for it. The 2nd texture can be either a normal
+ // texture (solid_2layer or lightmap_xxx) or a normal
+ // map (normal_..., parallax_...)
+ // *********************************************************
+ int idx = 1;
+ MaterialHelper* mat = ( MaterialHelper* ) curMat;
+
+ if (curMatFlags & AI_IRRMESH_MAT_lightmap){
+ mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_LIGHTMAP(0));
+ }
+ else if (curMatFlags & AI_IRRMESH_MAT_normalmap_solid){
+ mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_NORMALS(0));
+ }
+ else if (curMatFlags & AI_IRRMESH_MAT_solid_2layer) {
+ mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_DIFFUSE(1));
+ }
+ }
+ }
+ else if (!ASSIMP_stricmp("tangents", t)) {
+ curTangents.reserve (num);
+ curBitangents.reserve (num);
+ vertexFormat = 2;
+ }
+ else if (ASSIMP_stricmp("standard", t)) {
+ deleteAndSetNull(curMat);
+ DefaultLogger::get()->warn("IRRMESH: Unknown vertex format");
+ }
+ else vertexFormat = 0;
+ textMeaning = 1;
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"indices")) {
+ if (curVertices.empty() && curMat) {
+ deleteAndSetNull(curMat);
+ throw DeadlyImportError("IRRMESH: indices must come after vertices");
+ }
+
+ textMeaning = 2;
+
+ // start a new mesh
+ curMesh = new aiMesh();
+
+ // allocate storage for all faces
+ curMesh->mNumVertices = reader->getAttributeValueAsInt("indexCount");
+ if (!curMesh->mNumVertices) {
+ // This is possible ... remove the mesh from the list and skip further reading
+ DefaultLogger::get()->warn("IRRMESH: Found mesh with zero indices");
+
+ // mesh - away
+ delete curMesh; curMesh = NULL;
+
+ // material - away
+ deleteAndSetNull(curMat);
+
+ textMeaning = 0;
+ continue;
+ }
+
+ if (curMesh->mNumVertices % 3) {
+ DefaultLogger::get()->warn("IRRMESH: Number if indices isn't divisible by 3");
+ }
+
+ curMesh->mNumFaces = curMesh->mNumVertices / 3;
+ curMesh->mFaces = new aiFace[curMesh->mNumFaces];
+
+ // setup some members
+ curMesh->mMaterialIndex = (unsigned int)materials.size();
+ curMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // allocate storage for all vertices
+ curMesh->mVertices = new aiVector3D[curMesh->mNumVertices];
+
+ if (curNormals.size() == curVertices.size()) {
+ curMesh->mNormals = new aiVector3D[curMesh->mNumVertices];
+ }
+ if (curTangents.size() == curVertices.size()) {
+ curMesh->mTangents = new aiVector3D[curMesh->mNumVertices];
+ }
+ if (curBitangents.size() == curVertices.size()) {
+ curMesh->mBitangents = new aiVector3D[curMesh->mNumVertices];
+ }
+ if (curColors.size() == curVertices.size() && useColors) {
+ curMesh->mColors[0] = new aiColor4D[curMesh->mNumVertices];
+ }
+ if (curUVs.size() == curVertices.size()) {
+ curMesh->mTextureCoords[0] = new aiVector3D[curMesh->mNumVertices];
+ }
+ if (curUV2s.size() == curVertices.size()) {
+ curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices];
+ }
+ }
+ break;
+
+ case EXN_TEXT:
+ {
+ const char* sz = reader->getNodeData();
+ if (textMeaning == 1) {
+ textMeaning = 0;
+
+ // read vertices
+ do {
+ SkipSpacesAndLineEnd(&sz);
+ aiVector3D temp;aiColor4D c;
+
+ // Read the vertex position
+ sz = fast_atof_move(sz,(float&)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.y);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.z);
+ SkipSpaces(&sz);
+ curVertices.push_back(temp);
+
+ // Read the vertex normals
+ sz = fast_atof_move(sz,(float&)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.y);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.z);
+ SkipSpaces(&sz);
+ curNormals.push_back(temp);
+
+ // read the vertex colors
+ uint32_t clr = strtol16(sz,&sz);
+ ColorFromARGBPacked(clr,c);
+
+ if (!curColors.empty() && c != *(curColors.end()-1))
+ useColors = true;
+
+ curColors.push_back(c);
+ SkipSpaces(&sz);
+
+
+ // read the first UV coordinate set
+ sz = fast_atof_move(sz,(float&)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.y);
+ SkipSpaces(&sz);
+ temp.z = 0.f;
+ temp.y = 1.f - temp.y; // DX to OGL
+ curUVs.push_back(temp);
+
+ // read the (optional) second UV coordinate set
+ if (vertexFormat == 1) {
+ sz = fast_atof_move(sz,(float&)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.y);
+ temp.y = 1.f - temp.y; // DX to OGL
+ curUV2s.push_back(temp);
+ }
+ // read optional tangent and bitangent vectors
+ else if (vertexFormat == 2) {
+ // tangents
+ sz = fast_atof_move(sz,(float&)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.z);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.y);
+ SkipSpaces(&sz);
+ temp.y *= -1.0f;
+ curTangents.push_back(temp);
+
+ // bitangents
+ sz = fast_atof_move(sz,(float&)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.z);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.y);
+ SkipSpaces(&sz);
+ temp.y *= -1.0f;
+ curBitangents.push_back(temp);
+ }
+ }
+
+ /* IMPORTANT: We assume that each vertex is specified in one
+ line. So we can skip the rest of the line - unknown vertex
+ elements are ignored.
+ */
+
+ while (SkipLine(&sz));
+ }
+ else if (textMeaning == 2) {
+ textMeaning = 0;
+
+ // read indices
+ ai_assert(curMesh);
+ aiFace* curFace = curMesh->mFaces;
+ aiFace* const faceEnd = curMesh->mFaces + curMesh->mNumFaces;
+
+ aiVector3D* pcV = curMesh->mVertices;
+ aiVector3D* pcN = curMesh->mNormals;
+ aiVector3D* pcT = curMesh->mTangents;
+ aiVector3D* pcB = curMesh->mBitangents;
+ aiColor4D* pcC0 = curMesh->mColors[0];
+ aiVector3D* pcT0 = curMesh->mTextureCoords[0];
+ aiVector3D* pcT1 = curMesh->mTextureCoords[1];
+
+ unsigned int curIdx = 0;
+ unsigned int total = 0;
+ while (SkipSpacesAndLineEnd(&sz)) {
+ if (curFace >= faceEnd) {
+ DefaultLogger::get()->error("IRRMESH: Too many indices");
+ break;
+ }
+ if (!curIdx) {
+ curFace->mNumIndices = 3;
+ curFace->mIndices = new unsigned int[3];
+ }
+
+ unsigned int idx = strtol10(sz,&sz);
+ if (idx >= curVertices.size()) {
+ DefaultLogger::get()->error("IRRMESH: Index out of range");
+ idx = 0;
+ }
+
+ curFace->mIndices[curIdx] = total++;
+
+ *pcV++ = curVertices[idx];
+ if (pcN)*pcN++ = curNormals[idx];
+ if (pcT)*pcT++ = curTangents[idx];
+ if (pcB)*pcB++ = curBitangents[idx];
+ if (pcC0)*pcC0++ = curColors[idx];
+ if (pcT0)*pcT0++ = curUVs[idx];
+ if (pcT1)*pcT1++ = curUV2s[idx];
+
+ if (++curIdx == 3) {
+ ++curFace;
+ curIdx = 0;
+ }
+ }
+
+ if (curFace != faceEnd)
+ DefaultLogger::get()->error("IRRMESH: Not enough indices");
+
+ // Finish processing the mesh - do some small material workarounds
+ if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) {
+ // Take the opacity value of the current material
+ // from the common vertex color alpha
+ MaterialHelper* mat = (MaterialHelper*)curMat;
+ mat->AddProperty(&curColors[0].a,1,AI_MATKEY_OPACITY);
+ }
+ }}
+ break;
+
+ default:
+
+ // GCC complains here ...
+ break;
+
+ };
+ }
+
+ // End of the last buffer. A material and a mesh should be there
+ if (curMat || curMesh) {
+ if ( !curMat || !curMesh) {
+ DefaultLogger::get()->error("IRRMESH: A buffer must contain a mesh and a material");
+ deleteAndSetNull(curMat);
+ delete curMesh;
+ }
+ else {
+ materials.push_back(curMat);
+ meshes.push_back(curMesh);
+ }
+ }
+
+ if (materials.empty())
+ throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file");
+
+
+ // now generate the output scene
+ pScene->mNumMeshes = (unsigned int)meshes.size();
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i) {
+ pScene->mMeshes[i] = meshes[i];
+
+ // clean this value ...
+ pScene->mMeshes[i]->mNumUVComponents[3] = 0;
+ }
+
+ pScene->mNumMaterials = (unsigned int)materials.size();
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+ ::memcpy(pScene->mMaterials,&materials[0],sizeof(void*)*pScene->mNumMaterials);
+
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("<IRRMesh>");
+ pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+ pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
+
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mRootNode->mMeshes[i] = i;
+
+ // clean up and return
+ delete reader;
+ AI_DEBUG_INVALIDATE_PTR(reader);
+}
diff --git a/src/3rdparty/assimp/code/IRRMeshLoader.h b/src/3rdparty/assimp/code/IRRMeshLoader.h
new file mode 100644
index 000000000..e5a94dffe
--- /dev/null
+++ b/src/3rdparty/assimp/code/IRRMeshLoader.h
@@ -0,0 +1,99 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file IRRMeshLoader.h
+ * @brief Declaration of the .irrMesh (Irrlight Engine Mesh Format)
+ * importer class.
+ */
+#ifndef AI_IRRMESHLOADER_H_INCLUDED
+#define AI_IRRMESHLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "IRRShared.h"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** IrrMesh importer class.
+ *
+ * IrrMesh is the native file format of the Irrlight engine and its editor
+ * irrEdit. As IrrEdit itself is capable of importing quite many file formats,
+ * it might be a good file format for data exchange.
+ */
+class IRRMeshImporter : public BaseImporter, public IrrlichtBase
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ IRRMeshImporter();
+
+ /** Destructor, private as well */
+ ~IRRMeshImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+};
+
+} // end of namespace Assimp
+
+#endif // AI_IRRMESHIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/IRRShared.cpp b/src/3rdparty/assimp/code/IRRShared.cpp
new file mode 100644
index 000000000..d30107057
--- /dev/null
+++ b/src/3rdparty/assimp/code/IRRShared.cpp
@@ -0,0 +1,496 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file IRRShared.cpp
+ * @brief Shared utilities for the IRR and IRRMESH loaders
+ */
+
+#include "AssimpPCH.h"
+
+#include "IRRShared.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+using namespace irr;
+using namespace irr::io;
+
+// Transformation matrix to convert from Assimp to IRR space
+const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4 (
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+
+// ------------------------------------------------------------------------------------------------
+// read a property in hexadecimal format (i.e. ffffffff)
+void IrrlichtBase::ReadHexProperty (HexProperty& out)
+{
+ for (int i = 0; i < reader->getAttributeCount();++i)
+ {
+ if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
+ {
+ out.name = std::string( reader->getAttributeValue(i) );
+ }
+ else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
+ {
+ // parse the hexadecimal value
+ out.value = strtol16(reader->getAttributeValue(i));
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a decimal property
+void IrrlichtBase::ReadIntProperty (IntProperty& out)
+{
+ for (int i = 0; i < reader->getAttributeCount();++i)
+ {
+ if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
+ {
+ out.name = std::string( reader->getAttributeValue(i) );
+ }
+ else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
+ {
+ // parse the ecimal value
+ out.value = strtol10s(reader->getAttributeValue(i));
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a string property
+void IrrlichtBase::ReadStringProperty (StringProperty& out)
+{
+ for (int i = 0; i < reader->getAttributeCount();++i)
+ {
+ if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
+ {
+ out.name = std::string( reader->getAttributeValue(i) );
+ }
+ else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
+ {
+ // simple copy the string
+ out.value = std::string (reader->getAttributeValue(i));
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a boolean property
+void IrrlichtBase::ReadBoolProperty (BoolProperty& out)
+{
+ for (int i = 0; i < reader->getAttributeCount();++i)
+ {
+ if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
+ {
+ out.name = std::string( reader->getAttributeValue(i) );
+ }
+ else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
+ {
+ // true or false, case insensitive
+ out.value = (ASSIMP_stricmp( reader->getAttributeValue(i),
+ "true") ? false : true);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a float property
+void IrrlichtBase::ReadFloatProperty (FloatProperty& out)
+{
+ for (int i = 0; i < reader->getAttributeCount();++i)
+ {
+ if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
+ {
+ out.name = std::string( reader->getAttributeValue(i) );
+ }
+ else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
+ {
+ // just parse the float
+ out.value = fast_atof( reader->getAttributeValue(i) );
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a vector property
+void IrrlichtBase::ReadVectorProperty (VectorProperty& out)
+{
+ for (int i = 0; i < reader->getAttributeCount();++i)
+ {
+ if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
+ {
+ out.name = std::string( reader->getAttributeValue(i) );
+ }
+ else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
+ {
+ // three floats, separated with commas
+ const char* ptr = reader->getAttributeValue(i);
+
+ SkipSpaces(&ptr);
+ ptr = fast_atof_move( ptr,(float&)out.value.x );
+ SkipSpaces(&ptr);
+ if (',' != *ptr)
+ {
+ DefaultLogger::get()->error("IRR(MESH): Expected comma in vector definition");
+ }
+ else SkipSpaces(ptr+1,&ptr);
+ ptr = fast_atof_move( ptr,(float&)out.value.y );
+ SkipSpaces(&ptr);
+ if (',' != *ptr)
+ {
+ DefaultLogger::get()->error("IRR(MESH): Expected comma in vector definition");
+ }
+ else SkipSpaces(ptr+1,&ptr);
+ ptr = fast_atof_move( ptr,(float&)out.value.z );
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a string to a proper aiMappingMode
+int ConvertMappingMode(const std::string& mode)
+{
+ if (mode == "texture_clamp_repeat")
+ {
+ return aiTextureMapMode_Wrap;
+ }
+ else if (mode == "texture_clamp_mirror")
+ return aiTextureMapMode_Mirror;
+
+ return aiTextureMapMode_Clamp;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse a material from the XML file
+aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags)
+{
+ MaterialHelper* mat = new MaterialHelper();
+ aiColor4D clr;
+ aiString s;
+
+ matFlags = 0; // zero output flags
+ int cnt = 0; // number of used texture channels
+ unsigned int nd = 0;
+
+ // Continue reading from the file
+ while (reader->read())
+ {
+ switch (reader->getNodeType())
+ {
+ case EXN_ELEMENT:
+
+ // Hex properties
+ if (!ASSIMP_stricmp(reader->getNodeName(),"color"))
+ {
+ HexProperty prop;
+ ReadHexProperty(prop);
+ if (prop.name == "Diffuse")
+ {
+ ColorFromARGBPacked(prop.value,clr);
+ mat->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+ }
+ else if (prop.name == "Ambient")
+ {
+ ColorFromARGBPacked(prop.value,clr);
+ mat->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT);
+ }
+ else if (prop.name == "Specular")
+ {
+ ColorFromARGBPacked(prop.value,clr);
+ mat->AddProperty(&clr,1,AI_MATKEY_COLOR_SPECULAR);
+ }
+
+ // NOTE: The 'emissive' property causes problems. It is
+ // often != 0, even if there is obviously no light
+ // emitted by the described surface. In fact I think
+ // IRRLICHT ignores this property, too.
+#if 0
+ else if (prop.name == "Emissive")
+ {
+ ColorFromARGBPacked(prop.value,clr);
+ mat->AddProperty(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
+ }
+#endif
+ }
+ // Float properties
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"float"))
+ {
+ FloatProperty prop;
+ ReadFloatProperty(prop);
+ if (prop.name == "Shininess")
+ {
+ mat->AddProperty(&prop.value,1,AI_MATKEY_SHININESS);
+ }
+ }
+ // Bool properties
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"bool"))
+ {
+ BoolProperty prop;
+ ReadBoolProperty(prop);
+ if (prop.name == "Wireframe")
+ {
+ int val = (prop.value ? true : false);
+ mat->AddProperty(&val,1,AI_MATKEY_ENABLE_WIREFRAME);
+ }
+ else if (prop.name == "GouraudShading")
+ {
+ int val = (prop.value ? aiShadingMode_Gouraud
+ : aiShadingMode_NoShading);
+ mat->AddProperty(&val,1,AI_MATKEY_SHADING_MODEL);
+ }
+ else if (prop.name == "BackfaceCulling")
+ {
+ int val = (!prop.value);
+ mat->AddProperty(&val,1,AI_MATKEY_TWOSIDED);
+ }
+ }
+ // String properties - textures and texture related properties
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"texture") ||
+ !ASSIMP_stricmp(reader->getNodeName(),"enum"))
+ {
+ StringProperty prop;
+ ReadStringProperty(prop);
+ if (prop.value.length())
+ {
+ // material type (shader)
+ if (prop.name == "Type")
+ {
+ if (prop.value == "solid")
+ {
+ // default material ...
+ }
+ else if (prop.value == "trans_vertex_alpha")
+ {
+ matFlags = AI_IRRMESH_MAT_trans_vertex_alpha;
+ }
+ else if (prop.value == "lightmap")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap;
+ }
+ else if (prop.value == "solid_2layer")
+ {
+ matFlags = AI_IRRMESH_MAT_solid_2layer;
+ }
+ else if (prop.value == "lightmap_m2")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap_m2;
+ }
+ else if (prop.value == "lightmap_m4")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap_m4;
+ }
+ else if (prop.value == "lightmap_light")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap_light;
+ }
+ else if (prop.value == "lightmap_light_m2")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap_light_m2;
+ }
+ else if (prop.value == "lightmap_light_m4")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap_light_m4;
+ }
+ else if (prop.value == "lightmap_add")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap_add;
+ }
+ // Normal and parallax maps are treated equally
+ else if (prop.value == "normalmap_solid" ||
+ prop.value == "parallaxmap_solid")
+ {
+ matFlags = AI_IRRMESH_MAT_normalmap_solid;
+ }
+ else if (prop.value == "normalmap_trans_vertex_alpha" ||
+ prop.value == "parallaxmap_trans_vertex_alpha")
+ {
+ matFlags = AI_IRRMESH_MAT_normalmap_tva;
+ }
+ else if (prop.value == "normalmap_trans_add" ||
+ prop.value == "parallaxmap_trans_add")
+ {
+ matFlags = AI_IRRMESH_MAT_normalmap_ta;
+ }
+ else {
+ DefaultLogger::get()->warn("IRRMat: Unrecognized material type: " + prop.value);
+ }
+ }
+
+ // Up to 4 texture channels are supported
+ if (prop.name == "Texture1")
+ {
+ // Always accept the primary texture channel
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ else if (prop.name == "Texture2" && cnt == 1)
+ {
+ // 2-layer material lightmapped?
+ if (matFlags & AI_IRRMESH_MAT_lightmap) {
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_LIGHTMAP(0));
+
+ // set the corresponding material flag
+ matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
+ }
+ // alternatively: normal or parallax mapping
+ else if (matFlags & AI_IRRMESH_MAT_normalmap_solid) {
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_NORMALS(0));
+
+ // set the corresponding material flag
+ matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
+ }
+ // or just as second diffuse texture
+ else if (matFlags & AI_IRRMESH_MAT_solid_2layer) {
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(1));
+ ++nd;
+
+ // set the corresponding material flag
+ matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
+ }
+ else DefaultLogger::get()->warn("IRRmat: Skipping second texture");
+ }
+
+ else if (prop.name == "Texture3" && cnt == 2)
+ {
+ // Irrlicht does not seem to use these channels.
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(nd+1));
+ }
+ else if (prop.name == "Texture4" && cnt == 3)
+ {
+ // Irrlicht does not seem to use these channels.
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(nd+2));
+ }
+
+ // Texture mapping options
+ if (prop.name == "TextureWrap1" && cnt >= 1)
+ {
+ int map = ConvertMappingMode(prop.value);
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
+ }
+ else if (prop.name == "TextureWrap2" && cnt >= 2)
+ {
+ int map = ConvertMappingMode(prop.value);
+ if (matFlags & AI_IRRMESH_MAT_lightmap) {
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_LIGHTMAP(0));
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_LIGHTMAP(0));
+ }
+ else if (matFlags & (AI_IRRMESH_MAT_normalmap_solid)) {
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_NORMALS(0));
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_NORMALS(0));
+ }
+ else if (matFlags & AI_IRRMESH_MAT_solid_2layer) {
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(1));
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(1));
+ }
+ }
+ else if (prop.name == "TextureWrap3" && cnt >= 3)
+ {
+ int map = ConvertMappingMode(prop.value);
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd+1));
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd+1));
+ }
+ else if (prop.name == "TextureWrap4" && cnt >= 4)
+ {
+ int map = ConvertMappingMode(prop.value);
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd+2));
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd+2));
+ }
+ }
+ }
+ break;
+ case EXN_ELEMENT_END:
+
+ /* Assume there are no further nested nodes in <material> elements
+ */
+ if (/* IRRMESH */ !ASSIMP_stricmp(reader->getNodeName(),"material") ||
+ /* IRR */ !ASSIMP_stricmp(reader->getNodeName(),"attributes"))
+ {
+ // Now process lightmapping flags
+ // We should have at least one textur to do that ..
+ if (cnt && matFlags & AI_IRRMESH_MAT_lightmap)
+ {
+ float f = 1.f;
+ unsigned int unmasked = matFlags&~AI_IRRMESH_MAT_lightmap;
+
+ // Additive lightmap?
+ int op = (unmasked & AI_IRRMESH_MAT_lightmap_add
+ ? aiTextureOp_Add : aiTextureOp_Multiply);
+
+ // Handle Irrlicht's lightmapping scaling factor
+ if (unmasked & AI_IRRMESH_MAT_lightmap_m2 ||
+ unmasked & AI_IRRMESH_MAT_lightmap_light_m2)
+ {
+ f = 2.f;
+ }
+ else if (unmasked & AI_IRRMESH_MAT_lightmap_m4 ||
+ unmasked & AI_IRRMESH_MAT_lightmap_light_m4)
+ {
+ f = 4.f;
+ }
+ mat->AddProperty( &f, 1, AI_MATKEY_TEXBLEND_LIGHTMAP(0));
+ mat->AddProperty( &op,1, AI_MATKEY_TEXOP_LIGHTMAP(0));
+ }
+
+ return mat;
+ }
+ default:
+
+ // GCC complains here ...
+ break;
+ }
+ }
+ DefaultLogger::get()->error("IRRMESH: Unexpected end of file. Material is not complete");
+ return mat;
+}
diff --git a/src/3rdparty/assimp/code/IRRShared.h b/src/3rdparty/assimp/code/IRRShared.h
new file mode 100644
index 000000000..e40813367
--- /dev/null
+++ b/src/3rdparty/assimp/code/IRRShared.h
@@ -0,0 +1,115 @@
+
+
+/** @file IRRShared.h
+ * @brief Shared utilities for the IRR and IRRMESH loaders
+ */
+
+#ifndef INCLUDED_AI_IRRSHARED_H
+#define INCLUDED_AI_IRRSHARED_H
+
+#include "irrXMLWrapper.h"
+#include "BaseImporter.h"
+
+namespace Assimp {
+
+
+/** @brief Matrix to convert from Assimp to IRR and backwards
+ */
+extern const aiMatrix4x4 AI_TO_IRR_MATRIX;
+
+
+// Default: 0 = solid, one texture
+#define AI_IRRMESH_MAT_solid_2layer 0x10000
+
+// Transparency flags
+#define AI_IRRMESH_MAT_trans_vertex_alpha 0x1
+#define AI_IRRMESH_MAT_trans_add 0x2
+
+// Lightmapping flags
+#define AI_IRRMESH_MAT_lightmap 0x2
+#define AI_IRRMESH_MAT_lightmap_m2 (AI_IRRMESH_MAT_lightmap|0x4)
+#define AI_IRRMESH_MAT_lightmap_m4 (AI_IRRMESH_MAT_lightmap|0x8)
+#define AI_IRRMESH_MAT_lightmap_light (AI_IRRMESH_MAT_lightmap|0x10)
+#define AI_IRRMESH_MAT_lightmap_light_m2 (AI_IRRMESH_MAT_lightmap|0x20)
+#define AI_IRRMESH_MAT_lightmap_light_m4 (AI_IRRMESH_MAT_lightmap|0x40)
+#define AI_IRRMESH_MAT_lightmap_add (AI_IRRMESH_MAT_lightmap|0x80)
+
+// Standard NormalMap (or Parallax map, they're treated equally)
+#define AI_IRRMESH_MAT_normalmap_solid (0x100)
+
+// Normal map combined with vertex alpha
+#define AI_IRRMESH_MAT_normalmap_tva \
+ (AI_IRRMESH_MAT_normalmap_solid | AI_IRRMESH_MAT_trans_vertex_alpha)
+
+// Normal map combined with additive transparency
+#define AI_IRRMESH_MAT_normalmap_ta \
+ (AI_IRRMESH_MAT_normalmap_solid | AI_IRRMESH_MAT_trans_add)
+
+// Special flag. It indicates a second texture has been found
+// Its type depends ... either a normal textue or a normal map
+#define AI_IRRMESH_EXTRA_2ND_TEXTURE 0x100000
+
+// ---------------------------------------------------------------------------
+/** Base class for the Irr and IrrMesh importers.
+ *
+ * Declares some irrlight-related xml parsing utilities and provides tools
+ * to load materials from IRR and IRRMESH files.
+ */
+class IrrlichtBase
+{
+protected:
+
+ /** @brief Data structure for a simple name-value property
+ */
+ template <class T>
+ struct Property
+ {
+ std::string name;
+ T value;
+ };
+
+ typedef Property<uint32_t> HexProperty;
+ typedef Property<std::string> StringProperty;
+ typedef Property<bool> BoolProperty;
+ typedef Property<float> FloatProperty;
+ typedef Property<aiVector3D> VectorProperty;
+ typedef Property<int> IntProperty;
+
+ /** XML reader instance
+ */
+ irr::io::IrrXMLReader* reader;
+
+ // -------------------------------------------------------------------
+ /** Parse a material description from the XML
+ * @return The created material
+ * @param matFlags Receives AI_IRRMESH_MAT_XX flags
+ */
+ aiMaterial* ParseMaterial(unsigned int& matFlags);
+
+ // -------------------------------------------------------------------
+ /** Read a property of the specified type from the current XML element.
+ * @param out Recives output data
+ */
+ void ReadHexProperty (HexProperty& out);
+ void ReadStringProperty (StringProperty& out);
+ void ReadBoolProperty (BoolProperty& out);
+ void ReadFloatProperty (FloatProperty& out);
+ void ReadVectorProperty (VectorProperty& out);
+ void ReadIntProperty (IntProperty& out);
+};
+
+
+// ------------------------------------------------------------------------------------------------
+// Unpack a hex color, e.g. 0xdcdedfff
+inline void ColorFromARGBPacked(uint32_t in, aiColor4D& clr)
+{
+ clr.a = ((in >> 24) & 0xff) / 255.f;
+ clr.r = ((in >> 16) & 0xff) / 255.f;
+ clr.g = ((in >> 8) & 0xff) / 255.f;
+ clr.b = ((in ) & 0xff) / 255.f;
+}
+
+
+} // end namespace Assimp
+
+#endif // !! INCLUDED_AI_IRRSHARED_H
diff --git a/src/3rdparty/assimp/code/Importer.cpp b/src/3rdparty/assimp/code/Importer.cpp
new file mode 100644
index 000000000..abab91053
--- /dev/null
+++ b/src/3rdparty/assimp/code/Importer.cpp
@@ -0,0 +1,1423 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Importer.cpp
+ * @brief Implementation of the CPP-API class #Importer
+ */
+
+#include "AssimpPCH.h"
+#include "../include/aiVersion.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 ...
+ */
+// ------------------------------------------------------------------------------------------------
+#define ASSIMP_CATCH_GLOBAL_EXCEPTIONS
+
+
+// ------------------------------------------------------------------------------------------------
+// Internal headers
+// ------------------------------------------------------------------------------------------------
+#include "BaseImporter.h"
+#include "BaseProcess.h"
+#include "DefaultIOStream.h"
+#include "DefaultIOSystem.h"
+#include "DefaultProgressHandler.h"
+#include "GenericProperty.h"
+#include "ProcessHelper.h"
+#include "ScenePreprocessor.h"
+#include "MemoryIOWrapper.h"
+#include "Profiler.h"
+#include "TinyFormatter.h"
+
+using namespace Assimp::Profiling;
+using namespace Assimp::Formatter;
+
+// ------------------------------------------------------------------------------------------------
+// Importers
+// (include_new_importers_here)
+// ------------------------------------------------------------------------------------------------
+#ifndef ASSIMP_BUILD_NO_X_IMPORTER
+# include "XFileImporter.h"
+#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_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_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_SWORDOFMOONLIGHT_IMPORTER
+//# include "SomLoader.h"
+//#endif
+#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
+# include "Q3BSPFileImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_NDO_IMPORTER
+# include "NDOLoader.h"
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Post processing-Steps
+// ------------------------------------------------------------------------------------------------
+#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_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
+#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
+
+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()
+{
+ // allocate the pimpl first
+ 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;
+
+ // ----------------------------------------------------------------------------
+ // Add an instance of each worker class here
+ // (register_new_importers_here)
+ // ----------------------------------------------------------------------------
+ pimpl->mImporter.reserve(64);
+#if (!defined ASSIMP_BUILD_NO_X_IMPORTER)
+ pimpl->mImporter.push_back( new XFileImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_OBJ_IMPORTER)
+ pimpl->mImporter.push_back( new ObjFileImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER)
+ pimpl->mImporter.push_back( new Discreet3DSImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MD3_IMPORTER)
+ pimpl->mImporter.push_back( new MD3Importer());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MD2_IMPORTER)
+ pimpl->mImporter.push_back( new MD2Importer());
+#endif
+#if (!defined ASSIMP_BUILD_NO_PLY_IMPORTER)
+ pimpl->mImporter.push_back( new PLYImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MDL_IMPORTER)
+ pimpl->mImporter.push_back( new MDLImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_ASE_IMPORTER)
+ pimpl->mImporter.push_back( new ASEImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_HMP_IMPORTER)
+ pimpl->mImporter.push_back( new HMPImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_SMD_IMPORTER)
+ pimpl->mImporter.push_back( new SMDImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MDC_IMPORTER)
+ pimpl->mImporter.push_back( new MDCImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MD5_IMPORTER)
+ pimpl->mImporter.push_back( new MD5Importer());
+#endif
+#if (!defined ASSIMP_BUILD_NO_STL_IMPORTER)
+ pimpl->mImporter.push_back( new STLImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER)
+ pimpl->mImporter.push_back( new LWOImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_DXF_IMPORTER)
+ pimpl->mImporter.push_back( new DXFImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_NFF_IMPORTER)
+ pimpl->mImporter.push_back( new NFFImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_RAW_IMPORTER)
+ pimpl->mImporter.push_back( new RAWImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_OFF_IMPORTER)
+ pimpl->mImporter.push_back( new OFFImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_AC_IMPORTER)
+ pimpl->mImporter.push_back( new AC3DImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_BVH_IMPORTER)
+ pimpl->mImporter.push_back( new BVHLoader());
+#endif
+#if (!defined ASSIMP_BUILD_NO_IRRMESH_IMPORTER)
+ pimpl->mImporter.push_back( new IRRMeshImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_IRR_IMPORTER)
+ pimpl->mImporter.push_back( new IRRImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_Q3D_IMPORTER)
+ pimpl->mImporter.push_back( new Q3DImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_B3D_IMPORTER)
+ pimpl->mImporter.push_back( new B3DImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_COLLADA_IMPORTER)
+ pimpl->mImporter.push_back( new ColladaLoader());
+#endif
+#if (!defined ASSIMP_BUILD_NO_TERRAGEN_IMPORTER)
+ pimpl->mImporter.push_back( new TerragenImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_CSM_IMPORTER)
+ pimpl->mImporter.push_back( new CSMImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_3D_IMPORTER)
+ pimpl->mImporter.push_back( new UnrealImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_LWS_IMPORTER)
+ pimpl->mImporter.push_back( new LWSImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_OGRE_IMPORTER)
+ pimpl->mImporter.push_back( new Ogre::OgreImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MS3D_IMPORTER)
+ pimpl->mImporter.push_back( new MS3DImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_COB_IMPORTER)
+ pimpl->mImporter.push_back( new COBImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_BLEND_IMPORTER)
+ pimpl->mImporter.push_back( new BlenderImporter());
+#endif
+//#if (!defined ASSIMP_BUILD_NO_SWORDOFMOONLIGHT_IMPORTER)
+// pimpl->mImporter.push_back( new SomImporter());
+//#endif
+#if (!defined ASSIMP_BUILD_NO_Q3BSP_IMPORTER)
+ pimpl->mImporter.push_back( new Q3BSPFileImporter() );
+#endif
+#if (!defined ASSIMP_BUILD_NO_NDO_IMPORTER)
+ pimpl->mImporter.push_back( new NDOImporter() );
+#endif
+
+ // ----------------------------------------------------------------------------
+ // 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.
+ // ----------------------------------------------------------------------------
+ pimpl->mPostProcessingSteps.reserve(25);
+#if (!defined ASSIMP_BUILD_NO_REMOVEVC_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new RemoveVCProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new RemoveRedundantMatsProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new FindInstancesProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new OptimizeGraphProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new OptimizeMeshesProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new FindDegeneratesProcess());
+#endif
+#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS
+ pimpl->mPostProcessingSteps.push_back( new ComputeUVMappingProcess());
+#endif
+#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
+ pimpl->mPostProcessingSteps.push_back( new TextureTransformStep());
+#endif
+#if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new PretransformVertices());
+#endif
+#if (!defined ASSIMP_BUILD_NO_TRIANGULATE_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new TriangulateProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new SortByPTypeProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new FindInvalidDataProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new FixInfacingNormalsProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Triangle());
+#endif
+#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new GenFaceNormalsProcess());
+#endif
+
+ // .........................................................................
+ // DON'T change the order of these five!
+ pimpl->mPostProcessingSteps.push_back( new ComputeSpatialSortProcess());
+ // .........................................................................
+
+#if (!defined ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new GenVertexNormalsProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new CalcTangentsProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_JOINVERTICES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new JoinVerticesProcess());
+#endif
+
+ // .........................................................................
+ pimpl->mPostProcessingSteps.push_back( new DestroySpatialSortProcess());
+ // .........................................................................
+
+#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Vertex());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new MakeLeftHandedProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new FlipUVsProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new FlipWindingOrderProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new LimitBoneWeightsProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new ImproveCacheLocalityProcess());
+#endif
+
+ // 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
+ for ( unsigned int a = 0; a < pimpl->mImporter.size(); a++)
+ delete pimpl->mImporter[a];
+
+ // Delete all post-processing plug-ins
+ for ( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++)
+ delete pimpl->mPostProcessingSteps[a];
+
+ // Delete the assigned IO handler
+ delete pimpl->mIOHandler;
+
+ // Kill imported scene. Destructors should do that recursivly
+ delete pimpl->mScene;
+
+ // Delete shared post-processing data
+ delete pimpl->mPPShared;
+
+ // and finally the pimpl itself
+ delete pimpl;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Copy constructor - copies the config of another Importer, not the scene
+Importer::Importer(const Importer &other) : pimpl(0)
+{
+ new(this) Importer();
+
+ pimpl->mIntProperties = other.pimpl->mIntProperties;
+ pimpl->mFloatProperties = other.pimpl->mFloatProperties;
+ pimpl->mStringProperties = other.pimpl->mStringProperties;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Register a custom post-processing step
+aiReturn Importer::RegisterPPStep(BaseProcess* pImp)
+{
+ ai_assert(NULL != pImp);
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ pimpl->mPostProcessingSteps.push_back(pImp);
+ DefaultLogger::get()->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 _DEBUG
+ if (IsExtensionSupported(*it)) {
+ DefaultLogger::get()->warn("The file extension " + *it + " is already in use");
+ }
+#endif
+ baked += *it;
+ }
+
+ // add the loader
+ pimpl->mImporter.push_back(pImp);
+ DefaultLogger::get()->info("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);
+
+ std::set<std::string> st;
+ pImp->GetExtensionList(st);
+
+ DefaultLogger::get()->info("Unregistering custom importer: ");
+ return AI_SUCCESS;
+ }
+ DefaultLogger::get()->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);
+ DefaultLogger::get()->info("Unregistering custom post-processing step");
+ return AI_SUCCESS;
+ }
+ DefaultLogger::get()->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) {
+ DefaultLogger::get()->error("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible");
+ return false;
+ }
+ if (pFlags & aiProcess_OptimizeGraph && pFlags & aiProcess_PreTransformVertices) {
+ DefaultLogger::get()->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) > 100) {
+ 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));
+
+ // read the file and recover the previous IOSystem
+ char fbuff[128];
+ sprintf(fbuff,"%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)
+{
+ Logger* l = DefaultLogger::get();
+ if (!l) {
+ return;
+ }
+ l->info("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();
+ l->debug(format()
+ << "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
+
+#ifndef NDEBUG
+ << " debug"
+#endif
+
+ << (flags & ASSIMP_CFLAGS_NOBOOST ? " noboost" : "")
+ << (flags & ASSIMP_CFLAGS_SHARED ? " shared" : "")
+ << (flags & ASSIMP_CFLAGS_SINGLETHREADED ? " singlethreaded" : "")
+ );
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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) {
+
+ DefaultLogger::get()->debug("(Deleting previous scene)");
+ FreeScene();
+ }
+
+ // First check if the file is accessable at all
+ if ( !pimpl->mIOHandler->Exists( pFile)) {
+
+ pimpl->mErrorString = "Unable to open file \"" + pFile + "\".";
+ DefaultLogger::get()->error(pimpl->mErrorString);
+ return NULL;
+ }
+
+ boost::scoped_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) {
+ DefaultLogger::get()->info("File extension now 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 + "\".";
+ DefaultLogger::get()->error(pimpl->mErrorString);
+ return NULL;
+ }
+ }
+
+ // Dispatch the reading to the worker class for this format
+ DefaultLogger::get()->info("Found a matching importer for this file format");
+ pimpl->mProgressHandler->Update();
+
+ if (profiler) {
+ profiler->BeginRegion("import");
+ }
+
+ pimpl->mScene = imp->ReadFile( this, pFile, pimpl->mIOHandler);
+ pimpl->mProgressHandler->Update();
+
+ if (profiler) {
+ profiler->EndRegion("import");
+ }
+
+ // 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();
+
+ pimpl->mProgressHandler->Update();
+ 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 occured
+ pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what();
+#else
+ pimpl->mErrorString = std::string("std::exception: ") + e.what();
+#endif
+
+ DefaultLogger::get()->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));
+ DefaultLogger::get()->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 _DEBUG
+ if (pimpl->bExtraVerbose)
+ {
+#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
+ DefaultLogger::get()->error("Verbose Import is not available due to build settings");
+#endif // no validation
+ pFlags |= aiProcess_ValidateDataStructure;
+ }
+#else
+ if (pimpl->bExtraVerbose) {
+ DefaultLogger::get()->warn("Not a debug build, ignoring extra verbose setting");
+ }
+#endif // ! DEBUG
+
+ boost::scoped_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];
+ if ( process->IsActive( pFlags)) {
+
+ if (profiler) {
+ profiler->BeginRegion("postprocess");
+ }
+
+ process->ExecuteOnScene ( this );
+ pimpl->mProgressHandler->Update();
+
+ if (profiler) {
+ profiler->EndRegion("postprocess");
+ }
+ }
+ if ( !pimpl->mScene) {
+ break;
+ }
+#ifdef _DEBUG
+
+#ifndef 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) {
+ DefaultLogger::get()->debug("Verbose Import: revalidating data structures");
+
+ ValidateDSProcess ds;
+ ds.ExecuteOnScene (this);
+ if ( !pimpl->mScene) {
+ DefaultLogger::get()->error("Verbose Import: failed to revalidate data structures");
+ break;
+ }
+ }
+#endif // ! DEBUG
+ }
+
+ // clear any data allocated by post-process steps
+ pimpl->mPPShared->Clean();
+ DefaultLogger::get()->info("Leaving 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 NULL != FindLoader(szExtension);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Find a loader plugin for a given file extension
+BaseImporter* Importer::FindLoader (const char* szExtension) const
+{
+ ai_assert(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 NULL;
+ }
+ std::transform(ext.begin(),ext.end(), ext.begin(), tolower);
+
+ 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 (*i);
+ }
+ }
+ }
+ ASSIMP_END_EXCEPTION_REGION(BaseImporter*);
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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);
+ }
+
+ 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
+void Importer::SetPropertyInteger(const char* szName, int iValue,
+ bool* bWasExisting /*= NULL*/)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue,bWasExisting);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Set a configuration property
+void Importer::SetPropertyFloat(const char* szName, float iValue,
+ bool* bWasExisting /*= NULL*/)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ SetGenericProperty<float>(pimpl->mFloatProperties, szName,iValue,bWasExisting);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Set a configuration property
+void Importer::SetPropertyString(const char* szName, const std::string& value,
+ bool* bWasExisting /*= NULL*/)
+{
+ try {
+ std::cout << "";
+ }
+ catch (...) {
+ try {
+ throw;
+ }
+ catch(std::exception&) {
+ return;
+ }
+ }
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ SetGenericProperty<std::string>(pimpl->mStringProperties, szName,value,bWasExisting);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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
+float Importer::GetPropertyFloat(const char* szName,
+ float iErrorReturn /*= 10e10*/) const
+{
+ return GetGenericProperty<float>(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 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/src/3rdparty/assimp/code/ImproveCacheLocality.cpp b/src/3rdparty/assimp/code/ImproveCacheLocality.cpp
new file mode 100644
index 000000000..d167d7ff6
--- /dev/null
+++ b/src/3rdparty/assimp/code/ImproveCacheLocality.cpp
@@ -0,0 +1,382 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file 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 ...
+ */
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "ImproveCacheLocality.h"
+#include "VertexTriangleAdjacency.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+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) {
+ DefaultLogger::get()->debug("ImproveCacheLocalityProcess skipped; there are no meshes");
+ return;
+ }
+
+ DefaultLogger::get()->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()) {
+ char szBuff[128]; // should be sufficiently large in every case
+ ::sprintf(szBuff,"Cache relevant are %i meshes (%i faces). Average output ACMR is %f",
+ numm,numf,out/numf);
+
+ DefaultLogger::get()->info(szBuff);
+ DefaultLogger::get()->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) {
+ DefaultLogger::get()->error("This algorithm works on triangle meshes only");
+ 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 ...
+ sprintf(szBuff,"Mesh %i: Not suitable for vcache optimization",meshNum);
+ DefaultLogger::get()->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> sDeadEndVStack;
+
+ // create a copy of the piNumTriPtr buffer
+ unsigned int* const piNumTriPtr = adj.mLiveTriangles;
+ const unsigned int* const piNumTriPtrNoModify = new unsigned int[pMesh->mNumVertices];
+ memcpy(const_cast<unsigned int* const> (piNumTriPtrNoModify),piNumTriPtr,
+ pMesh->mNumVertices * sizeof(unsigned int));
+
+ // 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);
+ }
+ }
+ 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 ];
+ for (unsigned int* p = pcFace->mIndices, *p2 = pcFace->mIndices+3;p != p2;++p) {
+ const unsigned int dp = *p;
+
+ // 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) {
+ register 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) {
+ char szBuff[128]; // should be sufficiently large in every case
+
+ ::sprintf(szBuff,"Mesh %i | ACMR in: %f out: %f | ~%.1f%%",meshNum,fACMR,fACMR2,
+ ((fACMR - fACMR2) / fACMR) * 100.f);
+ DefaultLogger::get()->debug(szBuff);
+ }
+
+ fACMR2 *= pMesh->mNumFaces;
+ }
+ // sort the output index buffer back to the input array
+ piCSIter = piIBOutput;
+ for (aiFace* pcFace = pMesh->mFaces; pcFace != pcEnd;++pcFace) {
+ pcFace->mIndices[0] = *piCSIter++;
+ pcFace->mIndices[1] = *piCSIter++;
+ pcFace->mIndices[2] = *piCSIter++;
+ }
+
+ // delete temporary storage
+ delete[] piCachingStamps;
+ delete[] piIBOutput;
+ delete[] piCandidates;
+ delete[] piNumTriPtrNoModify;
+ return fACMR2;
+}
diff --git a/src/3rdparty/assimp/code/ImproveCacheLocality.h b/src/3rdparty/assimp/code/ImproveCacheLocality.h
new file mode 100644
index 000000000..58613e04c
--- /dev/null
+++ b/src/3rdparty/assimp/code/ImproveCacheLocality.h
@@ -0,0 +1,102 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiTypes.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 ASSIMP_API ImproveCacheLocalityProcess : public BaseProcess
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ ImproveCacheLocalityProcess();
+
+ /** Destructor, private as well */
+ ~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/src/3rdparty/assimp/code/JoinVerticesProcess.cpp b/src/3rdparty/assimp/code/JoinVerticesProcess.cpp
new file mode 100644
index 000000000..507c87cee
--- /dev/null
+++ b/src/3rdparty/assimp/code/JoinVerticesProcess.cpp
@@ -0,0 +1,398 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the post processing step to join identical vertices
+ * for all imported meshes
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
+
+#include "JoinVerticesProcess.h"
+#include "ProcessHelper.h"
+#include "Vertex.h"
+#include "TinyFormatter.h"
+
+#include <QtGlobal>
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+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)
+{
+ DefaultLogger::get()->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)
+ {
+ DefaultLogger::get()->debug("JoinVerticesProcess finished ");
+ } else
+ {
+ char szBuff[128]; // should be sufficiently large in every case
+ sprintf(szBuff,"JoinVerticesProcess finished | Verts in: %i out: %i | ~%.1f%%",
+ iNumOldVertices,
+ iNumVertices,
+ ((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f);
+ DefaultLogger::get()->info(szBuff);
+ }
+ }
+
+ pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Unites identical vertices in the given mesh
+int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
+{
+ Q_ASSERT( AI_MAX_NUMBER_OF_COLOR_SETS == 4);
+ Q_ASSERT( AI_MAX_NUMBER_OF_TEXTURECOORDS == 4);
+
+ // Return early if we don't have any positions
+ if (!pMesh->HasPositions() || !pMesh->HasFaces()) {
+ return 0;
+ }
+
+ // 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.
+ Q_ASSERT(AI_MAX_VERTICES == 0x7fffffff);
+ std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
+
+ // 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;
+ 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;
+ }
+ }
+ if (!vertexFinder) {
+ // bad, need to compute it.
+ _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
+ vertexFinder = &_vertexFinder;
+ }
+
+ // Squared because we check against squared length of the vector difference
+ static const float squareEpsilon = epsilon * epsilon;
+
+ // 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->mTextureCoords[1] ||
+ pMesh->mTextureCoords[2] ||
+ pMesh->mTextureCoords[3] ||
+ pMesh->mColors[0] ||
+ pMesh->mColors[1] ||
+ pMesh->mColors[2] ||
+ pMesh->mColors[3] );
+
+ // Now check each vertex if it brings something new to the table
+ for ( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
+ // 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];
+ // Position mismatch is impossible - the vertex finder already discarded all non-matching positions
+
+ // 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 comparision succeeds.
+ // By this method the non-present attributes are effectively ignored in the comparision.
+ if ( (uv.normal - v.normal).SquareLength() > squareEpsilon)
+ continue;
+ if ( (uv.texcoords[0] - v.texcoords[0]).SquareLength() > squareEpsilon)
+ continue;
+ if ( (uv.tangent - v.tangent).SquareLength() > squareEpsilon)
+ continue;
+ if ( (uv.bitangent - v.bitangent).SquareLength() > squareEpsilon)
+ continue;
+
+ // 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){
+ // manually unrolled because continue wouldn't work as desired in an inner loop
+ if ( GetColorDifference( uv.colors[0], v.colors[0]) > squareEpsilon)
+ continue;
+ if ( GetColorDifference( uv.colors[1], v.colors[1]) > squareEpsilon)
+ continue;
+ if ( GetColorDifference( uv.colors[2], v.colors[2]) > squareEpsilon)
+ continue;
+ if ( GetColorDifference( uv.colors[3], v.colors[3]) > squareEpsilon)
+ continue;
+
+ // texture coord matching manually unrolled as well
+ if ( (uv.texcoords[1] - v.texcoords[1]).SquareLength() > squareEpsilon)
+ continue;
+ if ( (uv.texcoords[2] - v.texcoords[2]).SquareLength() > squareEpsilon)
+ continue;
+ if ( (uv.texcoords[3] - v.texcoords[3]).SquareLength() > squareEpsilon)
+ 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 upto now -> so add it
+ replaceIndex[a] = (unsigned int)uniqueVertices.size();
+ uniqueVertices.push_back( v);
+ }
+ }
+
+ if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
+ DefaultLogger::get()->debug((Formatter::format(),
+ "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,
+ "%"
+ ));
+ }
+
+ // 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
+ 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];
+ }
+ }
+
+ // 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);
+
+ 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);
+ }
+ }
+
+ 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;
+ DefaultLogger::get()->warn("Removing bone -> no weights remaining");
+ }
+ }
+ return pMesh->mNumVertices;
+}
+
+#endif // !! ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
diff --git a/src/3rdparty/assimp/code/JoinVerticesProcess.h b/src/3rdparty/assimp/code/JoinVerticesProcess.h
new file mode 100644
index 000000000..0ce00ab58
--- /dev/null
+++ b/src/3rdparty/assimp/code/JoinVerticesProcess.h
@@ -0,0 +1,105 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiTypes.h"
+
+struct aiMesh;
+
+namespace Assimp
+{
+
+class JoinVerticesTest;
+
+// ---------------------------------------------------------------------------
+/** 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
+{
+ friend class Importer;
+ friend class JoinVerticesTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ JoinVerticesProcess();
+
+ /** Destructor, private as well */
+ ~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);
+
+protected:
+ // -------------------------------------------------------------------
+ /** 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/src/3rdparty/assimp/code/LWOAnimation.cpp b/src/3rdparty/assimp/code/LWOAnimation.cpp
new file mode 100644
index 000000000..e4b928675
--- /dev/null
+++ b/src/3rdparty/assimp/code/LWOAnimation.cpp
@@ -0,0 +1,589 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file LWOAnimation.cpp
+ * @brief LWOAnimationResolver utility class
+ *
+ * It's a very generic implementation of LightWave's system of
+ * componentwise-animated stuff. The one and only fully free
+ * implementation of LightWave envelopes of which I know.
+*/
+
+#include "AssimpPCH.h"
+#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER) && (!defined ASSIMP_BUILD_NO_LWS_IMPORTER)
+
+// internal headers
+#include "LWOFileData.h"
+
+#include <functional>
+
+using namespace Assimp;
+using namespace Assimp::LWO;
+
+// ------------------------------------------------------------------------------------------------
+// Construct an animation resolver from a given list of envelopes
+AnimResolver::AnimResolver(std::list<Envelope>& _envelopes,double tick)
+ : envelopes (_envelopes)
+ , sample_rate (0.)
+{
+ trans_x = trans_y = trans_z = NULL;
+ rotat_x = rotat_y = rotat_z = NULL;
+ scale_x = scale_y = scale_z = NULL;
+ envl_x = envl_y = envl_z = NULL;
+ end_x = end_y = end_z = false;
+ flags = 0;
+ sample_delta = 0.0;
+
+ first = last = 150392.;
+
+ // find transformation envelopes
+ for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
+
+ (*it).old_first = 0;
+ (*it).old_last = (*it).keys.size()-1;
+
+ if ((*it).keys.empty()) continue;
+ switch ((*it).type) {
+
+ // translation
+ case LWO::EnvelopeType_Position_X:
+ trans_x = &*it;break;
+ case LWO::EnvelopeType_Position_Y:
+ trans_y = &*it;break;
+ case LWO::EnvelopeType_Position_Z:
+ trans_z = &*it;break;
+
+ // rotation
+ case LWO::EnvelopeType_Rotation_Heading:
+ rotat_x = &*it;break;
+ case LWO::EnvelopeType_Rotation_Pitch:
+ rotat_y = &*it;break;
+ case LWO::EnvelopeType_Rotation_Bank:
+ rotat_z = &*it;break;
+
+ // scaling
+ case LWO::EnvelopeType_Scaling_X:
+ scale_x = &*it;break;
+ case LWO::EnvelopeType_Scaling_Y:
+ scale_y = &*it;break;
+ case LWO::EnvelopeType_Scaling_Z:
+ scale_z = &*it;break;
+ default:
+ continue;
+ };
+
+ // convert from seconds to ticks
+ for (std::vector<LWO::Key>::iterator d = (*it).keys.begin(); d != (*it).keys.end(); ++d)
+ (*d).time *= tick;
+
+ // set default animation range (minimum and maximum time value for which we have a keyframe)
+ first = std::min(first, (*it).keys.front().time );
+ last = std::max(last, (*it).keys.back().time );
+ }
+
+ // deferred setup of animation range to increase performance.
+ // typically the application will want to specify its own.
+ need_to_setup = true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reset all envelopes to their original contents
+void AnimResolver::ClearAnimRangeSetup()
+{
+ for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
+
+ (*it).keys.erase((*it).keys.begin(),(*it).keys.begin()+(*it).old_first);
+ (*it).keys.erase((*it).keys.begin()+(*it).old_last+1,(*it).keys.end());
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Insert additional keys to match LWO's pre& post behaviours.
+void AnimResolver::UpdateAnimRangeSetup()
+{
+ // XXX doesn't work yet (hangs if more than one envelope channels needs to be interpolated)
+
+ for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
+ if ((*it).keys.empty()) continue;
+
+ const double my_first = (*it).keys.front().time;
+ const double my_last = (*it).keys.back().time;
+
+ const double delta = my_last-my_first;
+ const size_t old_size = (*it).keys.size();
+
+ const float value_delta = (*it).keys.back().value - (*it).keys.front().value;
+
+ // NOTE: We won't handle reset, linear and constant here.
+ // See DoInterpolation() for their implementation.
+
+ // process pre behaviour
+ switch ((*it).pre) {
+ case LWO::PrePostBehaviour_OffsetRepeat:
+ case LWO::PrePostBehaviour_Repeat:
+ case LWO::PrePostBehaviour_Oscillate:
+ {
+ const double start_time = delta - fmod(my_first-first,delta);
+ std::vector<LWO::Key>::iterator n = std::find_if((*it).keys.begin(),(*it).keys.end(),
+ std::bind1st(std::greater<double>(),start_time)),m;
+
+ size_t ofs = 0;
+ if (n != (*it).keys.end()) {
+ // copy from here - don't use iterators, insert() would invalidate them
+ ofs = (*it).keys.end()-n;
+ (*it).keys.insert((*it).keys.begin(),ofs,LWO::Key());
+
+ std::copy((*it).keys.end()-ofs,(*it).keys.end(),(*it).keys.begin());
+ }
+
+ // do full copies. again, no iterators
+ const unsigned int num = (unsigned int)((my_first-first) / delta);
+ (*it).keys.resize((*it).keys.size() + num*old_size);
+
+ n = (*it).keys.begin()+ofs;
+ bool reverse = false;
+ for (unsigned int i = 0; i < num; ++i) {
+ m = n+old_size*(i+1);
+ std::copy(n,n+old_size,m);
+
+ if ((*it).pre == LWO::PrePostBehaviour_Oscillate && (reverse = !reverse))
+ std::reverse(m,m+old_size-1);
+ }
+
+ // update time values
+ n = (*it).keys.end() - (old_size+1);
+ double cur_minus = delta;
+ unsigned int tt = 1;
+ for (const double tmp = delta*(num+1);cur_minus <= tmp;cur_minus += delta,++tt) {
+ m = (delta == tmp ? (*it).keys.begin() : n - (old_size+1));
+ for (;m != n; --n) {
+ (*n).time -= cur_minus;
+
+ // offset repeat? add delta offset to key value
+ if ((*it).pre == LWO::PrePostBehaviour_OffsetRepeat) {
+ (*n).value += tt * value_delta;
+ }
+ }
+ }
+ break;
+ }
+ default:
+ // silence compiler warning
+ break;
+ }
+
+ // process post behaviour
+ switch ((*it).post) {
+
+ case LWO::PrePostBehaviour_OffsetRepeat:
+ case LWO::PrePostBehaviour_Repeat:
+ case LWO::PrePostBehaviour_Oscillate:
+
+ break;
+
+ default:
+ // silence compiler warning
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Extract bind pose matrix
+void AnimResolver::ExtractBindPose(aiMatrix4x4& out)
+{
+ // If we have no envelopes, return identity
+ if (envelopes.empty()) {
+ out = aiMatrix4x4();
+ return;
+ }
+ aiVector3D angles, scaling(1.f,1.f,1.f), translation;
+
+ if (trans_x) translation.x = trans_x->keys[0].value;
+ if (trans_y) translation.y = trans_y->keys[0].value;
+ if (trans_z) translation.z = trans_z->keys[0].value;
+
+ if (rotat_x) angles.x = rotat_x->keys[0].value;
+ if (rotat_y) angles.y = rotat_y->keys[0].value;
+ if (rotat_z) angles.z = rotat_z->keys[0].value;
+
+ if (scale_x) scaling.x = scale_x->keys[0].value;
+ if (scale_y) scaling.y = scale_y->keys[0].value;
+ if (scale_z) scaling.z = scale_z->keys[0].value;
+
+ // build the final matrix
+ aiMatrix4x4 s,r,t;
+
+ r.FromEulerAnglesXYZ(angles);
+ //aiMatrix4x4::RotationY(angles.y,r);
+ // fixme: make FromEulerAngles static, too
+ aiMatrix4x4::Translation(translation,t);
+ aiMatrix4x4::Scaling(scaling,s);
+ out = s*r*t;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Do a single interpolation on a channel
+void AnimResolver::DoInterpolation(std::vector<LWO::Key>::const_iterator cur,
+ LWO::Envelope* envl,double time, float& fill)
+{
+ if (envl->keys.size() == 1) {
+ fill = envl->keys[0].value;
+ return;
+ }
+
+ // check whether we're at the beginning of the animation track
+ if (cur == envl->keys.begin()) {
+
+ // ok ... this depends on pre behaviour now
+ // we don't need to handle repeat&offset repeat&oszillate here, see UpdateAnimRangeSetup()
+ switch (envl->pre)
+ {
+ case LWO::PrePostBehaviour_Linear:
+ DoInterpolation2(cur,cur+1,time,fill);
+ return;
+
+ case LWO::PrePostBehaviour_Reset:
+ fill = 0.f;
+ return;
+
+ default : //case LWO::PrePostBehaviour_Constant:
+ fill = (*cur).value;
+ return;
+ }
+ }
+ // check whether we're at the end of the animation track
+ else if (cur == envl->keys.end()-1 && time > envl->keys.rbegin()->time) {
+ // ok ... this depends on post behaviour now
+ switch (envl->post)
+ {
+ case LWO::PrePostBehaviour_Linear:
+ DoInterpolation2(cur,cur-1,time,fill);
+ return;
+
+ case LWO::PrePostBehaviour_Reset:
+ fill = 0.f;
+ return;
+
+ default : //case LWO::PrePostBehaviour_Constant:
+ fill = (*cur).value;
+ return;
+ }
+ }
+
+ // Otherwise do a simple interpolation
+ DoInterpolation2(cur-1,cur,time,fill);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Almost the same, except we won't handle pre/post conditions here
+void AnimResolver::DoInterpolation2(std::vector<LWO::Key>::const_iterator beg,
+ std::vector<LWO::Key>::const_iterator end,double time, float& fill)
+{
+ switch ((*end).inter) {
+
+ case LWO::IT_STEP:
+ // no interpolation at all - take the value of the last key
+ fill = (*beg).value;
+ return;
+ default:
+
+ // silence compiler warning
+ break;
+ }
+ // linear interpolation - default
+ fill = (*beg).value + ((*end).value - (*beg).value)*(float)(((time - (*beg).time) / ((*end).time - (*beg).time)));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Subsample animation track by given key values
+void AnimResolver::SubsampleAnimTrack(std::vector<aiVectorKey>& /*out*/,
+ double /*time*/ ,double /*sample_delta*/ )
+{
+ //ai_assert(out.empty() && sample_delta);
+
+ //const double time_start = out.back().mTime;
+// for ()
+}
+
+// ------------------------------------------------------------------------------------------------
+// Track interpolation
+void AnimResolver::InterpolateTrack(std::vector<aiVectorKey>& out,aiVectorKey& fill,double time)
+{
+ // subsample animation track?
+ if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
+ SubsampleAnimTrack(out,time, sample_delta);
+ }
+
+ fill.mTime = time;
+
+ // get x
+ if ((*cur_x).time == time) {
+ fill.mValue.x = (*cur_x).value;
+
+ if (cur_x != envl_x->keys.end()-1) /* increment x */
+ ++cur_x;
+ else end_x = true;
+ }
+ else DoInterpolation(cur_x,envl_x,time,(float&)fill.mValue.x);
+
+ // get y
+ if ((*cur_y).time == time) {
+ fill.mValue.y = (*cur_y).value;
+
+ if (cur_y != envl_y->keys.end()-1) /* increment y */
+ ++cur_y;
+ else end_y = true;
+ }
+ else DoInterpolation(cur_y,envl_y,time,(float&)fill.mValue.y);
+
+ // get z
+ if ((*cur_z).time == time) {
+ fill.mValue.z = (*cur_z).value;
+
+ if (cur_z != envl_z->keys.end()-1) /* increment z */
+ ++cur_z;
+ else end_x = true;
+ }
+ else DoInterpolation(cur_z,envl_z,time,(float&)fill.mValue.z);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build linearly subsampled keys from three single envelopes, one for each component (x,y,z)
+void AnimResolver::GetKeys(std::vector<aiVectorKey>& out,
+ LWO::Envelope* _envl_x,
+ LWO::Envelope* _envl_y,
+ LWO::Envelope* _envl_z,
+ unsigned int _flags)
+{
+ envl_x = _envl_x;
+ envl_y = _envl_y;
+ envl_z = _envl_z;
+ flags = _flags;
+
+ // generate default channels if none are given
+ LWO::Envelope def_x, def_y, def_z;
+ LWO::Key key_dummy;
+ key_dummy.time = 0.f;
+ if ((envl_x && envl_x->type == LWO::EnvelopeType_Scaling_X) ||
+ (envl_y && envl_y->type == LWO::EnvelopeType_Scaling_Y) ||
+ (envl_z && envl_z->type == LWO::EnvelopeType_Scaling_Z)) {
+ key_dummy.value = 1.f;
+ }
+ else key_dummy.value = 0.f;
+
+ if (!envl_x) {
+ envl_x = &def_x;
+ envl_x->keys.push_back(key_dummy);
+ }
+ if (!envl_y) {
+ envl_y = &def_y;
+ envl_y->keys.push_back(key_dummy);
+ }
+ if (!envl_z) {
+ envl_z = &def_z;
+ envl_z->keys.push_back(key_dummy);
+ }
+
+ // guess how many keys we'll get
+ size_t reserve;
+ double sr = 1.;
+ if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
+ if (!sample_rate)
+ sr = 100.f;
+ else sr = sample_rate;
+ sample_delta = 1.f / sr;
+
+ reserve = (size_t)(
+ std::max( envl_x->keys.end()->time,
+ std::max( envl_y->keys.end()->time, envl_z->keys.end()->time )) * sr);
+ }
+ else reserve = std::max(envl_x->keys.size(),std::max(envl_x->keys.size(),envl_z->keys.size()));
+ out.reserve(reserve+(reserve>>1));
+
+ // Iterate through all three arrays at once - it's tricky, but
+ // rather interesting to implement.
+
+ cur_x = envl_x->keys.begin();
+ cur_y = envl_y->keys.begin();
+ cur_z = envl_z->keys.begin();
+
+ end_x = end_y = end_z = false;
+ while (1) {
+
+ aiVectorKey fill;
+
+ if ((*cur_x).time == (*cur_y).time && (*cur_x).time == (*cur_z).time ) {
+
+ // we have a keyframe for all of them defined .. great,
+ // we don't need to interpolate here ...
+ fill.mTime = (*cur_x).time;
+
+ fill.mValue.x = (*cur_x).value;
+ fill.mValue.y = (*cur_y).value;
+ fill.mValue.z = (*cur_z).value;
+
+ // subsample animation track
+ if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
+ //SubsampleAnimTrack(out,cur_x, cur_y, cur_z, d, sample_delta);
+ }
+
+ if (cur_x != envl_x->keys.end()-1)
+ ++cur_x;
+ else end_x = true;
+ if (cur_y != envl_y->keys.end()-1)
+ ++cur_y;
+ else end_y = true;
+ if (cur_z != envl_z->keys.end()-1)
+ ++cur_z;
+ else end_z = true;
+ }
+
+ // Find key with lowest time value
+ else if ((*cur_x).time <= (*cur_y).time && !end_x) {
+
+ if ((*cur_z).time <= (*cur_x).time && !end_z) {
+ InterpolateTrack(out,fill,(*cur_z).time);
+ }
+ else {
+ InterpolateTrack(out,fill,(*cur_x).time);
+ }
+ }
+ else if ((*cur_z).time <= (*cur_y).time && !end_z) {
+ InterpolateTrack(out,fill,(*cur_z).time);
+ }
+ else if (!end_y) {
+ // welcome on the server, y
+ InterpolateTrack(out,fill,(*cur_y).time);
+ }
+ else {
+ // we have reached the end of at least 2 channels,
+ // only one is remaining. Extrapolate the 2.
+ if (end_y) {
+ InterpolateTrack(out,fill,(end_x ? (*cur_z) : (*cur_x)).time);
+ }
+ else if (end_x) {
+ InterpolateTrack(out,fill,(end_z ? (*cur_y) : (*cur_z)).time);
+ }
+ else { // if (end_z)
+ InterpolateTrack(out,fill,(end_y ? (*cur_x) : (*cur_y)).time);
+ }
+ }
+ out.push_back(fill);
+
+ if ( end_x && end_y && end_z ) /* finished? */
+ break;
+ }
+
+ if (flags & AI_LWO_ANIM_FLAG_START_AT_ZERO) {
+ for (std::vector<aiVectorKey>::iterator it = out.begin(); it != out.end(); ++it)
+ (*it).mTime -= first;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Extract animation channel
+void AnimResolver::ExtractAnimChannel(aiNodeAnim** out, unsigned int /* flags = 0 */)
+{
+ *out = NULL;
+
+
+ //FIXME: crashes if more than one component is animated at different timings, to be resolved.
+ return;
+
+#if 0
+ // If we have no envelopes, return NULL
+ if (envelopes.empty()) {
+ return;
+ }
+
+ // We won't spawn an animation channel if we don't have at least one envelope with more than one keyframe defined.
+ const bool trans = (trans_x && trans_x->keys.size() > 1 || trans_y && trans_y->keys.size() > 1 || trans_z && trans_z->keys.size() > 1);
+ const bool rotat = (rotat_x && rotat_x->keys.size() > 1 || rotat_y && rotat_y->keys.size() > 1 || rotat_z && rotat_z->keys.size() > 1);
+ const bool scale = (scale_x && scale_x->keys.size() > 1 || scale_y && scale_y->keys.size() > 1 || scale_z && scale_z->keys.size() > 1);
+ if (!trans && !rotat && !scale)
+ return;
+
+ // Allocate the output animation
+ aiNodeAnim* anim = *out = new aiNodeAnim();
+
+ // Setup default animation setup if necessary
+ if (need_to_setup) {
+ UpdateAnimRangeSetup();
+ need_to_setup = false;
+ }
+
+ // copy translation keys
+ if (trans) {
+ std::vector<aiVectorKey> keys;
+ GetKeys(keys,trans_x,trans_y,trans_z,flags);
+
+ anim->mPositionKeys = new aiVectorKey[ anim->mNumPositionKeys = keys.size() ];
+ std::copy(keys.begin(),keys.end(),anim->mPositionKeys);
+ }
+
+ // copy rotation keys
+ if (rotat) {
+ std::vector<aiVectorKey> keys;
+ GetKeys(keys,rotat_x,rotat_y,rotat_z,flags);
+
+ anim->mRotationKeys = new aiQuatKey[ anim->mNumRotationKeys = keys.size() ];
+
+ // convert heading, pitch, bank to quaternion
+ for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
+ aiQuatKey& qk = anim->mRotationKeys[i];
+ qk.mTime = keys[i].mTime;
+ qk.mValue = aiQuaternion( keys[i].mValue.x ,keys[i].mValue.z ,keys[i].mValue.y );
+ }
+ }
+
+ // copy scaling keys
+ if (scale) {
+ std::vector<aiVectorKey> keys;
+ GetKeys(keys,scale_x,scale_y,scale_z,flags);
+
+ anim->mScalingKeys = new aiVectorKey[ anim->mNumScalingKeys = keys.size() ];
+ std::copy(keys.begin(),keys.end(),anim->mScalingKeys);
+ }
+#endif
+}
+
+
+#endif // no lwo or no lws
diff --git a/src/3rdparty/assimp/code/LWOAnimation.h b/src/3rdparty/assimp/code/LWOAnimation.h
new file mode 100644
index 000000000..0f03e05dd
--- /dev/null
+++ b/src/3rdparty/assimp/code/LWOAnimation.h
@@ -0,0 +1,345 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file LWOAnimation.h
+ * @brief LWOAnimationResolver utility class
+ *
+ * This is for all lightwave-related file format, not only LWO.
+ * LWS isthe main purpose.
+*/
+#ifndef AI_LWO_ANIMATION_INCLUDED
+#define AI_LWO_ANIMATION_INCLUDED
+
+namespace Assimp {
+namespace LWO {
+
+// ---------------------------------------------------------------------------
+/** \brief List of recognized LWO envelopes
+ */
+enum EnvelopeType
+{
+ EnvelopeType_Position_X = 0x1,
+ EnvelopeType_Position_Y = 0x2,
+ EnvelopeType_Position_Z = 0x3,
+
+ EnvelopeType_Rotation_Heading = 0x4,
+ EnvelopeType_Rotation_Pitch = 0x5,
+ EnvelopeType_Rotation_Bank = 0x6,
+
+ EnvelopeType_Scaling_X = 0x7,
+ EnvelopeType_Scaling_Y = 0x8,
+ EnvelopeType_Scaling_Z = 0x9,
+
+ // -- currently not yet handled
+ EnvelopeType_Color_R = 0xa,
+ EnvelopeType_Color_G = 0xb,
+ EnvelopeType_Color_B = 0xc,
+
+ EnvelopeType_Falloff_X = 0xd,
+ EnvelopeType_Falloff_Y = 0xe,
+ EnvelopeType_Falloff_Z = 0xf,
+
+ EnvelopeType_Unknown
+};
+
+// ---------------------------------------------------------------------------
+/** \brief List of recognized LWO interpolation modes
+ */
+enum InterpolationType
+{
+ IT_STEP, IT_LINE, IT_TCB, IT_HERM, IT_BEZI, IT_BEZ2
+};
+
+
+// ---------------------------------------------------------------------------
+/** \brief List of recognized LWO pre or post range behaviours
+ */
+enum PrePostBehaviour
+{
+ PrePostBehaviour_Reset = 0x0,
+ PrePostBehaviour_Constant = 0x1,
+ PrePostBehaviour_Repeat = 0x2,
+ PrePostBehaviour_Oscillate = 0x3,
+ PrePostBehaviour_OffsetRepeat = 0x4,
+ PrePostBehaviour_Linear = 0x5
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO animation keyframe
+ */
+struct Key
+{
+ Key()
+ : time(0.0)
+ , value(0.0f)
+ , inter (IT_LINE)
+ {
+ params[0] = 0.0f;
+ params[1] = 0.0f;
+ params[2] = 0.0f;
+ params[3] = 0.0f;
+ params[4] = 0.0f;
+ }
+
+ //! Current time
+ double time;
+
+ //! Current value
+ float value;
+
+ //! How to interpolate this key with previous key?
+ InterpolationType inter;
+
+ //! Interpolation parameters
+ float params[5];
+
+
+ // for std::find()
+ operator double () {
+ return time;
+ }
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO animation envelope
+ */
+struct Envelope
+{
+ Envelope()
+ : index(0)
+ , type (EnvelopeType_Unknown)
+ , pre (PrePostBehaviour_Constant)
+ , post (PrePostBehaviour_Constant)
+
+ , old_first (0)
+ , old_last (0)
+ {}
+
+ //! Index of this envelope
+ unsigned int index;
+
+ //! Type of envelope
+ EnvelopeType type;
+
+ //! Pre and post-behaviour
+ PrePostBehaviour pre,post;
+
+ //! Keyframes for this envelope
+ std::vector<Key> keys;
+
+
+ // temporary data for AnimResolver
+ size_t old_first,old_last;
+};
+
+// ---------------------------------------------------------------------------
+//! @def AI_LWO_ANIM_FLAG_SAMPLE_ANIMS
+//! Flag for AnimResolver, subsamples the input data with the rate specified
+//! by AnimResolver::SetSampleRate().
+#define AI_LWO_ANIM_FLAG_SAMPLE_ANIMS 0x1
+
+
+// ---------------------------------------------------------------------------
+//! @def AI_LWO_ANIM_FLAG_START_AT_ZERO
+//! Flag for AnimResolver, ensures that the animations starts at zero.
+#define AI_LWO_ANIM_FLAG_START_AT_ZERO 0x2
+
+// ---------------------------------------------------------------------------
+/** @brief Utility class to build Assimp animations from LWO envelopes.
+ *
+ * Used for both LWO and LWS (MOT also).
+ */
+class AnimResolver
+{
+public:
+
+ // ------------------------------------------------------------------
+ /** @brief Construct an AnimResolver from a given list of envelopes
+ * @param envelopes Input envelopes. May be empty.
+ * @param Output tick rate, per second
+ * @note The input envelopes are possibly modified.
+ */
+ AnimResolver(std::list<Envelope>& envelopes,
+ double tick);
+
+public:
+
+ // ------------------------------------------------------------------
+ /** @brief Extract the bind-pose transformation matrix.
+ * @param out Receives bind-pose transformation matrix
+ */
+ void ExtractBindPose(aiMatrix4x4& out);
+
+ // ------------------------------------------------------------------
+ /** @brief Extract a node animation channel
+ * @param out Receives a pointer to a newly allocated node anim.
+ * If there's just one keyframe defined, *out is set to NULL and
+ * no animation channel is computed.
+ * @param flags Any combination of the AI_LWO_ANIM_FLAG_XXX flags.
+ */
+ void ExtractAnimChannel(aiNodeAnim** out, unsigned int flags = 0);
+
+
+ // ------------------------------------------------------------------
+ /** @brief Set the sampling rate for ExtractAnimChannel().
+ *
+ * Non-linear interpolations are subsampled with this rate (keys
+ * per second). Closer sampling positions, if existent, are kept.
+ * The sampling rate defaults to 0, if this value is not changed and
+ * AI_LWO_ANIM_FLAG_SAMPLE_ANIMS is specified for ExtractAnimChannel(),
+ * the class finds a suitable sample rate by itself.
+ */
+ void SetSampleRate(double sr) {
+ sample_rate = sr;
+ }
+
+ // ------------------------------------------------------------------
+ /** @brief Getter for SetSampleRate()
+ */
+ double GetSampleRate() const {
+ return sample_rate;
+ }
+
+ // ------------------------------------------------------------------
+ /** @brief Set the animation time range
+ *
+ * @param first Time where the animation starts, in ticks
+ * @param last Time where the animation ends, in ticks
+ */
+ void SetAnimationRange(double _first, double _last) {
+ first = _first;
+ last = _last;
+
+ ClearAnimRangeSetup();
+ UpdateAnimRangeSetup();
+ }
+
+protected:
+
+ // ------------------------------------------------------------------
+ /** @brief Build linearly subsampled keys from 3 single envelopes
+ * @param out Receives output keys
+ * @param envl_x X-component envelope
+ * @param envl_y Y-component envelope
+ * @param envl_z Z-component envelope
+ * @param flags Any combination of the AI_LWO_ANIM_FLAG_XXX flags.
+ * @note Up to two input envelopes may be NULL
+ */
+ void GetKeys(std::vector<aiVectorKey>& out,
+ LWO::Envelope* envl_x,
+ LWO::Envelope* envl_y,
+ LWO::Envelope* envl_z,
+ unsigned int flags);
+
+ // ------------------------------------------------------------------
+ /** @brief Resolve a single animation key by applying the right
+ * interpolation to it.
+ * @param cur Current key
+ * @param envl Envelope working on
+ * @param time time to be interpolated
+ * @param fill Receives the interpolated output value.
+ */
+ void DoInterpolation(std::vector<LWO::Key>::const_iterator cur,
+ LWO::Envelope* envl,double time, float& fill);
+
+ // ------------------------------------------------------------------
+ /** @brief Almost the same, except we won't handle pre/post
+ * conditions here.
+ * @see DoInterpolation
+ */
+ void DoInterpolation2(std::vector<LWO::Key>::const_iterator beg,
+ std::vector<LWO::Key>::const_iterator end,double time, float& fill);
+
+ // ------------------------------------------------------------------
+ /** @brief Interpolate 2 tracks if one is given
+ *
+ * @param out Receives extra output keys
+ * @param key_out Primary output key
+ * @param time Time to interpolate for
+ */
+ void InterpolateTrack(std::vector<aiVectorKey>& out,
+ aiVectorKey& key_out,double time);
+
+ // ------------------------------------------------------------------
+ /** @brief Subsample an animation track by a given sampling rate
+ *
+ * @param out Receives output keys. Last key at input defines the
+ * time where subsampling starts.
+ * @param time Time to end subsampling at
+ * @param sample_delta Time delta between two samples
+ */
+ void SubsampleAnimTrack(std::vector<aiVectorKey>& out,
+ double time,double sample_delta);
+
+ // ------------------------------------------------------------------
+ /** @brief Delete all keys which we inserted to match anim setup
+ */
+ void ClearAnimRangeSetup();
+
+ // ------------------------------------------------------------------
+ /** @brief Insert extra keys to match LWO's pre and post behaviours
+ * in a given time range [first...last]
+ */
+ void UpdateAnimRangeSetup();
+
+private:
+ std::list<Envelope>& envelopes;
+ double sample_rate;
+
+ LWO::Envelope* trans_x, *trans_y, *trans_z;
+ LWO::Envelope* rotat_x, *rotat_y, *rotat_z;
+ LWO::Envelope* scale_x, *scale_y, *scale_z;
+
+ double first, last;
+ bool need_to_setup;
+
+ // temporary storage
+ LWO::Envelope* envl_x, * envl_y, * envl_z;
+ std::vector<LWO::Key>::const_iterator cur_x,cur_y,cur_z;
+ bool end_x, end_y, end_z;
+
+ unsigned int flags;
+ double sample_delta;
+};
+
+} // end namespace LWO
+} // end namespace Assimp
+
+#endif // !! AI_LWO_ANIMATION_INCLUDED
diff --git a/src/3rdparty/assimp/code/LWOBLoader.cpp b/src/3rdparty/assimp/code/LWOBLoader.cpp
new file mode 100644
index 000000000..c4704ca72
--- /dev/null
+++ b/src/3rdparty/assimp/code/LWOBLoader.cpp
@@ -0,0 +1,396 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the LWO importer class for the older LWOB
+ file formats, including materials */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
+
+// Internal headers
+#include "LWOLoader.h"
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWOBFile()
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + fileSize;
+ bool running = true;
+ while (running)
+ {
+ if (mFileBuffer + sizeof(IFF::ChunkHeader) > end)break;
+ LE_NCONST IFF::ChunkHeader* const head = IFF::LoadChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ {
+ throw DeadlyImportError("LWOB: Invalid chunk length");
+ break;
+ }
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ // vertex list
+ case AI_LWO_PNTS:
+ {
+ if (!mCurLayer->mTempPoints.empty())
+ DefaultLogger::get()->warn("LWO: PNTS chunk encountered twice");
+ else LoadLWOPoints(head->length);
+ break;
+ }
+ // face list
+ case AI_LWO_POLS:
+ {
+ if (!mCurLayer->mFaces.empty())
+ DefaultLogger::get()->warn("LWO: POLS chunk encountered twice");
+ else LoadLWOBPolygons(head->length);
+ break;
+ }
+ // list of tags
+ case AI_LWO_SRFS:
+ {
+ if (!mTags->empty())
+ DefaultLogger::get()->warn("LWO: SRFS chunk encountered twice");
+ else LoadLWOTags(head->length);
+ break;
+ }
+
+ // surface chunk
+ case AI_LWO_SURF:
+ {
+ LoadLWOBSurface(head->length);
+ break;
+ }
+ }
+ mFileBuffer = next;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWOBPolygons(unsigned int length)
+{
+ // first find out how many faces and vertices we'll finally need
+ LE_NCONST uint16_t* const end = (LE_NCONST uint16_t*)(mFileBuffer+length);
+ LE_NCONST uint16_t* cursor = (LE_NCONST uint16_t*)mFileBuffer;
+
+ // perform endianess conversions
+#ifndef AI_BUILD_BIG_ENDIAN
+ while (cursor < end)ByteSwap::Swap2(cursor++);
+ cursor = (LE_NCONST uint16_t*)mFileBuffer;
+#endif
+
+ unsigned int iNumFaces = 0,iNumVertices = 0;
+ CountVertsAndFacesLWOB(iNumVertices,iNumFaces,cursor,end);
+
+ // allocate the output array and copy face indices
+ if (iNumFaces)
+ {
+ cursor = (LE_NCONST uint16_t*)mFileBuffer;
+
+ mCurLayer->mFaces.resize(iNumFaces);
+ FaceList::iterator it = mCurLayer->mFaces.begin();
+ CopyFaceIndicesLWOB(it,cursor,end);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::CountVertsAndFacesLWOB(unsigned int& verts, unsigned int& faces,
+ LE_NCONST uint16_t*& cursor, const uint16_t* const end, unsigned int max)
+{
+ while (cursor < end && max--)
+ {
+ uint16_t numIndices = *cursor++;
+ verts += numIndices;faces++;
+ cursor += numIndices;
+ int16_t surface = *cursor++;
+ if (surface < 0)
+ {
+ // there are detail polygons
+ numIndices = *cursor++;
+ CountVertsAndFacesLWOB(verts,faces,cursor,end,numIndices);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::CopyFaceIndicesLWOB(FaceList::iterator& it,
+ LE_NCONST uint16_t*& cursor,
+ const uint16_t* const end,
+ unsigned int max)
+{
+ while (cursor < end && max--)
+ {
+ LWO::Face& face = *it;++it;
+ if ((face.mNumIndices = *cursor++))
+ {
+ if (cursor + face.mNumIndices >= end)break;
+ face.mIndices = new unsigned int[face.mNumIndices];
+ for (unsigned int i = 0; i < face.mNumIndices;++i)
+ {
+ unsigned int & mi = face.mIndices[i] = *cursor++;
+ if (mi > mCurLayer->mTempPoints.size())
+ {
+ DefaultLogger::get()->warn("LWOB: face index is out of range");
+ mi = (unsigned int)mCurLayer->mTempPoints.size()-1;
+ }
+ }
+ }
+ else DefaultLogger::get()->warn("LWOB: Face has 0 indices");
+ int16_t surface = *cursor++;
+ if (surface < 0)
+ {
+ surface = -surface;
+
+ // there are detail polygons.
+ const uint16_t numPolygons = *cursor++;
+ if (cursor < end)CopyFaceIndicesLWOB(it,cursor,end,numPolygons);
+ }
+ face.surfaceIndex = surface-1;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+LWO::Texture* LWOImporter::SetupNewTextureLWOB(LWO::TextureList& list,unsigned int size)
+{
+ list.push_back(LWO::Texture());
+ LWO::Texture* tex = &list.back();
+
+ std::string type;
+ GetS0(type,size);
+ const char* s = type.c_str();
+
+ if (strstr(s, "Image Map"))
+ {
+ // Determine mapping type
+ if (strstr(s, "Planar"))
+ tex->mapMode = LWO::Texture::Planar;
+ else if (strstr(s, "Cylindrical"))
+ tex->mapMode = LWO::Texture::Cylindrical;
+ else if (strstr(s, "Spherical"))
+ tex->mapMode = LWO::Texture::Spherical;
+ else if (strstr(s, "Cubic"))
+ tex->mapMode = LWO::Texture::Cubic;
+ else if (strstr(s, "Front"))
+ tex->mapMode = LWO::Texture::FrontProjection;
+ }
+ else
+ {
+ // procedural or gradient, not supported
+ DefaultLogger::get()->error("LWOB: Unsupported legacy texture: " + type);
+ }
+
+ return tex;
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWOBSurface(unsigned int size)
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + size;
+
+ mSurfaces->push_back( LWO::Surface () );
+ LWO::Surface& surf = mSurfaces->back();
+ LWO::Texture* pTex = NULL;
+
+ GetS0(surf.mName,size);
+ bool runnning = true;
+ while (runnning) {
+ if (mFileBuffer + 6 >= end)
+ break;
+
+ IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+ /* A single test file (sonycam.lwo) seems to have invalid surface chunks.
+ * I'm assuming it's the fault of a single, unknown exporter so there are
+ * probably THOUSANDS of them. Here's a dirty workaround:
+ *
+ * We don't break if the chunk limit is exceeded. Instead, we're computing
+ * how much storage is actually left and work with this value from now on.
+ */
+ if (mFileBuffer + head->length > end) {
+ DefaultLogger::get()->error("LWOB: Invalid surface chunk length. Trying to continue.");
+ head->length = (uint16_t) (end - mFileBuffer);
+ }
+
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ // diffuse color
+ case AI_LWO_COLR:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,COLR,3);
+ surf.mColor.r = GetU1() / 255.0f;
+ surf.mColor.g = GetU1() / 255.0f;
+ surf.mColor.b = GetU1() / 255.0f;
+ break;
+ }
+ // diffuse strength ...
+ case AI_LWO_DIFF:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,DIFF,2);
+ surf.mDiffuseValue = GetU2() / 255.0f;
+ break;
+ }
+ // specular strength ...
+ case AI_LWO_SPEC:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPEC,2);
+ surf.mSpecularValue = GetU2() / 255.0f;
+ break;
+ }
+ // luminosity ...
+ case AI_LWO_LUMI:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,LUMI,2);
+ surf.mLuminosity = GetU2() / 255.0f;
+ break;
+ }
+ // transparency
+ case AI_LWO_TRAN:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TRAN,2);
+ surf.mTransparency = GetU2() / 255.0f;
+ break;
+ }
+ // surface flags
+ case AI_LWO_FLAG:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,FLAG,2);
+ uint16_t flag = GetU2();
+ if (flag & 0x4 ) surf.mMaximumSmoothAngle = 1.56207f;
+ if (flag & 0x8 ) surf.mColorHighlights = 1.f;
+ if (flag & 0x100) surf.bDoubleSided = true;
+ break;
+ }
+ // maximum smoothing angle
+ case AI_LWO_SMAN:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
+ surf.mMaximumSmoothAngle = fabs( GetF4() );
+ break;
+ }
+ // glossiness
+ case AI_LWO_GLOS:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,GLOS,2);
+ surf.mGlossiness = (float)GetU2();
+ break;
+ }
+ // color texture
+ case AI_LWO_CTEX:
+ {
+ pTex = SetupNewTextureLWOB(surf.mColorTextures,
+ head->length);
+ break;
+ }
+ // diffuse texture
+ case AI_LWO_DTEX:
+ {
+ pTex = SetupNewTextureLWOB(surf.mDiffuseTextures,
+ head->length);
+ break;
+ }
+ // specular texture
+ case AI_LWO_STEX:
+ {
+ pTex = SetupNewTextureLWOB(surf.mSpecularTextures,
+ head->length);
+ break;
+ }
+ // bump texture
+ case AI_LWO_BTEX:
+ {
+ pTex = SetupNewTextureLWOB(surf.mBumpTextures,
+ head->length);
+ break;
+ }
+ // transparency texture
+ case AI_LWO_TTEX:
+ {
+ pTex = SetupNewTextureLWOB(surf.mOpacityTextures,
+ head->length);
+ break;
+ }
+ // texture path
+ case AI_LWO_TIMG:
+ {
+ if (pTex) {
+ GetS0(pTex->mFileName,head->length);
+ }
+ else DefaultLogger::get()->warn("LWOB: Unexpected TIMG chunk");
+ break;
+ }
+ // texture strength
+ case AI_LWO_TVAL:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TVAL,1);
+ if (pTex) {
+ pTex->mStrength = (float)GetU1()/ 255.f;
+ }
+ else DefaultLogger::get()->warn("LWOB: Unexpected TVAL chunk");
+ break;
+ }
+ // texture flags
+ case AI_LWO_TFLG:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TFLG,2);
+
+ if (pTex)
+ {
+ const uint16_t s = GetU2();
+ if (s & 1)
+ pTex->majorAxis = LWO::Texture::AXIS_X;
+ else if (s & 2)
+ pTex->majorAxis = LWO::Texture::AXIS_Y;
+ else if (s & 4)
+ pTex->majorAxis = LWO::Texture::AXIS_Z;
+
+ if (s & 16)
+ DefaultLogger::get()->warn("LWOB: Ignoring \'negate\' flag on texture");
+ }
+ else DefaultLogger::get()->warn("LWOB: Unexpected TFLG chunk");
+ break;
+ }
+ }
+ mFileBuffer = next;
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_LWO_IMPORTER
diff --git a/src/3rdparty/assimp/code/LWOFileData.h b/src/3rdparty/assimp/code/LWOFileData.h
new file mode 100644
index 000000000..88bbc0aa6
--- /dev/null
+++ b/src/3rdparty/assimp/code/LWOFileData.h
@@ -0,0 +1,701 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file LWOFileData.h
+ * @brief Defines chunk constants used by the LWO file format
+
+The chunks are taken from the official LightWave SDK headers.
+
+*/
+#ifndef AI_LWO_FILEDATA_INCLUDED
+#define AI_LWO_FILEDATA_INCLUDED
+
+// STL headers
+#include <vector>
+#include <list>
+
+// public ASSIMP headers
+#include "../include/aiMesh.h"
+
+// internal headers
+#include "IFF.h"
+#include "LWOAnimation.h"
+
+namespace Assimp {
+namespace LWO {
+
+#define AI_LWO_FOURCC_LWOB AI_IFF_FOURCC('L','W','O','B')
+#define AI_LWO_FOURCC_LWO2 AI_IFF_FOURCC('L','W','O','2')
+#define AI_LWO_FOURCC_LXOB AI_IFF_FOURCC('L','X','O','B')
+
+// chunks specific to the LWOB format
+#define AI_LWO_SRFS AI_IFF_FOURCC('S','R','F','S')
+#define AI_LWO_FLAG AI_IFF_FOURCC('F','L','A','G')
+#define AI_LWO_VLUM AI_IFF_FOURCC('V','L','U','M')
+#define AI_LWO_VDIF AI_IFF_FOURCC('V','D','I','F')
+#define AI_LWO_VSPC AI_IFF_FOURCC('V','S','P','C')
+#define AI_LWO_RFLT AI_IFF_FOURCC('R','F','L','T')
+#define AI_LWO_BTEX AI_IFF_FOURCC('B','T','E','X')
+#define AI_LWO_CTEX AI_IFF_FOURCC('C','T','E','X')
+#define AI_LWO_DTEX AI_IFF_FOURCC('D','T','E','X')
+#define AI_LWO_LTEX AI_IFF_FOURCC('L','T','E','X')
+#define AI_LWO_RTEX AI_IFF_FOURCC('R','T','E','X')
+#define AI_LWO_STEX AI_IFF_FOURCC('S','T','E','X')
+#define AI_LWO_TTEX AI_IFF_FOURCC('T','T','E','X')
+#define AI_LWO_TFLG AI_IFF_FOURCC('T','F','L','G')
+#define AI_LWO_TSIZ AI_IFF_FOURCC('T','S','I','Z')
+#define AI_LWO_TCTR AI_IFF_FOURCC('T','C','T','R')
+#define AI_LWO_TFAL AI_IFF_FOURCC('T','F','A','L')
+#define AI_LWO_TVEL AI_IFF_FOURCC('T','V','E','L')
+#define AI_LWO_TCLR AI_IFF_FOURCC('T','C','L','R')
+#define AI_LWO_TVAL AI_IFF_FOURCC('T','V','A','L')
+#define AI_LWO_TAMP AI_IFF_FOURCC('T','A','M','P')
+#define AI_LWO_TIMG AI_IFF_FOURCC('T','I','M','G')
+#define AI_LWO_TAAS AI_IFF_FOURCC('T','A','A','S')
+#define AI_LWO_TREF AI_IFF_FOURCC('T','R','E','F')
+#define AI_LWO_TOPC AI_IFF_FOURCC('T','O','P','C')
+#define AI_LWO_SDAT AI_IFF_FOURCC('S','D','A','T')
+#define AI_LWO_TFP0 AI_IFF_FOURCC('T','F','P','0')
+#define AI_LWO_TFP1 AI_IFF_FOURCC('T','F','P','1')
+
+
+/* top-level chunks */
+#define AI_LWO_LAYR AI_IFF_FOURCC('L','A','Y','R')
+#define AI_LWO_TAGS AI_IFF_FOURCC('T','A','G','S')
+#define AI_LWO_PNTS AI_IFF_FOURCC('P','N','T','S')
+#define AI_LWO_BBOX AI_IFF_FOURCC('B','B','O','X')
+#define AI_LWO_VMAP AI_IFF_FOURCC('V','M','A','P')
+#define AI_LWO_VMAD AI_IFF_FOURCC('V','M','A','D')
+#define AI_LWO_POLS AI_IFF_FOURCC('P','O','L','S')
+#define AI_LWO_PTAG AI_IFF_FOURCC('P','T','A','G')
+#define AI_LWO_ENVL AI_IFF_FOURCC('E','N','V','L')
+#define AI_LWO_CLIP AI_IFF_FOURCC('C','L','I','P')
+#define AI_LWO_SURF AI_IFF_FOURCC('S','U','R','F')
+#define AI_LWO_DESC AI_IFF_FOURCC('D','E','S','C')
+#define AI_LWO_TEXT AI_IFF_FOURCC('T','E','X','T')
+#define AI_LWO_ICON AI_IFF_FOURCC('I','C','O','N')
+
+/* polygon types */
+#define AI_LWO_FACE AI_IFF_FOURCC('F','A','C','E')
+#define AI_LWO_CURV AI_IFF_FOURCC('C','U','R','V')
+#define AI_LWO_PTCH AI_IFF_FOURCC('P','T','C','H')
+#define AI_LWO_MBAL AI_IFF_FOURCC('M','B','A','L')
+#define AI_LWO_BONE AI_IFF_FOURCC('B','O','N','E')
+#define AI_LWO_SUBD AI_IFF_FOURCC('S','U','B','D')
+
+/* polygon tags */
+#define AI_LWO_SURF AI_IFF_FOURCC('S','U','R','F')
+#define AI_LWO_PART AI_IFF_FOURCC('P','A','R','T')
+#define AI_LWO_SMGP AI_IFF_FOURCC('S','M','G','P')
+
+/* envelopes */
+#define AI_LWO_PRE AI_IFF_FOURCC('P','R','E',' ')
+#define AI_LWO_POST AI_IFF_FOURCC('P','O','S','T')
+#define AI_LWO_KEY AI_IFF_FOURCC('K','E','Y',' ')
+#define AI_LWO_SPAN AI_IFF_FOURCC('S','P','A','N')
+#define AI_LWO_TCB AI_IFF_FOURCC('T','C','B',' ')
+#define AI_LWO_HERM AI_IFF_FOURCC('H','E','R','M')
+#define AI_LWO_BEZI AI_IFF_FOURCC('B','E','Z','I')
+#define AI_LWO_BEZ2 AI_IFF_FOURCC('B','E','Z','2')
+#define AI_LWO_LINE AI_IFF_FOURCC('L','I','N','E')
+#define AI_LWO_STEP AI_IFF_FOURCC('S','T','E','P')
+
+/* clips */
+#define AI_LWO_STIL AI_IFF_FOURCC('S','T','I','L')
+#define AI_LWO_ISEQ AI_IFF_FOURCC('I','S','E','Q')
+#define AI_LWO_ANIM AI_IFF_FOURCC('A','N','I','M')
+#define AI_LWO_XREF AI_IFF_FOURCC('X','R','E','F')
+#define AI_LWO_STCC AI_IFF_FOURCC('S','T','C','C')
+#define AI_LWO_TIME AI_IFF_FOURCC('T','I','M','E')
+#define AI_LWO_CONT AI_IFF_FOURCC('C','O','N','T')
+#define AI_LWO_BRIT AI_IFF_FOURCC('B','R','I','T')
+#define AI_LWO_SATR AI_IFF_FOURCC('S','A','T','R')
+#define AI_LWO_HUE AI_IFF_FOURCC('H','U','E',' ')
+#define AI_LWO_GAMM AI_IFF_FOURCC('G','A','M','M')
+#define AI_LWO_NEGA AI_IFF_FOURCC('N','E','G','A')
+#define AI_LWO_IFLT AI_IFF_FOURCC('I','F','L','T')
+#define AI_LWO_PFLT AI_IFF_FOURCC('P','F','L','T')
+
+/* surfaces */
+#define AI_LWO_COLR AI_IFF_FOURCC('C','O','L','R')
+#define AI_LWO_LUMI AI_IFF_FOURCC('L','U','M','I')
+#define AI_LWO_DIFF AI_IFF_FOURCC('D','I','F','F')
+#define AI_LWO_SPEC AI_IFF_FOURCC('S','P','E','C')
+#define AI_LWO_GLOS AI_IFF_FOURCC('G','L','O','S')
+#define AI_LWO_REFL AI_IFF_FOURCC('R','E','F','L')
+#define AI_LWO_RFOP AI_IFF_FOURCC('R','F','O','P')
+#define AI_LWO_RIMG AI_IFF_FOURCC('R','I','M','G')
+#define AI_LWO_RSAN AI_IFF_FOURCC('R','S','A','N')
+#define AI_LWO_TRAN AI_IFF_FOURCC('T','R','A','N')
+#define AI_LWO_TROP AI_IFF_FOURCC('T','R','O','P')
+#define AI_LWO_TIMG AI_IFF_FOURCC('T','I','M','G')
+#define AI_LWO_RIND AI_IFF_FOURCC('R','I','N','D')
+#define AI_LWO_TRNL AI_IFF_FOURCC('T','R','N','L')
+#define AI_LWO_BUMP AI_IFF_FOURCC('B','U','M','P')
+#define AI_LWO_SMAN AI_IFF_FOURCC('S','M','A','N')
+#define AI_LWO_SIDE AI_IFF_FOURCC('S','I','D','E')
+#define AI_LWO_CLRH AI_IFF_FOURCC('C','L','R','H')
+#define AI_LWO_CLRF AI_IFF_FOURCC('C','L','R','F')
+#define AI_LWO_ADTR AI_IFF_FOURCC('A','D','T','R')
+#define AI_LWO_SHRP AI_IFF_FOURCC('S','H','R','P')
+#define AI_LWO_LINE AI_IFF_FOURCC('L','I','N','E')
+#define AI_LWO_LSIZ AI_IFF_FOURCC('L','S','I','Z')
+#define AI_LWO_ALPH AI_IFF_FOURCC('A','L','P','H')
+#define AI_LWO_AVAL AI_IFF_FOURCC('A','V','A','L')
+#define AI_LWO_GVAL AI_IFF_FOURCC('G','V','A','L')
+#define AI_LWO_BLOK AI_IFF_FOURCC('B','L','O','K')
+#define AI_LWO_VCOL AI_IFF_FOURCC('V','C','O','L')
+
+/* texture layer */
+#define AI_LWO_TYPE AI_IFF_FOURCC('T','Y','P','E')
+#define AI_LWO_CHAN AI_IFF_FOURCC('C','H','A','N')
+#define AI_LWO_NAME AI_IFF_FOURCC('N','A','M','E')
+#define AI_LWO_ENAB AI_IFF_FOURCC('E','N','A','B')
+#define AI_LWO_OPAC AI_IFF_FOURCC('O','P','A','C')
+#define AI_LWO_FLAG AI_IFF_FOURCC('F','L','A','G')
+#define AI_LWO_PROJ AI_IFF_FOURCC('P','R','O','J')
+#define AI_LWO_STCK AI_IFF_FOURCC('S','T','C','K')
+#define AI_LWO_TAMP AI_IFF_FOURCC('T','A','M','P')
+
+/* texture coordinates */
+#define AI_LWO_TMAP AI_IFF_FOURCC('T','M','A','P')
+#define AI_LWO_AXIS AI_IFF_FOURCC('A','X','I','S')
+#define AI_LWO_CNTR AI_IFF_FOURCC('C','N','T','R')
+#define AI_LWO_SIZE AI_IFF_FOURCC('S','I','Z','E')
+#define AI_LWO_ROTA AI_IFF_FOURCC('R','O','T','A')
+#define AI_LWO_OREF AI_IFF_FOURCC('O','R','E','F')
+#define AI_LWO_FALL AI_IFF_FOURCC('F','A','L','L')
+#define AI_LWO_CSYS AI_IFF_FOURCC('C','S','Y','S')
+
+/* image map */
+#define AI_LWO_IMAP AI_IFF_FOURCC('I','M','A','P')
+#define AI_LWO_IMAG AI_IFF_FOURCC('I','M','A','G')
+#define AI_LWO_WRAP AI_IFF_FOURCC('W','R','A','P')
+#define AI_LWO_WRPW AI_IFF_FOURCC('W','R','P','W')
+#define AI_LWO_WRPH AI_IFF_FOURCC('W','R','P','H')
+#define AI_LWO_VMAP AI_IFF_FOURCC('V','M','A','P')
+#define AI_LWO_AAST AI_IFF_FOURCC('A','A','S','T')
+#define AI_LWO_PIXB AI_IFF_FOURCC('P','I','X','B')
+
+/* procedural */
+#define AI_LWO_PROC AI_IFF_FOURCC('P','R','O','C')
+#define AI_LWO_COLR AI_IFF_FOURCC('C','O','L','R')
+#define AI_LWO_VALU AI_IFF_FOURCC('V','A','L','U')
+#define AI_LWO_FUNC AI_IFF_FOURCC('F','U','N','C')
+#define AI_LWO_FTPS AI_IFF_FOURCC('F','T','P','S')
+#define AI_LWO_ITPS AI_IFF_FOURCC('I','T','P','S')
+#define AI_LWO_ETPS AI_IFF_FOURCC('E','T','P','S')
+
+/* gradient */
+#define AI_LWO_GRAD AI_IFF_FOURCC('G','R','A','D')
+#define AI_LWO_GRST AI_IFF_FOURCC('G','R','S','T')
+#define AI_LWO_GREN AI_IFF_FOURCC('G','R','E','N')
+#define AI_LWO_PNAM AI_IFF_FOURCC('P','N','A','M')
+#define AI_LWO_INAM AI_IFF_FOURCC('I','N','A','M')
+#define AI_LWO_GRPT AI_IFF_FOURCC('G','R','P','T')
+#define AI_LWO_FKEY AI_IFF_FOURCC('F','K','E','Y')
+#define AI_LWO_IKEY AI_IFF_FOURCC('I','K','E','Y')
+
+/* shader */
+#define AI_LWO_SHDR AI_IFF_FOURCC('S','H','D','R')
+#define AI_LWO_DATA AI_IFF_FOURCC('D','A','T','A')
+
+
+/* VMAP types */
+#define AI_LWO_TXUV AI_IFF_FOURCC('T','X','U','V')
+#define AI_LWO_RGB AI_IFF_FOURCC('R','G','B',' ')
+#define AI_LWO_RGBA AI_IFF_FOURCC('R','G','B','A')
+#define AI_LWO_WGHT AI_IFF_FOURCC('W','G','H','T')
+
+#define AI_LWO_MNVW AI_IFF_FOURCC('M','N','V','W')
+#define AI_LWO_MORF AI_IFF_FOURCC('M','O','R','F')
+#define AI_LWO_SPOT AI_IFF_FOURCC('S','P','O','T')
+#define AI_LWO_PICK AI_IFF_FOURCC('P','I','C','K')
+
+// MODO extension - per-vertex normal vectors
+#define AI_LWO_MODO_NORM AI_IFF_FOURCC('N', 'O', 'R', 'M')
+
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a face in a LWO file
+ *
+ * \note We can't use the code in SmoothingGroups.inl here - the mesh
+ * structures of 3DS/ASE and LWO are too different.
+ */
+struct Face : public aiFace
+{
+ //! Default construction
+ Face()
+ : surfaceIndex (0)
+ , smoothGroup (0)
+ , type (AI_LWO_FACE)
+ {}
+
+ //! Construction from given type
+ Face(uint32_t _type)
+ : surfaceIndex (0)
+ , smoothGroup (0)
+ , type (_type)
+ {}
+
+ //! Copy construction
+ Face(const Face& f) : aiFace() {
+ *this = f;
+ }
+
+ //! Zero-based index into tags chunk
+ unsigned int surfaceIndex;
+
+ //! Smooth group this face is assigned to
+ unsigned int smoothGroup;
+
+ //! Type of face
+ uint32_t type;
+
+
+ //! Assignment operator
+ Face& operator=(const LWO::Face& f) {
+ aiFace::operator =(f);
+ surfaceIndex = f.surfaceIndex;
+ smoothGroup = f.smoothGroup;
+ type = f.type;
+ return *this;
+ }
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Base structure for all vertex map representations
+ */
+struct VMapEntry
+{
+ VMapEntry(unsigned int _dims)
+ : dims(_dims)
+ {}
+
+ virtual ~VMapEntry() {}
+
+ //! allocates memory for the vertex map
+ virtual void Allocate(unsigned int num)
+ {
+ if (!rawData.empty())
+ return; // return if already allocated
+
+ const unsigned int m = num*dims;
+ rawData.reserve(m + (m>>2u)); // 25% as extra storage for VMADs
+ rawData.resize(m,0.f);
+ abAssigned.resize(num,false);
+ }
+
+ std::string name;
+ unsigned int dims;
+
+ std::vector<float> rawData;
+ std::vector<bool> abAssigned;
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Represents an extra vertex color channel
+ */
+struct VColorChannel : public VMapEntry
+{
+ VColorChannel()
+ : VMapEntry(4)
+ {}
+
+ //! need to overwrite this function - the alpha channel must
+ //! be initialized to 1.0 by default
+ virtual void Allocate(unsigned int num)
+ {
+ if (!rawData.empty())
+ return; // return if already allocated
+
+ register unsigned int m = num*dims;
+ rawData.reserve(m + (m>>2u)); // 25% as extra storage for VMADs
+ rawData.resize(m);
+
+ for (aiColor4D* p = (aiColor4D*)&rawData[0]; p < (aiColor4D*)&rawData[m-1]; ++p)
+ p->a = 1.f;
+
+ abAssigned.resize(num,false);
+ }
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Represents an extra vertex UV channel
+ */
+struct UVChannel : public VMapEntry
+{
+ UVChannel()
+ : VMapEntry(2)
+ {}
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Represents a weight map
+ */
+struct WeightChannel : public VMapEntry
+{
+ WeightChannel()
+ : VMapEntry(1)
+ {}
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Represents a vertex-normals channel (MODO extension)
+ */
+struct NormalChannel : public VMapEntry
+{
+ NormalChannel()
+ : VMapEntry(3)
+ {}
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO file texture
+ */
+struct Texture
+{
+ // we write the enum values out here to make debugging easier ...
+ enum BlendType
+ {
+ Normal = 0,
+ Subtractive = 1,
+ Difference = 2,
+ Multiply = 3,
+ Divide = 4,
+ Alpha = 5,
+ TextureDispl = 6,
+ Additive = 7
+ };
+
+ enum MappingMode
+ {
+ Planar = 0,
+ Cylindrical = 1,
+ Spherical = 2,
+ Cubic = 3,
+ FrontProjection = 4,
+ UV = 5
+ };
+
+ enum Axes
+ {
+ AXIS_X = 0,
+ AXIS_Y = 1,
+ AXIS_Z = 2
+ };
+
+ enum Wrap
+ {
+ RESET = 0,
+ REPEAT = 1,
+ MIRROR = 2,
+ EDGE = 3
+ };
+
+ Texture()
+ : mClipIdx(0xffffffff)
+ , mStrength (1.0f)
+ , type(0)
+ , mUVChannelIndex ("unknown")
+ , mRealUVIndex (0xffffffff)
+ , enabled (true)
+ , blendType (Additive)
+ , bCanUse (true)
+ , mapMode (UV)
+ , majorAxis (AXIS_X)
+ , wrapAmountH (1.0f)
+ , wrapAmountW (1.0f)
+ , wrapModeWidth (REPEAT)
+ , wrapModeHeight (REPEAT)
+ , ordinal ("\x00")
+ {}
+
+ //! File name of the texture
+ std::string mFileName;
+
+ //! Clip index
+ unsigned int mClipIdx;
+
+ //! Strength of the texture - blend factor
+ float mStrength;
+
+ uint32_t type; // type of the texture
+
+ //! Name of the corresponding UV channel
+ std::string mUVChannelIndex;
+ unsigned int mRealUVIndex;
+
+ //! is the texture enabled?
+ bool enabled;
+
+ //! blend type
+ BlendType blendType;
+
+ //! are we able to use the texture?
+ bool bCanUse;
+
+ //! mapping mode
+ MappingMode mapMode;
+
+ //! major axis for planar, cylindrical, spherical projections
+ Axes majorAxis;
+
+ //! wrap amount for cylindrical and spherical projections
+ float wrapAmountH,wrapAmountW;
+
+ //! wrapping mode for the texture
+ Wrap wrapModeWidth,wrapModeHeight;
+
+ //! ordinal string of the texture
+ std::string ordinal;
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO file clip
+ */
+struct Clip
+{
+ enum Type
+ {
+ STILL, SEQ, REF, UNSUPPORTED
+ } type;
+
+ Clip()
+ : type (UNSUPPORTED)
+ , clipRef(0)
+ , idx (0)
+ , negate (false)
+ {}
+
+ //! path to the base texture -
+ std::string path;
+
+ //! reference to another CLIP
+ unsigned int clipRef;
+
+ //! index of the clip
+ unsigned int idx;
+
+ //! Negate the clip?
+ bool negate;
+};
+
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO file shader
+ *
+ * Later
+ */
+struct Shader
+{
+ Shader()
+ : ordinal ("\x00")
+ , functionName ("unknown")
+ , enabled (true)
+ {}
+
+ std::string ordinal;
+ std::string functionName;
+ bool enabled;
+};
+
+typedef std::list < Texture > TextureList;
+typedef std::list < Shader > ShaderList;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO file surface (= material)
+ */
+struct Surface
+{
+ Surface()
+ : mColor (0.78431f,0.78431f,0.78431f)
+ , bDoubleSided (false)
+ , mDiffuseValue (1.f)
+ , mSpecularValue (0.f)
+ , mTransparency (0.f)
+ , mGlossiness (0.4f)
+ , mLuminosity (0.f)
+ , mColorHighlights (0.f)
+ , mMaximumSmoothAngle (0.f) // 0 == not specified, no smoothing
+ , mVCMap ("")
+ , mVCMapType (AI_LWO_RGBA)
+ , mIOR (1.f) // vakuum
+ , mBumpIntensity (1.f)
+ , mWireframe (false)
+ , mAdditiveTransparency (0.f)
+ {}
+
+ //! Name of the surface
+ std::string mName;
+
+ //! Color of the surface
+ aiColor3D mColor;
+
+ //! true for two-sided materials
+ bool bDoubleSided;
+
+ //! Various material parameters
+ float mDiffuseValue,mSpecularValue,mTransparency,mGlossiness,mLuminosity,mColorHighlights;
+
+ //! Maximum angle between two adjacent triangles
+ //! that they can be smoothed - in degrees
+ float mMaximumSmoothAngle;
+
+ //! Vertex color map to be used to color the surface
+ std::string mVCMap;
+ uint32_t mVCMapType;
+
+ //! Names of the special shaders to be applied to the surface
+ ShaderList mShaders;
+
+ //! Textures - the first entry in the list is evaluated first
+ TextureList mColorTextures, // color textures are added to both diffuse and specular texture stacks
+ mDiffuseTextures,
+ mSpecularTextures,
+ mOpacityTextures,
+ mBumpTextures,
+ mGlossinessTextures,
+ mReflectionTextures;
+
+ //! Index of refraction
+ float mIOR;
+
+ //! Bump intensity scaling
+ float mBumpIntensity;
+
+ //! Wireframe flag
+ bool mWireframe;
+
+ //! Intensity of additive blending
+ float mAdditiveTransparency;
+};
+
+// ---------------------------------------------------------------------------
+#define AI_LWO_VALIDATE_CHUNK_LENGTH(length,name,size) \
+ if (length < size) \
+ { \
+ throw DeadlyImportError("LWO: "#name" chunk is too small"); \
+ } \
+
+
+// some typedefs ... to make life with loader monsters like this easier
+typedef std::vector < aiVector3D > PointList;
+typedef std::vector < LWO::Face > FaceList;
+typedef std::vector < LWO::Surface > SurfaceList;
+typedef std::vector < std::string > TagList;
+typedef std::vector < unsigned int > TagMappingTable;
+typedef std::vector < unsigned int > ReferrerList;
+typedef std::vector < WeightChannel > WeightChannelList;
+typedef std::vector < VColorChannel > VColorChannelList;
+typedef std::vector < UVChannel > UVChannelList;
+typedef std::vector < Clip > ClipList;
+typedef std::vector < Envelope > EnvelopeList;
+typedef std::vector < unsigned int > SortedRep;
+
+// ---------------------------------------------------------------------------
+/** \brief Represents a layer in the file
+ */
+struct Layer
+{
+ Layer()
+ : mFaceIDXOfs (0)
+ , mPointIDXOfs (0)
+ , mParent (0x0)
+ , mIndex (0xffff)
+ , skip (false)
+ {}
+
+ /** Temporary point list from the file */
+ PointList mTempPoints;
+
+ /** Lists for every point the index of another point
+ that has been copied from *this* point or 0xffffffff if
+ no copy of the point has been made */
+ ReferrerList mPointReferrers;
+
+ /** Weight channel list from the file */
+ WeightChannelList mWeightChannels;
+
+ /** Subdivision weight channel list from the file */
+ WeightChannelList mSWeightChannels;
+
+ /** Vertex color list from the file */
+ VColorChannelList mVColorChannels;
+
+ /** UV channel list from the file */
+ UVChannelList mUVChannels;
+
+ /** Normal vector channel from the file */
+ NormalChannel mNormals;
+
+ /** Temporary face list from the file*/
+ FaceList mFaces;
+
+ /** Current face indexing offset from the beginning of the buffers*/
+ unsigned int mFaceIDXOfs;
+
+ /** Current point indexing offset from the beginning of the buffers*/
+ unsigned int mPointIDXOfs;
+
+ /** Parent index */
+ uint16_t mParent;
+
+ /** Index of the layer */
+ uint16_t mIndex;
+
+ /** Name of the layer */
+ std::string mName;
+
+ /** Pivot point of the layer */
+ aiVector3D mPivot;
+
+ /** Skip this layer? */
+ bool skip;
+};
+
+typedef std::list<LWO::Layer> LayerList;
+
+
+}}
+
+
+#endif // !! AI_LWO_FILEDATA_INCLUDED
+
diff --git a/src/3rdparty/assimp/code/LWOLoader.cpp b/src/3rdparty/assimp/code/LWOLoader.cpp
new file mode 100644
index 000000000..ac82c9394
--- /dev/null
+++ b/src/3rdparty/assimp/code/LWOLoader.cpp
@@ -0,0 +1,1420 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file LWOLoader.cpp
+ * @brief Implementation of the LWO importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
+
+// internal headers
+#include "LWOLoader.h"
+#include "MaterialSystem.h"
+#include "StringComparison.h"
+#include "SGSpatialSort.h"
+#include "ByteSwap.h"
+#include "ProcessHelper.h"
+#include "ConvertToLHProcess.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+LWOImporter::LWOImporter()
+ : mIsLWO2(false)
+ , mIsLXOB(false)
+ , mLayers(0)
+ , mCurLayer(0)
+ , mTags(0)
+ , mMapping()
+ , mSurfaces(0)
+ , mFileBuffer(0)
+ , fileSize(0)
+ , pScene(0)
+ , configSpeedFlag(false)
+ , configLayerIndex(0)
+ , hasNamedLayer(false)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+LWOImporter::~LWOImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool LWOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+ if (extension == "lwo" || extension == "lxo")
+ return true;
+
+ // if check for extension is not enough, check for the magic tokens
+ if (!extension.length() || checkSig) {
+ uint32_t tokens[3];
+ tokens[0] = AI_LWO_FOURCC_LWOB;
+ tokens[1] = AI_LWO_FOURCC_LWO2;
+ tokens[2] = AI_LWO_FOURCC_LXOB;
+ return CheckMagicToken(pIOHandler,pFile,tokens,3,8);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void LWOImporter::SetupProperties(const Importer* pImp)
+{
+ configSpeedFlag = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0) ? true : false);
+ configLayerIndex = pImp->GetPropertyInteger (AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,0xffffffff);
+ configLayerName = pImp->GetPropertyString (AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,"");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void LWOImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene,
+ IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open LWO file " + pFile + ".");
+
+ if ((this->fileSize = (unsigned int)file->FileSize()) < 12)
+ throw DeadlyImportError("LWO: The file is too small to contain the IFF header");
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ std::vector< uint8_t > mBuffer(fileSize);
+ file->Read( &mBuffer[0], 1, fileSize);
+ this->pScene = pScene;
+
+ // Determine the type of the file
+ uint32_t fileType;
+ const char* sz = IFF::ReadHeader(&mBuffer[0],fileType);
+ if (sz)throw DeadlyImportError(sz);
+
+ mFileBuffer = &mBuffer[0] + 12;
+ fileSize -= 12;
+
+ // Initialize some members with their default values
+ hasNamedLayer = false;
+
+ // Create temporary storage on the stack but store pointers to it in the class
+ // instance. Therefore everything will be destructed properly if an exception
+ // is thrown and we needn't take care of that.
+ LayerList _mLayers;
+ SurfaceList _mSurfaces;
+ TagList _mTags;
+ TagMappingTable _mMapping;
+
+ mLayers = &_mLayers;
+ mTags = &_mTags;
+ mMapping = &_mMapping;
+ mSurfaces = &_mSurfaces;
+
+ // Allocate a default layer (layer indices are 1-based from now)
+ mLayers->push_back(Layer());
+ mCurLayer = &mLayers->back();
+ mCurLayer->mName = "<LWODefault>";
+
+ // old lightwave file format (prior to v6)
+ if (AI_LWO_FOURCC_LWOB == fileType) {
+ DefaultLogger::get()->info("LWO file format: LWOB (<= LightWave 5.5)");
+
+ mIsLWO2 = false;
+ mIsLXOB = false;
+ LoadLWOBFile();
+ }
+ // New lightwave format
+ else if (AI_LWO_FOURCC_LWO2 == fileType) {
+ mIsLXOB = false;
+ DefaultLogger::get()->info("LWO file format: LWO2 (>= LightWave 6)");
+ }
+ // MODO file format
+ else if (AI_LWO_FOURCC_LXOB == fileType) {
+ mIsLXOB = true;
+ DefaultLogger::get()->info("LWO file format: LXOB (Modo)");
+ }
+ // we don't know this format
+ else
+ {
+ char szBuff[5];
+ szBuff[0] = (char)(fileType >> 24u);
+ szBuff[1] = (char)(fileType >> 16u);
+ szBuff[2] = (char)(fileType >> 8u);
+ szBuff[3] = (char)(fileType);
+ szBuff[4] = '\0';
+ throw DeadlyImportError(std::string("Unknown LWO sub format: ") + szBuff);
+ }
+
+ if (AI_LWO_FOURCC_LWOB != fileType) {
+ mIsLWO2 = true;
+ LoadLWO2File();
+
+ // The newer lightwave format allows the user to configure the
+ // loader that just one layer is used. If this is the case
+ // we need to check now whether the requested layer has been found.
+ if (0xffffffff != configLayerIndex && configLayerIndex > mLayers->size())
+ throw DeadlyImportError("LWO2: The requested layer was not found");
+
+ if (configLayerName.length() && !hasNamedLayer) {
+ throw DeadlyImportError("LWO2: Unable to find the requested layer: "
+ + configLayerName);
+ }
+ }
+
+ // now, as we have loaded all data, we can resolve cross-referenced tags and clips
+ ResolveTags();
+ ResolveClips();
+
+ // now process all layers and build meshes and nodes
+ std::vector<aiMesh*> apcMeshes;
+ std::vector<aiNode*> apcNodes;
+ apcNodes. reserve(mLayers->size());
+ apcMeshes.reserve(mLayers->size()*std::min(((unsigned int)mSurfaces->size()/2u), 1u));
+
+ unsigned int iDefaultSurface = 0xffffffff; // index of the default surface
+ for (LayerList::iterator lit = mLayers->begin(), lend = mLayers->end();lit != lend;++lit) {
+ LWO::Layer& layer = *lit;
+ if (layer.skip)
+ continue;
+
+ // I don't know whether there could be dummy layers, but it would be possible
+ const unsigned int meshStart = (unsigned int)apcMeshes.size();
+ if (!layer.mFaces.empty() && !layer.mTempPoints.empty()) {
+
+ // now sort all faces by the surfaces assigned to them
+ std::vector<SortedRep> pSorted(mSurfaces->size()+1);
+
+ unsigned int i = 0;
+ for (FaceList::iterator it = layer.mFaces.begin(), end = layer.mFaces.end();it != end;++it,++i) {
+ // Check whether we support this face's type
+ if ((*it).type != AI_LWO_FACE && (*it).type != AI_LWO_PTCH &&
+ (*it).type != AI_LWO_BONE && (*it).type != AI_LWO_SUBD) {
+ continue;
+ }
+
+ unsigned int idx = (*it).surfaceIndex;
+ if (idx >= mTags->size())
+ {
+ DefaultLogger::get()->warn("LWO: Invalid face surface index");
+ idx = 0xffffffff;
+ }
+ if (0xffffffff == idx || 0xffffffff == (idx = _mMapping[idx])) {
+ if (0xffffffff == iDefaultSurface) {
+ iDefaultSurface = (unsigned int)mSurfaces->size();
+ mSurfaces->push_back(LWO::Surface());
+ LWO::Surface& surf = mSurfaces->back();
+ surf.mColor.r = surf.mColor.g = surf.mColor.b = 0.6f;
+ surf.mName = "LWODefaultSurface";
+ }
+ idx = iDefaultSurface;
+ }
+ pSorted[idx].push_back(i);
+ }
+ if (0xffffffff == iDefaultSurface) {
+ pSorted.erase(pSorted.end()-1);
+ }
+ for (unsigned int p = 0,i = 0;i < mSurfaces->size();++i) {
+ SortedRep& sorted = pSorted[i];
+ if (sorted.empty())
+ continue;
+
+ // generate the mesh
+ aiMesh* mesh = new aiMesh();
+ apcMeshes.push_back(mesh);
+ mesh->mNumFaces = (unsigned int)sorted.size();
+
+ // count the number of vertices
+ SortedRep::const_iterator it = sorted.begin(), end = sorted.end();
+ for (;it != end;++it) {
+ mesh->mNumVertices += layer.mFaces[*it].mNumIndices;
+ }
+
+ aiVector3D *nrm = NULL, * pv = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+ aiFace* pf = mesh->mFaces = new aiFace[mesh->mNumFaces];
+ mesh->mMaterialIndex = i;
+
+ // find out which vertex color channels and which texture coordinate
+ // channels are really required by the material attached to this mesh
+ unsigned int vUVChannelIndices[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ unsigned int vVColorIndices[AI_MAX_NUMBER_OF_COLOR_SETS];
+
+#if _DEBUG
+ for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_TEXTURECOORDS;++mui )
+ vUVChannelIndices[mui] = 0xffffffff;
+ for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_COLOR_SETS;++mui )
+ vVColorIndices[mui] = 0xffffffff;
+#endif
+
+ FindUVChannels(_mSurfaces[i],sorted,layer,vUVChannelIndices);
+ FindVCChannels(_mSurfaces[i],sorted,layer,vVColorIndices);
+
+ // allocate storage for UV and CV channels
+ aiVector3D* pvUV[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_TEXTURECOORDS;++mui ) {
+ if (0xffffffff == vUVChannelIndices[mui])
+ break;
+
+ pvUV[mui] = mesh->mTextureCoords[mui] = new aiVector3D[mesh->mNumVertices];
+
+ // LightWave doesn't support more than 2 UV components (?)
+ mesh->mNumUVComponents[0] = 2;
+ }
+
+ if (layer.mNormals.name.length())
+ nrm = mesh->mNormals = new aiVector3D[mesh->mNumVertices];
+
+ aiColor4D* pvVC[AI_MAX_NUMBER_OF_COLOR_SETS];
+ for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_COLOR_SETS;++mui) {
+ if (0xffffffff == vVColorIndices[mui])break;
+ pvVC[mui] = mesh->mColors[mui] = new aiColor4D[mesh->mNumVertices];
+ }
+
+ // we would not need this extra array, but the code is much cleaner if we use it
+ std::vector<unsigned int>& smoothingGroups = layer.mPointReferrers;
+ smoothingGroups.erase (smoothingGroups.begin(),smoothingGroups.end());
+ smoothingGroups.resize(mesh->mNumFaces,0);
+
+ // now convert all faces
+ unsigned int vert = 0;
+ std::vector<unsigned int>::iterator outIt = smoothingGroups.begin();
+ for (it = sorted.begin(); it != end;++it,++outIt) {
+ const LWO::Face& face = layer.mFaces[*it];
+ *outIt = face.smoothGroup;
+
+ // copy all vertices
+ for (unsigned int q = 0; q < face.mNumIndices;++q,++vert) {
+ register unsigned int idx = face.mIndices[q];
+ *pv++ = layer.mTempPoints[idx] /*- layer.mPivot*/;
+
+ // process UV coordinates
+ for (unsigned int w = 0; w < AI_MAX_NUMBER_OF_TEXTURECOORDS;++w) {
+ if (0xffffffff == vUVChannelIndices[w])
+ break;
+ aiVector3D*& pp = pvUV[w];
+ const aiVector2D& src = ((aiVector2D*)&layer.mUVChannels[vUVChannelIndices[w]].rawData[0])[idx];
+ pp->x = src.x;
+ pp->y = src.y;
+ pp++;
+ }
+
+ // process normals (MODO extension)
+ if (nrm) {
+ *nrm = ((aiVector3D*)&layer.mNormals.rawData[0])[idx];
+ nrm->z *= -1.f;
+ ++nrm;
+ }
+
+ // process vertex colors
+ for (unsigned int w = 0; w < AI_MAX_NUMBER_OF_COLOR_SETS;++w) {
+ if (0xffffffff == vVColorIndices[w])
+ break;
+ *pvVC[w] = ((aiColor4D*)&layer.mVColorChannels[vVColorIndices[w]].rawData[0])[idx];
+
+ // If a RGB color map is explicitly requested delete the
+ // alpha channel - it could theoretically be != 1.
+ if (_mSurfaces[i].mVCMapType == AI_LWO_RGB)
+ pvVC[w]->a = 1.f;
+
+ pvVC[w]++;
+ }
+
+#if 0
+ // process vertex weights. We can't properly reconstruct the whole skeleton for now,
+ // but we can create dummy bones for all weight channels which we have.
+ for (unsigned int w = 0; w < layer.mWeightChannels.size();++w)
+ {
+ }
+#endif
+
+ face.mIndices[q] = vert;
+ }
+ pf->mIndices = face.mIndices;
+ pf->mNumIndices = face.mNumIndices;
+ unsigned int** p = (unsigned int**)&face.mIndices;*p = NULL; // HACK: make sure it won't be deleted
+ pf++;
+ }
+
+ if (!mesh->mNormals) {
+ // Compute normal vectors for the mesh - we can't use our GenSmoothNormal-
+ // Step here since it wouldn't handle smoothing groups correctly for LWO.
+ // So we use a separate implementation.
+ ComputeNormals(mesh,smoothingGroups,_mSurfaces[i]);
+ }
+ else DefaultLogger::get()->debug("LWO2: No need to compute normals, they're already there");
+ ++p;
+ }
+ }
+
+ // Generate nodes to render the mesh. Store the source layer in the mParent member of the nodes
+ unsigned int num = apcMeshes.size() - meshStart;
+ if (layer.mName != "<LWODefault>" || num > 0) {
+ aiNode* pcNode = new aiNode();
+ apcNodes.push_back(pcNode);
+ pcNode->mName.Set(layer.mName);
+ pcNode->mParent = (aiNode*)&layer;
+ pcNode->mNumMeshes = num;
+
+ if (pcNode->mNumMeshes) {
+ pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
+ for (unsigned int p = 0; p < pcNode->mNumMeshes;++p)
+ pcNode->mMeshes[p] = p + meshStart;
+ }
+ }
+ }
+
+ if (apcNodes.empty() || apcMeshes.empty())
+ throw DeadlyImportError("LWO: No meshes loaded");
+
+ // The RemoveRedundantMaterials step will clean this up later
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = (unsigned int)mSurfaces->size()];
+ for (unsigned int mat = 0; mat < pScene->mNumMaterials;++mat) {
+ MaterialHelper* pcMat = new MaterialHelper();
+ pScene->mMaterials[mat] = pcMat;
+ ConvertMaterial((*mSurfaces)[mat],pcMat);
+ }
+
+ // copy the meshes to the output structure
+ pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = (unsigned int)apcMeshes.size() ];
+ ::memcpy(pScene->mMeshes,&apcMeshes[0],pScene->mNumMeshes*sizeof(void*));
+
+ // generate the final node graph
+ GenerateNodeGraph(apcNodes);
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector<unsigned int>& smoothingGroups,
+ const LWO::Surface& surface)
+{
+ // Allocate output storage
+ mesh->mNormals = new aiVector3D[mesh->mNumVertices];
+
+ // First generate per-face normals
+ aiVector3D* out;
+ std::vector<aiVector3D> faceNormals;
+
+ // ... in some cases that's already enough
+ if (!surface.mMaximumSmoothAngle)
+ out = mesh->mNormals;
+ else {
+ faceNormals.resize(mesh->mNumVertices);
+ out = &faceNormals[0];
+ }
+
+ aiFace* begin = mesh->mFaces, *const end = mesh->mFaces+mesh->mNumFaces;
+ for (; begin != end; ++begin) {
+ aiFace& face = *begin;
+
+ // LWO doc: "the normal is defined as the cross product of the first and last edges"
+ aiVector3D* pV1 = mesh->mVertices + face.mIndices[0];
+ aiVector3D* pV2 = mesh->mVertices + face.mIndices[1];
+ aiVector3D* pV3 = mesh->mVertices + face.mIndices[face.mNumIndices-1];
+
+ aiVector3D vNor = ((*pV2 - *pV1) ^(*pV3 - *pV1)).Normalize();
+ for (unsigned int i = 0; i < face.mNumIndices;++i)
+ out[face.mIndices[i]] = vNor;
+ }
+ if (!surface.mMaximumSmoothAngle)return;
+ const float posEpsilon = ComputePositionEpsilon(mesh);
+
+ // Now generate the spatial sort tree
+ SGSpatialSort sSort;
+ std::vector<unsigned int>::const_iterator it = smoothingGroups.begin();
+ for ( begin = mesh->mFaces; begin != end; ++begin, ++it)
+ {
+ aiFace& face = *begin;
+ for (unsigned int i = 0; i < face.mNumIndices;++i)
+ {
+ register unsigned int tt = face.mIndices[i];
+ sSort.Add(mesh->mVertices[tt],tt,*it);
+ }
+ }
+ // Sort everything - this takes O(nlogn) time
+ sSort.Prepare();
+ std::vector<unsigned int> poResult;
+ poResult.reserve(20);
+
+ // Generate vertex normals. We have O(logn) for the binary lookup, which we need
+ // for n elements, thus the EXPECTED complexity is O(nlogn)
+ if (surface.mMaximumSmoothAngle < 3.f && !configSpeedFlag) {
+ const float fLimit = cos(surface.mMaximumSmoothAngle);
+
+ for ( begin = mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it) {
+ const aiFace& face = *begin;
+ unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices;
+ for (; beginIdx != endIdx; ++beginIdx)
+ {
+ register unsigned int idx = *beginIdx;
+ sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true);
+ std::vector<unsigned int>::const_iterator a, end = poResult.end();
+
+ aiVector3D vNormals;
+ for (a = poResult.begin();a != end;++a) {
+ const aiVector3D& v = faceNormals[*a];
+ if (v * faceNormals[idx] < fLimit)
+ continue;
+ vNormals += v;
+ }
+ mesh->mNormals[idx] = vNormals.Normalize();
+ }
+ }
+ }
+ // faster code path in case there is no smooth angle
+ else {
+ std::vector<bool> vertexDone(mesh->mNumVertices,false);
+ for ( begin = mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it) {
+ const aiFace& face = *begin;
+ unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices;
+ for (; beginIdx != endIdx; ++beginIdx)
+ {
+ register unsigned int idx = *beginIdx;
+ if (vertexDone[idx])
+ continue;
+ sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true);
+ std::vector<unsigned int>::const_iterator a, end = poResult.end();
+
+ aiVector3D vNormals;
+ for (a = poResult.begin();a != end;++a) {
+ const aiVector3D& v = faceNormals[*a];
+ vNormals += v;
+ }
+ vNormals.Normalize();
+ for (a = poResult.begin();a != end;++a) {
+ mesh->mNormals[*a] = vNormals;
+ vertexDone[*a] = true;
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::AddChildren(aiNode* node, uint16_t parent, std::vector<aiNode*>& apcNodes)
+{
+ for (std::vector<aiNode*>::iterator it = apcNodes.begin(); it != apcNodes.end(); ++it) {
+ if (*it) {
+ LWO::Layer* layer = (LWO::Layer*)(*it)->mParent;
+ if (layer->mParent == parent && layer->mIndex != parent)
+ ++node->mNumChildren;
+ }
+ }
+
+ if (node->mNumChildren) {
+ unsigned int p = 0;
+
+ node->mChildren = new aiNode* [ node->mNumChildren ];
+ for (std::vector<aiNode*>::iterator it = apcNodes.begin(); it != apcNodes.end(); ++it) {
+ if (*it) {
+ LWO::Layer* layer = (LWO::Layer*)(*it)->mParent;
+ if (layer->mParent == parent && layer->mIndex != parent) {
+ aiNode* nd = node->mChildren[p++] = *it;
+ nd->mParent = node;
+
+ // fixme: ignore pivot points for the moment
+ //nd->mTransformation.a4 = layer->mPivot.x;
+ //nd->mTransformation.b4 = layer->mPivot.y;
+ //nd->mTransformation.c4 = layer->mPivot.z;
+
+ // recursively add more children
+ (*it) = NULL;
+ AddChildren(nd,layer->mIndex,apcNodes);
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::GenerateNodeGraph(std::vector<aiNode*>& apcNodes)
+{
+ // now generate the final nodegraph - generate a root node and attach children
+ aiNode* root = pScene->mRootNode = new aiNode();
+ root->mName.Set("<LWORoot>");
+ AddChildren(root,0,apcNodes);
+
+ // check whether we added all layers with meshes assigned to the output graph.
+ // if not, add them to the root node
+ unsigned int extra = 0;
+ for (std::vector<aiNode*>::iterator it = apcNodes.begin(); it != apcNodes.end(); ++it) {
+ if ((*it) && (*it)->mNumMeshes)
+ ++extra;
+ }
+
+ if (extra) {
+ const unsigned int newSize = extra + pScene->mRootNode->mNumChildren;
+ aiNode** const apcNewNodes = new aiNode*[newSize];
+ if ((extra = root->mNumChildren))
+ ::memcpy(apcNewNodes,root->mChildren,extra*sizeof(void*));
+
+ aiNode** cc = apcNewNodes+extra;
+ for (std::vector<aiNode*>::iterator it = apcNodes.begin(); it != apcNodes.end(); ++it) {
+ if ((*it) && (*it)->mNumMeshes) {
+ aiNode* nd = *cc++ = *it;
+ nd->mParent = pScene->mRootNode;
+
+ // recursively add more children
+ (*it) = NULL;
+ AddChildren(nd,((LWO::Layer*)nd->mParent)->mIndex,apcNodes);
+ }
+ }
+ delete[] root->mChildren;
+ root->mChildren = apcNewNodes;
+ root->mNumChildren = newSize;
+ }
+ if (!pScene->mRootNode->mNumChildren)
+ throw DeadlyImportError("LWO: Unable to build a valid node graph");
+
+ // Remove a single root node with no meshes assigned to it ...
+ if (1 == pScene->mRootNode->mNumChildren) {
+ aiNode* pc = pScene->mRootNode->mChildren[0];
+ pc->mParent = pScene->mRootNode->mChildren[0] = NULL;
+ delete pScene->mRootNode;
+ pScene->mRootNode = pc;
+ }
+
+ // convert the whole stuff to RH with CCW winding
+ MakeLeftHandedProcess maker;
+ maker.Execute(pScene);
+
+ FlipWindingOrderProcess flipper;
+ flipper.Execute(pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::ResolveTags()
+{
+ // --- this function is used for both LWO2 and LWOB
+ mMapping->resize(mTags->size(),0xffffffff);
+ for (unsigned int a = 0; a < mTags->size();++a) {
+
+ const std::string& c = (*mTags)[a];
+ for (unsigned int i = 0; i < mSurfaces->size();++i) {
+
+ const std::string& d = (*mSurfaces)[i].mName;
+ if (!ASSIMP_stricmp(c,d)) {
+
+ (*mMapping)[a] = i;
+ break;
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::ResolveClips()
+{
+ for ( unsigned int i = 0; i < mClips.size();++i) {
+
+ Clip& clip = mClips[i];
+ if (Clip::REF == clip.type) {
+
+ if (clip.clipRef >= mClips.size()) {
+ DefaultLogger::get()->error("LWO2: Clip referrer index is out of range");
+ clip.clipRef = 0;
+ }
+
+ Clip& dest = mClips[clip.clipRef];
+ if (Clip::REF == dest.type) {
+ DefaultLogger::get()->error("LWO2: Clip references another clip reference");
+ clip.type = Clip::UNSUPPORTED;
+ }
+
+ else {
+ clip.path = dest.path;
+ clip.type = dest.type;
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::AdjustTexturePath(std::string& out)
+{
+ // --- this function is used for both LWO2 and LWOB
+ if (!mIsLWO2 && ::strstr(out.c_str(), "(sequence)")) {
+
+ // remove the (sequence) and append 000
+ DefaultLogger::get()->info("LWOB: Sequence of animated texture found. It will be ignored");
+ out = out.substr(0,out.length()-10) + "000";
+ }
+
+ // format: drive:path/file - we just need to insert a slash after the drive
+ std::string::size_type n = out.find_first_of(':');
+ if (std::string::npos != n) {
+ out.insert(n+1,"/");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWOTags(unsigned int size)
+{
+ // --- this function is used for both LWO2 and LWOB
+
+ const char* szCur = (const char*)mFileBuffer, *szLast = szCur;
+ const char* const szEnd = szLast+size;
+ while (szCur < szEnd)
+ {
+ if (!(*szCur))
+ {
+ const size_t len = (size_t)(szCur-szLast);
+ // FIX: skip empty-sized tags
+ if (len)
+ mTags->push_back(std::string(szLast,len));
+ szCur += (len&0x1 ? 1 : 2);
+ szLast = szCur;
+ }
+ szCur++;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWOPoints(unsigned int length)
+{
+ // --- this function is used for both LWO2 and LWOB but for
+ // LWO2 we need to allocate 25% more storage - it could be we'll
+ // need to duplicate some points later.
+ register unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12;
+ if (mIsLWO2)
+ {
+ mCurLayer->mTempPoints.reserve ( regularSize + (regularSize>>2u) );
+ mCurLayer->mTempPoints.resize ( regularSize );
+
+ // initialize all point referrers with the default values
+ mCurLayer->mPointReferrers.reserve ( regularSize + (regularSize>>2u) );
+ mCurLayer->mPointReferrers.resize ( regularSize, 0xffffffff );
+ }
+ else mCurLayer->mTempPoints.resize( regularSize );
+
+ // perform endianess conversions
+#ifndef AI_BUILD_BIG_ENDIAN
+ for (unsigned int i = 0; i < length>>2;++i)
+ ByteSwap::Swap4( mFileBuffer + (i << 2));
+#endif
+ ::memcpy(&mCurLayer->mTempPoints[0],mFileBuffer,length);
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2Polygons(unsigned int length)
+{
+ LE_NCONST uint16_t* const end = (LE_NCONST uint16_t*)(mFileBuffer+length);
+ const uint32_t type = GetU4();
+
+ // Determine the type of the polygons
+ switch (type)
+ {
+ // read unsupported stuff too (although we wont process it)
+ case AI_LWO_MBAL:
+ DefaultLogger::get()->warn("LWO2: Encountered unsupported primitive chunk (METABALL)");
+ break;
+ case AI_LWO_CURV:
+ DefaultLogger::get()->warn("LWO2: Encountered unsupported primitive chunk (SPLINE)");;
+ break;
+
+ // These are ok with no restrictions
+ case AI_LWO_PTCH:
+ case AI_LWO_FACE:
+ case AI_LWO_BONE:
+ case AI_LWO_SUBD:
+ break;
+ default:
+
+ // hm!? wtf is this? ok ...
+ DefaultLogger::get()->error("LWO2: Ignoring unknown polygon type.");
+ break;
+ }
+
+ // first find out how many faces and vertices we'll finally need
+ uint16_t* cursor= (uint16_t*)mFileBuffer;
+
+ unsigned int iNumFaces = 0,iNumVertices = 0;
+ CountVertsAndFacesLWO2(iNumVertices,iNumFaces,cursor,end);
+
+ // allocate the output array and copy face indices
+ if (iNumFaces) {
+ cursor = (uint16_t*)mFileBuffer;
+
+ mCurLayer->mFaces.resize(iNumFaces,LWO::Face(type));
+ FaceList::iterator it = mCurLayer->mFaces.begin();
+ CopyFaceIndicesLWO2(it,cursor,end);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::CountVertsAndFacesLWO2(unsigned int& verts, unsigned int& faces,
+ uint16_t*& cursor, const uint16_t* const end, unsigned int max)
+{
+ while (cursor < end && max--)
+ {
+ AI_LSWAP2P(cursor);
+ uint16_t numIndices = *cursor++;
+ numIndices &= 0x03FF;
+ verts += numIndices;++faces;
+
+ for (uint16_t i = 0; i < numIndices; i++)
+ ReadVSizedIntLWO2((uint8_t*&)cursor);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::CopyFaceIndicesLWO2(FaceList::iterator& it,
+ uint16_t*& cursor,
+ const uint16_t* const end)
+{
+ while (cursor < end) {
+
+ LWO::Face& face = *it++;;
+ if ((face.mNumIndices = (*cursor++) & 0x03FF)) /* byte swapping has already been done */ {
+ face.mIndices = new unsigned int[face.mNumIndices];
+ for (unsigned int i = 0; i < face.mNumIndices; i++)
+ {
+ face.mIndices[i] = ReadVSizedIntLWO2((uint8_t*&)cursor) + mCurLayer->mPointIDXOfs;
+ if (face.mIndices[i] > mCurLayer->mTempPoints.size())
+ {
+ DefaultLogger::get()->warn("LWO2: Failure evaluating face record, index is out of range");
+ face.mIndices[i] = (unsigned int)mCurLayer->mTempPoints.size()-1;
+ }
+ }
+ }
+ else throw DeadlyImportError("LWO2: Encountered invalid face record with zero indices");
+ }
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2PolygonTags(unsigned int length)
+{
+ LE_NCONST uint8_t* const end = mFileBuffer+length;
+
+ AI_LWO_VALIDATE_CHUNK_LENGTH(length,PTAG,4);
+ uint32_t type = GetU4();
+
+ if (type != AI_LWO_SURF && type != AI_LWO_SMGP)
+ return;
+
+ while (mFileBuffer < end) {
+
+ unsigned int i = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
+ unsigned int j = GetU2();
+
+ if (i >= mCurLayer->mFaces.size()) {
+ DefaultLogger::get()->warn("LWO2: face index in PTAG is out of range");
+ continue;
+ }
+
+ switch (type) {
+
+ case AI_LWO_SURF:
+ mCurLayer->mFaces[i].surfaceIndex = j;
+ break;
+ case AI_LWO_SMGP: /* is that really used? */
+ mCurLayer->mFaces[i].smoothGroup = j;
+ break;
+ };
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+VMapEntry* FindEntry(std::vector< T >& list,const std::string& name, bool perPoly)
+{
+ for (typename std::vector< T >::iterator it = list.begin(), end = list.end();it != end; ++it) {
+ if ((*it).name == name) {
+ if (!perPoly) {
+ DefaultLogger::get()->warn("LWO2: Found two VMAP sections with equal names");
+ }
+ return &(*it);
+ }
+ }
+ list.push_back( T() );
+ VMapEntry* p = &list.back();
+ p->name = name;
+ return p;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline void CreateNewEntry(T& chan, unsigned int srcIdx)
+{
+ if (!chan.name.length())
+ return;
+
+ chan.abAssigned[srcIdx] = true;
+ chan.abAssigned.resize(chan.abAssigned.size()+1,false);
+
+ for (unsigned int a = 0; a < chan.dims;++a)
+ chan.rawData.push_back(chan.rawData[srcIdx*chan.dims+a]);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline void CreateNewEntry(std::vector< T >& list, unsigned int srcIdx)
+{
+ for (typename std::vector< T >::iterator it = list.begin(), end = list.end();it != end;++it) {
+ CreateNewEntry( *it, srcIdx );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+inline void LWOImporter::DoRecursiveVMAPAssignment(VMapEntry* base, unsigned int numRead,
+ unsigned int idx, float* data)
+{
+ ai_assert(NULL != data);
+ LWO::ReferrerList& refList = mCurLayer->mPointReferrers;
+ unsigned int i;
+
+ base->abAssigned[idx] = true;
+ for (i = 0; i < numRead;++i) {
+ base->rawData[idx*base->dims+i]= data[i];
+ }
+
+ if (0xffffffff != (i = refList[idx])) {
+ DoRecursiveVMAPAssignment(base,numRead,i,data);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+inline void AddToSingleLinkedList(ReferrerList& refList, unsigned int srcIdx, unsigned int destIdx)
+{
+ if (0xffffffff == refList[srcIdx]) {
+ refList[srcIdx] = destIdx;
+ return;
+ }
+ AddToSingleLinkedList(refList,refList[srcIdx],destIdx);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load LWO2 vertex map
+void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly)
+{
+ LE_NCONST uint8_t* const end = mFileBuffer+length;
+
+ AI_LWO_VALIDATE_CHUNK_LENGTH(length,VMAP,6);
+ unsigned int type = GetU4();
+ unsigned int dims = GetU2();
+
+ VMapEntry* base;
+
+ // read the name of the vertex map
+ std::string name;
+ GetS0(name,length);
+
+ switch (type)
+ {
+ case AI_LWO_TXUV:
+ if (dims != 2) {
+ DefaultLogger::get()->warn("LWO2: Skipping UV channel \'"
+ + name + "\' with !2 components");
+ return;
+ }
+ base = FindEntry(mCurLayer->mUVChannels,name,perPoly);
+ break;
+ case AI_LWO_WGHT:
+ case AI_LWO_MNVW:
+ if (dims != 1) {
+ DefaultLogger::get()->warn("LWO2: Skipping Weight Channel \'"
+ + name + "\' with !1 components");
+ return;
+ }
+ base = FindEntry((type == AI_LWO_WGHT ? mCurLayer->mWeightChannels
+ : mCurLayer->mSWeightChannels),name,perPoly);
+ break;
+ case AI_LWO_RGB:
+ case AI_LWO_RGBA:
+ if (dims != 3 && dims != 4) {
+ DefaultLogger::get()->warn("LWO2: Skipping Color Map \'"
+ + name + "\' with a dimension > 4 or < 3");
+ return;
+ }
+ base = FindEntry(mCurLayer->mVColorChannels,name,perPoly);
+ break;
+
+ case AI_LWO_MODO_NORM:
+ /* This is a non-standard extension chunk used by Luxology's MODO.
+ * It stores per-vertex normals. This VMAP exists just once, has
+ * 3 dimensions and is btw extremely beautiful.
+ */
+ if (name != "vert_normals" || dims != 3 || mCurLayer->mNormals.name.length())
+ return;
+
+ DefaultLogger::get()->info("Processing non-standard extension: MODO VMAP.NORM.vert_normals");
+
+ mCurLayer->mNormals.name = name;
+ base = & mCurLayer->mNormals;
+ break;
+
+ case AI_LWO_PICK: /* these VMAPs are just silently dropped */
+ case AI_LWO_MORF:
+ case AI_LWO_SPOT:
+ return;
+
+ default:
+ if (name == "APS.Level") {
+ // XXX handle this (seems to be subdivision-related).
+ }
+ DefaultLogger::get()->warn("LWO2: Skipping unknown VMAP/VMAD channel \'" + name + "\'");
+ return;
+ };
+ base->Allocate((unsigned int)mCurLayer->mTempPoints.size());
+
+ // now read all entries in the map
+ type = std::min(dims,base->dims);
+ const unsigned int diff = (dims - type)<<2u;
+
+ LWO::FaceList& list = mCurLayer->mFaces;
+ LWO::PointList& pointList = mCurLayer->mTempPoints;
+ LWO::ReferrerList& refList = mCurLayer->mPointReferrers;
+
+ float temp[4];
+
+ const unsigned int numPoints = (unsigned int)pointList.size();
+ const unsigned int numFaces = (unsigned int)list.size();
+
+ while (mFileBuffer < end) {
+
+ unsigned int idx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mPointIDXOfs;
+ if (idx >= numPoints) {
+ DefaultLogger::get()->warn("LWO2: Failure evaluating VMAP/VMAD entry \'" + name + "\', vertex index is out of range");
+ mFileBuffer += base->dims<<2u;
+ continue;
+ }
+ if (perPoly) {
+ unsigned int polyIdx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
+ if (base->abAssigned[idx]) {
+ // we have already a VMAP entry for this vertex - thus
+ // we need to duplicate the corresponding polygon.
+ if (polyIdx >= numFaces) {
+ DefaultLogger::get()->warn("LWO2: Failure evaluating VMAD entry \'" + name + "\', polygon index is out of range");
+ mFileBuffer += base->dims<<2u;
+ continue;
+ }
+
+ LWO::Face& src = list[polyIdx];
+
+ // generate a new unique vertex for the corresponding index - but only
+ // if we can find the index in the face
+ bool had = false;
+ for (unsigned int i = 0; i < src.mNumIndices;++i) {
+
+ unsigned int srcIdx = src.mIndices[i], tmp = idx;
+ do {
+ if (tmp == srcIdx)
+ break;
+ }
+ while ((tmp = refList[tmp]) != 0xffffffff);
+ if (tmp == 0xffffffff)
+ continue;
+
+ had = true;
+ refList.resize(refList.size()+1, 0xffffffff);
+
+ idx = (unsigned int)pointList.size();
+ src.mIndices[i] = (unsigned int)pointList.size();
+
+ // store the index of the new vertex in the old vertex
+ // so we get a single linked list we can traverse in
+ // only one direction
+ AddToSingleLinkedList(refList,srcIdx,src.mIndices[i]);
+ pointList.push_back(pointList[srcIdx]);
+
+ CreateNewEntry(mCurLayer->mVColorChannels, srcIdx );
+ CreateNewEntry(mCurLayer->mUVChannels, srcIdx );
+ CreateNewEntry(mCurLayer->mWeightChannels, srcIdx );
+ CreateNewEntry(mCurLayer->mSWeightChannels, srcIdx );
+ CreateNewEntry(mCurLayer->mNormals, srcIdx );
+ }
+ if (!had) {
+ DefaultLogger::get()->warn("LWO2: Failure evaluating VMAD entry \'" + name + "\', vertex index wasn't found in that polygon");
+ ai_assert(had);
+ }
+ }
+ }
+ for (unsigned int l = 0; l < type;++l)
+ temp[l] = GetF4();
+
+ DoRecursiveVMAPAssignment(base,type,idx, temp);
+ mFileBuffer += diff;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load LWO2 clip
+void LWOImporter::LoadLWO2Clip(unsigned int length)
+{
+ AI_LWO_VALIDATE_CHUNK_LENGTH(length,CLIP,10);
+
+ mClips.push_back(LWO::Clip());
+ LWO::Clip& clip = mClips.back();
+
+ // first - get the index of the clip
+ clip.idx = GetU4();
+
+ IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+ switch (head->type)
+ {
+ case AI_LWO_STIL:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,STIL,1);
+
+ // "Normal" texture
+ GetS0(clip.path,head->length);
+ clip.type = Clip::STILL;
+ break;
+
+ case AI_LWO_ISEQ:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,ISEQ,16);
+ // Image sequence. We'll later take the first.
+ {
+ uint8_t digits = GetU1(); mFileBuffer++;
+ int16_t offset = GetU2(); mFileBuffer+=4;
+ int16_t start = GetU2(); mFileBuffer+=4;
+
+ std::string s;std::stringstream ss;
+ GetS0(s,head->length);
+
+ head->length -= (unsigned int)s.length()+1;
+ ss << s;
+ ss << std::setw(digits) << offset + start;
+ GetS0(s,head->length);
+ ss << s;
+ clip.path = ss.str();
+ clip.type = Clip::SEQ;
+ }
+ break;
+
+ case AI_LWO_STCC:
+ DefaultLogger::get()->warn("LWO2: Color shifted images are not supported");
+ break;
+
+ case AI_LWO_ANIM:
+ DefaultLogger::get()->warn("LWO2: Animated textures are not supported");
+ break;
+
+ case AI_LWO_XREF:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,XREF,4);
+
+ // Just a cross-reference to another CLIp
+ clip.type = Clip::REF;
+ clip.clipRef = GetU4();
+ break;
+
+ case AI_LWO_NEGA:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,NEGA,2);
+ clip.negate = (0 != GetU2());
+ break;
+
+ default:
+ DefaultLogger::get()->warn("LWO2: Encountered unknown CLIP subchunk");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load envelope description
+void LWOImporter::LoadLWO2Envelope(unsigned int length)
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + length;
+ AI_LWO_VALIDATE_CHUNK_LENGTH(length,ENVL,4);
+
+ mEnvelopes.push_back(LWO::Envelope());
+ LWO::Envelope& envelope = mEnvelopes.back();
+
+ // Get the index of the envelope
+ envelope.index = ReadVSizedIntLWO2(mFileBuffer);
+
+ // It looks like there might be an extra U4 right after the index,
+ // at least in modo (LXOB) files: we'll ignore it if it's zero,
+ // otherwise it represents the start of a subchunk, so we backtrack.
+ if (mIsLXOB)
+ {
+ uint32_t extra = GetU4();
+ if (extra)
+ {
+ mFileBuffer -= 4;
+ }
+ }
+
+ // ... and read all subchunks
+ while (true)
+ {
+ if (mFileBuffer + 6 >= end)break;
+ LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ throw DeadlyImportError("LWO2: Invalid envelope chunk length");
+
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ // Type & representation of the envelope
+ case AI_LWO_TYPE:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TYPE,2);
+ mFileBuffer++; // skip user format
+
+ // Determine type of envelope
+ envelope.type = (LWO::EnvelopeType)*mFileBuffer;
+ ++mFileBuffer;
+ break;
+
+ // precondition
+ case AI_LWO_PRE:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,PRE,2);
+ envelope.pre = (LWO::PrePostBehaviour)GetU2();
+ break;
+
+ // postcondition
+ case AI_LWO_POST:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,POST,2);
+ envelope.post = (LWO::PrePostBehaviour)GetU2();
+ break;
+
+ // keyframe
+ case AI_LWO_KEY:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,KEY,8);
+
+ envelope.keys.push_back(LWO::Key());
+ LWO::Key& key = envelope.keys.back();
+
+ key.time = GetF4();
+ key.value = GetF4();
+ break;
+ }
+
+ // interval interpolation
+ case AI_LWO_SPAN:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPAN,4);
+ if (envelope.keys.size()<2)
+ DefaultLogger::get()->warn("LWO2: Unexpected SPAN chunk");
+ else {
+ LWO::Key& key = envelope.keys.back();
+ switch (GetU4())
+ {
+ case AI_LWO_STEP:
+ key.inter = LWO::IT_STEP;break;
+ case AI_LWO_LINE:
+ key.inter = LWO::IT_LINE;break;
+ case AI_LWO_TCB:
+ key.inter = LWO::IT_TCB;break;
+ case AI_LWO_HERM:
+ key.inter = LWO::IT_HERM;break;
+ case AI_LWO_BEZI:
+ key.inter = LWO::IT_BEZI;break;
+ case AI_LWO_BEZ2:
+ key.inter = LWO::IT_BEZ2;break;
+ default:
+ DefaultLogger::get()->warn("LWO2: Unknown interval interpolation mode");
+ };
+
+ // todo ... read params
+ }
+ break;
+ }
+
+ default:
+ DefaultLogger::get()->warn("LWO2: Encountered unknown ENVL subchunk");
+ }
+ // regardless how much we did actually read, go to the next chunk
+ mFileBuffer = next;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load file - master function
+void LWOImporter::LoadLWO2File()
+{
+ bool skip = false;
+
+ LE_NCONST uint8_t* const end = mFileBuffer + fileSize;
+ while (true)
+ {
+ if (mFileBuffer + sizeof(IFF::ChunkHeader) > end)break;
+ IFF::ChunkHeader* const head = IFF::LoadChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ {
+ throw DeadlyImportError("LWO2: Chunk length points behind the file");
+ break;
+ }
+ uint8_t* const next = mFileBuffer+head->length;
+ unsigned int iUnnamed = 0;
+
+ switch (head->type)
+ {
+ // new layer
+ case AI_LWO_LAYR:
+ {
+ // add a new layer to the list ....
+ mLayers->push_back ( LWO::Layer() );
+ LWO::Layer& layer = mLayers->back();
+ mCurLayer = &layer;
+
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,LAYR,16);
+
+ // Continue loading this layer or ignore it? Check the layer index property
+ // NOTE: The first layer is the default layer, so the layer index is one-based now
+ if (0xffffffff != configLayerIndex && configLayerIndex != mLayers->size()-1) {
+ skip = true;
+ }
+ else skip = false;
+
+ // layer index. that's just for internal parenting, from the scope of a LWS file
+ // all layers are numbered in the oder in which they appear in the file
+ layer.mIndex = GetU2();
+
+ // pivot point
+ mFileBuffer += 2; /* unknown */
+ mCurLayer->mPivot.x = GetF4();
+ mCurLayer->mPivot.y = GetF4();
+ mCurLayer->mPivot.z = GetF4();
+ GetS0(layer.mName,head->length-16);
+
+ // if the name is empty, generate a default name
+ if (layer.mName.empty()) {
+ char buffer[128]; // should be sufficiently large
+ ::sprintf(buffer,"Layer_%i", iUnnamed++);
+ layer.mName = buffer;
+ }
+
+ // load this layer or ignore it? Check the layer name property
+ if (configLayerName.length() && configLayerName != layer.mName) {
+ skip = true;
+ }
+ else hasNamedLayer = true;
+
+ // optional: parent of this layer
+ if (mFileBuffer + 2 <= next)
+ layer.mParent = GetU2();
+
+ break;
+ }
+
+ // vertex list
+ case AI_LWO_PNTS:
+ {
+ if (skip)
+ break;
+
+ unsigned int old = (unsigned int)mCurLayer->mTempPoints.size();
+ LoadLWOPoints(head->length);
+ mCurLayer->mPointIDXOfs = old;
+ break;
+ }
+ // vertex tags
+ case AI_LWO_VMAD:
+ if (mCurLayer->mFaces.empty())
+ {
+ DefaultLogger::get()->warn("LWO2: Unexpected VMAD chunk");
+ break;
+ }
+ // --- intentionally no break here
+ case AI_LWO_VMAP:
+ {
+ if (skip)
+ break;
+
+ if (mCurLayer->mTempPoints.empty())
+ DefaultLogger::get()->warn("LWO2: Unexpected VMAP chunk");
+ else LoadLWO2VertexMap(head->length,head->type == AI_LWO_VMAD);
+ break;
+ }
+ // face list
+ case AI_LWO_POLS:
+ {
+ if (skip)
+ break;
+
+ unsigned int old = (unsigned int)mCurLayer->mFaces.size();
+ LoadLWO2Polygons(head->length);
+ mCurLayer->mFaceIDXOfs = old;
+ break;
+ }
+ // polygon tags
+ case AI_LWO_PTAG:
+ {
+ if (skip)
+ break;
+
+ if (mCurLayer->mFaces.empty())
+ DefaultLogger::get()->warn("LWO2: Unexpected PTAG");
+ else LoadLWO2PolygonTags(head->length);
+ break;
+ }
+ // list of tags
+ case AI_LWO_TAGS:
+ {
+ if (!mTags->empty())
+ DefaultLogger::get()->warn("LWO2: SRFS chunk encountered twice");
+ else LoadLWOTags(head->length);
+ break;
+ }
+
+ // surface chunk
+ case AI_LWO_SURF:
+ {
+ LoadLWO2Surface(head->length);
+ break;
+ }
+
+ // clip chunk
+ case AI_LWO_CLIP:
+ {
+ LoadLWO2Clip(head->length);
+ break;
+ }
+
+ // envelope chunk
+ case AI_LWO_ENVL:
+ {
+ LoadLWO2Envelope(head->length);
+ break;
+ }
+ }
+ mFileBuffer = next;
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_LWO_IMPORTER
diff --git a/src/3rdparty/assimp/code/LWOLoader.h b/src/3rdparty/assimp/code/LWOLoader.h
new file mode 100644
index 000000000..629ec072e
--- /dev/null
+++ b/src/3rdparty/assimp/code/LWOLoader.h
@@ -0,0 +1,490 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Declaration of the LWO importer class. */
+#ifndef AI_LWOLOADER_H_INCLUDED
+#define AI_LWOLOADER_H_INCLUDED
+
+#include "../include/aiTypes.h"
+#include "../include/DefaultLogger.h"
+
+#include "LWOFileData.h"
+#include "BaseImporter.h"
+#include "MaterialSystem.h"
+
+struct aiTexture;
+struct aiNode;
+
+namespace Assimp {
+using namespace LWO;
+
+// ---------------------------------------------------------------------------
+/** Class to load LWO files.
+ *
+ * @note Methods named "xxxLWO2[xxx]" are used with the newer LWO2 format.
+ * Methods named "xxxLWOB[xxx]" are used with the older LWOB format.
+ * Methods named "xxxLWO[xxx]" are used with both formats.
+ * Methods named "xxx" are used to preprocess the loaded data -
+ * they aren't specific to one format version
+*/
+// ---------------------------------------------------------------------------
+class LWOImporter : public BaseImporter
+{
+ friend class Importer;
+
+
+protected:
+ /** Constructor to be privately used by Importer */
+ LWOImporter();
+
+ /** Destructor, private as well */
+ ~LWOImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions)
+ {
+ extensions.insert("lxo");
+ extensions.insert("lwo");
+ }
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+ // -------------------------------------------------------------------
+ /** Loads a LWO file in the older LWOB format (LW < 6)
+ */
+ void LoadLWOBFile();
+
+ // -------------------------------------------------------------------
+ /** Loads a LWO file in the newer LWO2 format (LW >= 6)
+ */
+ void LoadLWO2File();
+
+
+ // -------------------------------------------------------------------
+ /** Parsing functions used for all file format versions
+ */
+ inline void GetS0(std::string& out,unsigned int max);
+ inline float GetF4();
+ inline uint32_t GetU4();
+ inline uint16_t GetU2();
+ inline uint8_t GetU1();
+
+
+ // -------------------------------------------------------------------
+ /** Loads a surface chunk from an LWOB file
+ * @param size Maximum size to be read, in bytes.
+ */
+ void LoadLWOBSurface(unsigned int size);
+
+ // -------------------------------------------------------------------
+ /** Loads a surface chunk from an LWO2 file
+ * @param size Maximum size to be read, in bytes.
+ */
+ void LoadLWO2Surface(unsigned int size);
+
+ // -------------------------------------------------------------------
+ /** Loads a texture block from a LWO2 file.
+ * @param size Maximum size to be read, in bytes.
+ * @param head Header of the SUF.BLOK header
+ */
+ void LoadLWO2TextureBlock(LE_NCONST IFF::SubChunkHeader* head,
+ unsigned int size );
+
+ // -------------------------------------------------------------------
+ /** Loads a shader block from a LWO2 file.
+ * @param size Maximum size to be read, in bytes.
+ * @param head Header of the SUF.BLOK header
+ */
+ void LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader* head,
+ unsigned int size );
+
+ // -------------------------------------------------------------------
+ /** Loads an image map from a LWO2 file
+ * @param size Maximum size to be read, in bytes.
+ * @param tex Texture object to be filled
+ */
+ void LoadLWO2ImageMap(unsigned int size, LWO::Texture& tex );
+ void LoadLWO2Gradient(unsigned int size, LWO::Texture& tex );
+ void LoadLWO2Procedural(unsigned int size, LWO::Texture& tex );
+
+ // loads the header - used by thethree functions above
+ void LoadLWO2TextureHeader(unsigned int size, LWO::Texture& tex );
+
+ // -------------------------------------------------------------------
+ /** Loads the LWO tag list from the file
+ * @param size Maximum size to be read, in bytes.
+ */
+ void LoadLWOTags(unsigned int size);
+
+ // -------------------------------------------------------------------
+ /** Load polygons from a POLS chunk
+ * @param length Size of the chunk
+ */
+ void LoadLWO2Polygons(unsigned int length);
+ void LoadLWOBPolygons(unsigned int length);
+
+ // -------------------------------------------------------------------
+ /** Load polygon tags from a PTAG chunk
+ * @param length Size of the chunk
+ */
+ void LoadLWO2PolygonTags(unsigned int length);
+
+ // -------------------------------------------------------------------
+ /** Load a vertex map from a VMAP/VMAD chunk
+ * @param length Size of the chunk
+ * @param perPoly Operate on per-polygon base?
+ */
+ void LoadLWO2VertexMap(unsigned int length, bool perPoly);
+
+ // -------------------------------------------------------------------
+ /** Load polygons from a PNTS chunk
+ * @param length Size of the chunk
+ */
+ void LoadLWOPoints(unsigned int length);
+
+ // -------------------------------------------------------------------
+ /** Load a clip from a CLIP chunk
+ * @param length Size of the chunk
+ */
+ void LoadLWO2Clip(unsigned int length);
+
+ // -------------------------------------------------------------------
+ /** Load an envelope from an EVL chunk
+ * @param length Size of the chunk
+ */
+ void LoadLWO2Envelope(unsigned int length);
+
+ // -------------------------------------------------------------------
+ /** Count vertices and faces in a LWOB/LWO2 file
+ */
+ void CountVertsAndFacesLWO2(unsigned int& verts,
+ unsigned int& faces,
+ uint16_t*& cursor,
+ const uint16_t* const end,
+ unsigned int max = 0xffffffff);
+
+ void CountVertsAndFacesLWOB(unsigned int& verts,
+ unsigned int& faces,
+ LE_NCONST uint16_t*& cursor,
+ const uint16_t* const end,
+ unsigned int max = 0xffffffff);
+
+ // -------------------------------------------------------------------
+ /** Read vertices and faces in a LWOB/LWO2 file
+ */
+ void CopyFaceIndicesLWO2(LWO::FaceList::iterator& it,
+ uint16_t*& cursor,
+ const uint16_t* const end);
+
+ // -------------------------------------------------------------------
+ void CopyFaceIndicesLWOB(LWO::FaceList::iterator& it,
+ LE_NCONST uint16_t*& cursor,
+ const uint16_t* const end,
+ unsigned int max = 0xffffffff);
+
+ // -------------------------------------------------------------------
+ /** Resolve the tag and surface lists that have been loaded.
+ * Generates the mMapping table.
+ */
+ void ResolveTags();
+
+ // -------------------------------------------------------------------
+ /** Resolve the clip list that has been loaded.
+ * Replaces clip references with real clips.
+ */
+ void ResolveClips();
+
+ // -------------------------------------------------------------------
+ /** Add a texture list to an output material description.
+ *
+ * @param pcMat Output material
+ * @param in Input texture list
+ * @param type Type identifier of the texture list
+ */
+ bool HandleTextures(MaterialHelper* pcMat, const TextureList& in,
+ aiTextureType type);
+
+ // -------------------------------------------------------------------
+ /** Adjust a texture path
+ */
+ void AdjustTexturePath(std::string& out);
+
+ // -------------------------------------------------------------------
+ /** Convert a LWO surface description to an ASSIMP material
+ */
+ void ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat);
+
+
+ // -------------------------------------------------------------------
+ /** Get a list of all UV/VC channels required by a specific surface.
+ *
+ * @param surf Working surface
+ * @param layer Working layer
+ * @param out Output list. The members are indices into the
+ * UV/VC channel lists of the layer
+ */
+ void FindUVChannels(/*const*/ LWO::Surface& surf,
+ LWO::SortedRep& sorted,
+ /*const*/ LWO::Layer& layer,
+ unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS]);
+
+ // -------------------------------------------------------------------
+ char FindUVChannels(LWO::TextureList& list,
+ LWO::Layer& layer,LWO::UVChannel& uv, unsigned int next);
+
+ // -------------------------------------------------------------------
+ void FindVCChannels(const LWO::Surface& surf,
+ LWO::SortedRep& sorted,
+ const LWO::Layer& layer,
+ unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS]);
+
+ // -------------------------------------------------------------------
+ /** Generate the final node graph
+ * Unused nodes are deleted.
+ * @param apcNodes Flat list of nodes
+ */
+ void GenerateNodeGraph(std::vector<aiNode*>& apcNodes);
+
+ // -------------------------------------------------------------------
+ /** Add children to a node
+ * @param node Node to become a father
+ * @param parent Index of the node
+ * @param apcNodes Flat list of nodes - used nodes are set to NULL.
+ */
+ void AddChildren(aiNode* node, uint16_t parent,
+ std::vector<aiNode*>& apcNodes);
+
+ // -------------------------------------------------------------------
+ /** Read a variable sized integer
+ * @param inout Input and output buffer
+ */
+ int ReadVSizedIntLWO2(uint8_t*& inout);
+
+ // -------------------------------------------------------------------
+ /** Assign a value from a VMAP to a vertex and all vertices
+ * attached to it.
+ * @param base VMAP destination data
+ * @param numRead Number of float's to be read
+ * @param idx Absolute index of the first vertex
+ * @param data Value of the VMAP to be assigned - read numRead
+ * floats from this array.
+ */
+ void DoRecursiveVMAPAssignment(VMapEntry* base, unsigned int numRead,
+ unsigned int idx, float* data);
+
+ // -------------------------------------------------------------------
+ /** Compute normal vectors for a mesh
+ * @param mesh Input mesh
+ * @param smoothingGroups Smoothing-groups-per-face array
+ * @param surface Surface for the mesh
+ */
+ void ComputeNormals(aiMesh* mesh, const std::vector<unsigned int>& smoothingGroups,
+ const LWO::Surface& surface);
+
+
+ // -------------------------------------------------------------------
+ /** Setup a new texture after the corresponding chunk was
+ * encountered in the file.
+ * @param list Texture list
+ * @param size Maximum number of bytes to be read
+ * @return Pointer to new texture
+ */
+ LWO::Texture* SetupNewTextureLWOB(LWO::TextureList& list,
+ unsigned int size);
+
+protected:
+
+ /** true if the file is a LWO2 file*/
+ bool mIsLWO2;
+
+ /** true if the file is a LXOB file*/
+ bool mIsLXOB;
+
+ /** Temporary list of layers from the file */
+ LayerList* mLayers;
+
+ /** Pointer to the current layer */
+ LWO::Layer* mCurLayer;
+
+ /** Temporary tag list from the file */
+ TagList* mTags;
+
+ /** Mapping table to convert from tag to surface indices.
+ 0xffffffff indicates that a no corresponding surface is available */
+ TagMappingTable* mMapping;
+
+ /** Temporary surface list from the file */
+ SurfaceList* mSurfaces;
+
+ /** Temporary clip list from the file */
+ ClipList mClips;
+
+ /** Temporary envelope list from the file */
+ EnvelopeList mEnvelopes;
+
+ /** file buffer */
+ uint8_t* mFileBuffer;
+
+ /** Size of the file, in bytes */
+ unsigned int fileSize;
+
+ /** Output scene */
+ aiScene* pScene;
+
+ /** Configuration option: speed flag set? */
+ bool configSpeedFlag;
+
+ /** Configuration option: index of layer to be loaded */
+ unsigned int configLayerIndex;
+
+ /** Configuration option: name of layer to be loaded */
+ std::string configLayerName;
+
+ /** True if we have a named layer */
+ bool hasNamedLayer;
+};
+
+
+// ------------------------------------------------------------------------------------------------
+inline float LWOImporter::GetF4()
+{
+ float f = *((float*)mFileBuffer);mFileBuffer += 4;
+ AI_LSWAP4(f);
+ return f;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline uint32_t LWOImporter::GetU4()
+{
+ uint32_t f = *((uint32_t*)mFileBuffer);mFileBuffer += 4;
+ AI_LSWAP4(f);
+ return f;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline uint16_t LWOImporter::GetU2()
+{
+ uint16_t f = *((uint16_t*)mFileBuffer);mFileBuffer += 2;
+ AI_LSWAP2(f);
+ return f;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline uint8_t LWOImporter::GetU1()
+{
+ return *mFileBuffer++;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline int LWOImporter::ReadVSizedIntLWO2(uint8_t*& inout)
+{
+ int i;
+ int c = *inout;inout++;
+ if (c != 0xFF)
+ {
+ i = c << 8;
+ c = *inout;inout++;
+ i |= c;
+ }
+ else
+ {
+ c = *inout;inout++;
+ i = c << 16;
+ c = *inout;inout++;
+ i |= c << 8;
+ c = *inout;inout++;
+ i |= c;
+ }
+ return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline void LWOImporter::GetS0(std::string& out,unsigned int max)
+{
+ unsigned int iCursor = 0;
+ const char*sz = (const char*)mFileBuffer;
+ while (*mFileBuffer)
+ {
+ if (++iCursor > max)
+ {
+ DefaultLogger::get()->warn("LWO: Invalid file, string is is too long");
+ break;
+ }
+ ++mFileBuffer;
+ }
+ size_t len = (size_t) ((const char*)mFileBuffer-sz);
+ out = std::string(sz,len);
+ mFileBuffer += (len&0x1 ? 1 : 2);
+}
+
+
+
+} // end of namespace Assimp
+
+#endif // AI_LWOIMPORTER_H_INCLUDED
diff --git a/src/3rdparty/assimp/code/LWOMaterial.cpp b/src/3rdparty/assimp/code/LWOMaterial.cpp
new file mode 100644
index 000000000..faee674a4
--- /dev/null
+++ b/src/3rdparty/assimp/code/LWOMaterial.cpp
@@ -0,0 +1,900 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the material oart of the LWO importer class */
+
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
+
+// internal headers
+#include "LWOLoader.h"
+#include "MaterialSystem.h"
+#include "ByteSwap.h"
+
+#include <QtGlobal>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+T lerp(const T& one, const T& two, float val)
+{
+ return one + (two-one)*val;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a lightwave mapping mode to our's
+inline aiTextureMapMode GetMapMode(LWO::Texture::Wrap in)
+{
+ switch (in)
+ {
+ case LWO::Texture::REPEAT:
+ return aiTextureMapMode_Wrap;
+
+ case LWO::Texture::MIRROR:
+ return aiTextureMapMode_Mirror;
+
+ case LWO::Texture::RESET:
+ DefaultLogger::get()->warn("LWO2: Unsupported texture map mode: RESET");
+
+ // fall though here
+ case LWO::Texture::EDGE:
+ return aiTextureMapMode_Clamp;
+ }
+ return (aiTextureMapMode)0;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool LWOImporter::HandleTextures(MaterialHelper* pcMat, const TextureList& in, aiTextureType type)
+{
+ ai_assert(NULL != pcMat);
+
+ unsigned int cur = 0, temp = 0;
+ aiString s;
+ bool ret = false;
+
+ for (TextureList::const_iterator it = in.begin(), end = in.end();it != end;++it) {
+ if (!(*it).enabled || !(*it).bCanUse)
+ continue;
+ ret = true;
+
+ // Convert lightwave's mapping modes to ours. We let them
+ // as they are, the GenUVcoords step will compute UV
+ // channels if they're not there.
+
+ aiTextureMapping mapping;
+ switch ((*it).mapMode)
+ {
+ case LWO::Texture::Planar:
+ mapping = aiTextureMapping_PLANE;
+ break;
+ case LWO::Texture::Cylindrical:
+ mapping = aiTextureMapping_CYLINDER;
+ break;
+ case LWO::Texture::Spherical:
+ mapping = aiTextureMapping_SPHERE;
+ break;
+ case LWO::Texture::Cubic:
+ mapping = aiTextureMapping_BOX;
+ break;
+ case LWO::Texture::FrontProjection:
+ DefaultLogger::get()->error("LWO2: Unsupported texture mapping: FrontProjection");
+ mapping = aiTextureMapping_OTHER;
+ break;
+ case LWO::Texture::UV:
+ {
+ if ( 0xffffffff == (*it).mRealUVIndex ) {
+ // We have no UV index for this texture, so we can't display it
+ continue;
+ }
+
+ // add the UV source index
+ temp = (*it).mRealUVIndex;
+ pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_UVWSRC(type,cur));
+
+ mapping = aiTextureMapping_UV;
+ }
+ break;
+ default:
+ ai_assert(false);
+ };
+
+ if (mapping != aiTextureMapping_UV) {
+ // Setup the main axis
+ aiVector3D v;
+ switch ((*it).majorAxis) {
+ case Texture::AXIS_X:
+ v = aiVector3D(1.f,0.f,0.f);
+ break;
+ case Texture::AXIS_Y:
+ v = aiVector3D(0.f,1.f,0.f);
+ break;
+ default: // case Texture::AXIS_Z:
+ v = aiVector3D(0.f,0.f,1.f);
+ break;
+ }
+
+ pcMat->AddProperty(&v,1,AI_MATKEY_TEXMAP_AXIS(type,cur));
+
+ // Setup UV scalings for cylindric and spherical projections
+ if (mapping == aiTextureMapping_CYLINDER || mapping == aiTextureMapping_SPHERE) {
+ aiUVTransform trafo;
+ trafo.mScaling.x = (*it).wrapAmountW;
+ trafo.mScaling.y = (*it).wrapAmountH;
+
+ Q_ASSERT(sizeof(aiUVTransform)/sizeof(float) == 5);
+ pcMat->AddProperty(&trafo,1,AI_MATKEY_UVTRANSFORM(type,cur));
+ }
+ DefaultLogger::get()->debug("LWO2: Setting up non-UV mapping");
+ }
+
+ // The older LWOB format does not use indirect references to clips.
+ // The file name of a texture is directly specified in the tex chunk.
+ if (mIsLWO2) {
+ // find the corresponding clip
+ ClipList::iterator clip = mClips.begin();
+ temp = (*it).mClipIdx;
+ for (ClipList::iterator end = mClips.end(); clip != end; ++clip) {
+ if ((*clip).idx == temp)
+ break;
+
+ }
+ if (mClips.end() == clip) {
+ DefaultLogger::get()->error("LWO2: Clip index is out of bounds");
+ temp = 0;
+
+ // fixme: appearently some LWO files shipping with Doom3 don't
+ // have clips at all ... check whether that's true or whether
+ // it's a bug in the loader.
+
+ s.Set("$texture.png");
+
+ //continue;
+ }
+ else {
+ if (Clip::UNSUPPORTED == (*clip).type) {
+ DefaultLogger::get()->error("LWO2: Clip type is not supported");
+ continue;
+ }
+ AdjustTexturePath((*clip).path);
+ s.Set((*clip).path);
+
+ // Additional image settings
+ int flags = 0;
+ if ((*clip).negate) {
+ flags |= aiTextureFlags_Invert;
+ }
+ pcMat->AddProperty(&flags,1,AI_MATKEY_TEXFLAGS(type,cur));
+ }
+ }
+ else
+ {
+ std::string ss = (*it).mFileName;
+ if (!ss.length()) {
+ DefaultLogger::get()->error("LWOB: Empty file name");
+ continue;
+ }
+ AdjustTexturePath(ss);
+ s.Set(ss);
+ }
+ pcMat->AddProperty(&s,AI_MATKEY_TEXTURE(type,cur));
+
+ // add the blend factor
+ pcMat->AddProperty<float>(&(*it).mStrength,1,AI_MATKEY_TEXBLEND(type,cur));
+
+ // add the blend operation
+ switch ((*it).blendType)
+ {
+ case LWO::Texture::Normal:
+ case LWO::Texture::Multiply:
+ temp = (unsigned int)aiTextureOp_Multiply;
+ break;
+
+ case LWO::Texture::Subtractive:
+ case LWO::Texture::Difference:
+ temp = (unsigned int)aiTextureOp_Subtract;
+ break;
+
+ case LWO::Texture::Divide:
+ temp = (unsigned int)aiTextureOp_Divide;
+ break;
+
+ case LWO::Texture::Additive:
+ temp = (unsigned int)aiTextureOp_Add;
+ break;
+
+ default:
+ temp = (unsigned int)aiTextureOp_Multiply;
+ DefaultLogger::get()->warn("LWO2: Unsupported texture blend mode: alpha or displacement");
+
+ }
+ // Setup texture operation
+ pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_TEXOP(type,cur));
+
+ // setup the mapping mode
+ pcMat->AddProperty<int>((int*)&mapping,1,AI_MATKEY_MAPPING(type,cur));
+
+ // add the u-wrapping
+ temp = (unsigned int)GetMapMode((*it).wrapModeWidth);
+ pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_MAPPINGMODE_U(type,cur));
+
+ // add the v-wrapping
+ temp = (unsigned int)GetMapMode((*it).wrapModeHeight);
+ pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_MAPPINGMODE_V(type,cur));
+
+ ++cur;
+ }
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat)
+{
+ // copy the name of the surface
+ aiString st;
+ st.Set(surf.mName);
+ pcMat->AddProperty(&st,AI_MATKEY_NAME);
+
+ const int i = surf.bDoubleSided ? 1 : 0;
+ pcMat->AddProperty(&i,1,AI_MATKEY_TWOSIDED);
+
+ // add the refraction index and the bump intensity
+ pcMat->AddProperty(&surf.mIOR,1,AI_MATKEY_REFRACTI);
+ pcMat->AddProperty(&surf.mBumpIntensity,1,AI_MATKEY_BUMPSCALING);
+
+ aiShadingMode m;
+ if (surf.mSpecularValue && surf.mGlossiness)
+ {
+ float fGloss;
+ if (mIsLWO2) {
+ fGloss = pow( surf.mGlossiness*10.0f+2.0f, 2.0f);
+ }
+ else
+ {
+ if (16.0f >= surf.mGlossiness)
+ fGloss = 6.0f;
+ else if (64.0f >= surf.mGlossiness)
+ fGloss = 20.0f;
+ else if (256.0f >= surf.mGlossiness)
+ fGloss = 50.0f;
+ else fGloss = 80.0f;
+ }
+
+ pcMat->AddProperty(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
+ pcMat->AddProperty(&fGloss,1,AI_MATKEY_SHININESS);
+ m = aiShadingMode_Phong;
+ }
+ else m = aiShadingMode_Gouraud;
+
+ // specular color
+ aiColor3D clr = lerp( aiColor3D(1.f,1.f,1.f), surf.mColor, surf.mColorHighlights );
+ pcMat->AddProperty(&clr,1,AI_MATKEY_COLOR_SPECULAR);
+ pcMat->AddProperty(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
+
+ // emissive color
+ // luminosity is not really the same but it affects the surface in a similar way. Some scaling looks good.
+ clr.g = clr.b = clr.r = surf.mLuminosity*0.8f;
+ pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
+
+ // opacity ... either additive or default-blended, please
+ if (0.f != surf.mAdditiveTransparency) {
+
+ const int add = aiBlendMode_Additive;
+ pcMat->AddProperty(&surf.mAdditiveTransparency,1,AI_MATKEY_OPACITY);
+ pcMat->AddProperty(&add,1,AI_MATKEY_BLEND_FUNC);
+ }
+
+ else if (10e10f != surf.mTransparency) {
+ const int def = aiBlendMode_Default;
+ const float f = 1.0f-surf.mTransparency;
+ pcMat->AddProperty(&f,1,AI_MATKEY_OPACITY);
+ pcMat->AddProperty(&def,1,AI_MATKEY_BLEND_FUNC);
+ }
+
+
+ // ADD TEXTURES to the material
+ // TODO: find out how we can handle COLOR textures correctly...
+ bool b = HandleTextures(pcMat,surf.mColorTextures,aiTextureType_DIFFUSE);
+ b = (b || HandleTextures(pcMat,surf.mDiffuseTextures,aiTextureType_DIFFUSE));
+ HandleTextures(pcMat,surf.mSpecularTextures,aiTextureType_SPECULAR);
+ HandleTextures(pcMat,surf.mGlossinessTextures,aiTextureType_SHININESS);
+ HandleTextures(pcMat,surf.mBumpTextures,aiTextureType_HEIGHT);
+ HandleTextures(pcMat,surf.mOpacityTextures,aiTextureType_OPACITY);
+ HandleTextures(pcMat,surf.mReflectionTextures,aiTextureType_REFLECTION);
+
+ // Now we need to know which shader to use .. iterate through the shader list of
+ // the surface and search for a name which we know ...
+ for (ShaderList::const_iterator it = surf.mShaders.begin(), end = surf.mShaders.end();it != end;++it) {
+ //if (!(*it).enabled)continue;
+
+ if ((*it).functionName == "LW_SuperCelShader" || (*it).functionName == "AH_CelShader") {
+ DefaultLogger::get()->info("LWO2: Mapping LW_SuperCelShader/AH_CelShader to aiShadingMode_Toon");
+
+ m = aiShadingMode_Toon;
+ break;
+ }
+ else if ((*it).functionName == "LW_RealFresnel" || (*it).functionName == "LW_FastFresnel") {
+ DefaultLogger::get()->info("LWO2: Mapping LW_RealFresnel/LW_FastFresnel to aiShadingMode_Fresnel");
+
+ m = aiShadingMode_Fresnel;
+ break;
+ }
+ else
+ {
+ DefaultLogger::get()->warn("LWO2: Unknown surface shader: " + (*it).functionName);
+ }
+ }
+ if (surf.mMaximumSmoothAngle <= 0.0f)
+ m = aiShadingMode_Flat;
+ pcMat->AddProperty((int*)&m,1,AI_MATKEY_SHADING_MODEL);
+
+ // (the diffuse value is just a scaling factor)
+ // If a diffuse texture is set, we set this value to 1.0
+ clr = (b && false ? aiColor3D(1.f,1.f,1.f) : surf.mColor);
+ clr.r *= surf.mDiffuseValue;
+ clr.g *= surf.mDiffuseValue;
+ clr.b *= surf.mDiffuseValue;
+ pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+}
+
+// ------------------------------------------------------------------------------------------------
+char LWOImporter::FindUVChannels(LWO::TextureList& list,
+ LWO::Layer&, LWO::UVChannel& uv, unsigned int next)
+{
+ char ret = 0;
+ for (TextureList::iterator it = list.begin(), end = list.end();it != end;++it) {
+
+ // Ignore textures with non-UV mappings for the moment.
+ if (!(*it).enabled || !(*it).bCanUse || (*it).mapMode != LWO::Texture::UV) {
+ continue;
+ }
+
+ if ((*it).mUVChannelIndex == uv.name) {
+ ret = 1;
+
+ // got it.
+ if ((*it).mRealUVIndex == 0xffffffff || (*it).mRealUVIndex == next)
+ {
+ (*it).mRealUVIndex = next;
+ }
+ else {
+ // channel mismatch. need to duplicate the material.
+ DefaultLogger::get()->warn("LWO: Channel mismatch, would need to duplicate surface [design bug]");
+
+ // TODO
+ }
+ }
+ }
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::FindUVChannels(LWO::Surface& surf,
+ LWO::SortedRep& sorted,LWO::Layer& layer,
+ unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS])
+{
+ unsigned int next = 0, extra = 0, num_extra = 0;
+
+ // Check whether we have an UV entry != 0 for one of the faces in 'sorted'
+ for (unsigned int i = 0; i < layer.mUVChannels.size();++i) {
+ LWO::UVChannel& uv = layer.mUVChannels[i];
+
+ for (LWO::SortedRep::const_iterator it = sorted.begin(); it != sorted.end(); ++it) {
+
+ LWO::Face& face = layer.mFaces[*it];
+
+ for (unsigned int n = 0; n < face.mNumIndices; ++n) {
+ unsigned int idx = face.mIndices[n];
+
+ if (uv.abAssigned[idx] && ((aiVector2D*)&uv.rawData[0])[idx] != aiVector2D()) {
+
+ if (next >= AI_MAX_NUMBER_OF_TEXTURECOORDS) {
+
+ DefaultLogger::get()->error("LWO: Maximum number of UV channels for "
+ "this mesh reached. Skipping channel \'" + uv.name + "\'");
+
+ }
+ else {
+ // Search through all textures assigned to 'surf' and look for this UV channel
+ char had = 0;
+ had |= FindUVChannels(surf.mColorTextures,layer,uv,next);
+ had |= FindUVChannels(surf.mDiffuseTextures,layer,uv,next);
+ had |= FindUVChannels(surf.mSpecularTextures,layer,uv,next);
+ had |= FindUVChannels(surf.mGlossinessTextures,layer,uv,next);
+ had |= FindUVChannels(surf.mOpacityTextures,layer,uv,next);
+ had |= FindUVChannels(surf.mBumpTextures,layer,uv,next);
+ had |= FindUVChannels(surf.mReflectionTextures,layer,uv,next);
+
+ if (had != 0) {
+
+ // We have a texture referencing this UV channel so we have to take special care of it
+ if (num_extra) {
+
+ for (unsigned int a = next; a < std::min( extra, AI_MAX_NUMBER_OF_TEXTURECOORDS-1u ); ++a) {
+ out[a+1] = out[a];
+ }
+ }
+ ++extra;
+ out[next++] = i;
+ }
+ else {
+
+ // Seems not to be used at all. Push to end if enough space is available.
+ out[extra++] = i;
+ ++num_extra;
+ }
+ }
+ it = sorted.end()-1;
+ break;
+ }
+ }
+ }
+ }
+ if (next != AI_MAX_NUMBER_OF_TEXTURECOORDS) {
+ out[extra] = 0xffffffff;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::FindVCChannels(const LWO::Surface& surf, LWO::SortedRep& sorted, const LWO::Layer& layer,
+ unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS])
+{
+ unsigned int next = 0;
+
+ // Check whether we have an vc entry != 0 for one of the faces in 'sorted'
+ for (unsigned int i = 0; i < layer.mVColorChannels.size();++i) {
+ const LWO::VColorChannel& vc = layer.mVColorChannels[i];
+
+ if (surf.mVCMap == vc.name) {
+ // The vertex color map is explicitely requested by the surface so we need to take special care of it
+ for (unsigned int a = 0; a < std::min(next,AI_MAX_NUMBER_OF_COLOR_SETS-1u); ++a) {
+ out[a+1] = out[a];
+ }
+ out[0] = i;
+ ++next;
+ }
+ else {
+
+ for (LWO::SortedRep::iterator it = sorted.begin(); it != sorted.end(); ++it) {
+ const LWO::Face& face = layer.mFaces[*it];
+
+ for (unsigned int n = 0; n < face.mNumIndices; ++n) {
+ unsigned int idx = face.mIndices[n];
+
+ if (vc.abAssigned[idx] && ((aiColor4D*)&vc.rawData[0])[idx] != aiColor4D(0.f,0.f,0.f,1.f)) {
+ if (next >= AI_MAX_NUMBER_OF_COLOR_SETS) {
+
+ DefaultLogger::get()->error("LWO: Maximum number of vertex color channels for "
+ "this mesh reached. Skipping channel \'" + vc.name + "\'");
+
+ }
+ else {
+ out[next++] = i;
+ }
+ it = sorted.end()-1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (next != AI_MAX_NUMBER_OF_COLOR_SETS) {
+ out[next] = 0xffffffff;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2ImageMap(unsigned int size, LWO::Texture& tex )
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + size;
+ while (true)
+ {
+ if (mFileBuffer + 6 >= end)break;
+ LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ throw DeadlyImportError("LWO2: Invalid SURF.BLOCK chunk length");
+
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ case AI_LWO_PROJ:
+ tex.mapMode = (Texture::MappingMode)GetU2();
+ break;
+ case AI_LWO_WRAP:
+ tex.wrapModeWidth = (Texture::Wrap)GetU2();
+ tex.wrapModeHeight = (Texture::Wrap)GetU2();
+ break;
+ case AI_LWO_AXIS:
+ tex.majorAxis = (Texture::Axes)GetU2();
+ break;
+ case AI_LWO_IMAG:
+ tex.mClipIdx = GetU2();
+ break;
+ case AI_LWO_VMAP:
+ GetS0(tex.mUVChannelIndex,head->length);
+ break;
+ case AI_LWO_WRPH:
+ tex.wrapAmountH = GetF4();
+ break;
+ case AI_LWO_WRPW:
+ tex.wrapAmountW = GetF4();
+ break;
+ }
+ mFileBuffer = next;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2Procedural(unsigned int /* size */, LWO::Texture& tex )
+{
+ // --- not supported at the moment
+ DefaultLogger::get()->error("LWO2: Found procedural texture, this is not supported");
+ tex.bCanUse = false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2Gradient(unsigned int /* size */, LWO::Texture& tex )
+{
+ // --- not supported at the moment
+ DefaultLogger::get()->error("LWO2: Found gradient texture, this is not supported");
+ tex.bCanUse = false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2TextureHeader(unsigned int size, LWO::Texture& tex )
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + size;
+
+ // get the ordinal string
+ GetS0( tex.ordinal, size);
+
+ // we could crash later if this is an empty string ...
+ if (!tex.ordinal.length())
+ {
+ DefaultLogger::get()->error("LWO2: Ill-formed SURF.BLOK ordinal string");
+ tex.ordinal = "\x00";
+ }
+ while (true)
+ {
+ if (mFileBuffer + 6 >= end)break;
+ LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ throw DeadlyImportError("LWO2: Invalid texture header chunk length");
+
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ case AI_LWO_CHAN:
+ tex.type = GetU4();
+ break;
+ case AI_LWO_ENAB:
+ tex.enabled = GetU2() ? true : false;
+ break;
+ case AI_LWO_OPAC:
+ tex.blendType = (Texture::BlendType)GetU2();
+ tex.mStrength = GetF4();
+ break;
+ }
+ mFileBuffer = next;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2TextureBlock(LE_NCONST IFF::SubChunkHeader* head, unsigned int size )
+{
+ ai_assert(!mSurfaces->empty());
+ LWO::Surface& surf = mSurfaces->back();
+ LWO::Texture tex;
+
+ // load the texture header
+ LoadLWO2TextureHeader(head->length,tex);
+ size -= head->length + 6;
+
+ // now get the exact type of the texture
+ switch (head->type)
+ {
+ case AI_LWO_PROC:
+ LoadLWO2Procedural(size,tex);
+ break;
+ case AI_LWO_GRAD:
+ LoadLWO2Gradient(size,tex);
+ break;
+ case AI_LWO_IMAP:
+ LoadLWO2ImageMap(size,tex);
+ }
+
+ // get the destination channel
+ TextureList* listRef = NULL;
+ switch (tex.type)
+ {
+ case AI_LWO_COLR:
+ listRef = &surf.mColorTextures;break;
+ case AI_LWO_DIFF:
+ listRef = &surf.mDiffuseTextures;break;
+ case AI_LWO_SPEC:
+ listRef = &surf.mSpecularTextures;break;
+ case AI_LWO_GLOS:
+ listRef = &surf.mGlossinessTextures;break;
+ case AI_LWO_BUMP:
+ listRef = &surf.mBumpTextures;break;
+ case AI_LWO_TRAN:
+ listRef = &surf.mOpacityTextures;break;
+ case AI_LWO_REFL:
+ listRef = &surf.mReflectionTextures;break;
+ default:
+ DefaultLogger::get()->warn("LWO2: Encountered unknown texture type");
+ return;
+ }
+
+ // now attach the texture to the parent surface - sort by ordinal string
+ for (TextureList::iterator it = listRef->begin();it != listRef->end(); ++it) {
+ if (::strcmp(tex.ordinal.c_str(),(*it).ordinal.c_str()) < 0) {
+ listRef->insert(it,tex);
+ return;
+ }
+ }
+ listRef->push_back(tex);
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader* /* head */, unsigned int size )
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + size;
+
+ ai_assert(!mSurfaces->empty());
+ LWO::Surface& surf = mSurfaces->back();
+ LWO::Shader shader;
+
+ // get the ordinal string
+ GetS0( shader.ordinal, size);
+
+ // we could crash later if this is an empty string ...
+ if (!shader.ordinal.length())
+ {
+ DefaultLogger::get()->error("LWO2: Ill-formed SURF.BLOK ordinal string");
+ shader.ordinal = "\x00";
+ }
+
+ // read the header
+ while (true)
+ {
+ if (mFileBuffer + 6 >= end)break;
+ LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ throw DeadlyImportError("LWO2: Invalid shader header chunk length");
+
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ case AI_LWO_ENAB:
+ shader.enabled = GetU2() ? true : false;
+ break;
+
+ case AI_LWO_FUNC:
+ GetS0( shader.functionName, head->length );
+ }
+ mFileBuffer = next;
+ }
+
+ // now attach the shader to the parent surface - sort by ordinal string
+ for (ShaderList::iterator it = surf.mShaders.begin();it != surf.mShaders.end(); ++it) {
+ if (::strcmp(shader.ordinal.c_str(),(*it).ordinal.c_str()) < 0) {
+ surf.mShaders.insert(it,shader);
+ return;
+ }
+ }
+ surf.mShaders.push_back(shader);
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2Surface(unsigned int size)
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + size;
+
+ mSurfaces->push_back( LWO::Surface () );
+ LWO::Surface& surf = mSurfaces->back();
+
+ GetS0(surf.mName,size);
+
+ // check whether this surface was derived from any other surface
+ std::string derived;
+ GetS0(derived,(unsigned int)(end - mFileBuffer));
+ if (derived.length()) {
+ // yes, find this surface
+ for (SurfaceList::iterator it = mSurfaces->begin(), end = mSurfaces->end()-1; it != end; ++it) {
+ if ((*it).mName == derived) {
+ // we have it ...
+ surf = *it;
+ derived.clear();break;
+ }
+ }
+ if (derived.size())
+ DefaultLogger::get()->warn("LWO2: Unable to find source surface: " + derived);
+ }
+
+ while (true)
+ {
+ if (mFileBuffer + 6 >= end)
+ break;
+ LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ throw DeadlyImportError("LWO2: Invalid surface chunk length");
+
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ // diffuse color
+ case AI_LWO_COLR:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,COLR,12);
+ surf.mColor.r = GetF4();
+ surf.mColor.g = GetF4();
+ surf.mColor.b = GetF4();
+ break;
+ }
+ // diffuse strength ... hopefully
+ case AI_LWO_DIFF:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,DIFF,4);
+ surf.mDiffuseValue = GetF4();
+ break;
+ }
+ // specular strength ... hopefully
+ case AI_LWO_SPEC:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPEC,4);
+ surf.mSpecularValue = GetF4();
+ break;
+ }
+ // transparency
+ case AI_LWO_TRAN:
+ {
+ // transparency explicitly disabled?
+ if (surf.mTransparency == 10e10f)
+ break;
+
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TRAN,4);
+ surf.mTransparency = GetF4();
+ break;
+ }
+ // additive transparency
+ case AI_LWO_ADTR:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,ADTR,4);
+ surf.mAdditiveTransparency = GetF4();
+ break;
+ }
+ // wireframe mode
+ case AI_LWO_LINE:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,LINE,2);
+ if (GetU2() & 0x1)
+ surf.mWireframe = true;
+ break;
+ }
+ // glossiness
+ case AI_LWO_GLOS:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,GLOS,4);
+ surf.mGlossiness = GetF4();
+ break;
+ }
+ // bump intensity
+ case AI_LWO_BUMP:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,BUMP,4);
+ surf.mBumpIntensity = GetF4();
+ break;
+ }
+ // color highlights
+ case AI_LWO_CLRH:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,CLRH,4);
+ surf.mColorHighlights = GetF4();
+ break;
+ }
+ // index of refraction
+ case AI_LWO_RIND:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,RIND,4);
+ surf.mIOR = GetF4();
+ break;
+ }
+ // polygon sidedness
+ case AI_LWO_SIDE:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SIDE,2);
+ surf.bDoubleSided = (3 == GetU2());
+ break;
+ }
+ // maximum smoothing angle
+ case AI_LWO_SMAN:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
+ surf.mMaximumSmoothAngle = fabs( GetF4() );
+ break;
+ }
+ // vertex color channel to be applied to the surface
+ case AI_LWO_VCOL:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,VCOL,12);
+ surf.mDiffuseValue *= GetF4(); // strength
+ ReadVSizedIntLWO2(mFileBuffer); // skip envelope
+ surf.mVCMapType = GetU4(); // type of the channel
+
+ // name of the channel
+ GetS0(surf.mVCMap, (unsigned int) (next - mFileBuffer ));
+ break;
+ }
+ // surface bock entry
+ case AI_LWO_BLOK:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,BLOK,4);
+ LE_NCONST IFF::SubChunkHeader* head2 = IFF::LoadSubChunk(mFileBuffer);
+
+ switch (head2->type)
+ {
+ case AI_LWO_PROC:
+ case AI_LWO_GRAD:
+ case AI_LWO_IMAP:
+ LoadLWO2TextureBlock(head2, head->length);
+ break;
+ case AI_LWO_SHDR:
+ LoadLWO2ShaderBlock(head2, head->length);
+ break;
+
+ default:
+ DefaultLogger::get()->warn("LWO2: Found an unsupported surface BLOK");
+ };
+
+ break;
+ }
+ }
+ mFileBuffer = next;
+ }
+}
+
+#endif // ASSIMP_BUILD_NO_X_IMPORTER
diff --git a/src/3rdparty/assimp/code/LWSLoader.cpp b/src/3rdparty/assimp/code/LWSLoader.cpp
new file mode 100644
index 000000000..2870c13bf
--- /dev/null
+++ b/src/3rdparty/assimp/code/LWSLoader.cpp
@@ -0,0 +1,889 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file LWSLoader.cpp
+ * @brief Implementation of the LWS importer class
+ */
+
+#include "AssimpPCH.h"
+
+#include "LWSLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+#include "SceneCombiner.h"
+#include "GenericProperty.h"
+#include "SkeletonMeshBuilder.h"
+#include "ConvertToLHProcess.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Recursive parsing of LWS files
+void LWS::Element::Parse (const char*& buffer)
+{
+ for (;SkipSpacesAndLineEnd(&buffer);SkipLine(&buffer)) {
+
+ // begin of a new element with children
+ bool sub = false;
+ if (*buffer == '{') {
+ ++buffer;
+ SkipSpaces(&buffer);
+ sub = true;
+ }
+ else if (*buffer == '}')
+ return;
+
+ children.push_back(Element());
+
+ // copy data line - read token per token
+
+ const char* cur = buffer;
+ while (!IsSpaceOrNewLine(*buffer)) ++buffer;
+ children.back().tokens[0] = std::string(cur,(size_t) (buffer-cur));
+ SkipSpaces(&buffer);
+
+ if (children.back().tokens[0] == "Plugin")
+ {
+ DefaultLogger::get()->debug("LWS: Skipping over plugin-specific data");
+
+ // strange stuff inside Plugin/Endplugin blocks. Needn't
+ // follow LWS syntax, so we skip over it
+ for (;SkipSpacesAndLineEnd(&buffer);SkipLine(&buffer)) {
+ if (!::strncmp(buffer,"EndPlugin",9)) {
+ //SkipLine(&buffer);
+ break;
+ }
+ }
+ continue;
+ }
+
+ cur = buffer;
+ while (!IsLineEnd(*buffer)) ++buffer;
+ children.back().tokens[1] = std::string(cur,(size_t) (buffer-cur));
+
+ // parse more elements recursively
+ if (sub)
+ children.back().Parse(buffer);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+LWSImporter::LWSImporter() : configSpeedFlag(false), io(0), first(0.0), last(0.0), fps(0.0)
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+LWSImporter::~LWSImporter()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool LWSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler,bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+ if (extension == "lws" || extension == "mot")
+ return true;
+
+ // if check for extension is not enough, check for the magic tokens LWSC and LWMO
+ if (!extension.length() || checkSig) {
+ uint32_t tokens[2];
+ tokens[0] = AI_MAKE_MAGIC("LWSC");
+ tokens[1] = AI_MAKE_MAGIC("LWMO");
+ return CheckMagicToken(pIOHandler,pFile,tokens,2);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get list of file extensions
+void LWSImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("lws");
+ extensions.insert("mot");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void LWSImporter::SetupProperties(const Importer* pImp)
+{
+ // AI_CONFIG_FAVOUR_SPEED
+ configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
+
+ // AI_CONFIG_IMPORT_LWS_ANIM_START
+ first = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_START,
+ 150392 /* magic hack */);
+
+ // AI_CONFIG_IMPORT_LWS_ANIM_END
+ last = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_END,
+ 150392 /* magic hack */);
+
+ if (last < first) {
+ std::swap(last,first);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read an envelope description
+void LWSImporter::ReadEnvelope(const LWS::Element& dad, LWO::Envelope& fill )
+{
+ if (dad.children.empty()) {
+ DefaultLogger::get()->error("LWS: Envelope descriptions must not be empty");
+ return;
+ }
+
+ // reserve enough storage
+ std::list< LWS::Element >::const_iterator it = dad.children.begin();;
+ fill.keys.reserve(strtol10(it->tokens[1].c_str()));
+
+ for (++it; it != dad.children.end(); ++it) {
+ const char* c = (*it).tokens[1].c_str();
+
+ if ((*it).tokens[0] == "Key") {
+ fill.keys.push_back(LWO::Key());
+ LWO::Key& key = fill.keys.back();
+
+ float f;
+ SkipSpaces(&c);
+ c = fast_atof_move(c,key.value);
+ SkipSpaces(&c);
+ c = fast_atof_move(c,f);
+
+ key.time = f;
+
+ unsigned int span = strtol10(c,&c), num = 0;
+ switch (span) {
+
+ case 0:
+ key.inter = LWO::IT_TCB;
+ num = 5;
+ break;
+ case 1:
+ case 2:
+ key.inter = LWO::IT_HERM;
+ num = 5;
+ break;
+ case 3:
+ key.inter = LWO::IT_LINE;
+ num = 0;
+ break;
+ case 4:
+ key.inter = LWO::IT_STEP;
+ num = 0;
+ break;
+ case 5:
+ key.inter = LWO::IT_BEZ2;
+ num = 4;
+ break;
+ default:
+ DefaultLogger::get()->error("LWS: Unknown span type");
+ }
+ for (unsigned int i = 0; i < num;++i) {
+ SkipSpaces(&c);
+ c = fast_atof_move(c,key.params[i]);
+ }
+ }
+ else if ((*it).tokens[0] == "Behaviors") {
+ SkipSpaces(&c);
+ fill.pre = (LWO::PrePostBehaviour) strtol10(c,&c);
+ SkipSpaces(&c);
+ fill.post = (LWO::PrePostBehaviour) strtol10(c,&c);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read animation channels in the old LightWave animation format
+void LWSImporter::ReadEnvelope_Old(
+ std::list< LWS::Element >::const_iterator& it,
+ const std::list< LWS::Element >::const_iterator& end,
+ LWS::NodeDesc& nodes,
+ unsigned int /* version */)
+{
+ unsigned int num,sub_num;
+ if (++it == end)goto unexpected_end;
+
+ num = strtol10((*it).tokens[0].c_str());
+ for (unsigned int i = 0; i < num; ++i) {
+
+ nodes.channels.push_back(LWO::Envelope());
+ LWO::Envelope& envl = nodes.channels.back();
+
+ envl.index = i;
+ envl.type = (LWO::EnvelopeType)(i+1);
+
+ if (++it == end)goto unexpected_end;
+ sub_num = strtol10((*it).tokens[0].c_str());
+
+ for (unsigned int n = 0; n < sub_num;++n) {
+
+ if (++it == end)goto unexpected_end;
+
+ // parse value and time, skip the rest for the moment.
+ LWO::Key key;
+ const char* c = fast_atof_move((*it).tokens[0].c_str(),key.value);
+ SkipSpaces(&c);
+ float f;
+ fast_atof_move((*it).tokens[0].c_str(),f);
+ key.time = f;
+
+ envl.keys.push_back(key);
+ }
+ }
+ return;
+
+unexpected_end:
+ DefaultLogger::get()->error("LWS: Encountered unexpected end of file while parsing object motion");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup a nice name for a node
+void LWSImporter::SetupNodeName(aiNode* nd, LWS::NodeDesc& src)
+{
+ const unsigned int combined = src.number | ((unsigned int)src.type) << 28u;
+
+ // the name depends on the type. We break LWS's strange naming convention
+ // and return human-readable, but still machine-parsable and unique, strings.
+ if (src.type == LWS::NodeDesc::OBJECT) {
+
+ if (src.path.length()) {
+ std::string::size_type s = src.path.find_last_of("\\/");
+ if (s == std::string::npos)
+ s = 0;
+ else ++s;
+
+ nd->mName.length = ::sprintf(nd->mName.data,"%s_(%08X)",src.path.substr(s).c_str(),combined);
+ return;
+ }
+ }
+ nd->mName.length = ::sprintf(nd->mName.data,"%s_(%08X)",src.name,combined);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively build the scenegraph
+void LWSImporter::BuildGraph(aiNode* nd, LWS::NodeDesc& src, std::vector<AttachmentInfo>& attach,
+ BatchLoader& batch,
+ aiCamera**& camOut,
+ aiLight**& lightOut,
+ std::vector<aiNodeAnim*>& animOut)
+{
+ // Setup a very cryptic name for the node, we want the user to be happy
+ SetupNodeName(nd,src);
+
+ // If this is an object from an external file - get the scene and setup proper attachment tags
+ aiScene* obj = NULL;
+ if (src.type == LWS::NodeDesc::OBJECT && src.path.length() ) {
+ obj = batch.GetImport(src.id);
+ if (!obj) {
+ DefaultLogger::get()->error("LWS: Failed to read external file " + src.path);
+ }
+ else {
+ attach.push_back(AttachmentInfo(obj,nd));
+ }
+ }
+
+ // If object is a light source - setup a corresponding ai structure
+ else if (src.type == LWS::NodeDesc::LIGHT) {
+ aiLight* lit = *lightOut++ = new aiLight();
+
+ // compute final light color
+ lit->mColorDiffuse = lit->mColorSpecular = src.lightColor*src.lightIntensity;
+
+ // name to attach light to node -> unique due to LWs indexing system
+ lit->mName = nd->mName;
+
+ // detemine light type and setup additional members
+ if (src.lightType == 2) { /* spot light */
+
+ lit->mType = aiLightSource_SPOT;
+ lit->mAngleInnerCone = (float)AI_DEG_TO_RAD( src.lightConeAngle );
+ lit->mAngleOuterCone = lit->mAngleInnerCone+(float)AI_DEG_TO_RAD( src.lightEdgeAngle );
+
+ }
+ else if (src.lightType == 1) { /* directional light source */
+ lit->mType = aiLightSource_DIRECTIONAL;
+ }
+ else lit->mType = aiLightSource_POINT;
+
+ // fixme: no proper handling of light falloffs yet
+ if (src.lightFalloffType == 1)
+ lit->mAttenuationConstant = 1.f;
+ else if (src.lightFalloffType == 1)
+ lit->mAttenuationLinear = 1.f;
+ else
+ lit->mAttenuationQuadratic = 1.f;
+ }
+
+ // If object is a camera - setup a corresponding ai structure
+ else if (src.type == LWS::NodeDesc::CAMERA) {
+ aiCamera* cam = *camOut++ = new aiCamera();
+
+ // name to attach cam to node -> unique due to LWs indexing system
+ cam->mName = nd->mName;
+ }
+
+ // Get the node transformation from the LWO key
+ LWO::AnimResolver resolver(src.channels,fps);
+ resolver.ExtractBindPose(nd->mTransformation);
+
+ // .. and construct animation channels
+ aiNodeAnim* anim = NULL;
+
+ if (first != last) {
+ resolver.SetAnimationRange(first,last);
+ resolver.ExtractAnimChannel(&anim,AI_LWO_ANIM_FLAG_SAMPLE_ANIMS|AI_LWO_ANIM_FLAG_START_AT_ZERO);
+ if (anim) {
+ anim->mNodeName = nd->mName;
+ animOut.push_back(anim);
+ }
+ }
+
+ // process pivot point, if any
+ if (src.pivotPos != aiVector3D()) {
+ aiMatrix4x4 tmp;
+ aiMatrix4x4::Translation(-src.pivotPos,tmp);
+
+ if (anim) {
+
+ // We have an animation channel for this node. Problem: to combine the pivot
+ // point with the node anims, we'd need to interpolate *all* keys, get
+ // transformation matrices from them, apply the translation and decompose
+ // the resulting matrices again in order to reconstruct the keys. This
+ // solution here is *much* easier ... we're just inserting an extra node
+ // in the hierarchy.
+ // Maybe the final optimization here will be done during postprocessing.
+
+ aiNode* pivot = new aiNode();
+ pivot->mName.length = sprintf( pivot->mName.data, "$Pivot_%s",nd->mName.data);
+ pivot->mTransformation = tmp;
+
+ pivot->mChildren = new aiNode*[pivot->mNumChildren = 1];
+ pivot->mChildren[0] = nd;
+
+ pivot->mParent = nd->mParent;
+ nd->mParent = pivot;
+
+ // swap children and hope the parents wont see a huge difference
+ pivot->mParent->mChildren[pivot->mParent->mNumChildren-1] = pivot;
+ }
+ else {
+ nd->mTransformation = tmp*nd->mTransformation;
+ }
+ }
+
+ // Add children
+ if (src.children.size()) {
+ nd->mChildren = new aiNode*[src.children.size()];
+ for (std::list<LWS::NodeDesc*>::iterator it = src.children.begin(); it != src.children.end(); ++it) {
+ aiNode* ndd = nd->mChildren[nd->mNumChildren++] = new aiNode();
+ ndd->mParent = nd;
+
+ BuildGraph(ndd,**it,attach,batch,camOut,lightOut,animOut);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Determine the exact location of a LWO file
+std::string LWSImporter::FindLWOFile(const std::string& in)
+{
+ // insert missing directory seperator if necessary
+ std::string tmp;
+ if (in.length() > 3 && in[1] == ':'&& in[2] != '\\' && in[2] != '/')
+ {
+ tmp = in[0] + ":\\" + in.substr(2);
+ }
+ else tmp = in;
+
+ if (io->Exists(tmp))
+ return in;
+
+ // file is not accessible for us ... maybe it's packed by
+ // LightWave's 'Package Scene' command?
+
+ // Relevant for us are the following two directories:
+ // <folder>\Objects\<hh>\<*>.lwo
+ // <folder>\Scenes\<hh>\<*>.lws
+ // where <hh> is optional.
+
+ std::string test = ".." + io->getOsSeparator() + tmp;
+ if (io->Exists(test))
+ return test;
+
+ test = ".." + io->getOsSeparator() + test;
+ if (io->Exists(test))
+ return test;
+
+ // return original path, maybe the IOsystem knows better
+ return tmp;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read file into given scene data structure
+void LWSImporter::InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler)
+{
+ io = pIOHandler;
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open LWS file " + pFile + ".");
+ }
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ std::vector< char > mBuffer;
+ TextFileToBuffer(file.get(),mBuffer);
+
+ // Parse the file structure
+ LWS::Element root; const char* dummy = &mBuffer[0];
+ root.Parse(dummy);
+
+ // Construct a Batchimporter to read more files recursively
+ BatchLoader batch(pIOHandler);
+// batch.SetBasePath(pFile);
+
+ // Construct an array to receive the flat output graph
+ std::list<LWS::NodeDesc> nodes;
+
+ unsigned int cur_light = 0, cur_camera = 0, cur_object = 0;
+ unsigned int num_light = 0, num_camera = 0, num_object = 0;
+
+ // check magic identifier, 'LWSC'
+ bool motion_file = false;
+ std::list< LWS::Element >::const_iterator it = root.children.begin();
+
+ if ((*it).tokens[0] == "LWMO")
+ motion_file = true;
+
+ if ((*it).tokens[0] != "LWSC" && !motion_file)
+ throw DeadlyImportError("LWS: Not a LightWave scene, magic tag LWSC not found");
+
+ // get file format version and print to log
+ ++it;
+ unsigned int version = strtol10((*it).tokens[0].c_str());
+ DefaultLogger::get()->info("LWS file format version is " + (*it).tokens[0]);
+ first = 0.;
+ last = 60.;
+ fps = 25.; /* seems to be a good default frame rate */
+
+ // Now read all elements in a very straghtforward manner
+ for (; it != root.children.end(); ++it) {
+ const char* c = (*it).tokens[1].c_str();
+
+ // 'FirstFrame': begin of animation slice
+ if ((*it).tokens[0] == "FirstFrame") {
+ if (150392. != first /* see SetupProperties() */)
+ first = strtol10(c,&c)-1.; /* we're zero-based */
+ }
+
+ // 'LastFrame': end of animation slice
+ else if ((*it).tokens[0] == "LastFrame") {
+ if (150392. != last /* see SetupProperties() */)
+ last = strtol10(c,&c)-1.; /* we're zero-based */
+ }
+
+ // 'FramesPerSecond': frames per second
+ else if ((*it).tokens[0] == "FramesPerSecond") {
+ fps = strtol10(c,&c);
+ }
+
+ // 'LoadObjectLayer': load a layer of a specific LWO file
+ else if ((*it).tokens[0] == "LoadObjectLayer") {
+
+ // get layer index
+ const int layer = strtol10(c,&c);
+
+ // setup the layer to be loaded
+ BatchLoader::PropertyMap props;
+ SetGenericProperty(props.ints,AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,layer);
+
+ // add node to list
+ LWS::NodeDesc d;
+ d.type = LWS::NodeDesc::OBJECT;
+ if (version >= 4) { // handle LWSC 4 explicit ID
+ SkipSpaces(&c);
+ d.number = strtol16(c,&c) & AI_LWS_MASK;
+ }
+ else d.number = cur_object++;
+
+ // and add the file to the import list
+ SkipSpaces(&c);
+ std::string path = FindLWOFile( c );
+ d.path = path;
+ d.id = batch.AddLoadRequest(path,0,&props);
+
+ nodes.push_back(d);
+ num_object++;
+ }
+ // 'LoadObject': load a LWO file into the scenegraph
+ else if ((*it).tokens[0] == "LoadObject") {
+
+ // add node to list
+ LWS::NodeDesc d;
+ d.type = LWS::NodeDesc::OBJECT;
+
+ if (version >= 4) { // handle LWSC 4 explicit ID
+ d.number = strtol16(c,&c) & AI_LWS_MASK;
+ SkipSpaces(&c);
+ }
+ else d.number = cur_object++;
+ std::string path = FindLWOFile( c );
+ d.id = batch.AddLoadRequest(path,0,NULL);
+
+ d.path = path;
+ nodes.push_back(d);
+ num_object++;
+ }
+ // 'AddNullObject': add a dummy node to the hierarchy
+ else if ((*it).tokens[0] == "AddNullObject") {
+
+ // add node to list
+ LWS::NodeDesc d;
+ d.type = LWS::NodeDesc::OBJECT;
+ d.name = c;
+ if (version >= 4) { // handle LWSC 4 explicit ID
+ d.number = strtol16(c,&c) & AI_LWS_MASK;
+ }
+ else d.number = cur_object++;
+ nodes.push_back(d);
+
+ num_object++;
+ }
+ // 'NumChannels': Number of envelope channels assigned to last layer
+ else if ((*it).tokens[0] == "NumChannels") {
+ // ignore for now
+ }
+ // 'Channel': preceedes any envelope description
+ else if ((*it).tokens[0] == "Channel") {
+ if (nodes.empty()) {
+ if (motion_file) {
+
+ // LightWave motion file. Add dummy node
+ LWS::NodeDesc d;
+ d.type = LWS::NodeDesc::OBJECT;
+ d.name = c;
+ d.number = cur_object++;
+ nodes.push_back(d);
+ }
+ else DefaultLogger::get()->error("LWS: Unexpected keyword: \'Channel\'");
+ }
+
+ // important: index of channel
+ nodes.back().channels.push_back(LWO::Envelope());
+ LWO::Envelope& env = nodes.back().channels.back();
+
+ env.index = strtol10(c);
+
+ // currently we can just interpret the standard channels 0...9
+ // (hack) assume that index-i yields the binary channel type from LWO
+ env.type = (LWO::EnvelopeType)(env.index+1);
+
+ }
+ // 'Envelope': a single animation channel
+ else if ((*it).tokens[0] == "Envelope") {
+ if (nodes.empty() || nodes.back().channels.empty())
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'Envelope\'");
+ else {
+ ReadEnvelope((*it),nodes.back().channels.back());
+ }
+ }
+ // 'ObjectMotion': animation information for older lightwave formats
+ else if (version < 3 && ((*it).tokens[0] == "ObjectMotion" ||
+ (*it).tokens[0] == "CameraMotion" ||
+ (*it).tokens[0] == "LightMotion")) {
+
+ if (nodes.empty())
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'<Light|Object|Camera>Motion\'");
+ else {
+ ReadEnvelope_Old(it,root.children.end(),nodes.back(),version);
+ }
+ }
+ // 'Pre/PostBehavior': pre/post animation behaviour for LWSC 2
+ else if (version == 2 && (*it).tokens[0] == "Pre/PostBehavior") {
+ if (nodes.empty())
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'Pre/PostBehavior'");
+ else {
+ for (std::list<LWO::Envelope>::iterator it = nodes.back().channels.begin(); it != nodes.back().channels.end(); ++it) {
+ // two ints per envelope
+ LWO::Envelope& env = *it;
+ env.pre = (LWO::PrePostBehaviour) strtol10(c,&c); SkipSpaces(&c);
+ env.post = (LWO::PrePostBehaviour) strtol10(c,&c); SkipSpaces(&c);
+ }
+ }
+ }
+ // 'ParentItem': specifies the parent of the current element
+ else if ((*it).tokens[0] == "ParentItem") {
+ if (nodes.empty())
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'ParentItem\'");
+
+ else nodes.back().parent = strtol16(c,&c);
+ }
+ // 'ParentObject': deprecated one for older formats
+ else if (version < 3 && (*it).tokens[0] == "ParentObject") {
+ if (nodes.empty())
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'ParentObject\'");
+
+ else {
+ nodes.back().parent = strtol10(c,&c) | (1u << 28u);
+ }
+ }
+ // 'AddCamera': add a camera to the scenegraph
+ else if ((*it).tokens[0] == "AddCamera") {
+
+ // add node to list
+ LWS::NodeDesc d;
+ d.type = LWS::NodeDesc::CAMERA;
+
+ if (version >= 4) { // handle LWSC 4 explicit ID
+ d.number = strtol16(c,&c) & AI_LWS_MASK;
+ }
+ else d.number = cur_camera++;
+ nodes.push_back(d);
+
+ num_camera++;
+ }
+ // 'CameraName': set name of currently active camera
+ else if ((*it).tokens[0] == "CameraName") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::CAMERA)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'CameraName\'");
+
+ else nodes.back().name = c;
+ }
+ // 'AddLight': add a light to the scenegraph
+ else if ((*it).tokens[0] == "AddLight") {
+
+ // add node to list
+ LWS::NodeDesc d;
+ d.type = LWS::NodeDesc::LIGHT;
+
+ if (version >= 4) { // handle LWSC 4 explicit ID
+ d.number = strtol16(c,&c) & AI_LWS_MASK;
+ }
+ else d.number = cur_light++;
+ nodes.push_back(d);
+
+ num_light++;
+ }
+ // 'LightName': set name of currently active light
+ else if ((*it).tokens[0] == "LightName") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightName\'");
+
+ else nodes.back().name = c;
+ }
+ // 'LightIntensity': set intensity of currently active light
+ else if ((*it).tokens[0] == "LightIntensity" || (*it).tokens[0] == "LgtIntensity" ) {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightIntensity\'");
+
+ else fast_atof_move(c, nodes.back().lightIntensity );
+
+ }
+ // 'LightType': set type of currently active light
+ else if ((*it).tokens[0] == "LightType") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightType\'");
+
+ else nodes.back().lightType = strtol10(c);
+
+ }
+ // 'LightFalloffType': set falloff type of currently active light
+ else if ((*it).tokens[0] == "LightFalloffType") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightFalloffType\'");
+
+ else nodes.back().lightFalloffType = strtol10(c);
+
+ }
+ // 'LightConeAngle': set cone angle of currently active light
+ else if ((*it).tokens[0] == "LightConeAngle") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightConeAngle\'");
+
+ else nodes.back().lightConeAngle = fast_atof(c);
+
+ }
+ // 'LightEdgeAngle': set area where we're smoothing from min to max intensity
+ else if ((*it).tokens[0] == "LightEdgeAngle") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightEdgeAngle\'");
+
+ else nodes.back().lightEdgeAngle = fast_atof(c);
+
+ }
+ // 'LightColor': set color of currently active light
+ else if ((*it).tokens[0] == "LightColor") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightColor\'");
+
+ else {
+ c = fast_atof_move(c, (float&) nodes.back().lightColor.r );
+ SkipSpaces(&c);
+ c = fast_atof_move(c, (float&) nodes.back().lightColor.g );
+ SkipSpaces(&c);
+ c = fast_atof_move(c, (float&) nodes.back().lightColor.b );
+ }
+ }
+
+ // 'PivotPosition': position of local transformation origin
+ else if ((*it).tokens[0] == "PivotPosition" || (*it).tokens[0] == "PivotPoint") {
+ if (nodes.empty())
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'PivotPosition\'");
+ else {
+ c = fast_atof_move(c, (float&) nodes.back().pivotPos.x );
+ SkipSpaces(&c);
+ c = fast_atof_move(c, (float&) nodes.back().pivotPos.y );
+ SkipSpaces(&c);
+ c = fast_atof_move(c, (float&) nodes.back().pivotPos.z );
+ }
+ }
+ }
+
+ // resolve parenting
+ for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
+
+ // check whether there is another node which calls us a parent
+ for (std::list<LWS::NodeDesc>::iterator dit = nodes.begin(); dit != nodes.end(); ++dit) {
+ if (dit != it && *it == (*dit).parent) {
+ if ((*dit).parent_resolved) {
+ // fixme: it's still possible to produce an overflow due to cross references ..
+ DefaultLogger::get()->error("LWS: Found cross reference in scenegraph");
+ continue;
+ }
+
+ (*it).children.push_back(&*dit);
+ (*dit).parent_resolved = &*it;
+ }
+ }
+ }
+
+ // find out how many nodes have no parent yet
+ unsigned int no_parent = 0;
+ for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
+ if (!(*it).parent_resolved)
+ ++ no_parent;
+ }
+ if (!no_parent)
+ throw DeadlyImportError("LWS: Unable to find scene root node");
+
+
+ // Load all subsequent files
+ batch.LoadAll();
+
+ // and build the final output graph by attaching the loaded external
+ // files to ourselves. first build a master graph
+ aiScene* master = new aiScene();
+ aiNode* nd = master->mRootNode = new aiNode();
+
+ // allocate storage for cameras&lights
+ if (num_camera) {
+ master->mCameras = new aiCamera*[master->mNumCameras = num_camera];
+ }
+ aiCamera** cams = master->mCameras;
+ if (num_light) {
+ master->mLights = new aiLight*[master->mNumLights = num_light];
+ }
+ aiLight** lights = master->mLights;
+
+ std::vector<AttachmentInfo> attach;
+ std::vector<aiNodeAnim*> anims;
+
+ nd->mName.Set("<LWSRoot>");
+ nd->mChildren = new aiNode*[no_parent];
+ for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
+ if (!(*it).parent_resolved) {
+ aiNode* ro = nd->mChildren[ nd->mNumChildren++ ] = new aiNode();
+ ro->mParent = nd;
+
+ // ... and build the scene graph. If we encounter object nodes,
+ // add then to our attachment table.
+ BuildGraph(ro,*it, attach, batch, cams, lights, anims);
+ }
+ }
+
+ // create a master animation channel for us
+ if (anims.size()) {
+ master->mAnimations = new aiAnimation*[master->mNumAnimations = 1];
+ aiAnimation* anim = master->mAnimations[0] = new aiAnimation();
+ anim->mName.Set("LWSMasterAnim");
+
+ // LWS uses seconds as time units, but we convert to frames
+ anim->mTicksPerSecond = fps;
+ anim->mDuration = last-(first-1); /* fixme ... zero or one-based?*/
+
+ anim->mChannels = new aiNodeAnim*[anim->mNumChannels = anims.size()];
+ std::copy(anims.begin(),anims.end(),anim->mChannels);
+ }
+
+ // convert the master scene to RH
+ MakeLeftHandedProcess monster_cheat;
+ monster_cheat.Execute(master);
+
+ // .. ccw
+ FlipWindingOrderProcess flipper;
+ flipper.Execute(pScene);
+
+ // OK ... finally build the output graph
+ SceneCombiner::MergeScenes(&pScene,master,attach,
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) : 0));
+
+ // Check flags
+ if (!pScene->mNumMeshes || !pScene->mNumMaterials) {
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+
+ if (pScene->mNumAnimations) {
+ // construct skeleton mesh
+ SkeletonMeshBuilder builder(pScene);
+ }
+ }
+
+}
diff --git a/src/3rdparty/assimp/code/LWSLoader.h b/src/3rdparty/assimp/code/LWSLoader.h
new file mode 100644
index 000000000..7fcf6ead0
--- /dev/null
+++ b/src/3rdparty/assimp/code/LWSLoader.h
@@ -0,0 +1,245 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file LWSLoader.h
+ * @brief Declaration of the LightWave scene importer class.
+ */
+#ifndef AI_LWSLOADER_H_INCLUDED
+#define AI_LWSLOADER_H_INCLUDED
+
+#include "LWOFileData.h"
+#include "SceneCombiner.h"
+
+namespace Assimp {
+ namespace LWS {
+
+// ---------------------------------------------------------------------------
+/** Represents an element in a LWS file.
+ *
+ * This can either be a single data line - <name> <value> or a data
+ * group - { name <data_line0> ... n }
+ */
+class Element
+{
+public:
+ Element()
+ {}
+
+ // first: name, second: rest
+ std::string tokens[2];
+ std::list<Element> children;
+
+ //! Recursive parsing function
+ void Parse (const char*& buffer);
+};
+
+#define AI_LWS_MASK (0xffffffff >> 4u)
+
+// ---------------------------------------------------------------------------
+/** Represents a LWS scenegraph element
+ */
+struct NodeDesc
+{
+ NodeDesc()
+ : type(OBJECT)
+ , id(0)
+ , number (0)
+ , parent (0)
+ , name ("")
+ , lightColor (1.f,1.f,1.f)
+ , lightIntensity (1.f)
+ , lightType (0)
+ , lightFalloffType (0)
+ , lightConeAngle (45.f)
+ , lightEdgeAngle(0.0)
+ , parent_resolved (NULL)
+ {}
+
+ enum {
+
+ OBJECT = 1,
+ LIGHT = 2,
+ CAMERA = 3,
+ BONE = 4
+ } type; // type of node
+
+ // if object: path
+ std::string path;
+ unsigned int id;
+
+ // number of object
+ unsigned int number;
+
+ // index of parent index
+ unsigned int parent;
+
+ // lights & cameras & dummies: name
+ const char* name;
+
+ // animation channels
+ std::list< LWO::Envelope > channels;
+
+ // position of pivot point
+ aiVector3D pivotPos;
+
+
+
+ // color of light source
+ aiColor3D lightColor;
+
+ // intensity of light source
+ float lightIntensity;
+
+ // type of light source
+ unsigned int lightType;
+
+ // falloff type of light source
+ unsigned int lightFalloffType;
+
+ // cone angle of (spot) light source
+ float lightConeAngle;
+
+ // soft cone angle of (spot) light source
+ float lightEdgeAngle;
+
+
+
+ // list of resolved children
+ std::list< NodeDesc* > children;
+
+ // resolved parent node
+ NodeDesc* parent_resolved;
+
+
+ // for std::find()
+ bool operator == (unsigned int num) const {
+ if (!num)
+ return false;
+ unsigned int _type = num >> 28u;
+
+ return _type == static_cast<unsigned int>(type) && (num & AI_LWS_MASK) == number;
+ }
+};
+
+} // end namespace LWS
+
+// ---------------------------------------------------------------------------
+/** LWS (LightWave Scene Format) importer class.
+ *
+ * This class does heavily depend on the LWO importer class. LWS files
+ * contain mainly descriptions how LWO objects are composed together
+ * in a scene.
+*/
+class LWSImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ LWSImporter();
+
+ /** Destructor, private as well */
+ ~LWSImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ // Check whether we can read a specific file
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ // Get list of supported extensions
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ // Import file into given scene data structure
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ // Setup import properties
+ void SetupProperties(const Importer* pImp);
+
+private:
+
+
+ // -------------------------------------------------------------------
+ // Read an envelope description
+ void ReadEnvelope(const LWS::Element& dad, LWO::Envelope& out );
+
+ // -------------------------------------------------------------------
+ // Read an envelope description for the older LW file format
+ void ReadEnvelope_Old(std::list< LWS::Element >::const_iterator& it,
+ const std::list< LWS::Element >::const_iterator& end,
+ LWS::NodeDesc& nodes,
+ unsigned int version);
+
+ // -------------------------------------------------------------------
+ // Setup a nice name for a node
+ void SetupNodeName(aiNode* nd, LWS::NodeDesc& src);
+
+ // -------------------------------------------------------------------
+ // Recursively build the scenegraph
+ void BuildGraph(aiNode* nd,
+ LWS::NodeDesc& src,
+ std::vector<AttachmentInfo>& attach,
+ BatchLoader& batch,
+ aiCamera**& camOut,
+ aiLight**& lightOut,
+ std::vector<aiNodeAnim*>& animOut);
+
+ // -------------------------------------------------------------------
+ // Try several dirs until we find the right location of a LWS file.
+ std::string FindLWOFile(const std::string& in);
+
+private:
+
+ bool configSpeedFlag;
+ IOSystem* io;
+
+ double first,last,fps;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_LWSIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/LimitBoneWeightsProcess.cpp b/src/3rdparty/assimp/code/LimitBoneWeightsProcess.cpp
new file mode 100644
index 000000000..03507b4fe
--- /dev/null
+++ b/src/3rdparty/assimp/code/LimitBoneWeightsProcess.cpp
@@ -0,0 +1,208 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** Implementation of the LimitBoneWeightsProcess post processing step */
+
+#include "AssimpPCH.h"
+#include "LimitBoneWeightsProcess.h"
+
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+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)
+{
+ DefaultLogger::get()->debug("LimitBoneWeightsProcess begin");
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ ProcessMesh( pScene->mMeshes[a]);
+
+ DefaultLogger::get()->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 = vit->size();
+ vit->erase( vit->begin() + mMaxWeights, vit->end());
+ removed += 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;
+ for ( std::vector<Weight>::iterator it = vit->begin(); it != vit->end(); ++it)
+ it->mWeight /= sum;
+ }
+
+ 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];
+
+ // ignore the bone if no vertex weights were removed there
+
+ // FIX (Aramis, 07|22|08)
+ // NO! we can't ignore it in this case ... it is possible that
+ // the number of weights did not change, but the weight values did.
+
+ // if ( bw.size() == bone->mNumWeights)
+ // continue;
+
+ // FIX (Aramis, 07|21|08)
+ // It is possible that all weights of a bone have been removed.
+ // This would naturally cause an exception in &bw[0].
+ 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 = (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()) {
+ char buffer[1024];
+ ::sprintf(buffer,"Removed %i weights. Input bones: %i. Output bones: %i",removed,old_bones,pMesh->mNumBones);
+ DefaultLogger::get()->info(buffer);
+ }
+ }
+}
diff --git a/src/3rdparty/assimp/code/LimitBoneWeightsProcess.h b/src/3rdparty/assimp/code/LimitBoneWeightsProcess.h
new file mode 100644
index 000000000..99b72ef91
--- /dev/null
+++ b/src/3rdparty/assimp/code/LimitBoneWeightsProcess.h
@@ -0,0 +1,146 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** 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
+{
+ friend class Importer;
+ friend class ::LimitBoneWeightsTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ LimitBoneWeightsProcess();
+
+ /** Destructor, private as well */
+ ~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);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** 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() { }
+ Weight( unsigned int pBone, float pWeight)
+ {
+ mBone = pBone;
+ mWeight = pWeight;
+ }
+
+ /** Comparision 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/src/3rdparty/assimp/code/LineSplitter.h b/src/3rdparty/assimp/code/LineSplitter.h
new file mode 100644
index 000000000..9021e9341
--- /dev/null
+++ b/src/3rdparty/assimp/code/LineSplitter.h
@@ -0,0 +1,217 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file LineSplitter.h
+ * @brief LineSplitter, a helper class to iterate through all lines
+ * of a file easily. Works with StreamReader.
+ */
+#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;
+
+public:
+
+ // -----------------------------------------
+ /** construct from existing stream reader */
+ LineSplitter(StreamReaderLE& stream)
+ : stream(stream)
+ , swallow()
+ {
+ cur.reserve(1024);
+ operator++();
+
+ idx = 0;
+ }
+
+public:
+
+ // -----------------------------------------
+ /** pseudo-iterator increment */
+ LineSplitter& operator++() {
+ if (swallow) {
+ swallow = false;
+ return *this;
+ }
+
+ if (!*this) {
+ throw std::logic_error("End of file, no more lines to be retrieved.");
+ }
+
+ char s;
+
+ cur.clear(); // I will kill you if you deallocate.
+ while (stream.GetRemainingSize() && (s = stream.GetI1(),1)) {
+ if (s == '\n' || s == '\r') {
+ while (stream.GetRemainingSize() && ((s = stream.GetI1()) == ' ' || s == '\r' || s == '\n')) {}
+ if (stream.GetRemainingSize()) {
+ stream.IncPtr(-1);
+ }
+ break;
+ }
+ cur += s;
+ }
+
+ ++idx;
+ return *this;
+ }
+
+ // -----------------------------------------
+ /** get a pointer to the beginning of a particular token */
+ const char* 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;
+ }
+
+ // -----------------------------------------
+ /** extract the start positions of N tokens from the current line*/
+ template <size_t N>
+ void 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);
+ }
+ }
+
+ // -----------------------------------------
+ /** member access */
+ const std::string* operator -> () const {
+ return &cur;
+ }
+
+ const std::string& operator* () const {
+ return cur;
+ }
+
+ // -----------------------------------------
+ /** boolean context */
+ operator bool() const {
+ return stream.GetRemainingSize()>0;
+ }
+
+ // -----------------------------------------
+ /** line indices are zero-based, empty lines are included */
+ operator line_idx() const {
+ return idx;
+ }
+
+ line_idx get_index() const {
+ return idx;
+ }
+
+ // -----------------------------------------
+ /** access the underlying stream object */
+ StreamReaderLE& get_stream() {
+ return stream;
+ }
+
+ // -----------------------------------------
+ /** !strcmp((*this)->substr(0,strlen(check)),check) */
+ bool match_start(const char* check) {
+ const size_t len = strlen(check);
+
+ return len <= cur.length() && std::equal(check,check+len,cur.begin());
+ }
+
+
+ // -----------------------------------------
+ /** swallow the next call to ++, return the previous value. */
+ void swallow_next_increment() {
+ swallow = true;
+ }
+
+private:
+
+ line_idx idx;
+ std::string cur;
+ StreamReaderLE& stream;
+ bool swallow;
+};
+
+}
+#endif // INCLUDED_LINE_SPLITTER_H
diff --git a/src/3rdparty/assimp/code/MD2FileData.h b/src/3rdparty/assimp/code/MD2FileData.h
new file mode 100644
index 000000000..864374704
--- /dev/null
+++ b/src/3rdparty/assimp/code/MD2FileData.h
@@ -0,0 +1,163 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file MD2FileData.h
+ * @brief Defines helper data structures for importing MD2 files
+ * http://linux.ucla.edu/~phaethon/q3/formats/md2-schoenblum.html
+ */
+#ifndef AI_MD2FILEHELPER_H_INC
+#define AI_MD2FILEHELPER_H_INC
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+#include "./../include/Compiler/pushpack1.h"
+
+namespace Assimp {
+namespace MD2 {
+
+// to make it easier for us, we test the magic word against both "endianesses"
+#define AI_MD2_MAGIC_NUMBER_BE AI_MAKE_MAGIC("IDP2")
+#define AI_MD2_MAGIC_NUMBER_LE AI_MAKE_MAGIC("2PDI")
+
+// common limitations
+#define AI_MD2_VERSION 15
+#define AI_MD2_MAXQPATH 64
+#define AI_MD2_MAX_FRAMES 512
+#define AI_MD2_MAX_SKINS 32
+#define AI_MD2_MAX_VERTS 2048
+#define AI_MD2_MAX_TRIANGLES 4096
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for the MD2 main header
+ */
+struct Header
+{
+ uint32_t magic;
+ uint32_t version;
+ uint32_t skinWidth;
+ uint32_t skinHeight;
+ uint32_t frameSize;
+ uint32_t numSkins;
+ uint32_t numVertices;
+ uint32_t numTexCoords;
+ uint32_t numTriangles;
+ uint32_t numGlCommands;
+ uint32_t numFrames;
+ uint32_t offsetSkins;
+ uint32_t offsetTexCoords;
+ uint32_t offsetTriangles;
+ uint32_t offsetFrames;
+ uint32_t offsetGlCommands;
+ uint32_t offsetEnd;
+
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD2 OpenGl draw command
+ */
+struct GLCommand
+{
+ float s, t;
+ uint32_t vertexIndex;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD2 triangle
+ */
+struct Triangle
+{
+ uint16_t vertexIndices[3];
+ uint16_t textureIndices[3];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD2 vertex
+ */
+struct Vertex
+{
+ uint8_t vertex[3];
+ uint8_t lightNormalIndex;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD2 frame
+ */
+struct Frame
+{
+ float scale[3];
+ float translate[3];
+ char name[16];
+ Vertex vertices[1];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD2 texture coordinate
+ */
+struct TexCoord
+{
+ uint16_t s;
+ uint16_t t;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD2 skin
+ */
+struct Skin
+{
+ char name[AI_MD2_MAXQPATH]; /* texture file name */
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+
+// ---------------------------------------------------------------------------
+//! Lookup a normal vector from Quake's normal lookup table
+//! \param index Input index (0-161)
+//! \param vOut Receives the output normal
+void LookupNormalIndex(uint8_t index,aiVector3D& vOut);
+
+
+}
+}
+
+#endif // !! include guard
+
diff --git a/src/3rdparty/assimp/code/MD2Loader.cpp b/src/3rdparty/assimp/code/MD2Loader.cpp
new file mode 100644
index 000000000..dbfeabc39
--- /dev/null
+++ b/src/3rdparty/assimp/code/MD2Loader.cpp
@@ -0,0 +1,414 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER
+
+/** @file Implementation of the MD2 importer class */
+#include "MD2Loader.h"
+#include "MaterialSystem.h"
+#include "ByteSwap.h"
+#include "MD2NormalTable.h" // shouldn't be included by other units
+
+using namespace Assimp;
+using namespace Assimp::MD2;
+
+
+// helper macro to determine the size of an array
+#if (!defined ARRAYSIZE)
+# define ARRAYSIZE(_array) (int(sizeof(_array) / sizeof(_array[0])))
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Helper function to lookup a normal in Quake 2's precalculated table
+void MD2::LookupNormalIndex(uint8_t iNormalIndex,aiVector3D& vOut)
+{
+ // make sure the normal index has a valid value
+ if (iNormalIndex >= ARRAYSIZE(g_avNormals)) {
+ DefaultLogger::get()->warn("Index overflow in Quake II normal vector list");
+ iNormalIndex = ARRAYSIZE(g_avNormals) - 1;
+ }
+ vOut = *((const aiVector3D*)(&g_avNormals[iNormalIndex]));
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MD2Importer::MD2Importer() : configFrameID(0), m_pcHeader(0), mBuffer(0), fileSize(0)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MD2Importer::~MD2Importer()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool MD2Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+ if (extension == "md2")
+ return true;
+
+ // if check for extension is not enough, check for the magic tokens
+ if (!extension.length() || checkSig) {
+ uint32_t tokens[1];
+ tokens[0] = AI_MD2_MAGIC_NUMBER_LE;
+ return CheckMagicToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all extensions supported by this loader
+void MD2Importer::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("md2");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void MD2Importer::SetupProperties(const Importer* pImp)
+{
+ // The
+ // AI_CONFIG_IMPORT_MD2_KEYFRAME option overrides the
+ // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD2_KEYFRAME,0xffffffff);
+ if (0xffffffff == configFrameID){
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+ }
+}
+// ------------------------------------------------------------------------------------------------
+// Validate the file header
+void MD2Importer::ValidateHeader( )
+{
+ // check magic number
+ if (m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_BE &&
+ m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_LE)
+ {
+ char szBuffer[5];
+ szBuffer[0] = ((char*)&m_pcHeader->magic)[0];
+ szBuffer[1] = ((char*)&m_pcHeader->magic)[1];
+ szBuffer[2] = ((char*)&m_pcHeader->magic)[2];
+ szBuffer[3] = ((char*)&m_pcHeader->magic)[3];
+ szBuffer[4] = '\0';
+
+ throw DeadlyImportError("Invalid MD2 magic word: should be IDP2, the "
+ "magic word found is " + std::string(szBuffer));
+ }
+
+ // check file format version
+ if (m_pcHeader->version != 8)
+ DefaultLogger::get()->warn( "Unsupported md2 file version. Continuing happily ...");
+
+ // check some values whether they are valid
+ if (0 == m_pcHeader->numFrames)
+ throw DeadlyImportError( "Invalid md2 file: NUM_FRAMES is 0");
+
+ if (m_pcHeader->offsetEnd > (uint32_t)fileSize)
+ throw DeadlyImportError( "Invalid md2 file: File is too small");
+
+ if (m_pcHeader->offsetSkins + m_pcHeader->numSkins * sizeof (MD2::Skin) >= fileSize ||
+ m_pcHeader->offsetTexCoords + m_pcHeader->numTexCoords * sizeof (MD2::TexCoord) >= fileSize ||
+ m_pcHeader->offsetTriangles + m_pcHeader->numTriangles * sizeof (MD2::Triangle) >= fileSize ||
+ m_pcHeader->offsetFrames + m_pcHeader->numFrames * sizeof (MD2::Frame) >= fileSize ||
+ m_pcHeader->offsetEnd > fileSize)
+ {
+ throw DeadlyImportError("Invalid MD2 header: some offsets are outside the file");
+ }
+
+ if (m_pcHeader->numSkins > AI_MD2_MAX_SKINS)
+ DefaultLogger::get()->warn("The model contains more skins than Quake 2 supports");
+ if ( m_pcHeader->numFrames > AI_MD2_MAX_FRAMES)
+ DefaultLogger::get()->warn("The model contains more frames than Quake 2 supports");
+ if (m_pcHeader->numVertices > AI_MD2_MAX_VERTS)
+ DefaultLogger::get()->warn("The model contains more vertices than Quake 2 supports");
+
+ if (m_pcHeader->numFrames <= configFrameID )
+ throw DeadlyImportError("The requested frame is not existing the file");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void MD2Importer::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open MD2 file " + pFile + "");
+
+ // check whether the md3 file is large enough to contain
+ // at least the file header
+ fileSize = (unsigned int)file->FileSize();
+ if ( fileSize < sizeof(MD2::Header))
+ throw DeadlyImportError( "MD2 File is too small");
+
+ std::vector<uint8_t> mBuffer2(fileSize);
+ file->Read(&mBuffer2[0], 1, fileSize);
+ mBuffer = &mBuffer2[0];
+
+
+ m_pcHeader = (BE_NCONST MD2::Header*)mBuffer;
+
+#ifdef AI_BUILD_BIG_ENDIAN
+
+ ByteSwap::Swap4(&m_pcHeader->frameSize);
+ ByteSwap::Swap4(&m_pcHeader->magic);
+ ByteSwap::Swap4(&m_pcHeader->numFrames);
+ ByteSwap::Swap4(&m_pcHeader->numGlCommands);
+ ByteSwap::Swap4(&m_pcHeader->numSkins);
+ ByteSwap::Swap4(&m_pcHeader->numTexCoords);
+ ByteSwap::Swap4(&m_pcHeader->numTriangles);
+ ByteSwap::Swap4(&m_pcHeader->numVertices);
+ ByteSwap::Swap4(&m_pcHeader->offsetEnd);
+ ByteSwap::Swap4(&m_pcHeader->offsetFrames);
+ ByteSwap::Swap4(&m_pcHeader->offsetGlCommands);
+ ByteSwap::Swap4(&m_pcHeader->offsetSkins);
+ ByteSwap::Swap4(&m_pcHeader->offsetTexCoords);
+ ByteSwap::Swap4(&m_pcHeader->offsetTriangles);
+ ByteSwap::Swap4(&m_pcHeader->skinHeight);
+ ByteSwap::Swap4(&m_pcHeader->skinWidth);
+ ByteSwap::Swap4(&m_pcHeader->version);
+
+#endif
+
+ ValidateHeader();
+
+ // there won't be more than one mesh inside the file
+ pScene->mNumMaterials = 1;
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = new MaterialHelper();
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes = new aiMesh*[1];
+
+ aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
+ pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // navigate to the begin of the frame data
+ BE_NCONST MD2::Frame* pcFrame = (BE_NCONST MD2::Frame*) ((uint8_t*)
+ m_pcHeader + m_pcHeader->offsetFrames);
+
+ pcFrame += configFrameID;
+
+ // navigate to the begin of the triangle data
+ MD2::Triangle* pcTriangles = (MD2::Triangle*) ((uint8_t*)
+ m_pcHeader + m_pcHeader->offsetTriangles);
+
+ // navigate to the begin of the tex coords data
+ BE_NCONST MD2::TexCoord* pcTexCoords = (BE_NCONST MD2::TexCoord*) ((uint8_t*)
+ m_pcHeader + m_pcHeader->offsetTexCoords);
+
+ // navigate to the begin of the vertex data
+ BE_NCONST MD2::Vertex* pcVerts = (BE_NCONST MD2::Vertex*) (pcFrame->vertices);
+
+#ifdef AI_BUILD_BIG_ENDIAN
+ for (uint32_t i = 0; i< m_pcHeader->numTriangles; ++i)
+ {
+ for (unsigned int p = 0; p < 3;++p)
+ {
+ ByteSwap::Swap2(& pcTriangles[i].textureIndices[p]);
+ ByteSwap::Swap2(& pcTriangles[i].vertexIndices[p]);
+ }
+ }
+ for (uint32_t i = 0; i < m_pcHeader->offsetTexCoords;++i)
+ {
+ ByteSwap::Swap2(& pcTexCoords[i].s);
+ ByteSwap::Swap2(& pcTexCoords[i].t);
+ }
+ ByteSwap::Swap4( & pcFrame->scale[0] );
+ ByteSwap::Swap4( & pcFrame->scale[1] );
+ ByteSwap::Swap4( & pcFrame->scale[2] );
+ ByteSwap::Swap4( & pcFrame->translate[0] );
+ ByteSwap::Swap4( & pcFrame->translate[1] );
+ ByteSwap::Swap4( & pcFrame->translate[2] );
+#endif
+
+ pcMesh->mNumFaces = m_pcHeader->numTriangles;
+ pcMesh->mFaces = new aiFace[m_pcHeader->numTriangles];
+
+ // allocate output storage
+ pcMesh->mNumVertices = (unsigned int)pcMesh->mNumFaces*3;
+ pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+
+ // Not sure whether there are MD2 files without texture coordinates
+ // NOTE: texture coordinates can be there without a texture,
+ // but a texture can't be there without a valid UV channel
+ MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
+ const int iMode = (int)aiShadingMode_Gouraud;
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ if (m_pcHeader->numTexCoords && m_pcHeader->numSkins)
+ {
+ // navigate to the first texture associated with the mesh
+ const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)m_pcHeader +
+ m_pcHeader->offsetSkins);
+
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 1.0f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ if (pcSkins->name[0])
+ {
+ aiString szString;
+ const size_t iLen = ::strlen(pcSkins->name);
+ ::memcpy(szString.data,pcSkins->name,iLen);
+ szString.data[iLen] = '\0';
+ szString.length = iLen;
+
+ pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ else{
+ DefaultLogger::get()->warn("Texture file name has zero length. It will be skipped.");
+ }
+ }
+ else {
+ // apply a default material
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.6f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ aiString szName;
+ szName.Set(AI_DEFAULT_TEXTURED_MATERIAL_NAME);
+ pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
+
+ aiString sz;
+
+ // TODO: Try to guess the name of the texture file from the model file name
+
+ sz.Set("$texture_dummy.bmp");
+ pcHelper->AddProperty(&sz,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+
+
+ // now read all triangles of the first frame, apply scaling and translation
+ unsigned int iCurrent = 0;
+
+ float fDivisorU = 1.0f,fDivisorV = 1.0f;
+ if (m_pcHeader->numTexCoords) {
+ // allocate storage for texture coordinates, too
+ pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNumUVComponents[0] = 2;
+
+ // check whether the skin width or height are zero (this would
+ // cause a division through zero)
+ if (!m_pcHeader->skinWidth) {
+ DefaultLogger::get()->error("MD2: No valid skin width given");
+ }
+ else fDivisorU = (float)m_pcHeader->skinWidth;
+ if (!m_pcHeader->skinHeight){
+ DefaultLogger::get()->error("MD2: No valid skin height given");
+ }
+ else fDivisorV = (float)m_pcHeader->skinHeight;
+ }
+
+ for (unsigned int i = 0; i < (unsigned int)m_pcHeader->numTriangles;++i) {
+ // Allocate the face
+ pScene->mMeshes[0]->mFaces[i].mIndices = new unsigned int[3];
+ pScene->mMeshes[0]->mFaces[i].mNumIndices = 3;
+
+ // copy texture coordinates
+ // check whether they are different from the previous value at this index.
+ // In this case, create a full separate set of vertices/normals/texcoords
+ for (unsigned int c = 0; c < 3;++c,++iCurrent) {
+
+ // validate vertex indices
+ register unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
+ if (iIndex >= m_pcHeader->numVertices) {
+ DefaultLogger::get()->error("MD2: Vertex index is outside the allowed range");
+ iIndex = m_pcHeader->numVertices-1;
+ }
+
+ // read x,y, and z component of the vertex
+ aiVector3D& vec = pcMesh->mVertices[iCurrent];
+
+ vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0];
+ vec.x += pcFrame->translate[0];
+
+ vec.y = (float)pcVerts[iIndex].vertex[1] * pcFrame->scale[1];
+ vec.y += pcFrame->translate[1];
+
+ vec.z = (float)pcVerts[iIndex].vertex[2] * pcFrame->scale[2];
+ vec.z += pcFrame->translate[2];
+
+ // read the normal vector from the precalculated normal table
+ aiVector3D& vNormal = pcMesh->mNormals[iCurrent];
+ LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal);
+
+ // flip z and y to become right-handed
+ std::swap((float&)vNormal.z,(float&)vNormal.y);
+ std::swap((float&)vec.z,(float&)vec.y);
+
+ if (m_pcHeader->numTexCoords) {
+ // validate texture coordinates
+ iIndex = pcTriangles[i].textureIndices[c];
+ if (iIndex >= m_pcHeader->numTexCoords) {
+ DefaultLogger::get()->error("MD2: UV index is outside the allowed range");
+ iIndex = m_pcHeader->numTexCoords-1;
+ }
+
+ aiVector3D& pcOut = pcMesh->mTextureCoords[0][iCurrent];
+
+ // the texture coordinates are absolute values but we
+ // need relative values between 0 and 1
+ pcOut.x = pcTexCoords[iIndex].s / fDivisorU;
+ pcOut.y = 1.f-pcTexCoords[iIndex].t / fDivisorV;
+ }
+ pScene->mMeshes[0]->mFaces[i].mIndices[c] = iCurrent;
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_MD2_IMPORTER
diff --git a/src/3rdparty/assimp/code/MD2Loader.h b/src/3rdparty/assimp/code/MD2Loader.h
new file mode 100644
index 000000000..5a646b8dc
--- /dev/null
+++ b/src/3rdparty/assimp/code/MD2Loader.h
@@ -0,0 +1,126 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file MD2Loader.h
+ * @brief Declaration of the .MD2 importer class.
+ */
+#ifndef AI_MD2LOADER_H_INCLUDED
+#define AI_MD2LOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+struct aiNode;
+#include "MD2FileData.h"
+
+namespace Assimp {
+class MaterialHelper;
+
+using namespace MD2;
+
+// ---------------------------------------------------------------------------
+/** Importer class for MD2
+*/
+class MD2Importer : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ MD2Importer();
+
+ /** Destructor, private as well */
+ ~MD2Importer();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details. */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+
+ // -------------------------------------------------------------------
+ /** Validate the header of the file
+ */
+ void ValidateHeader();
+
+protected:
+
+ /** Configuration option: frame to be loaded */
+ unsigned int configFrameID;
+
+ /** Header of the MD2 file */
+ BE_NCONST MD2::Header* m_pcHeader;
+
+ /** Buffer to hold the loaded file */
+ BE_NCONST uint8_t* mBuffer;
+
+ /** Size of the file, in bytes */
+ unsigned int fileSize;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/MD2NormalTable.h b/src/3rdparty/assimp/code/MD2NormalTable.h
new file mode 100644
index 000000000..288fb4a17
--- /dev/null
+++ b/src/3rdparty/assimp/code/MD2NormalTable.h
@@ -0,0 +1,217 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/*
+ * @file Slightly modified version of the anorms.h header file
+ * released by ID software with the Quake 2 source code.
+ *
+ * Table of normals used by MD2 models
+ */
+
+#ifndef AI_MDL_NORMALTABLE_H_INC
+#define AI_MDL_NORMALTABLE_H_INC
+
+
+float g_avNormals[162][3] = {
+{ -0.525731f, 0.000000f, 0.850651f },
+{ -0.442863f, 0.238856f, 0.864188f },
+{ -0.295242f, 0.000000f, 0.955423f },
+{ -0.309017f, 0.500000f, 0.809017f },
+{ -0.162460f, 0.262866f, 0.951056f },
+{ 0.000000f, 0.000000f, 1.000000f },
+{ 0.000000f, 0.850651f, 0.525731f },
+{ -0.147621f, 0.716567f, 0.681718f },
+{ 0.147621f, 0.716567f, 0.681718f },
+{ 0.000000f, 0.525731f, 0.850651f },
+{ 0.309017f, 0.500000f, 0.809017f },
+{ 0.525731f, 0.000000f, 0.850651f },
+{ 0.295242f, 0.000000f, 0.955423f },
+{ 0.442863f, 0.238856f, 0.864188f },
+{ 0.162460f, 0.262866f, 0.951056f },
+{ -0.681718f, 0.147621f, 0.716567f },
+{ -0.809017f, 0.309017f, 0.500000f },
+{ -0.587785f, 0.425325f, 0.688191f },
+{ -0.850651f, 0.525731f, 0.000000f },
+{ -0.864188f, 0.442863f, 0.238856f },
+{ -0.716567f, 0.681718f, 0.147621f },
+{ -0.688191f, 0.587785f, 0.425325f },
+{ -0.500000f, 0.809017f, 0.309017f },
+{ -0.238856f, 0.864188f, 0.442863f },
+{ -0.425325f, 0.688191f, 0.587785f },
+{ -0.716567f, 0.681718f, -0.147621f },
+{ -0.500000f, 0.809017f, -0.309017f },
+{ -0.525731f, 0.850651f, 0.000000f },
+{ 0.000000f, 0.850651f, -0.525731f },
+{ -0.238856f, 0.864188f, -0.442863f },
+{ 0.000000f, 0.955423f, -0.295242f },
+{ -0.262866f, 0.951056f, -0.162460f },
+{ 0.000000f, 1.000000f, 0.000000f },
+{ 0.000000f, 0.955423f, 0.295242f },
+{ -0.262866f, 0.951056f, 0.162460f },
+{ 0.238856f, 0.864188f, 0.442863f },
+{ 0.262866f, 0.951056f, 0.162460f },
+{ 0.500000f, 0.809017f, 0.309017f },
+{ 0.238856f, 0.864188f, -0.442863f },
+{ 0.262866f, 0.951056f, -0.162460f },
+{ 0.500000f, 0.809017f, -0.309017f },
+{ 0.850651f, 0.525731f, 0.000000f },
+{ 0.716567f, 0.681718f, 0.147621f },
+{ 0.716567f, 0.681718f, -0.147621f },
+{ 0.525731f, 0.850651f, 0.000000f },
+{ 0.425325f, 0.688191f, 0.587785f },
+{ 0.864188f, 0.442863f, 0.238856f },
+{ 0.688191f, 0.587785f, 0.425325f },
+{ 0.809017f, 0.309017f, 0.500000f },
+{ 0.681718f, 0.147621f, 0.716567f },
+{ 0.587785f, 0.425325f, 0.688191f },
+{ 0.955423f, 0.295242f, 0.000000f },
+{ 1.000000f, 0.000000f, 0.000000f },
+{ 0.951056f, 0.162460f, 0.262866f },
+{ 0.850651f, -0.525731f, 0.000000f },
+{ 0.955423f, -0.295242f, 0.000000f },
+{ 0.864188f, -0.442863f, 0.238856f },
+{ 0.951056f, -0.162460f, 0.262866f },
+{ 0.809017f, -0.309017f, 0.500000f },
+{ 0.681718f, -0.147621f, 0.716567f },
+{ 0.850651f, 0.000000f, 0.525731f },
+{ 0.864188f, 0.442863f, -0.238856f },
+{ 0.809017f, 0.309017f, -0.500000f },
+{ 0.951056f, 0.162460f, -0.262866f },
+{ 0.525731f, 0.000000f, -0.850651f },
+{ 0.681718f, 0.147621f, -0.716567f },
+{ 0.681718f, -0.147621f, -0.716567f },
+{ 0.850651f, 0.000000f, -0.525731f },
+{ 0.809017f, -0.309017f, -0.500000f },
+{ 0.864188f, -0.442863f, -0.238856f },
+{ 0.951056f, -0.162460f, -0.262866f },
+{ 0.147621f, 0.716567f, -0.681718f },
+{ 0.309017f, 0.500000f, -0.809017f },
+{ 0.425325f, 0.688191f, -0.587785f },
+{ 0.442863f, 0.238856f, -0.864188f },
+{ 0.587785f, 0.425325f, -0.688191f },
+{ 0.688191f, 0.587785f, -0.425325f },
+{ -0.147621f, 0.716567f, -0.681718f },
+{ -0.309017f, 0.500000f, -0.809017f },
+{ 0.000000f, 0.525731f, -0.850651f },
+{ -0.525731f, 0.000000f, -0.850651f },
+{ -0.442863f, 0.238856f, -0.864188f },
+{ -0.295242f, 0.000000f, -0.955423f },
+{ -0.162460f, 0.262866f, -0.951056f },
+{ 0.000000f, 0.000000f, -1.000000f },
+{ 0.295242f, 0.000000f, -0.955423f },
+{ 0.162460f, 0.262866f, -0.951056f },
+{ -0.442863f, -0.238856f, -0.864188f },
+{ -0.309017f, -0.500000f, -0.809017f },
+{ -0.162460f, -0.262866f, -0.951056f },
+{ 0.000000f, -0.850651f, -0.525731f },
+{ -0.147621f, -0.716567f, -0.681718f },
+{ 0.147621f, -0.716567f, -0.681718f },
+{ 0.000000f, -0.525731f, -0.850651f },
+{ 0.309017f, -0.500000f, -0.809017f },
+{ 0.442863f, -0.238856f, -0.864188f },
+{ 0.162460f, -0.262866f, -0.951056f },
+{ 0.238856f, -0.864188f, -0.442863f },
+{ 0.500000f, -0.809017f, -0.309017f },
+{ 0.425325f, -0.688191f, -0.587785f },
+{ 0.716567f, -0.681718f, -0.147621f },
+{ 0.688191f, -0.587785f, -0.425325f },
+{ 0.587785f, -0.425325f, -0.688191f },
+{ 0.000000f, -0.955423f, -0.295242f },
+{ 0.000000f, -1.000000f, 0.000000f },
+{ 0.262866f, -0.951056f, -0.162460f },
+{ 0.000000f, -0.850651f, 0.525731f },
+{ 0.000000f, -0.955423f, 0.295242f },
+{ 0.238856f, -0.864188f, 0.442863f },
+{ 0.262866f, -0.951056f, 0.162460f },
+{ 0.500000f, -0.809017f, 0.309017f },
+{ 0.716567f, -0.681718f, 0.147621f },
+{ 0.525731f, -0.850651f, 0.000000f },
+{ -0.238856f, -0.864188f, -0.442863f },
+{ -0.500000f, -0.809017f, -0.309017f },
+{ -0.262866f, -0.951056f, -0.162460f },
+{ -0.850651f, -0.525731f, 0.000000f },
+{ -0.716567f, -0.681718f, -0.147621f },
+{ -0.716567f, -0.681718f, 0.147621f },
+{ -0.525731f, -0.850651f, 0.000000f },
+{ -0.500000f, -0.809017f, 0.309017f },
+{ -0.238856f, -0.864188f, 0.442863f },
+{ -0.262866f, -0.951056f, 0.162460f },
+{ -0.864188f, -0.442863f, 0.238856f },
+{ -0.809017f, -0.309017f, 0.500000f },
+{ -0.688191f, -0.587785f, 0.425325f },
+{ -0.681718f, -0.147621f, 0.716567f },
+{ -0.442863f, -0.238856f, 0.864188f },
+{ -0.587785f, -0.425325f, 0.688191f },
+{ -0.309017f, -0.500000f, 0.809017f },
+{ -0.147621f, -0.716567f, 0.681718f },
+{ -0.425325f, -0.688191f, 0.587785f },
+{ -0.162460f, -0.262866f, 0.951056f },
+{ 0.442863f, -0.238856f, 0.864188f },
+{ 0.162460f, -0.262866f, 0.951056f },
+{ 0.309017f, -0.500000f, 0.809017f },
+{ 0.147621f, -0.716567f, 0.681718f },
+{ 0.000000f, -0.525731f, 0.850651f },
+{ 0.425325f, -0.688191f, 0.587785f },
+{ 0.587785f, -0.425325f, 0.688191f },
+{ 0.688191f, -0.587785f, 0.425325f },
+{ -0.955423f, 0.295242f, 0.000000f },
+{ -0.951056f, 0.162460f, 0.262866f },
+{ -1.000000f, 0.000000f, 0.000000f },
+{ -0.850651f, 0.000000f, 0.525731f },
+{ -0.955423f, -0.295242f, 0.000000f },
+{ -0.951056f, -0.162460f, 0.262866f },
+{ -0.864188f, 0.442863f, -0.238856f },
+{ -0.951056f, 0.162460f, -0.262866f },
+{ -0.809017f, 0.309017f, -0.500000f },
+{ -0.864188f, -0.442863f, -0.238856f },
+{ -0.951056f, -0.162460f, -0.262866f },
+{ -0.809017f, -0.309017f, -0.500000f },
+{ -0.681718f, 0.147621f, -0.716567f },
+{ -0.681718f, -0.147621f, -0.716567f },
+{ -0.850651f, 0.000000f, -0.525731f },
+{ -0.688191f, 0.587785f, -0.425325f },
+{ -0.587785f, 0.425325f, -0.688191f },
+{ -0.425325f, 0.688191f, -0.587785f },
+{ -0.425325f, -0.688191f, -0.587785f },
+{ -0.587785f, -0.425325f, -0.688191f },
+{ -0.688191f, -0.587785f, -0.425325f }
+};
+
+#endif // !! include guard
diff --git a/src/3rdparty/assimp/code/MD3FileData.h b/src/3rdparty/assimp/code/MD3FileData.h
new file mode 100644
index 000000000..edc34d2f7
--- /dev/null
+++ b/src/3rdparty/assimp/code/MD3FileData.h
@@ -0,0 +1,315 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Md3FileData.h
+ *
+ * @brief Defines helper data structures for importing MD3 files.
+ * http://linux.ucla.edu/~phaethon/q3/formats/md3format.html
+ */
+#ifndef AI_MD3FILEHELPER_H_INC
+#define AI_MD3FILEHELPER_H_INC
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+#include "./../include/Compiler/pushpack1.h"
+
+namespace Assimp {
+namespace MD3 {
+
+// to make it easier for us, we test the magic word against both "endianesses"
+#define AI_MD3_MAGIC_NUMBER_BE AI_MAKE_MAGIC("IDP3")
+#define AI_MD3_MAGIC_NUMBER_LE AI_MAKE_MAGIC("3PDI")
+
+// common limitations
+#define AI_MD3_VERSION 15
+#define AI_MD3_MAXQPATH 64
+#define AI_MD3_MAXFRAME 16
+#define AI_MD3_MAX_FRAMES 1024
+#define AI_MD3_MAX_TAGS 16
+#define AI_MD3_MAX_SURFACES 32
+#define AI_MD3_MAX_SHADERS 256
+#define AI_MD3_MAX_VERTS 4096
+#define AI_MD3_MAX_TRIANGLES 8192
+
+// master scale factor for all vertices in a MD3 model
+#define AI_MD3_XYZ_SCALE (1.0f/64.0f)
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for the MD3 main header
+ */
+struct Header
+{
+ //! magic number
+ uint32_t IDENT;
+
+ //! file format version
+ uint32_t VERSION;
+
+ //! original name in .pak archive
+ char NAME[ AI_MD3_MAXQPATH ];
+
+ //! unknown
+ int32_t FLAGS;
+
+ //! number of frames in the file
+ uint32_t NUM_FRAMES;
+
+ //! number of tags in the file
+ uint32_t NUM_TAGS;
+
+ //! number of surfaces in the file
+ uint32_t NUM_SURFACES;
+
+ //! number of skins in the file
+ uint32_t NUM_SKINS;
+
+ //! offset of the first frame
+ uint32_t OFS_FRAMES;
+
+ //! offset of the first tag
+ uint32_t OFS_TAGS;
+
+ //! offset of the first surface
+ uint32_t OFS_SURFACES;
+
+ //! end of file
+ uint32_t OFS_EOF;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for the frame header
+ */
+struct Frame
+{
+ //! minimum bounds
+ aiVector3D min;
+
+ //! maximum bounds
+ aiVector3D max;
+
+ //! local origin for this frame
+ aiVector3D origin;
+
+ //! radius of bounding sphere
+ float radius;
+
+ //! name of frame
+ char name[ AI_MD3_MAXFRAME ];
+
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for the tag header
+ */
+struct Tag
+{
+ //! name of the tag
+ char NAME[ AI_MD3_MAXQPATH ];
+
+ //! Local tag origin and orientation
+ aiVector3D origin;
+ float orientation[3][3];
+
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for the surface header
+ */
+struct Surface
+{
+ //! magic number
+ int32_t IDENT;
+
+ //! original name of the surface
+ char NAME[ AI_MD3_MAXQPATH ];
+
+ //! unknown
+ int32_t FLAGS;
+
+ //! number of frames in the surface
+ uint32_t NUM_FRAMES;
+
+ //! number of shaders in the surface
+ uint32_t NUM_SHADER;
+
+ //! number of vertices in the surface
+ uint32_t NUM_VERTICES;
+
+ //! number of triangles in the surface
+ uint32_t NUM_TRIANGLES;
+
+
+ //! offset to the triangle data
+ uint32_t OFS_TRIANGLES;
+
+ //! offset to the shader data
+ uint32_t OFS_SHADERS;
+
+ //! offset to the texture coordinate data
+ uint32_t OFS_ST;
+
+ //! offset to the vertex/normal data
+ uint32_t OFS_XYZNORMAL;
+
+ //! offset to the end of the Surface object
+ int32_t OFS_END;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for a shader defined in there
+ */
+struct Shader
+{
+ //! filename of the shader
+ char NAME[ AI_MD3_MAXQPATH ];
+
+ //! index of the shader
+ uint32_t SHADER_INDEX;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for a triangle
+ */
+struct Triangle
+{
+ //! triangle indices
+ uint32_t INDEXES[3];
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for an UV coord
+ */
+struct TexCoord
+{
+ //! UV coordinates
+ float U,V;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for a vertex
+ */
+struct Vertex
+{
+ //! X/Y/Z coordinates
+ int16_t X,Y,Z;
+
+ //! encoded normal vector
+ uint16_t NORMAL;
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+// -------------------------------------------------------------------------------
+/** @brief Unpack a Q3 16 bit vector to its full float3 representation
+ *
+ * @param p_iNormal Input normal vector in latitude/longitude form
+ * @param p_afOut Pointer to an array of three floats to receive the result
+ *
+ * @note This has been taken from q3 source (misc_model.c)
+ */
+inline void LatLngNormalToVec3(uint16_t p_iNormal, float* p_afOut)
+{
+ float lat = (float)(( p_iNormal >> 8u ) & 0xff);
+ float lng = (float)(( p_iNormal & 0xff ));
+ lat *= 3.141926f/128.0f;
+ lng *= 3.141926f/128.0f;
+
+ p_afOut[0] = cosf(lat) * sinf(lng);
+ p_afOut[1] = sinf(lat) * sinf(lng);
+ p_afOut[2] = cosf(lng);
+ return;
+}
+
+
+// -------------------------------------------------------------------------------
+/** @brief Pack a Q3 normal into 16bit latitute/longitude representation
+ * @param p_vIn Input vector
+ * @param p_iOut Output normal
+ *
+ * @note This has been taken from q3 source (mathlib.c)
+ */
+inline void Vec3NormalToLatLng( const aiVector3D& p_vIn, uint16_t& p_iOut )
+{
+ // check for singularities
+ if ( 0.0f == p_vIn[0] && 0.0f == p_vIn[1] )
+ {
+ if ( p_vIn[2] > 0.0f )
+ {
+ ((unsigned char*)&p_iOut)[0] = 0;
+ ((unsigned char*)&p_iOut)[1] = 0; // lat = 0, long = 0
+ }
+ else
+ {
+ ((unsigned char*)&p_iOut)[0] = 128;
+ ((unsigned char*)&p_iOut)[1] = 0; // lat = 0, long = 128
+ }
+ }
+ else
+ {
+ int a, b;
+
+ a = int(57.2957795f * ( atan2f( p_vIn[1], p_vIn[0] ) ) * (255.0f / 360.0f ));
+ a &= 0xff;
+
+ b = int(57.2957795f * ( acosf( p_vIn[2] ) ) * ( 255.0f / 360.0f ));
+ b &= 0xff;
+
+ ((unsigned char*)&p_iOut)[0] = b; // longitude
+ ((unsigned char*)&p_iOut)[1] = a; // lattitude
+ }
+}
+
+}
+}
+
+#endif // !! AI_MD3FILEHELPER_H_INC
+
diff --git a/src/3rdparty/assimp/code/MD3Loader.cpp b/src/3rdparty/assimp/code/MD3Loader.cpp
new file mode 100644
index 000000000..eb17df06f
--- /dev/null
+++ b/src/3rdparty/assimp/code/MD3Loader.cpp
@@ -0,0 +1,1050 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file MD3Loader.cpp
+ * @brief Implementation of the MD3 importer class
+ *
+ * Sources:
+ * http://www.gamers.org/dEngine/quake3/UQ3S
+ * http://linux.ucla.edu/~phaethon/q3/formats/md3format.html
+ * http://www.heppler.com/shader/shader/
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER
+
+#include "MD3Loader.h"
+#include "ByteSwap.h"
+#include "SceneCombiner.h"
+#include "GenericProperty.h"
+#include "RemoveComments.h"
+#include "ParsingUtils.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Convert a Q3 shader blend function to the appropriate enum value
+Q3Shader::BlendFunc StringToBlendFunc(const std::string& m)
+{
+ if (m == "GL_ONE") {
+ return Q3Shader::BLEND_GL_ONE;
+ }
+ if (m == "GL_ZERO") {
+ return Q3Shader::BLEND_GL_ZERO;
+ }
+ if (m == "GL_SRC_ALPHA") {
+ return Q3Shader::BLEND_GL_SRC_ALPHA;
+ }
+ if (m == "GL_ONE_MINUS_SRC_ALPHA") {
+ return Q3Shader::BLEND_GL_ONE_MINUS_SRC_ALPHA;
+ }
+ if (m == "GL_ONE_MINUS_DST_COLOR") {
+ return Q3Shader::BLEND_GL_ONE_MINUS_DST_COLOR;
+ }
+ DefaultLogger::get()->error("Q3Shader: Unknown blend function: " + m);
+ return Q3Shader::BLEND_NONE;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load a Quake 3 shader
+bool Q3Shader::LoadShader(ShaderData& fill, const std::string& pFile,IOSystem* io)
+{
+ boost::scoped_ptr<IOStream> file( io->Open( pFile, "rt"));
+ if (!file.get())
+ return false; // if we can't access the file, don't worry and return
+
+ DefaultLogger::get()->info("Loading Quake3 shader file " + pFile);
+
+ // read file in memory
+ const size_t s = file->FileSize();
+ std::vector<char> _buff(s+1);
+ file->Read(&_buff[0],s,1);
+ _buff[s] = 0;
+
+ // remove comments from it (C++ style)
+ CommentRemover::RemoveLineComments("//",&_buff[0]);
+ const char* buff = &_buff[0];
+
+ Q3Shader::ShaderDataBlock* curData = NULL;
+ Q3Shader::ShaderMapBlock* curMap = NULL;
+
+ // read line per line
+ for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) {
+
+ if (*buff == '{') {
+ ++buff;
+
+ // append to last section, if any
+ if (!curData) {
+ DefaultLogger::get()->error("Q3Shader: Unexpected shader section token \'{\'");
+ return true; // still no failure, the file is there
+ }
+
+ // read this data section
+ for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) {
+ if (*buff == '{') {
+ ++buff;
+ // add new map section
+ curData->maps.push_back(Q3Shader::ShaderMapBlock());
+ curMap = &curData->maps.back();
+
+ for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) {
+ // 'map' - Specifies texture file name
+ if (TokenMatchI(buff,"map",3) || TokenMatchI(buff,"clampmap",8)) {
+ curMap->name = GetNextToken(buff);
+ }
+ // 'blendfunc' - Alpha blending mode
+ else if (TokenMatchI(buff,"blendfunc",9)) {
+ const std::string blend_src = GetNextToken(buff);
+ if (blend_src == "add") {
+ curMap->blend_src = Q3Shader::BLEND_GL_ONE;
+ curMap->blend_dest = Q3Shader::BLEND_GL_ONE;
+ }
+ else if (blend_src == "filter") {
+ curMap->blend_src = Q3Shader::BLEND_GL_DST_COLOR;
+ curMap->blend_dest = Q3Shader::BLEND_GL_ZERO;
+ }
+ else if (blend_src == "blend") {
+ curMap->blend_src = Q3Shader::BLEND_GL_SRC_ALPHA;
+ curMap->blend_dest = Q3Shader::BLEND_GL_ONE_MINUS_SRC_ALPHA;
+ }
+ else {
+ curMap->blend_src = StringToBlendFunc(blend_src);
+ curMap->blend_dest = StringToBlendFunc(GetNextToken(buff));
+ }
+ }
+ // 'alphafunc' - Alpha testing mode
+ else if (TokenMatchI(buff,"alphafunc",9)) {
+ const std::string at = GetNextToken(buff);
+ if (at == "GT0") {
+ curMap->alpha_test = Q3Shader::AT_GT0;
+ }
+ else if (at == "LT128") {
+ curMap->alpha_test = Q3Shader::AT_LT128;
+ }
+ else if (at == "GE128") {
+ curMap->alpha_test = Q3Shader::AT_GE128;
+ }
+ }
+ else if (*buff == '}') {
+ ++buff;
+ // close this map section
+ curMap = NULL;
+ break;
+ }
+ }
+
+ }
+ else if (*buff == '}') {
+ ++buff;
+ curData = NULL;
+ break;
+ }
+
+ // 'cull' specifies culling behaviour for the model
+ else if (TokenMatchI(buff,"cull",4)) {
+ SkipSpaces(&buff);
+ if (!ASSIMP_strincmp(buff,"back",4)) {
+ curData->cull = Q3Shader::CULL_CCW;
+ }
+ else if (!ASSIMP_strincmp(buff,"front",5)) {
+ curData->cull = Q3Shader::CULL_CW;
+ }
+ else if (!ASSIMP_strincmp(buff,"none",4) || !ASSIMP_strincmp(buff,"disable",7)) {
+ curData->cull = Q3Shader::CULL_NONE;
+ }
+ else DefaultLogger::get()->error("Q3Shader: Unrecognized cull mode");
+ }
+ }
+ }
+
+ else {
+ // add new section
+ fill.blocks.push_back(Q3Shader::ShaderDataBlock());
+ curData = &fill.blocks.back();
+
+ // get the name of this section
+ curData->name = GetNextToken(buff);
+ }
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load a Quake 3 skin
+bool Q3Shader::LoadSkin(SkinData& fill, const std::string& pFile,IOSystem* io)
+{
+ boost::scoped_ptr<IOStream> file( io->Open( pFile, "rt"));
+ if (!file.get())
+ return false; // if we can't access the file, don't worry and return
+
+ DefaultLogger::get()->info("Loading Quake3 skin file " + pFile);
+
+ // read file in memory
+ const size_t s = file->FileSize();
+ std::vector<char> _buff(s+1);const char* buff = &_buff[0];
+ file->Read(&_buff[0],s,1);
+ _buff[s] = 0;
+
+ // remove commas
+ std::replace(_buff.begin(),_buff.end(),',',' ');
+
+ // read token by token and fill output table
+ for (;*buff;) {
+ SkipSpacesAndLineEnd(&buff);
+
+ // get first identifier
+ std::string ss = GetNextToken(buff);
+
+ // ignore tokens starting with tag_
+ if (!::strncmp(&ss[0],"tag_",std::min((size_t)4, ss.length())))
+ continue;
+
+ fill.textures.push_back(SkinData::TextureEntry());
+ SkinData::TextureEntry& s = fill.textures.back();
+
+ s.first = ss;
+ s.second = GetNextToken(buff);
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert Q3Shader to material
+void Q3Shader::ConvertShaderToMaterial(MaterialHelper* out, const ShaderDataBlock& shader)
+{
+ ai_assert(NULL != out);
+
+ /* IMPORTANT: This is not a real conversion. Actually we're just guessing and
+ * hacking around to build an aiMaterial that looks nearly equal to the
+ * original Quake 3 shader. We're missing some important features like
+ * animatable material properties in our material system, but at least
+ * multiple textures should be handled correctly.
+ */
+
+ // Two-sided material?
+ if (shader.cull == Q3Shader::CULL_NONE) {
+ const int twosided = 1;
+ out->AddProperty(&twosided,1,AI_MATKEY_TWOSIDED);
+ }
+
+ unsigned int cur_emissive = 0, cur_diffuse = 0, cur_lm =0;
+
+ // Iterate through all textures
+ for (std::list< Q3Shader::ShaderMapBlock >::const_iterator it = shader.maps.begin(); it != shader.maps.end();++it) {
+
+ // CONVERSION BEHAVIOUR:
+ //
+ //
+ // If the texture is additive
+ // - if it is the first texture, assume additive blending for the whole material
+ // - otherwise register it as emissive texture.
+ //
+ // If the texture is using standard blend (or if the blend mode is unknown)
+ // - if first texture: assume default blending for material
+ // - in any case: set it as diffuse texture
+ //
+ // If the texture is using 'filter' blending
+ // - take as lightmap
+ //
+ // Textures with alpha funcs
+ // - aiTextureFlags_UseAlpha is set (otherwise aiTextureFlags_NoAlpha is explicitly set)
+ aiString s((*it).name);
+ aiTextureType type; unsigned int index;
+
+ if ((*it).blend_src == Q3Shader::BLEND_GL_ONE && (*it).blend_dest == Q3Shader::BLEND_GL_ONE) {
+ if (it == shader.maps.begin()) {
+ const int additive = aiBlendMode_Additive;
+ out->AddProperty(&additive,1,AI_MATKEY_BLEND_FUNC);
+
+ index = cur_diffuse++;
+ type = aiTextureType_DIFFUSE;
+ }
+ else {
+ index = cur_emissive++;
+ type = aiTextureType_EMISSIVE;
+ }
+ }
+ else if ((*it).blend_src == Q3Shader::BLEND_GL_DST_COLOR && (*it).blend_dest == Q3Shader::BLEND_GL_ZERO) {
+ index = cur_lm++;
+ type = aiTextureType_LIGHTMAP;
+ }
+ else {
+ const int blend = aiBlendMode_Default;
+ out->AddProperty(&blend,1,AI_MATKEY_BLEND_FUNC);
+
+ index = cur_diffuse++;
+ type = aiTextureType_DIFFUSE;
+ }
+
+ // setup texture
+ out->AddProperty(&s,AI_MATKEY_TEXTURE(type,index));
+
+ // setup texture flags
+ const int use_alpha = ((*it).alpha_test != Q3Shader::AT_NONE ? aiTextureFlags_UseAlpha : aiTextureFlags_IgnoreAlpha);
+ out->AddProperty(&use_alpha,1,AI_MATKEY_TEXFLAGS(type,index));
+ }
+ // If at least one emissive texture was set, set the emissive base color to 1 to ensure
+ // the texture is actually displayed.
+ if (0 != cur_emissive) {
+ aiColor3D one(1.f,1.f,1.f);
+ out->AddProperty(&one,1,AI_MATKEY_COLOR_EMISSIVE);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MD3Importer::MD3Importer()
+ : configFrameID (0)
+ , configHandleMP (true)
+ , configSpeedFlag(false)
+ , pcHeader(0)
+ , mBuffer(0)
+ , fileSize(0)
+ , mScene(0)
+ , mIOHandler(0)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MD3Importer::~MD3Importer()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool MD3Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+ if (extension == "md3")
+ return true;
+
+ // if check for extension is not enough, check for the magic tokens
+ if (!extension.length() || checkSig) {
+ uint32_t tokens[1];
+ tokens[0] = AI_MD3_MAGIC_NUMBER_LE;
+ return CheckMagicToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MD3Importer::ValidateHeaderOffsets()
+{
+ // Check magic number
+ if (pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE &&
+ pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE)
+ throw DeadlyImportError( "Invalid MD3 file: Magic bytes not found");
+
+ // Check file format version
+ if (pcHeader->VERSION > 15)
+ DefaultLogger::get()->warn( "Unsupported MD3 file version. Continuing happily ...");
+
+ // Check some offset values whether they are valid
+ if (!pcHeader->NUM_SURFACES)
+ throw DeadlyImportError( "Invalid md3 file: NUM_SURFACES is 0");
+
+ if (pcHeader->OFS_FRAMES >= fileSize || pcHeader->OFS_SURFACES >= fileSize ||
+ pcHeader->OFS_EOF > fileSize) {
+ throw DeadlyImportError("Invalid MD3 header: some offsets are outside the file");
+ }
+
+ if (pcHeader->NUM_FRAMES <= configFrameID )
+ throw DeadlyImportError("The requested frame is not existing the file");
+}
+
+// ------------------------------------------------------------------------------------------------
+void MD3Importer::ValidateSurfaceHeaderOffsets(const MD3::Surface* pcSurf)
+{
+ // Calculate the relative offset of the surface
+ const int32_t ofs = int32_t((const unsigned char*)pcSurf-this->mBuffer);
+
+ // Check whether all data chunks are inside the valid range
+ if (pcSurf->OFS_TRIANGLES + ofs + pcSurf->NUM_TRIANGLES * sizeof(MD3::Triangle) > fileSize ||
+ pcSurf->OFS_SHADERS + ofs + pcSurf->NUM_SHADER * sizeof(MD3::Shader) > fileSize ||
+ pcSurf->OFS_ST + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::TexCoord) > fileSize ||
+ pcSurf->OFS_XYZNORMAL + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::Vertex) > fileSize) {
+
+ throw DeadlyImportError("Invalid MD3 surface header: some offsets are outside the file");
+ }
+
+ // Check whether all requirements for Q3 files are met. We don't
+ // care, but probably someone does.
+ if (pcSurf->NUM_TRIANGLES > AI_MD3_MAX_TRIANGLES) {
+ DefaultLogger::get()->warn("MD3: Quake III triangle limit exceeded");
+ }
+
+ if (pcSurf->NUM_SHADER > AI_MD3_MAX_SHADERS) {
+ DefaultLogger::get()->warn("MD3: Quake III shader limit exceeded");
+ }
+
+ if (pcSurf->NUM_VERTICES > AI_MD3_MAX_VERTS) {
+ DefaultLogger::get()->warn("MD3: Quake III vertex limit exceeded");
+ }
+
+ if (pcSurf->NUM_FRAMES > AI_MD3_MAX_FRAMES) {
+ DefaultLogger::get()->warn("MD3: Quake III frame limit exceeded");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void MD3Importer::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("md3");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void MD3Importer::SetupProperties(const Importer* pImp)
+{
+ // The
+ // AI_CONFIG_IMPORT_MD3_KEYFRAME option overrides the
+ // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD3_KEYFRAME,0xffffffff);
+ if (0xffffffff == configFrameID) {
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+ }
+
+ // AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART
+ configHandleMP = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART,1));
+
+ // AI_CONFIG_IMPORT_MD3_SKIN_NAME
+ configSkinFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SKIN_NAME,"default"));
+
+ // AI_CONFIG_IMPORT_MD3_SHADER_SRC
+ configShaderFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SHADER_SRC,""));
+
+ // AI_CONFIG_FAVOUR_SPEED
+ configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Try to read the skin for a MD3 file
+void MD3Importer::ReadSkin(Q3Shader::SkinData& fill) const
+{
+ // skip any postfixes (e.g. lower_1.md3)
+ std::string::size_type s = filename.find_last_of('_');
+ if (s == std::string::npos) {
+ s = filename.find_last_of('.');
+ }
+ ai_assert(s != std::string::npos);
+
+ const std::string skin_file = path + filename.substr(0,s) + "_" + configSkinFile + ".skin";
+ Q3Shader::LoadSkin(fill,skin_file,mIOHandler);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Try to read the shader for a MD3 file
+void MD3Importer::ReadShader(Q3Shader::ShaderData& fill) const
+{
+ // Determine Q3 model name from given path
+ std::string::size_type s = path.find_last_of("\\/",path.length()-2);
+ const std::string model_file = path.substr(s+1,path.length()-(s+2));
+
+ // If no specific dir or file is given, use our default search behaviour
+ if (!configShaderFile.length()) {
+ if (!Q3Shader::LoadShader(fill,path + "..\\..\\..\\scripts\\" + model_file + ".shader",mIOHandler)) {
+ Q3Shader::LoadShader(fill,path + "..\\..\\..\\scripts\\" + filename + ".shader",mIOHandler);
+ }
+ }
+ else {
+ // If the given string specifies a file, load this file.
+ // Otherwise it's a directory.
+ std::string::size_type st = configShaderFile.find_last_of('.');
+ if (st == std::string::npos) {
+
+ if (!Q3Shader::LoadShader(fill,configShaderFile + model_file + ".shader",mIOHandler)) {
+ Q3Shader::LoadShader(fill,configShaderFile + filename + ".shader",mIOHandler);
+ }
+ }
+ else {
+ Q3Shader::LoadShader(fill,configShaderFile,mIOHandler);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tiny helper to remove a single node from its parent' list
+void RemoveSingleNodeFromList(aiNode* nd)
+{
+ if (!nd || nd->mNumChildren || !nd->mParent)return;
+ aiNode* par = nd->mParent;
+ for (unsigned int i = 0; i < par->mNumChildren;++i) {
+ if (par->mChildren[i] == nd) {
+ --par->mNumChildren;
+ for (;i < par->mNumChildren;++i) {
+ par->mChildren[i] = par->mChildren[i+1];
+ }
+ delete nd;
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a multi-part Q3 player model
+bool MD3Importer::ReadMultipartFile()
+{
+ // check whether the file name contains a common postfix, e.g lower_2.md3
+ std::string::size_type s = filename.find_last_of('_'), t = filename.find_last_of('.');
+ ai_assert(t != std::string::npos);
+ if (s == std::string::npos)
+ s = t;
+
+ const std::string mod_filename = filename.substr(0,s);
+ const std::string suffix = filename.substr(s,t-s);
+
+ if (mod_filename == "lower" || mod_filename == "upper" || mod_filename == "head"){
+ const std::string lower = path + "lower" + suffix + ".md3";
+ const std::string upper = path + "upper" + suffix + ".md3";
+ const std::string head = path + "head" + suffix + ".md3";
+
+ aiScene* scene_upper = NULL;
+ aiScene* scene_lower = NULL;
+ aiScene* scene_head = NULL;
+ std::string failure;
+
+ aiNode* tag_torso, *tag_head;
+ std::vector<AttachmentInfo> attach;
+
+ DefaultLogger::get()->info("Multi part MD3 player model: lower, upper and head parts are joined");
+
+ // ensure we won't try to load ourselves recursively
+ BatchLoader::PropertyMap props;
+ SetGenericProperty( props.ints, AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART, 0, NULL);
+
+ // now read these three files
+ BatchLoader batch(mIOHandler);
+ const unsigned int _lower = batch.AddLoadRequest(lower,0,&props);
+ const unsigned int _upper = batch.AddLoadRequest(upper,0,&props);
+ const unsigned int _head = batch.AddLoadRequest(head,0,&props);
+ batch.LoadAll();
+
+ // now construct a dummy scene to place these three parts in
+ aiScene* master = new aiScene();
+ aiNode* nd = master->mRootNode = new aiNode();
+ nd->mName.Set("<MD3_Player>");
+
+ // ... and get them. We need all of them.
+ scene_lower = batch.GetImport(_lower);
+ if (!scene_lower) {
+ DefaultLogger::get()->error("M3D: Failed to read multi part model, lower.md3 fails to load");
+ failure = "lower";
+ goto error_cleanup;
+ }
+
+ scene_upper = batch.GetImport(_upper);
+ if (!scene_upper) {
+ DefaultLogger::get()->error("M3D: Failed to read multi part model, upper.md3 fails to load");
+ failure = "upper";
+ goto error_cleanup;
+ }
+
+ scene_head = batch.GetImport(_head);
+ if (!scene_head) {
+ DefaultLogger::get()->error("M3D: Failed to read multi part model, head.md3 fails to load");
+ failure = "head";
+ goto error_cleanup;
+ }
+
+ // build attachment infos. search for typical Q3 tags
+
+ // original root
+ scene_lower->mRootNode->mName.Set("lower");
+ attach.push_back(AttachmentInfo(scene_lower, nd));
+
+ // tag_torso
+ tag_torso = scene_lower->mRootNode->FindNode("tag_torso");
+ if (!tag_torso) {
+ DefaultLogger::get()->error("M3D: Failed to find attachment tag for multi part model: tag_torso expected");
+ goto error_cleanup;
+ }
+ scene_upper->mRootNode->mName.Set("upper");
+ attach.push_back(AttachmentInfo(scene_upper,tag_torso));
+
+ // tag_head
+ tag_head = scene_upper->mRootNode->FindNode("tag_head");
+ if (!tag_head) {
+ DefaultLogger::get()->error("M3D: Failed to find attachment tag for multi part model: tag_head expected");
+ goto error_cleanup;
+ }
+ scene_head->mRootNode->mName.Set("head");
+ attach.push_back(AttachmentInfo(scene_head,tag_head));
+
+ // Remove tag_head and tag_torso from all other model parts ...
+ // this ensures (together with AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY)
+ // that tag_torso/tag_head is also the name of the (unique) output node
+ RemoveSingleNodeFromList (scene_upper->mRootNode->FindNode("tag_torso"));
+ RemoveSingleNodeFromList (scene_head-> mRootNode->FindNode("tag_head" ));
+
+ // Undo the rotations which we applied to the coordinate systems. We're
+ // working in global Quake space here
+ scene_head->mRootNode->mTransformation = aiMatrix4x4();
+ scene_lower->mRootNode->mTransformation = aiMatrix4x4();
+ scene_upper->mRootNode->mTransformation = aiMatrix4x4();
+
+ // and merge the scenes
+ SceneCombiner::MergeScenes(&mScene,master, attach,
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES |
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES |
+ AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS |
+ (!configSpeedFlag ? AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY : 0));
+
+ // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
+ mScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
+ 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
+
+ return true;
+
+error_cleanup:
+ delete scene_upper;
+ delete scene_lower;
+ delete scene_head;
+ delete master;
+
+ if (failure == mod_filename) {
+ throw DeadlyImportError("MD3: failure to read multipart host file");
+ }
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a MD3 path to a proper value
+void MD3Importer::ConvertPath(const char* texture_name, const char* header_name, std::string& out) const
+{
+ // If the MD3's internal path itself and the given path are using
+ // the same directory, remove it completely to get right output paths.
+ const char* end1 = ::strrchr(header_name,'\\');
+ if (!end1)end1 = ::strrchr(header_name,'/');
+
+ const char* end2 = ::strrchr(texture_name,'\\');
+ if (!end2)end2 = ::strrchr(texture_name,'/');
+
+ // HACK: If the paths starts with "models", ignore the
+ // next two hierarchy levels, it specifies just the model name.
+ // Ignored by Q3, it might be not equal to the real model location.
+ if (end2) {
+
+ size_t len2;
+ const size_t len1 = (size_t)(end1 - header_name);
+ if (!ASSIMP_strincmp(texture_name,"models",6) && (texture_name[6] == '/' || texture_name[6] == '\\')) {
+ len2 = 6; // ignore the seventh - could be slash or backslash
+
+ if (!header_name[0]) {
+ // Use the file name only
+ out = end2+1;
+ return;
+ }
+ }
+ else len2 = std::min (len1, (size_t)(end2 - texture_name ));
+ if (!ASSIMP_strincmp(texture_name,header_name,len2)) {
+ // Use the file name only
+ out = end2+1;
+ return;
+ }
+ }
+ // Use the full path
+ out = texture_name;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void MD3Importer::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ mFile = pFile;
+ mScene = pScene;
+ mIOHandler = pIOHandler;
+
+ // get base path and file name
+ // todo ... move to PathConverter
+ std::string::size_type s = mFile.find_last_of("/\\");
+ if (s == std::string::npos) {
+ s = 0;
+ }
+ else ++s;
+ filename = mFile.substr(s), path = mFile.substr(0,s);
+ for ( std::string::iterator it = filename .begin(); it != filename.end(); ++it)
+ *it = tolower( *it);
+
+ // Load multi-part model file, if necessary
+ if (configHandleMP) {
+ if (ReadMultipartFile())
+ return;
+ }
+
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open MD3 file " + pFile + ".");
+
+ // Check whether the md3 file is large enough to contain the header
+ fileSize = (unsigned int)file->FileSize();
+ if ( fileSize < sizeof(MD3::Header))
+ throw DeadlyImportError( "MD3 File is too small.");
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ std::vector<unsigned char> mBuffer2 (fileSize);
+ file->Read( &mBuffer2[0], 1, fileSize);
+ mBuffer = &mBuffer2[0];
+
+ pcHeader = (BE_NCONST MD3::Header*)mBuffer;
+
+ // Ensure correct endianess
+#ifdef AI_BUILD_BIG_ENDIAN
+
+ AI_SWAP4(pcHeader->VERSION);
+ AI_SWAP4(pcHeader->FLAGS);
+ AI_SWAP4(pcHeader->IDENT);
+ AI_SWAP4(pcHeader->NUM_FRAMES);
+ AI_SWAP4(pcHeader->NUM_SKINS);
+ AI_SWAP4(pcHeader->NUM_SURFACES);
+ AI_SWAP4(pcHeader->NUM_TAGS);
+ AI_SWAP4(pcHeader->OFS_EOF);
+ AI_SWAP4(pcHeader->OFS_FRAMES);
+ AI_SWAP4(pcHeader->OFS_SURFACES);
+ AI_SWAP4(pcHeader->OFS_TAGS);
+
+#endif
+
+ // Validate the file header
+ ValidateHeaderOffsets();
+
+ // Navigate to the list of surfaces
+ BE_NCONST MD3::Surface* pcSurfaces = (BE_NCONST MD3::Surface*)(mBuffer + pcHeader->OFS_SURFACES);
+
+ // Navigate to the list of tags
+ BE_NCONST MD3::Tag* pcTags = (BE_NCONST MD3::Tag*)(mBuffer + pcHeader->OFS_TAGS);
+
+ // Allocate output storage
+ pScene->mNumMeshes = pcHeader->NUM_SURFACES;
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+
+ pScene->mNumMaterials = pcHeader->NUM_SURFACES;
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
+
+ // Set arrays to zero to ensue proper destruction if an exception is raised
+ ::memset(pScene->mMeshes,0,pScene->mNumMeshes*sizeof(aiMesh*));
+ ::memset(pScene->mMaterials,0,pScene->mNumMaterials*sizeof(aiMaterial*));
+
+ // Now read possible skins from .skin file
+ Q3Shader::SkinData skins;
+ ReadSkin(skins);
+
+ // And check whether we can locate a shader file for this model
+ Q3Shader::ShaderData shaders;
+ ReadShader(shaders);
+
+ // Adjust all texture paths in the shader
+ const char* header_name = pcHeader->NAME;
+ if (shaders.blocks.size()) {
+ for (std::list< Q3Shader::ShaderDataBlock >::iterator dit = shaders.blocks.begin(); dit != shaders.blocks.end(); ++dit) {
+ ConvertPath((*dit).name.c_str(),header_name,(*dit).name);
+
+ for (std::list< Q3Shader::ShaderMapBlock >::iterator mit = (*dit).maps.begin(); mit != (*dit).maps.end(); ++mit) {
+ ConvertPath((*mit).name.c_str(),header_name,(*mit).name);
+ }
+ }
+ }
+
+ // Read all surfaces from the file
+ unsigned int iNum = pcHeader->NUM_SURFACES;
+ unsigned int iNumMaterials = 0;
+ while (iNum-- > 0) {
+
+ // Ensure correct endianess
+#ifdef AI_BUILD_BIG_ENDIAN
+
+ AI_SWAP4(pcSurfaces->FLAGS);
+ AI_SWAP4(pcSurfaces->IDENT);
+ AI_SWAP4(pcSurfaces->NUM_FRAMES);
+ AI_SWAP4(pcSurfaces->NUM_SHADER);
+ AI_SWAP4(pcSurfaces->NUM_TRIANGLES);
+ AI_SWAP4(pcSurfaces->NUM_VERTICES);
+ AI_SWAP4(pcSurfaces->OFS_END);
+ AI_SWAP4(pcSurfaces->OFS_SHADERS);
+ AI_SWAP4(pcSurfaces->OFS_ST);
+ AI_SWAP4(pcSurfaces->OFS_TRIANGLES);
+ AI_SWAP4(pcSurfaces->OFS_XYZNORMAL);
+
+#endif
+
+ // Validate the surface header
+ ValidateSurfaceHeaderOffsets(pcSurfaces);
+
+ // Navigate to the vertex list of the surface
+ BE_NCONST MD3::Vertex* pcVertices = (BE_NCONST MD3::Vertex*)
+ (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL);
+
+ // Navigate to the triangle list of the surface
+ BE_NCONST MD3::Triangle* pcTriangles = (BE_NCONST MD3::Triangle*)
+ (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_TRIANGLES);
+
+ // Navigate to the texture coordinate list of the surface
+ BE_NCONST MD3::TexCoord* pcUVs = (BE_NCONST MD3::TexCoord*)
+ (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_ST);
+
+ // Navigate to the shader list of the surface
+ BE_NCONST MD3::Shader* pcShaders = (BE_NCONST MD3::Shader*)
+ (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_SHADERS);
+
+ // If the submesh is empty ignore it
+ if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES)
+ {
+ pcSurfaces = (BE_NCONST MD3::Surface*)(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_END);
+ pScene->mNumMeshes--;
+ continue;
+ }
+
+ // Allocate output mesh
+ pScene->mMeshes[iNum] = new aiMesh();
+ aiMesh* pcMesh = pScene->mMeshes[iNum];
+
+ std::string _texture_name;
+ const char* texture_name = NULL;
+
+ // Check whether we have a texture record for this surface in the .skin file
+ const char *surface_name = pcSurfaces->NAME;
+ std::list< Q3Shader::SkinData::TextureEntry >::iterator it = std::find(
+ skins.textures.begin(), skins.textures.end(), surface_name);
+
+ if (it != skins.textures.end()) {
+ texture_name = &*( _texture_name = (*it).second).begin();
+ DefaultLogger::get()->debug("MD3: Assigning skin texture " + (*it).second + " to surface " + pcSurfaces->NAME);
+ (*it).resolved = true; // mark entry as resolved
+ }
+
+ // Get the first shader (= texture?) assigned to the surface
+ if (!texture_name && pcSurfaces->NUM_SHADER) {
+ texture_name = pcShaders->NAME;
+ }
+
+ std::string convertedPath;
+ if (texture_name) {
+ ConvertPath(texture_name,header_name,convertedPath);
+ }
+
+ const Q3Shader::ShaderDataBlock* shader = NULL;
+
+ // Now search the current shader for a record with this name (
+ // excluding texture file extension)
+ if (shaders.blocks.size()) {
+
+ std::string::size_type s = convertedPath.find_last_of('.');
+ if (s == std::string::npos)
+ s = convertedPath.length();
+
+ const std::string without_ext = convertedPath.substr(0,s);
+ std::list< Q3Shader::ShaderDataBlock >::const_iterator dit = std::find(shaders.blocks.begin(),shaders.blocks.end(),without_ext);
+ if (dit != shaders.blocks.end()) {
+ // Hurra, wir haben einen. Tolle Sache.
+ shader = &*dit;
+ DefaultLogger::get()->info("Found shader record for " +without_ext );
+ }
+ else DefaultLogger::get()->warn("Unable to find shader record for " +without_ext );
+ }
+
+ MaterialHelper* pcHelper = new MaterialHelper();
+
+ const int iMode = (int)aiShadingMode_Gouraud;
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ // Add a small ambient color value - Quake 3 seems to have one
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ clr.b = clr.g = clr.r = 1.0f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ // use surface name + skin_name as material name
+ aiString name;
+ name.Set("MD3_[" + configSkinFile + "][" + pcSurfaces->NAME + "]");
+ pcHelper->AddProperty(&name,AI_MATKEY_NAME);
+
+ if (!shader) {
+ // Setup dummy texture file name to ensure UV coordinates are kept during postprocessing
+ aiString szString;
+ if (convertedPath.length()) {
+ szString.Set(convertedPath);
+ }
+ else {
+ DefaultLogger::get()->warn("Texture file name has zero length. Using default name");
+ szString.Set("dummy_texture.bmp");
+ }
+ pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ // prevent transparency by default
+ int no_alpha = aiTextureFlags_IgnoreAlpha;
+ pcHelper->AddProperty(&no_alpha,1,AI_MATKEY_TEXFLAGS_DIFFUSE(0));
+ }
+ else {
+ Q3Shader::ConvertShaderToMaterial(pcHelper,*shader);
+ }
+
+ pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
+ pcMesh->mMaterialIndex = iNumMaterials++;
+
+ // Ensure correct endianess
+#ifdef AI_BUILD_BIG_ENDIAN
+
+ for (uint32_t i = 0; i < pcSurfaces->NUM_VERTICES;++i) {
+ AI_SWAP2( pcVertices[i].NORMAL );
+ AI_SWAP2( pcVertices[i].X );
+ AI_SWAP2( pcVertices[i].Y );
+ AI_SWAP2( pcVertices[i].Z );
+
+ AI_SWAP4( pcUVs[i].U );
+ AI_SWAP4( pcUVs[i].U );
+ }
+ for (uint32_t i = 0; i < pcSurfaces->NUM_TRIANGLES;++i) {
+ AI_SWAP4(pcTriangles[i].INDEXES[0]);
+ AI_SWAP4(pcTriangles[i].INDEXES[1]);
+ AI_SWAP4(pcTriangles[i].INDEXES[2]);
+ }
+
+#endif
+
+ // Fill mesh information
+ pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ pcMesh->mNumVertices = pcSurfaces->NUM_TRIANGLES*3;
+ pcMesh->mNumFaces = pcSurfaces->NUM_TRIANGLES;
+ pcMesh->mFaces = new aiFace[pcSurfaces->NUM_TRIANGLES];
+ pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNumUVComponents[0] = 2;
+
+ // Fill in all triangles
+ unsigned int iCurrent = 0;
+ for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_TRIANGLES;++i) {
+ pcMesh->mFaces[i].mIndices = new unsigned int[3];
+ pcMesh->mFaces[i].mNumIndices = 3;
+
+ //unsigned int iTemp = iCurrent;
+ for (unsigned int c = 0; c < 3;++c,++iCurrent) {
+ pcMesh->mFaces[i].mIndices[c] = iCurrent;
+
+ // Read vertices
+ aiVector3D& vec = pcMesh->mVertices[iCurrent];
+ vec.x = pcVertices[ pcTriangles->INDEXES[c]].X*AI_MD3_XYZ_SCALE;
+ vec.y = pcVertices[ pcTriangles->INDEXES[c]].Y*AI_MD3_XYZ_SCALE;
+ vec.z = pcVertices[ pcTriangles->INDEXES[c]].Z*AI_MD3_XYZ_SCALE;
+
+ // Convert the normal vector to uncompressed float3 format
+ aiVector3D& nor = pcMesh->mNormals[iCurrent];
+ LatLngNormalToVec3(pcVertices[pcTriangles->INDEXES[c]].NORMAL,(float*)&nor);
+
+ // Read texture coordinates
+ pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U;
+ pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[ pcTriangles->INDEXES[c]].V;
+ }
+ // Flip face order if necessary
+ if (!shader || shader->cull == Q3Shader::CULL_CW) {
+ std::swap(pcMesh->mFaces[i].mIndices[2],pcMesh->mFaces[i].mIndices[1]);
+ }
+ pcTriangles++;
+ }
+
+ // Go to the next surface
+ pcSurfaces = (BE_NCONST MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
+ }
+
+ // For debugging purposes: check whether we found matches for all entries in the skins file
+ if (!DefaultLogger::isNullLogger()) {
+ for (std::list< Q3Shader::SkinData::TextureEntry>::const_iterator it = skins.textures.begin();it != skins.textures.end(); ++it) {
+ if (!(*it).resolved) {
+ DefaultLogger::get()->error("MD3: Failed to match skin " + (*it).first + " to surface " + (*it).second);
+ }
+ }
+ }
+
+ if (!pScene->mNumMeshes)
+ throw DeadlyImportError( "MD3: File contains no valid mesh");
+ pScene->mNumMaterials = iNumMaterials;
+
+ // Now we need to generate an empty node graph
+ pScene->mRootNode = new aiNode("<MD3Root>");
+ pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+ pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
+
+ // Attach tiny children for all tags
+ if (pcHeader->NUM_TAGS) {
+ pScene->mRootNode->mNumChildren = pcHeader->NUM_TAGS;
+ pScene->mRootNode->mChildren = new aiNode*[pcHeader->NUM_TAGS];
+
+ for (unsigned int i = 0; i < pcHeader->NUM_TAGS; ++i, ++pcTags) {
+
+ aiNode* nd = pScene->mRootNode->mChildren[i] = new aiNode();
+ nd->mName.Set((const char*)pcTags->NAME);
+ nd->mParent = pScene->mRootNode;
+
+ AI_SWAP4(pcTags->origin.x);
+ AI_SWAP4(pcTags->origin.y);
+ AI_SWAP4(pcTags->origin.z);
+
+ // Copy local origin, again flip z,y
+ nd->mTransformation.a4 = pcTags->origin.x;
+ nd->mTransformation.b4 = pcTags->origin.y;
+ nd->mTransformation.c4 = pcTags->origin.z;
+
+ // Copy rest of transformation (need to transpose to match row-order matrix)
+ for (unsigned int a = 0; a < 3;++a) {
+ for (unsigned int m = 0; m < 3;++m) {
+ nd->mTransformation[m][a] = pcTags->orientation[a][m];
+ AI_SWAP4(nd->mTransformation[m][a]);
+ }
+ }
+ }
+ }
+
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mRootNode->mMeshes[i] = i;
+
+ // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
+ pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
+ 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
+}
+
+#endif // !! ASSIMP_BUILD_NO_MD3_IMPORTER
diff --git a/src/3rdparty/assimp/code/MD3Loader.h b/src/3rdparty/assimp/code/MD3Loader.h
new file mode 100644
index 000000000..f2d07834d
--- /dev/null
+++ b/src/3rdparty/assimp/code/MD3Loader.h
@@ -0,0 +1,331 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Md3Loader.h
+ * @brief Declaration of the .MD3 importer class.
+ */
+#ifndef AI_MD3LOADER_H_INCLUDED
+#define AI_MD3LOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "ByteSwap.h"
+
+#include "../include/aiTypes.h"
+
+#include "MD3FileData.h"
+namespace Assimp {
+class MaterialHelper;
+
+using namespace MD3;
+namespace Q3Shader {
+
+// ---------------------------------------------------------------------------
+/** @brief Tiny utility data structure to hold the data of a .skin file
+ */
+struct SkinData
+{
+ //! A single entryin texture list
+ struct TextureEntry : public std::pair<std::string,std::string>
+ {
+ // did we resolve this texture entry?
+ bool resolved;
+
+ // for std::find()
+ bool operator == (const std::string& f) const {
+ return f == first;
+ }
+ };
+
+ //! List of textures
+ std::list<TextureEntry> textures;
+
+ // rest is ignored for the moment
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies cull modi for Quake shader files.
+ */
+enum ShaderCullMode
+{
+ CULL_NONE,
+ CULL_CW,
+ CULL_CCW
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies alpha blend modi (src + dest) for Quake shader files
+ */
+enum BlendFunc
+{
+ BLEND_NONE,
+ BLEND_GL_ONE,
+ BLEND_GL_ZERO,
+ BLEND_GL_DST_COLOR,
+ BLEND_GL_ONE_MINUS_DST_COLOR,
+ BLEND_GL_SRC_ALPHA,
+ BLEND_GL_ONE_MINUS_SRC_ALPHA
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies alpha test modi for Quake texture maps
+ */
+enum AlphaTestFunc
+{
+ AT_NONE,
+ AT_GT0,
+ AT_LT128,
+ AT_GE128
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Tiny utility data structure to hold a .shader map data block
+ */
+struct ShaderMapBlock
+{
+ ShaderMapBlock()
+ : blend_src (BLEND_NONE)
+ , blend_dest (BLEND_NONE)
+ , alpha_test (AT_NONE)
+ {}
+
+ //! Name of referenced map
+ std::string name;
+
+ //! Blend and alpha test settings for texture
+ BlendFunc blend_src,blend_dest;
+ AlphaTestFunc alpha_test;
+
+
+ //! For std::find()
+ bool operator== (const std::string& o) const {
+ return !ASSIMP_stricmp(o,name);
+ }
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Tiny utility data structure to hold a .shader data block
+ */
+struct ShaderDataBlock
+{
+ ShaderDataBlock()
+ : cull (CULL_CW)
+ {}
+
+ //! Name of referenced data element
+ std::string name;
+
+ //! Cull mode for the element
+ ShaderCullMode cull;
+
+ //! Maps defined in the shader
+ std::list<ShaderMapBlock> maps;
+
+
+ //! For std::find()
+ bool operator== (const std::string& o) const {
+ return !ASSIMP_stricmp(o,name);
+ }
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Tiny utility data structure to hold the data of a .shader file
+ */
+struct ShaderData
+{
+ //! Shader data blocks
+ std::list<ShaderDataBlock> blocks;
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Load a shader file
+ *
+ * Generally, parsing is error tolerant. There's no failure.
+ * @param fill Receives output data
+ * @param file File to be read.
+ * @param io IOSystem to be used for reading
+ * @return false if file is not accessible
+ */
+bool LoadShader(ShaderData& fill, const std::string& file,IOSystem* io);
+
+
+// ---------------------------------------------------------------------------
+/** @brief Convert a Q3Shader to an aiMaterial
+ *
+ * @param[out] out Material structure to be filled.
+ * @param[in] shader Input shader
+ */
+void ConvertShaderToMaterial(MaterialHelper* out, const ShaderDataBlock& shader);
+
+// ---------------------------------------------------------------------------
+/** @brief Load a skin file
+ *
+ * Generally, parsing is error tolerant. There's no failure.
+ * @param fill Receives output data
+ * @param file File to be read.
+ * @param io IOSystem to be used for reading
+ * @return false if file is not accessible
+ */
+bool LoadSkin(SkinData& fill, const std::string& file,IOSystem* io);
+
+} // ! namespace Q3SHader
+
+// ---------------------------------------------------------------------------
+/** @brief Importer class to load MD3 files
+*/
+class MD3Importer : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ MD3Importer();
+
+ /** Destructor, private as well */
+ ~MD3Importer();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details. */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /** Validate offsets in the header
+ */
+ void ValidateHeaderOffsets();
+ void ValidateSurfaceHeaderOffsets(const MD3::Surface* pcSurfHeader);
+
+ // -------------------------------------------------------------------
+ /** Read a Q3 multipart file
+ * @return true if multi part has been processed
+ */
+ bool ReadMultipartFile();
+
+ // -------------------------------------------------------------------
+ /** Try to read the skin for a MD3 file
+ * @param fill Receives output information
+ */
+ void ReadSkin(Q3Shader::SkinData& fill) const;
+
+ // -------------------------------------------------------------------
+ /** Try to read the shader for a MD3 file
+ * @param fill Receives output information
+ */
+ void ReadShader(Q3Shader::ShaderData& fill) const;
+
+ // -------------------------------------------------------------------
+ /** Convert a texture path in a MD3 file to a proper value
+ * @param[in] texture_name Path to be converted
+ * @param[in] header_path Base path specified in MD3 header
+ * @param[out] out Receives the converted output string
+ */
+ void ConvertPath(const char* texture_name, const char* header_path,
+ std::string& out) const;
+
+protected:
+
+ /** Configuration option: frame to be loaded */
+ unsigned int configFrameID;
+
+ /** Configuration option: process multi-part files */
+ bool configHandleMP;
+
+ /** Configuration option: name of skin file to be read */
+ std::string configSkinFile;
+
+ /** Configuration option: name or path of shader */
+ std::string configShaderFile;
+
+ /** Configuration option: speed flag was set? */
+ bool configSpeedFlag;
+
+ /** Header of the MD3 file */
+ BE_NCONST MD3::Header* pcHeader;
+
+ /** File buffer */
+ BE_NCONST unsigned char* mBuffer;
+
+ /** Size of the file, in bytes */
+ unsigned int fileSize;
+
+ /** Current file name */
+ std::string mFile;
+
+ /** Current base directory */
+ std::string path;
+
+ /** Pure file we're currently reading */
+ std::string filename;
+
+ /** Output scene to be filled */
+ aiScene* mScene;
+
+ /** IO system to be used to access the data*/
+ IOSystem* mIOHandler;
+ };
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/MD4FileData.h b/src/3rdparty/assimp/code/MD4FileData.h
new file mode 100644
index 000000000..c61665c2a
--- /dev/null
+++ b/src/3rdparty/assimp/code/MD4FileData.h
@@ -0,0 +1,218 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Defines the helper data structures for importing MD4 files */
+#ifndef AI_MD4FILEHELPER_H_INC
+#define AI_MD4FILEHELPER_H_INC
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
+# pragma pack(push,1)
+# define PACK_STRUCT
+#elif defined( __GNUC__ )
+# define PACK_STRUCT __attribute__((packed))
+#else
+# error Compiler not supported
+#endif
+
+
+namespace Assimp
+{
+// http://gongo.quakedev.com/md4.html
+namespace MD4
+{
+
+#define AI_MD4_MAGIC_NUMBER_BE 'IDP4'
+#define AI_MD4_MAGIC_NUMBER_LE '4PDI'
+
+// common limitations
+#define AI_MD4_VERSION 4
+#define AI_MD4_MAXQPATH 64
+#define AI_MD4_MAX_FRAMES 2028
+#define AI_MD4_MAX_SURFACES 32
+#define AI_MD4_MAX_BONES 256
+#define AI_MD4_MAX_VERTS 4096
+#define AI_MD4_MAX_TRIANGLES 8192
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for the MD4 main header
+ */
+// ---------------------------------------------------------------------------
+struct Header
+{
+ //! magic number
+ int32_t magic;
+
+ //! file format version
+ int32_t version;
+
+ //! original name in .pak archive
+ unsigned char name[ AI_MD4_MAXQPATH ];
+
+ //! number of frames in the file
+ int32_t NUM_FRAMES;
+
+ //! number of bones in the file
+ int32_t NUM_BONES;
+
+ //! number of surfaces in the file
+ int32_t NUM_SURFACES;
+
+ //! offset of the first frame
+ int32_t OFS_FRAMES;
+
+ //! offset of the first bone
+ int32_t OFS_BONES;
+
+ //! offset of the first surface
+ int32_t OFS_SURFACES;
+
+ //! end of file
+ int32_t OFS_EOF;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Stores the local transformation matrix of a bone
+ */
+// ---------------------------------------------------------------------------
+struct BoneFrame
+{
+ float matrix[3][4];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Stores the name / parent index / flag of a node
+ */
+// ---------------------------------------------------------------------------
+struct BoneName
+{
+ char name[32] ;
+ int parent ;
+ int flags ;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a surface in a MD4 file
+ */
+// ---------------------------------------------------------------------------
+struct Surface
+{
+ int32_t ident;
+ char name[64];
+ char shader[64];
+ int32_t shaderIndex;
+ int32_t lodBias;
+ int32_t minLod;
+ int32_t ofsHeader;
+ int32_t numVerts;
+ int32_t ofsVerts;
+ int32_t numTris;
+ int32_t ofsTris;
+ int32_t numBoneRefs;
+ int32_t ofsBoneRefs;
+ int32_t ofsCollapseMap;
+ int32_t ofsEnd;
+} PACK_STRUCT;
+
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD4 vertex' weight
+ */
+// ---------------------------------------------------------------------------
+struct Weight
+{
+ int32_t boneIndex;
+ float boneWeight;
+ float offset[3];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a vertex in a MD4 file
+ */
+// ---------------------------------------------------------------------------
+struct Vertex
+{
+ float vertex[3];
+ float normal[3];
+ float texCoords[2];
+ int32_t numWeights;
+ Weight weights[1];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a triangle in a MD4 file
+ */
+// ---------------------------------------------------------------------------
+struct Triangle
+{
+ int32_t indexes[3];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD4 frame
+ */
+// ---------------------------------------------------------------------------
+struct Frame
+{
+ float bounds[3][2];
+ float localOrigin[3];
+ float radius;
+ BoneFrame bones[1];
+} PACK_STRUCT;
+
+
+// reset packing to the original value
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
+# pragma pack( pop )
+#endif
+#undef PACK_STRUCT
+
+
+};
+};
+
+#endif // !! AI_MD4FILEHELPER_H_INC
diff --git a/src/3rdparty/assimp/code/MD5Loader.cpp b/src/3rdparty/assimp/code/MD5Loader.cpp
new file mode 100644
index 000000000..b6553035c
--- /dev/null
+++ b/src/3rdparty/assimp/code/MD5Loader.cpp
@@ -0,0 +1,739 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file MD5Loader.cpp
+ * @brief Implementation of the MD5 importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
+
+// internal headers
+#include "MaterialSystem.h"
+#include "RemoveComments.h"
+#include "MD5Loader.h"
+#include "StringComparison.h"
+#include "fast_atof.h"
+#include "SkeletonMeshBuilder.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+
+// Minimum weight value. Weights inside [-n ... n] are ignored
+#define AI_MD5_WEIGHT_EPSILON 1e-5f
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MD5Importer::MD5Importer()
+ : mIOHandler(0)
+ , mBuffer(0)
+ , fileSize(0)
+ , iLineNumber(0)
+ , pScene(0)
+ , pIOHandler(0)
+ , bHadMD5Mesh(false)
+ , bHadMD5Anim(false)
+ , bHadMD5Camera(false)
+ , configNoAutoLoad (false)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MD5Importer::~MD5Importer()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool MD5Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "md5anim" || extension == "md5mesh" || extension == "md5camera")
+ return true;
+ else if (!extension.length() || checkSig) {
+ if (!pIOHandler)
+ return true;
+ const char* tokens[] = {"MD5Version"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get list of all supported extensions
+void MD5Importer::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("md5anim");
+ extensions.insert("md5mesh");
+ extensions.insert("md5camera");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup import properties
+void MD5Importer::SetupProperties(const Importer* pImp)
+{
+ // AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD
+ configNoAutoLoad = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void MD5Importer::InternReadFile( const std::string& pFile,
+ aiScene* _pScene, IOSystem* _pIOHandler)
+{
+ pIOHandler = _pIOHandler;
+ pScene = _pScene;
+ bHadMD5Mesh = bHadMD5Anim = bHadMD5Camera = false;
+
+ // remove the file extension
+ std::string::size_type pos = pFile.find_last_of('.');
+ mFile = (std::string::npos == pos ? pFile : pFile.substr(0,pos+1));
+
+ const std::string extension = GetExtension(pFile);
+ try {
+ if (extension == "md5camera") {
+ LoadMD5CameraFile();
+ }
+ else if (configNoAutoLoad || extension == "md5anim") {
+ // determine file extension and process just *one* file
+ if (extension.length() == 0) {
+ /* fixme */
+ }
+ if (extension == "md5anim") {
+ LoadMD5AnimFile();
+ }
+ else if (extension == "md5mesh") {
+ LoadMD5MeshFile();
+ }
+ }
+ else {
+ LoadMD5MeshFile();
+ LoadMD5AnimFile();
+ }
+ }
+ catch ( std::exception&) {
+ // XXX use more idiomatic RAII solution
+ UnloadFileFromMemory();
+ throw;
+ }
+
+ // make sure we have at least one file
+ if (!bHadMD5Mesh && !bHadMD5Anim && !bHadMD5Camera)
+ throw DeadlyImportError("Failed to read valid contents from this MD5* file");
+
+ // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
+ pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
+ 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
+
+ // the output scene wouldn't pass the validation without this flag
+ if (!bHadMD5Mesh)
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load a file into a memory buffer
+void MD5Importer::LoadFileIntoMemory (IOStream* file)
+{
+ ai_assert(NULL != file);
+ fileSize = (unsigned int)file->FileSize();
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ pScene = pScene;
+ mBuffer = new char[fileSize+1];
+ file->Read( (void*)mBuffer, 1, fileSize);
+ iLineNumber = 1;
+
+ // append a terminal 0
+ mBuffer[fileSize] = '\0';
+
+ // now remove all line comments from the file
+ CommentRemover::RemoveLineComments("//",mBuffer,' ');
+}
+
+// ------------------------------------------------------------------------------------------------
+// Unload the current memory buffer
+void MD5Importer::UnloadFileFromMemory ()
+{
+ // delete the file buffer
+ delete[] mBuffer;
+ mBuffer = NULL;
+ fileSize = 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build unique vertices
+void MD5Importer::MakeDataUnique (MD5::MeshDesc& meshSrc)
+{
+ std::vector<bool> abHad(meshSrc.mVertices.size(),false);
+
+ // allocate enough storage to keep the output structures
+ const unsigned int iNewNum = meshSrc.mFaces.size()*3;
+ unsigned int iNewIndex = meshSrc.mVertices.size();
+ meshSrc.mVertices.resize(iNewNum);
+
+ // try to guess how much storage we'll need for new weights
+ const float fWeightsPerVert = meshSrc.mWeights.size() / (float)iNewIndex;
+ const unsigned int guess = (unsigned int)(fWeightsPerVert*iNewNum);
+ meshSrc.mWeights.reserve(guess + (guess >> 3)); // + 12.5% as buffer
+
+ for (FaceList::const_iterator iter = meshSrc.mFaces.begin(),iterEnd = meshSrc.mFaces.end();iter != iterEnd;++iter){
+ const aiFace& face = *iter;
+ for (unsigned int i = 0; i < 3;++i) {
+ if (face.mIndices[0] >= meshSrc.mVertices.size())
+ throw DeadlyImportError("MD5MESH: Invalid vertex index");
+
+ if (abHad[face.mIndices[i]]) {
+ // generate a new vertex
+ meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]];
+ face.mIndices[i] = iNewIndex++;
+ }
+ else abHad[face.mIndices[i]] = true;
+ }
+ // swap face order
+ std::swap(face.mIndices[0],face.mIndices[2]);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursive node graph construction from a MD5MESH
+void MD5Importer::AttachChilds_Mesh(int iParentID,aiNode* piParent, BoneList& bones)
+{
+ ai_assert(NULL != piParent && !piParent->mNumChildren);
+
+ // First find out how many children we'll have
+ for (int i = 0; i < (int)bones.size();++i) {
+ if (iParentID != i && bones[i].mParentIndex == iParentID) {
+ ++piParent->mNumChildren;
+ }
+ }
+ if (piParent->mNumChildren) {
+ piParent->mChildren = new aiNode*[piParent->mNumChildren];
+ for (int i = 0; i < (int)bones.size();++i) {
+ // (avoid infinite recursion)
+ if (iParentID != i && bones[i].mParentIndex == iParentID) {
+ aiNode* pc;
+ // setup a new node
+ *piParent->mChildren++ = pc = new aiNode();
+ pc->mName = aiString(bones[i].mName);
+ pc->mParent = piParent;
+
+ // get the transformation matrix from rotation and translational components
+ aiQuaternion quat;
+ MD5::ConvertQuaternion ( bones[i].mRotationQuat, quat );
+
+ // FIX to get to Assimp's quaternion conventions
+ quat.w *= -1.f;
+
+ bones[i].mTransform = aiMatrix4x4 ( quat.GetMatrix());
+ bones[i].mTransform.a4 = bones[i].mPositionXYZ.x;
+ bones[i].mTransform.b4 = bones[i].mPositionXYZ.y;
+ bones[i].mTransform.c4 = bones[i].mPositionXYZ.z;
+
+ // store it for later use
+ pc->mTransformation = bones[i].mInvTransform = bones[i].mTransform;
+ bones[i].mInvTransform.Inverse();
+
+ // the transformations for each bone are absolute, so we need to multiply them
+ // with the inverse of the absolute matrix of the parent joint
+ if (-1 != iParentID) {
+ pc->mTransformation = bones[iParentID].mInvTransform * pc->mTransformation;
+ }
+
+ // add children to this node, too
+ AttachChilds_Mesh( i, pc, bones);
+ }
+ }
+ // undo offset computations
+ piParent->mChildren -= piParent->mNumChildren;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursive node graph construction from a MD5ANIM
+void MD5Importer::AttachChilds_Anim(int iParentID,aiNode* piParent, AnimBoneList& bones,const aiNodeAnim** node_anims)
+{
+ ai_assert(NULL != piParent && !piParent->mNumChildren);
+
+ // First find out how many children we'll have
+ for (int i = 0; i < (int)bones.size();++i) {
+ if (iParentID != i && bones[i].mParentIndex == iParentID) {
+ ++piParent->mNumChildren;
+ }
+ }
+ if (piParent->mNumChildren) {
+ piParent->mChildren = new aiNode*[piParent->mNumChildren];
+ for (int i = 0; i < (int)bones.size();++i) {
+ // (avoid infinite recursion)
+ if (iParentID != i && bones[i].mParentIndex == iParentID)
+ {
+ aiNode* pc;
+ // setup a new node
+ *piParent->mChildren++ = pc = new aiNode();
+ pc->mName = aiString(bones[i].mName);
+ pc->mParent = piParent;
+
+ // get the corresponding animation channel and its first frame
+ const aiNodeAnim** cur = node_anims;
+ while ((**cur).mNodeName != pc->mName)++cur;
+
+ aiMatrix4x4::Translation((**cur).mPositionKeys[0].mValue,pc->mTransformation);
+ pc->mTransformation = pc->mTransformation * aiMatrix4x4((**cur).mRotationKeys[0].mValue.GetMatrix()) ;
+
+ // add children to this node, too
+ AttachChilds_Anim( i, pc, bones,node_anims);
+ }
+ }
+ // undo offset computations
+ piParent->mChildren -= piParent->mNumChildren;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load a MD5MESH file
+void MD5Importer::LoadMD5MeshFile ()
+{
+ std::string pFile = mFile + "md5mesh";
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ DefaultLogger::get()->warn("Failed to read MD5MESH file: " + pFile);
+ return;
+ }
+ bHadMD5Mesh = true;
+ LoadFileIntoMemory(file.get());
+
+ // now construct a parser and parse the file
+ MD5::MD5Parser parser(mBuffer,fileSize);
+
+ // load the mesh information from it
+ MD5::MD5MeshParser meshParser(parser.mSections);
+
+ // create the bone hierarchy - first the root node and dummy nodes for all meshes
+ pScene->mRootNode = new aiNode("<MD5_Root>");
+ pScene->mRootNode->mNumChildren = 2;
+ pScene->mRootNode->mChildren = new aiNode*[2];
+
+ // build the hierarchy from the MD5MESH file
+ aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode();
+ pcNode->mName.Set("<MD5_Hierarchy>");
+ pcNode->mParent = pScene->mRootNode;
+ AttachChilds_Mesh(-1,pcNode,meshParser.mJoints);
+
+ pcNode = pScene->mRootNode->mChildren[0] = new aiNode();
+ pcNode->mName.Set("<MD5_Mesh>");
+ pcNode->mParent = pScene->mRootNode;
+
+#if 0
+ if (pScene->mRootNode->mChildren[1]->mNumChildren) /* start at the right hierarchy level */
+ SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[1]->mChildren[0]);
+#else
+ const std::vector<MD5::MeshDesc>::const_iterator end = meshParser.mMeshes.end();
+ // FIX: MD5 files exported from Blender can have empty meshes
+ for (std::vector<MD5::MeshDesc>::const_iterator it = meshParser.mMeshes.begin(); it != end;++it) {
+ if (!(*it).mFaces.empty() && !(*it).mVertices.empty())
+ ++pScene->mNumMaterials;
+ }
+
+ // generate all meshes
+ pScene->mNumMeshes = pScene->mNumMaterials;
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
+
+ // storage for node mesh indices
+ pcNode->mNumMeshes = pScene->mNumMeshes;
+ pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
+ for (unsigned int m = 0; m < pcNode->mNumMeshes;++m)
+ pcNode->mMeshes[m] = m;
+
+ unsigned int n = 0;
+ for (std::vector<MD5::MeshDesc>::iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) {
+ MD5::MeshDesc& meshSrc = *it;
+ if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty())
+ continue;
+
+ aiMesh* mesh = pScene->mMeshes[n] = new aiMesh();
+ mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // generate unique vertices in our internal verbose format
+ MakeDataUnique(meshSrc);
+
+ mesh->mNumVertices = (unsigned int) meshSrc.mVertices.size();
+ mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+ mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
+ mesh->mNumUVComponents[0] = 2;
+
+ // copy texture coordinates
+ aiVector3D* pv = mesh->mTextureCoords[0];
+ for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
+ pv->x = (*iter).mUV.x;
+ pv->y = 1.0f-(*iter).mUV.y; // D3D to OpenGL
+ pv->z = 0.0f;
+ }
+
+ // sort all bone weights - per bone
+ unsigned int* piCount = new unsigned int[meshParser.mJoints.size()];
+ ::memset(piCount,0,sizeof(unsigned int)*meshParser.mJoints.size());
+
+ for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
+ for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
+ {
+ MD5::WeightDesc& desc = meshSrc.mWeights[w];
+ /* FIX for some invalid exporters */
+ if (!(desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON ))
+ ++piCount[desc.mBone];
+ }
+ }
+
+ // check how many we will need
+ for (unsigned int p = 0; p < meshParser.mJoints.size();++p)
+ if (piCount[p])mesh->mNumBones++;
+
+ if (mesh->mNumBones) // just for safety
+ {
+ mesh->mBones = new aiBone*[mesh->mNumBones];
+ for (unsigned int q = 0,h = 0; q < meshParser.mJoints.size();++q)
+ {
+ if (!piCount[q])continue;
+ aiBone* p = mesh->mBones[h] = new aiBone();
+ p->mNumWeights = piCount[q];
+ p->mWeights = new aiVertexWeight[p->mNumWeights];
+ p->mName = aiString(meshParser.mJoints[q].mName);
+ p->mOffsetMatrix = meshParser.mJoints[q].mInvTransform;
+
+ // store the index for later use
+ MD5::BoneDesc& boneSrc = meshParser.mJoints[q];
+ boneSrc.mMap = h++;
+
+ // compute w-component of quaternion
+ MD5::ConvertQuaternion( boneSrc.mRotationQuat, boneSrc.mRotationQuatConverted );
+ }
+
+ //unsigned int g = 0;
+ pv = mesh->mVertices;
+ for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
+ // compute the final vertex position from all single weights
+ *pv = aiVector3D();
+
+ // there are models which have weights which don't sum to 1 ...
+ float fSum = 0.0f;
+ for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
+ fSum += meshSrc.mWeights[w].mWeight;
+ if (!fSum) {
+ DefaultLogger::get()->error("MD5MESH: The sum of all vertex bone weights is 0");
+ continue;
+ }
+
+ // process bone weights
+ for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) {
+ if (w >= meshSrc.mWeights.size())
+ throw DeadlyImportError("MD5MESH: Invalid weight index");
+
+ MD5::WeightDesc& desc = meshSrc.mWeights[w];
+ if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON)
+ continue;
+
+ const float fNewWeight = desc.mWeight / fSum;
+
+ // transform the local position into worldspace
+ MD5::BoneDesc& boneSrc = meshParser.mJoints[desc.mBone];
+ const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate (desc.vOffsetPosition);
+
+ // use the original weight to compute the vertex position
+ // (some MD5s seem to depend on the invalid weight values ...)
+ *pv += ((boneSrc.mPositionXYZ+v)* desc.mWeight);
+
+ aiBone* bone = mesh->mBones[boneSrc.mMap];
+ *bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight);
+ }
+ }
+
+ // undo our nice offset tricks ...
+ for (unsigned int p = 0; p < mesh->mNumBones;++p)
+ mesh->mBones[p]->mWeights -= mesh->mBones[p]->mNumWeights;
+ }
+
+ delete[] piCount;
+
+ // now setup all faces - we can directly copy the list
+ // (however, take care that the aiFace destructor doesn't delete the mIndices array)
+ mesh->mNumFaces = (unsigned int)meshSrc.mFaces.size();
+ mesh->mFaces = new aiFace[mesh->mNumFaces];
+ for (unsigned int c = 0; c < mesh->mNumFaces;++c) {
+ mesh->mFaces[c].mNumIndices = 3;
+ mesh->mFaces[c].mIndices = meshSrc.mFaces[c].mIndices;
+ meshSrc.mFaces[c].mIndices = NULL;
+ }
+
+ // generate a material for the mesh
+ MaterialHelper* mat = new MaterialHelper();
+ pScene->mMaterials[n] = mat;
+
+ // insert the typical doom3 textures:
+ // nnn_local.tga - normal map
+ // nnn_h.tga - height map
+ // nnn_s.tga - specular map
+ // nnn_d.tga - diffuse map
+ if (meshSrc.mShader.length && !strchr(meshSrc.mShader.data,'.')) {
+
+ aiString temp(meshSrc.mShader);
+ temp.Append("_local.tga");
+ mat->AddProperty(&temp,AI_MATKEY_TEXTURE_NORMALS(0));
+
+ temp = aiString(meshSrc.mShader);
+ temp.Append("_s.tga");
+ mat->AddProperty(&temp,AI_MATKEY_TEXTURE_SPECULAR(0));
+
+ temp = aiString(meshSrc.mShader);
+ temp.Append("_d.tga");
+ mat->AddProperty(&temp,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ temp = aiString(meshSrc.mShader);
+ temp.Append("_h.tga");
+ mat->AddProperty(&temp,AI_MATKEY_TEXTURE_HEIGHT(0));
+
+ // set this also as material name
+ mat->AddProperty(&meshSrc.mShader,AI_MATKEY_NAME);
+ }
+ else mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ mesh->mMaterialIndex = n++;
+ }
+#endif
+ // delete the file again
+ UnloadFileFromMemory();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load an MD5ANIM file
+void MD5Importer::LoadMD5AnimFile ()
+{
+ std::string pFile = mFile + "md5anim";
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ DefaultLogger::get()->warn("Failed to read MD5ANIM file: " + pFile);
+ return;
+ }
+ LoadFileIntoMemory(file.get());
+
+ // parse the basic file structure
+ MD5::MD5Parser parser(mBuffer,fileSize);
+
+ // load the animation information from the parse tree
+ MD5::MD5AnimParser animParser(parser.mSections);
+
+ // generate and fill the output animation
+ if (animParser.mAnimatedBones.empty() || animParser.mFrames.empty() ||
+ animParser.mBaseFrames.size() != animParser.mAnimatedBones.size()) {
+
+ DefaultLogger::get()->error("MD5ANIM: No frames or animated bones loaded");
+ }
+ else {
+ bHadMD5Anim = true;
+
+ pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations = 1];
+ aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation();
+ anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size();
+ anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
+ for (unsigned int i = 0; i < anim->mNumChannels;++i) {
+ aiNodeAnim* node = anim->mChannels[i] = new aiNodeAnim();
+ node->mNodeName = aiString( animParser.mAnimatedBones[i].mName );
+
+ // allocate storage for the keyframes
+ node->mPositionKeys = new aiVectorKey[animParser.mFrames.size()];
+ node->mRotationKeys = new aiQuatKey[animParser.mFrames.size()];
+ }
+
+ // 1 tick == 1 frame
+ anim->mTicksPerSecond = animParser.fFrameRate;
+
+ for (FrameList::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end();iter != iterEnd;++iter){
+ double dTime = (double)(*iter).iIndex;
+ aiNodeAnim** pcAnimNode = anim->mChannels;
+ if (!(*iter).mValues.empty() || iter == animParser.mFrames.begin()) /* be sure we have at least one frame */
+ {
+ // now process all values in there ... read all joints
+ MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0];
+ for (AnimBoneList::const_iterator iter2 = animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end();++iter2,
+ ++pcAnimNode,++pcBaseFrame)
+ {
+ if ((*iter2).iFirstKeyIndex >= (*iter).mValues.size()) {
+
+ // Allow for empty frames
+ if ((*iter2).iFlags != 0) {
+ throw DeadlyImportError("MD5: Keyframe index is out of range");
+
+ }
+ continue;
+ }
+ const float* fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex];
+ aiNodeAnim* pcCurAnimBone = *pcAnimNode;
+
+ aiVectorKey* vKey = &pcCurAnimBone->mPositionKeys[pcCurAnimBone->mNumPositionKeys++];
+ aiQuatKey* qKey = &pcCurAnimBone->mRotationKeys [pcCurAnimBone->mNumRotationKeys++];
+ aiVector3D vTemp;
+
+ // translational component
+ for (unsigned int i = 0; i < 3; ++i) {
+ if ((*iter2).iFlags & (1u << i))
+ vKey->mValue[i] = *fpCur++;
+ else vKey->mValue[i] = pcBaseFrame->vPositionXYZ[i];
+ }
+
+ // orientation component
+ for (unsigned int i = 0; i < 3; ++i) {
+ if ((*iter2).iFlags & (8u << i))
+ vTemp[i] = *fpCur++;
+ else vTemp[i] = pcBaseFrame->vRotationQuat[i];
+ }
+
+ MD5::ConvertQuaternion(vTemp, qKey->mValue);
+ qKey->mTime = vKey->mTime = dTime;
+
+ // we need this to get to Assimp quaternion conventions
+ qKey->mValue.w *= -1.f;
+ }
+ }
+
+ // compute the duration of the animation
+ anim->mDuration = std::max(dTime,anim->mDuration);
+ }
+
+ // If we didn't build the hierarchy yet (== we didn't load a MD5MESH),
+ // construct it now from the data given in the MD5ANIM.
+ if (!pScene->mRootNode) {
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("<MD5_Hierarchy>");
+
+ AttachChilds_Anim(-1,pScene->mRootNode,animParser.mAnimatedBones,(const aiNodeAnim**)anim->mChannels);
+
+ // Call SkeletonMeshBuilder to construct a mesh to represent the shape
+ if (pScene->mRootNode->mNumChildren) {
+ SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[0]);
+ }
+ }
+ }
+ // delete the file again
+ UnloadFileFromMemory();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load an MD5CAMERA file
+void MD5Importer::LoadMD5CameraFile ()
+{
+ std::string pFile = mFile + "md5camera";
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError("Failed to read MD5CAMERA file: " + pFile);
+ }
+ bHadMD5Camera = true;
+ LoadFileIntoMemory(file.get());
+
+ // parse the basic file structure
+ MD5::MD5Parser parser(mBuffer,fileSize);
+
+ // load the camera animation data from the parse tree
+ MD5::MD5CameraParser cameraParser(parser.mSections);
+
+ if (cameraParser.frames.empty())
+ throw DeadlyImportError("MD5CAMERA: No frames parsed");
+
+ std::vector<unsigned int>& cuts = cameraParser.cuts;
+ std::vector<MD5::CameraAnimFrameDesc>& frames = cameraParser.frames;
+
+ // Construct output graph - a simple root with a dummy child.
+ // The root node performs the coordinate system conversion
+ aiNode* root = pScene->mRootNode = new aiNode("<MD5CameraRoot>");
+ root->mChildren = new aiNode*[root->mNumChildren = 1];
+ root->mChildren[0] = new aiNode("<MD5Camera>");
+ root->mChildren[0]->mParent = root;
+
+ // ... but with one camera assigned to it
+ pScene->mCameras = new aiCamera*[pScene->mNumCameras = 1];
+ aiCamera* cam = pScene->mCameras[0] = new aiCamera();
+ cam->mName = "<MD5Camera>";
+
+ // FIXME: Fov is currently set to the first frame's value
+ cam->mHorizontalFOV = AI_DEG_TO_RAD( frames.front().fFOV );
+
+ // every cut is written to a separate aiAnimation
+ if (!cuts.size()) {
+ cuts.push_back(0);
+ cuts.push_back(frames.size()-1);
+ }
+ else {
+ cuts.insert(cuts.begin(),0);
+
+ if (cuts.back() < frames.size()-1)
+ cuts.push_back(frames.size()-1);
+ }
+
+ pScene->mNumAnimations = cuts.size()-1;
+ aiAnimation** tmp = pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations];
+ for (std::vector<unsigned int>::const_iterator it = cuts.begin(); it != cuts.end()-1; ++it) {
+
+ aiAnimation* anim = *tmp++ = new aiAnimation();
+ anim->mName.length = ::sprintf(anim->mName.data,"anim%u_from_%u_to_%u",(unsigned int)(it-cuts.begin()),(*it),*(it+1));
+
+ anim->mTicksPerSecond = cameraParser.fFrameRate;
+ anim->mChannels = new aiNodeAnim*[anim->mNumChannels = 1];
+ aiNodeAnim* nd = anim->mChannels[0] = new aiNodeAnim();
+ nd->mNodeName.Set("<MD5Camera>");
+
+ nd->mNumPositionKeys = nd->mNumRotationKeys = *(it+1) - (*it);
+ nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
+ nd->mRotationKeys = new aiQuatKey [nd->mNumRotationKeys];
+ for (unsigned int i = 0; i < nd->mNumPositionKeys; ++i) {
+
+ nd->mPositionKeys[i].mValue = frames[*it+i].vPositionXYZ;
+ MD5::ConvertQuaternion(frames[*it+i].vRotationQuat,nd->mRotationKeys[i].mValue);
+ nd->mRotationKeys[i].mTime = nd->mPositionKeys[i].mTime = *it+i;
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_MD5_IMPORTER
diff --git a/src/3rdparty/assimp/code/MD5Loader.h b/src/3rdparty/assimp/code/MD5Loader.h
new file mode 100644
index 000000000..d56cefb1b
--- /dev/null
+++ b/src/3rdparty/assimp/code/MD5Loader.h
@@ -0,0 +1,194 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file MD5Loader.h
+ * @brief Definition of the .MD5 importer class.
+ * http://www.modwiki.net/wiki/MD5_(file_format)
+*/
+#ifndef AI_MD5LOADER_H_INCLUDED
+#define AI_MD5LOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "MD5Parser.h"
+
+#include "../include/aiTypes.h"
+
+namespace Assimp {
+
+class IOStream;
+using namespace Assimp::MD5;
+
+// ---------------------------------------------------------------------------
+/** Importer class for the MD5 file format
+*/
+class MD5Importer : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ MD5Importer();
+
+ /** Destructor, private as well */
+ ~MD5Importer();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Load a *.MD5MESH file.
+ */
+ void LoadMD5MeshFile ();
+
+ // -------------------------------------------------------------------
+ /** Load a *.MD5ANIM file.
+ */
+ void LoadMD5AnimFile ();
+
+ // -------------------------------------------------------------------
+ /** Load a *.MD5CAMERA file.
+ */
+ void LoadMD5CameraFile ();
+
+ // -------------------------------------------------------------------
+ /** Construct node hierarchy from a given MD5ANIM
+ * @param iParentID Current parent ID
+ * @param piParent Parent node to attach to
+ * @param bones Input bones
+ * @param node_anims Generated node animations
+ */
+ void AttachChilds_Anim(int iParentID,aiNode* piParent,
+ AnimBoneList& bones,const aiNodeAnim** node_anims);
+
+ // -------------------------------------------------------------------
+ /** Construct node hierarchy from a given MD5MESH
+ * @param iParentID Current parent ID
+ * @param piParent Parent node to attach to
+ * @param bones Input bones
+ */
+ void AttachChilds_Mesh(int iParentID,aiNode* piParent,BoneList& bones);
+
+ // -------------------------------------------------------------------
+ /** Build unique vertex buffers from a given MD5ANIM
+ * @param meshSrc Input data
+ */
+ void MakeDataUnique (MD5::MeshDesc& meshSrc);
+
+ // -------------------------------------------------------------------
+ /** Load the contents of a specific file into memory and
+ * alocates a buffer to keep it.
+ *
+ * mBuffer is modified to point to this buffer.
+ * @param pFile File stream to be read
+ */
+ void LoadFileIntoMemory (IOStream* pFile);
+ void UnloadFileFromMemory ();
+
+
+ /** IOSystem to be used to access files */
+ IOSystem* mIOHandler;
+
+ /** Path to the file, excluding the file extension but
+ with the dot */
+ std::string mFile;
+
+ /** Buffer to hold the loaded file */
+ char* mBuffer;
+
+ /** Size of the file */
+ unsigned int fileSize;
+
+ /** Current line number. For debugging purposes */
+ unsigned int iLineNumber;
+
+ /** Scene to be filled */
+ aiScene* pScene;
+
+ /** (Custom) I/O handler implementation */
+ IOSystem* pIOHandler;
+
+ /** true if a MD5MESH file has already been parsed */
+ bool bHadMD5Mesh;
+
+ /** true if a MD5ANIM file has already been parsed */
+ bool bHadMD5Anim;
+
+ /** true if a MD5CAMERA file has already been parsed */
+ bool bHadMD5Camera;
+
+ /** configuration option: prevent anim autoload */
+ bool configNoAutoLoad;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/MD5Parser.cpp b/src/3rdparty/assimp/code/MD5Parser.cpp
new file mode 100644
index 000000000..ef688e5ec
--- /dev/null
+++ b/src/3rdparty/assimp/code/MD5Parser.cpp
@@ -0,0 +1,475 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file MD5Parser.cpp
+ * @brief Implementation of the MD5 parser class
+ */
+#include "AssimpPCH.h"
+
+// internal headers
+#include "MD5Loader.h"
+#include "MaterialSystem.h"
+#include "fast_atof.h"
+#include "ParsingUtils.h"
+#include "StringComparison.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+using namespace Assimp::MD5;
+
+// ------------------------------------------------------------------------------------------------
+// Parse the segment structure fo a MD5 file
+MD5Parser::MD5Parser(char* _buffer, unsigned int _fileSize )
+{
+ ai_assert(NULL != _buffer && 0 != _fileSize);
+
+ buffer = _buffer;
+ fileSize = _fileSize;
+ lineNumber = 0;
+
+ DefaultLogger::get()->debug("MD5Parser begin");
+
+ // parse the file header
+ ParseHeader();
+
+ // and read all sections until we're finished
+ bool running = true;
+ while (running) {
+ mSections.push_back(Section());
+ Section& sec = mSections.back();
+ if (!ParseSection(sec)) {
+ break;
+ }
+ }
+
+ if ( !DefaultLogger::isNullLogger()) {
+ char szBuffer[128]; // should be sufficiently large
+ ::sprintf(szBuffer,"MD5Parser end. Parsed %i sections",(int)mSections.size());
+ DefaultLogger::get()->debug(szBuffer);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Report error to the log stream
+/*static*/ void MD5Parser::ReportError (const char* error, unsigned int line)
+{
+ char szBuffer[1024];
+ ::sprintf(szBuffer,"[MD5] Line %i: %s",line,error);
+ throw DeadlyImportError(szBuffer);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Report warning to the log stream
+/*static*/ void MD5Parser::ReportWarning (const char* warn, unsigned int line)
+{
+ char szBuffer[1024];
+ ::sprintf(szBuffer,"[MD5] Line %i: %s",line,warn);
+ DefaultLogger::get()->warn(szBuffer);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse and validate the MD5 header
+void MD5Parser::ParseHeader()
+{
+ // parse and validate the file version
+ SkipSpaces();
+ if (!TokenMatch(buffer,"MD5Version",10)) {
+ ReportError("Invalid MD5 file: MD5Version tag has not been found");
+ }
+ SkipSpaces();
+ unsigned int iVer = ::strtol10(buffer,(const char**)&buffer);
+ if (10 != iVer) {
+ ReportError("MD5 version tag is unknown (10 is expected)");
+ }
+ SkipLine();
+
+ // print the command line options to the console
+ // FIX: can break the log length limit, so we need to be careful
+ char* sz = buffer;
+ while (!IsLineEnd( *buffer++)) {}
+ DefaultLogger::get()->info(std::string(sz,std::min((uintptr_t)MAX_LOG_MESSAGE_LENGTH, (uintptr_t)(buffer-sz))));
+ SkipSpacesAndLineEnd();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursive MD5 parsing function
+bool MD5Parser::ParseSection(Section& out)
+{
+ // store the current line number for use in error messages
+ out.iLineNumber = lineNumber;
+
+ // first parse the name of the section
+ char* sz = buffer;
+ while (!IsSpaceOrNewLine( *buffer))buffer++;
+ out.mName = std::string(sz,(uintptr_t)(buffer-sz));
+ SkipSpaces();
+
+ bool running = true;
+ while (running) {
+ if ('{' == *buffer) {
+ // it is a normal section so read all lines
+ buffer++;
+ bool run = true;
+ while (run)
+ {
+ if (!SkipSpacesAndLineEnd()) {
+ return false; // seems this was the last section
+ }
+ if ('}' == *buffer) {
+ buffer++;
+ break;
+ }
+
+ out.mElements.push_back(Element());
+ Element& elem = out.mElements.back();
+
+ elem.iLineNumber = lineNumber;
+ elem.szStart = buffer;
+
+ // terminate the line with zero
+ while (!IsLineEnd( *buffer))buffer++;
+ if (*buffer) {
+ ++lineNumber;
+ *buffer++ = '\0';
+ }
+ }
+ break;
+ }
+ else if (!IsSpaceOrNewLine(*buffer)) {
+ // it is an element at global scope. Parse its value and go on
+ sz = buffer;
+ while (!IsSpaceOrNewLine( *buffer++)) {}
+ out.mGlobalValue = std::string(sz,(uintptr_t)(buffer-sz));
+ continue;
+ }
+ break;
+ }
+ return SkipSpacesAndLineEnd();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Some dirty macros just because they're so funny and easy to debug
+
+// skip all spaces ... handle EOL correctly
+#define AI_MD5_SKIP_SPACES() if (!SkipSpaces(&sz)) \
+ MD5Parser::ReportWarning("Unexpected end of line",(*eit).iLineNumber);
+
+ // read a triple float in brackets: (1.0 1.0 1.0)
+#define AI_MD5_READ_TRIPLE(vec) \
+ AI_MD5_SKIP_SPACES(); \
+ if ('(' != *sz++) \
+ MD5Parser::ReportWarning("Unexpected token: ( was expected",(*eit).iLineNumber); \
+ AI_MD5_SKIP_SPACES(); \
+ sz = fast_atof_move(sz,(float&)vec.x); \
+ AI_MD5_SKIP_SPACES(); \
+ sz = fast_atof_move(sz,(float&)vec.y); \
+ AI_MD5_SKIP_SPACES(); \
+ sz = fast_atof_move(sz,(float&)vec.z); \
+ AI_MD5_SKIP_SPACES(); \
+ if (')' != *sz++) \
+ MD5Parser::ReportWarning("Unexpected token: ) was expected",(*eit).iLineNumber);
+
+ // parse a string, enclosed in quotation marks or not
+#define AI_MD5_PARSE_STRING(out) \
+ bool bQuota = (*sz == '\"'); \
+ const char* szStart = sz; \
+ while (!IsSpaceOrNewLine(*sz))++sz; \
+ const char* szEnd = sz; \
+ if (bQuota) { \
+ szStart++; \
+ if ('\"' != *(szEnd-=1)) { \
+ MD5Parser::ReportWarning("Expected closing quotation marks in string", \
+ (*eit).iLineNumber); \
+ continue; \
+ } \
+ } \
+ out.length = (size_t)(szEnd - szStart); \
+ ::memcpy(out.data,szStart,out.length); \
+ out.data[out.length] = '\0';
+
+// ------------------------------------------------------------------------------------------------
+// .MD5MESH parsing function
+MD5MeshParser::MD5MeshParser(SectionList& mSections)
+{
+ DefaultLogger::get()->debug("MD5MeshParser begin");
+
+ // now parse all sections
+ for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter){
+ if ( (*iter).mName == "numMeshes") {
+ mMeshes.reserve(::strtol10((*iter).mGlobalValue.c_str()));
+ }
+ else if ( (*iter).mName == "numJoints") {
+ mJoints.reserve(::strtol10((*iter).mGlobalValue.c_str()));
+ }
+ else if ((*iter).mName == "joints") {
+ // "origin" -1 ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000000 0.707107 )
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();eit != eitEnd; ++eit){
+ mJoints.push_back(BoneDesc());
+ BoneDesc& desc = mJoints.back();
+
+ const char* sz = (*eit).szStart;
+ AI_MD5_PARSE_STRING(desc.mName);
+ AI_MD5_SKIP_SPACES();
+
+ // negative values, at least -1, is allowed here
+ desc.mParentIndex = (int)strtol10s(sz,&sz);
+
+ AI_MD5_READ_TRIPLE(desc.mPositionXYZ);
+ AI_MD5_READ_TRIPLE(desc.mRotationQuat); // normalized quaternion, so w is not there
+ }
+ }
+ else if ((*iter).mName == "mesh") {
+ mMeshes.push_back(MeshDesc());
+ MeshDesc& desc = mMeshes.back();
+
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();eit != eitEnd; ++eit){
+ const char* sz = (*eit).szStart;
+
+ // shader attribute
+ if (TokenMatch(sz,"shader",6)) {
+ AI_MD5_SKIP_SPACES();
+ AI_MD5_PARSE_STRING(desc.mShader);
+ }
+ // numverts attribute
+ else if (TokenMatch(sz,"numverts",8)) {
+ AI_MD5_SKIP_SPACES();
+ desc.mVertices.resize(strtol10(sz));
+ }
+ // numtris attribute
+ else if (TokenMatch(sz,"numtris",7)) {
+ AI_MD5_SKIP_SPACES();
+ desc.mFaces.resize(strtol10(sz));
+ }
+ // numweights attribute
+ else if (TokenMatch(sz,"numweights",10)) {
+ AI_MD5_SKIP_SPACES();
+ desc.mWeights.resize(strtol10(sz));
+ }
+ // vert attribute
+ // "vert 0 ( 0.394531 0.513672 ) 0 1"
+ else if (TokenMatch(sz,"vert",4)) {
+ AI_MD5_SKIP_SPACES();
+ const unsigned int idx = ::strtol10(sz,&sz);
+ AI_MD5_SKIP_SPACES();
+ if (idx >= desc.mVertices.size())
+ desc.mVertices.resize(idx+1);
+
+ VertexDesc& vert = desc.mVertices[idx];
+ if ('(' != *sz++)
+ MD5Parser::ReportWarning("Unexpected token: ( was expected",(*eit).iLineNumber);
+ AI_MD5_SKIP_SPACES();
+ sz = fast_atof_move(sz,(float&)vert.mUV.x);
+ AI_MD5_SKIP_SPACES();
+ sz = fast_atof_move(sz,(float&)vert.mUV.y);
+ AI_MD5_SKIP_SPACES();
+ if (')' != *sz++)
+ MD5Parser::ReportWarning("Unexpected token: ) was expected",(*eit).iLineNumber);
+ AI_MD5_SKIP_SPACES();
+ vert.mFirstWeight = ::strtol10(sz,&sz);
+ AI_MD5_SKIP_SPACES();
+ vert.mNumWeights = ::strtol10(sz,&sz);
+ }
+ // tri attribute
+ // "tri 0 15 13 12"
+ else if (TokenMatch(sz,"tri",3)) {
+ AI_MD5_SKIP_SPACES();
+ const unsigned int idx = strtol10(sz,&sz);
+ if (idx >= desc.mFaces.size())
+ desc.mFaces.resize(idx+1);
+
+ aiFace& face = desc.mFaces[idx];
+ face.mIndices = new unsigned int[face.mNumIndices = 3];
+ for (unsigned int i = 0; i < 3;++i) {
+ AI_MD5_SKIP_SPACES();
+ face.mIndices[i] = strtol10(sz,&sz);
+ }
+ }
+ // weight attribute
+ // "weight 362 5 0.500000 ( -3.553583 11.893474 9.719339 )"
+ else if (TokenMatch(sz,"weight",6)) {
+ AI_MD5_SKIP_SPACES();
+ const unsigned int idx = strtol10(sz,&sz);
+ AI_MD5_SKIP_SPACES();
+ if (idx >= desc.mWeights.size())
+ desc.mWeights.resize(idx+1);
+
+ WeightDesc& weight = desc.mWeights[idx];
+ weight.mBone = strtol10(sz,&sz);
+ AI_MD5_SKIP_SPACES();
+ sz = fast_atof_move(sz,weight.mWeight);
+ AI_MD5_READ_TRIPLE(weight.vOffsetPosition);
+ }
+ }
+ }
+ }
+ DefaultLogger::get()->debug("MD5MeshParser end");
+}
+
+// ------------------------------------------------------------------------------------------------
+// .MD5ANIM parsing function
+MD5AnimParser::MD5AnimParser(SectionList& mSections)
+{
+ DefaultLogger::get()->debug("MD5AnimParser begin");
+
+ fFrameRate = 24.0f;
+ mNumAnimatedComponents = 0xffffffff;
+ for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter) {
+ if ((*iter).mName == "hierarchy") {
+ // "sheath" 0 63 6
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();eit != eitEnd; ++eit) {
+ mAnimatedBones.push_back ( AnimBoneDesc () );
+ AnimBoneDesc& desc = mAnimatedBones.back();
+
+ const char* sz = (*eit).szStart;
+ AI_MD5_PARSE_STRING(desc.mName);
+ AI_MD5_SKIP_SPACES();
+
+ // parent index - negative values are allowed (at least -1)
+ desc.mParentIndex = ::strtol10s(sz,&sz);
+
+ // flags (highest is 2^6-1)
+ AI_MD5_SKIP_SPACES();
+ if (63 < (desc.iFlags = ::strtol10(sz,&sz))){
+ MD5Parser::ReportWarning("Invalid flag combination in hierarchy section",(*eit).iLineNumber);
+ }
+ AI_MD5_SKIP_SPACES();
+
+ // index of the first animation keyframe component for this joint
+ desc.iFirstKeyIndex = ::strtol10(sz,&sz);
+ }
+ }
+ else if ((*iter).mName == "baseframe") {
+ // ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000242 0.707107 )
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit) {
+ const char* sz = (*eit).szStart;
+
+ mBaseFrames.push_back ( BaseFrameDesc () );
+ BaseFrameDesc& desc = mBaseFrames.back();
+
+ AI_MD5_READ_TRIPLE(desc.vPositionXYZ);
+ AI_MD5_READ_TRIPLE(desc.vRotationQuat);
+ }
+ }
+ else if ((*iter).mName == "frame") {
+ if (!(*iter).mGlobalValue.length()) {
+ MD5Parser::ReportWarning("A frame section must have a frame index",(*iter).iLineNumber);
+ continue;
+ }
+
+ mFrames.push_back ( FrameDesc () );
+ FrameDesc& desc = mFrames.back();
+ desc.iIndex = strtol10((*iter).mGlobalValue.c_str());
+
+ // we do already know how much storage we will presumably need
+ if (0xffffffff != mNumAnimatedComponents)
+ desc.mValues.reserve(mNumAnimatedComponents);
+
+ // now read all elements (continous list of floats)
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit){
+ const char* sz = (*eit).szStart;
+ while (SkipSpacesAndLineEnd(&sz)) {
+ float f;sz = fast_atof_move(sz,f);
+ desc.mValues.push_back(f);
+ }
+ }
+ }
+ else if ((*iter).mName == "numFrames") {
+ mFrames.reserve(strtol10((*iter).mGlobalValue.c_str()));
+ }
+ else if ((*iter).mName == "numJoints") {
+ const unsigned int num = strtol10((*iter).mGlobalValue.c_str());
+ mAnimatedBones.reserve(num);
+
+ // try to guess the number of animated components if that element is not given
+ if (0xffffffff == mNumAnimatedComponents)
+ mNumAnimatedComponents = num * 6;
+ }
+ else if ((*iter).mName == "numAnimatedComponents") {
+ mAnimatedBones.reserve( strtol10((*iter).mGlobalValue.c_str()));
+ }
+ else if ((*iter).mName == "frameRate") {
+ fast_atof_move((*iter).mGlobalValue.c_str(),fFrameRate);
+ }
+ }
+ DefaultLogger::get()->debug("MD5AnimParser end");
+}
+
+// ------------------------------------------------------------------------------------------------
+// .MD5CAMERA parsing function
+MD5CameraParser::MD5CameraParser(SectionList& mSections)
+{
+ DefaultLogger::get()->debug("MD5CameraParser begin");
+ fFrameRate = 24.0f;
+
+ for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter) {
+ if ((*iter).mName == "numFrames") {
+ frames.reserve(strtol10((*iter).mGlobalValue.c_str()));
+ }
+ else if ((*iter).mName == "frameRate") {
+ fFrameRate = fast_atof ((*iter).mGlobalValue.c_str());
+ }
+ else if ((*iter).mName == "numCuts") {
+ cuts.reserve(strtol10((*iter).mGlobalValue.c_str()));
+ }
+ else if ((*iter).mName == "cuts") {
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit){
+ cuts.push_back(strtol10((*eit).szStart)+1);
+ }
+ }
+ else if ((*iter).mName == "camera") {
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit){
+ const char* sz = (*eit).szStart;
+
+ frames.push_back(CameraAnimFrameDesc());
+ CameraAnimFrameDesc& cur = frames.back();
+ AI_MD5_READ_TRIPLE(cur.vPositionXYZ);
+ AI_MD5_READ_TRIPLE(cur.vRotationQuat);
+ AI_MD5_SKIP_SPACES();
+ cur.fFOV = fast_atof(sz);
+ }
+ }
+ }
+ DefaultLogger::get()->debug("MD5CameraParser end");
+}
+
diff --git a/src/3rdparty/assimp/code/MD5Parser.h b/src/3rdparty/assimp/code/MD5Parser.h
new file mode 100644
index 000000000..afb5db8e3
--- /dev/null
+++ b/src/3rdparty/assimp/code/MD5Parser.h
@@ -0,0 +1,460 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file MD5Parser.h
+ * @brief Definition of the .MD5 parser class.
+ * http://www.modwiki.net/wiki/MD5_(file_format)
+ */
+#ifndef AI_MD5PARSER_H_INCLUDED
+#define AI_MD5PARSER_H_INCLUDED
+
+#include "../include/aiTypes.h"
+#include "ParsingUtils.h"
+
+struct aiFace;
+
+namespace Assimp {
+namespace MD5 {
+
+// ---------------------------------------------------------------------------
+/** Represents a single element in a MD5 file
+ *
+ * Elements are always contained in sections.
+*/
+struct Element
+{
+ //! Points to the starting point of the element
+ //! Whitespace at the beginning and at the end have been removed,
+ //! Elements are terminated with \0
+ char* szStart;
+
+ //! Original line number (can be used in error messages
+ //! if a parsing error occurs)
+ unsigned int iLineNumber;
+};
+
+typedef std::vector< Element > ElementList;
+
+// ---------------------------------------------------------------------------
+/** Represents a section of a MD5 file (such as the mesh or the joints section)
+ *
+ * A section is always enclosed in { and } brackets.
+*/
+struct Section
+{
+ //! Original line number (can be used in error messages
+ //! if a parsing error occurs)
+ unsigned int iLineNumber;
+
+ //! List of all elements which have been parsed in this section.
+ ElementList mElements;
+
+ //! Name of the section
+ std::string mName;
+
+ //! For global elements: the value of the element as string
+ //! Iif !length() the section is not a global element
+ std::string mGlobalValue;
+};
+
+typedef std::vector< Section> SectionList;
+
+// ---------------------------------------------------------------------------
+/** Basic information about a joint
+*/
+struct BaseJointDescription
+{
+ //! Name of the bone
+ aiString mName;
+
+ //! Parent index of the bone
+ int mParentIndex;
+};
+
+// ---------------------------------------------------------------------------
+/** Represents a bone (joint) descriptor in a MD5Mesh file
+*/
+struct BoneDesc : BaseJointDescription
+{
+ //! Absolute position of the bone
+ aiVector3D mPositionXYZ;
+
+ //! Absolute rotation of the bone
+ aiVector3D mRotationQuat;
+ aiQuaternion mRotationQuatConverted;
+
+ //! Absolute transformation of the bone
+ //! (temporary)
+ aiMatrix4x4 mTransform;
+
+ //! Inverse transformation of the bone
+ //! (temporary)
+ aiMatrix4x4 mInvTransform;
+
+ //! Internal
+ unsigned int mMap;
+};
+
+typedef std::vector< BoneDesc > BoneList;
+
+// ---------------------------------------------------------------------------
+/** Represents a bone (joint) descriptor in a MD5Anim file
+*/
+struct AnimBoneDesc : BaseJointDescription
+{
+ //! Flags (AI_MD5_ANIMATION_FLAG_xxx)
+ unsigned int iFlags;
+
+ //! Index of the first key that corresponds to this anim bone
+ unsigned int iFirstKeyIndex;
+};
+
+typedef std::vector< AnimBoneDesc > AnimBoneList;
+
+
+// ---------------------------------------------------------------------------
+/** Represents a base frame descriptor in a MD5Anim file
+*/
+struct BaseFrameDesc
+{
+ aiVector3D vPositionXYZ;
+ aiVector3D vRotationQuat;
+};
+
+typedef std::vector< BaseFrameDesc > BaseFrameList;
+
+// ---------------------------------------------------------------------------
+/** Represents a camera animation frame in a MDCamera file
+*/
+struct CameraAnimFrameDesc : BaseFrameDesc
+{
+ float fFOV;
+};
+
+typedef std::vector< CameraAnimFrameDesc > CameraFrameList;
+
+// ---------------------------------------------------------------------------
+/** Represents a frame descriptor in a MD5Anim file
+*/
+struct FrameDesc
+{
+ //! Index of the frame
+ unsigned int iIndex;
+
+ //! Animation keyframes - a large blob of data at first
+ std::vector< float > mValues;
+};
+
+typedef std::vector< FrameDesc > FrameList;
+
+// ---------------------------------------------------------------------------
+/** Represents a vertex descriptor in a MD5 file
+*/
+struct VertexDesc
+{
+ VertexDesc()
+ : mFirstWeight (0)
+ , mNumWeights (0)
+ {}
+
+ //! UV cordinate of the vertex
+ aiVector2D mUV;
+
+ //! Index of the first weight of the vertex in
+ //! the vertex weight list
+ unsigned int mFirstWeight;
+
+ //! Number of weights assigned to this vertex
+ unsigned int mNumWeights;
+};
+
+typedef std::vector< VertexDesc > VertexList;
+
+// ---------------------------------------------------------------------------
+/** Represents a vertex weight descriptor in a MD5 file
+*/
+struct WeightDesc
+{
+ //! Index of the bone to which this weight refers
+ unsigned int mBone;
+
+ //! The weight value
+ float mWeight;
+
+ //! The offset position of this weight
+ // ! (in the coordinate system defined by the parent bone)
+ aiVector3D vOffsetPosition;
+};
+
+typedef std::vector< WeightDesc > WeightList;
+typedef std::vector< aiFace > FaceList;
+
+// ---------------------------------------------------------------------------
+/** Represents a mesh in a MD5 file
+*/
+struct MeshDesc
+{
+ //! Weights of the mesh
+ WeightList mWeights;
+
+ //! Vertices of the mesh
+ VertexList mVertices;
+
+ //! Faces of the mesh
+ FaceList mFaces;
+
+ //! Name of the shader (=texture) to be assigned to the mesh
+ aiString mShader;
+};
+
+typedef std::vector< MeshDesc > MeshList;
+
+// ---------------------------------------------------------------------------
+// Convert a quaternion to its usual representation
+inline void ConvertQuaternion (const aiVector3D& in, aiQuaternion& out) {
+
+ out.x = in.x;
+ out.y = in.y;
+ out.z = in.z;
+
+ const float t = 1.0f - (in.x*in.x) - (in.y*in.y) - (in.z*in.z);
+
+ if (t < 0.0f)
+ out.w = 0.0f;
+ else out.w = sqrt (t);
+}
+
+// ---------------------------------------------------------------------------
+/** Parses the data sections of a MD5 mesh file
+*/
+class MD5MeshParser
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** Constructs a new MD5MeshParser instance from an existing
+ * preparsed list of file sections.
+ *
+ * @param mSections List of file sections (output of MD5Parser)
+ */
+ MD5MeshParser(SectionList& mSections);
+
+ //! List of all meshes
+ MeshList mMeshes;
+
+ //! List of all joints
+ BoneList mJoints;
+};
+
+// remove this flag if you need to the bounding box data
+#define AI_MD5_PARSE_NO_BOUNDS
+
+// ---------------------------------------------------------------------------
+/** Parses the data sections of a MD5 animation file
+*/
+class MD5AnimParser
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** Constructs a new MD5AnimParser instance from an existing
+ * preparsed list of file sections.
+ *
+ * @param mSections List of file sections (output of MD5Parser)
+ */
+ MD5AnimParser(SectionList& mSections);
+
+
+ //! Output frame rate
+ float fFrameRate;
+
+ //! List of animation bones
+ AnimBoneList mAnimatedBones;
+
+ //! List of base frames
+ BaseFrameList mBaseFrames;
+
+ //! List of animation frames
+ FrameList mFrames;
+
+ //! Number of animated components
+ unsigned int mNumAnimatedComponents;
+};
+
+// ---------------------------------------------------------------------------
+/** Parses the data sections of a MD5 camera animation file
+*/
+class MD5CameraParser
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** Constructs a new MD5CameraParser instance from an existing
+ * preparsed list of file sections.
+ *
+ * @param mSections List of file sections (output of MD5Parser)
+ */
+ MD5CameraParser(SectionList& mSections);
+
+
+ //! Output frame rate
+ float fFrameRate;
+
+ //! List of cuts
+ std::vector<unsigned int> cuts;
+
+ //! Frames
+ CameraFrameList frames;
+};
+
+// ---------------------------------------------------------------------------
+/** Parses the block structure of MD5MESH and MD5ANIM files (but does no
+ * further processing)
+*/
+class MD5Parser
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** Constructs a new MD5Parser instance from an existing buffer.
+ *
+ * @param buffer File buffer
+ * @param fileSize Length of the file in bytes (excluding a terminal 0)
+ */
+ MD5Parser(char* buffer, unsigned int fileSize);
+
+
+ // -------------------------------------------------------------------
+ /** Report a specific error message and throw an exception
+ * @param error Error message to be reported
+ * @param line Index of the line where the error occured
+ */
+ static void ReportError (const char* error, unsigned int line);
+
+ // -------------------------------------------------------------------
+ /** Report a specific warning
+ * @param warn Warn message to be reported
+ * @param line Index of the line where the error occured
+ */
+ static void ReportWarning (const char* warn, unsigned int line);
+
+
+ void ReportError (const char* error) {
+ return ReportError(error, lineNumber);
+ }
+
+ void ReportWarning (const char* warn) {
+ return ReportWarning(warn, lineNumber);
+ }
+
+public:
+
+ //! List of all sections which have been read
+ SectionList mSections;
+
+private:
+
+ // -------------------------------------------------------------------
+ /** Parses a file section. The current file pointer must be outside
+ * of a section.
+ * @param out Receives the section data
+ * @return true if the end of the file has been reached
+ * @throws ImportErrorException if an error occurs
+ */
+ bool ParseSection(Section& out);
+
+ // -------------------------------------------------------------------
+ /** Parses the file header
+ * @throws ImportErrorException if an error occurs
+ */
+ void ParseHeader();
+
+
+ // override these functions to make sure the line counter gets incremented
+ // -------------------------------------------------------------------
+ bool SkipLine( const char* in, const char** out)
+ {
+ ++lineNumber;
+ return Assimp::SkipLine(in,out);
+ }
+ // -------------------------------------------------------------------
+ bool SkipLine( )
+ {
+ return SkipLine(buffer,(const char**)&buffer);
+ }
+ // -------------------------------------------------------------------
+ bool SkipSpacesAndLineEnd( const char* in, const char** out)
+ {
+ bool bHad = false;
+ bool running = true;
+ while (running) {
+ if ( *in == '\r' || *in == '\n') {
+ // we open files in binary mode, so there could be \r\n sequences ...
+ if (!bHad) {
+ bHad = true;
+ ++lineNumber;
+ }
+ }
+ else if (*in == '\t' || *in == ' ')bHad = false;
+ else break;
+ in++;
+ }
+ *out = in;
+ return *in != '\0';
+ }
+ // -------------------------------------------------------------------
+ bool SkipSpacesAndLineEnd( )
+ {
+ return SkipSpacesAndLineEnd(buffer,(const char**)&buffer);
+ }
+ // -------------------------------------------------------------------
+ bool SkipSpaces( )
+ {
+ return Assimp::SkipSpaces((const char**)&buffer);
+ }
+
+ char* buffer;
+ unsigned int fileSize;
+ unsigned int lineNumber;
+};
+}}
+
+#endif // AI_MD5PARSER_H_INCLUDED
diff --git a/src/3rdparty/assimp/code/MDCFileData.h b/src/3rdparty/assimp/code/MDCFileData.h
new file mode 100644
index 000000000..6c33880c6
--- /dev/null
+++ b/src/3rdparty/assimp/code/MDCFileData.h
@@ -0,0 +1,199 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Defines the helper data structures for importing MDC files
+
+**********************************************************************
+File format specification:
+http://themdcfile.planetwolfenstein.gamespy.com/MDC_File_Format.pdf
+**********************************************************************
+
+*/
+#ifndef AI_MDCFILEHELPER_H_INC
+#define AI_MDCFILEHELPER_H_INC
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+#include "./../include/Compiler/pushpack1.h"
+
+
+namespace Assimp {
+namespace MDC {
+
+
+// to make it easier for us, we test the magic word against both "endianesses"
+#define AI_MDC_MAGIC_NUMBER_BE AI_MAKE_MAGIC("CPDI")
+#define AI_MDC_MAGIC_NUMBER_LE AI_MAKE_MAGIC("IDPC")
+
+// common limitations
+#define AI_MDC_VERSION 2
+#define AI_MDC_MAXQPATH 64
+#define AI_MDC_MAX_BONES 128
+
+#define AI_MDC_CVERT_BIAS 127.0f
+#define AI_MDC_DELTA_SCALING 4.0f
+#define AI_MDC_BASE_SCALING (1.0f / 64.0f)
+
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC file's main header
+ */
+struct Header
+{
+ uint32_t ulIdent ;
+ uint32_t ulVersion ;
+ char ucName [ AI_MDC_MAXQPATH ] ;
+ uint32_t ulFlags ;
+ uint32_t ulNumFrames ;
+ uint32_t ulNumTags ;
+ uint32_t ulNumSurfaces ;
+ uint32_t ulNumSkins ;
+ uint32_t ulOffsetBorderFrames ;
+ uint32_t ulOffsetTagNames ;
+ uint32_t ulOffsetTagFrames ;
+ uint32_t ulOffsetSurfaces ;
+ uint32_t ulOffsetEnd ;
+} PACK_STRUCT ;
+
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC file's surface header
+ */
+struct Surface
+{
+ uint32_t ulIdent ;
+ char ucName [ AI_MDC_MAXQPATH ] ;
+ uint32_t ulFlags ;
+ uint32_t ulNumCompFrames ;
+ uint32_t ulNumBaseFrames ;
+ uint32_t ulNumShaders ;
+ uint32_t ulNumVertices ;
+ uint32_t ulNumTriangles ;
+ uint32_t ulOffsetTriangles ;
+ uint32_t ulOffsetShaders ;
+ uint32_t ulOffsetTexCoords ;
+ uint32_t ulOffsetBaseVerts ;
+ uint32_t ulOffsetCompVerts ;
+ uint32_t ulOffsetFrameBaseFrames ;
+ uint32_t ulOffsetFrameCompFrames ;
+ uint32_t ulOffsetEnd ;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC frame
+ */
+struct Frame
+{
+ //! bounding box minimum coords
+ aiVector3D bboxMin ;
+
+ //! bounding box maximum coords
+ aiVector3D bboxMax ;
+
+ //! local origin of the frame
+ aiVector3D localOrigin ;
+
+ //! radius of the BB
+ float radius ;
+
+ //! Name of the frame
+ char name [ 16 ] ;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC triangle
+ */
+struct Triangle
+{
+ uint32_t aiIndices[3];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC texture coordinate
+ */
+struct TexturCoord
+{
+ float u,v;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC base vertex
+ */
+struct BaseVertex
+{
+ int16_t x,y,z;
+ uint16_t normal;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC compressed vertex
+ */
+struct CompressedVertex
+{
+ uint8_t xd,yd,zd,nd;
+} PACK_STRUCT;
+
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC shader
+ */
+struct Shader
+{
+ char ucName [ AI_MDC_MAXQPATH ] ;
+ uint32_t ulPath;
+
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+
+// ---------------------------------------------------------------------------
+/** Build a floating point vertex from the compressed data in MDC files
+ */
+void BuildVertex(const Frame& frame,
+ const BaseVertex& bvert,
+ const CompressedVertex& cvert,
+ aiVector3D& vXYZOut,
+ aiVector3D& vNorOut);
+}}
+
+#endif // !! AI_MDCFILEHELPER_H_INC
diff --git a/src/3rdparty/assimp/code/MDCLoader.cpp b/src/3rdparty/assimp/code/MDCLoader.cpp
new file mode 100644
index 000000000..9ab3e52ec
--- /dev/null
+++ b/src/3rdparty/assimp/code/MDCLoader.cpp
@@ -0,0 +1,472 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the MDC importer class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER
+
+// internal headers
+#include "MDCLoader.h"
+#include "MD3FileData.h"
+#include "MDCNormalTable.h" // shouldn't be included by other units
+
+using namespace Assimp;
+using namespace Assimp::MDC;
+
+
+// ------------------------------------------------------------------------------------------------
+void MDC::BuildVertex(const Frame& frame,
+ const BaseVertex& bvert,
+ const CompressedVertex& cvert,
+ aiVector3D& vXYZOut,
+ aiVector3D& vNorOut)
+{
+ // compute the position
+ const float xd = (cvert.xd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING;
+ const float yd = (cvert.yd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING;
+ const float zd = (cvert.zd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING;
+ vXYZOut.x = frame.localOrigin.x + AI_MDC_BASE_SCALING * (bvert.x + xd);
+ vXYZOut.y = frame.localOrigin.y + AI_MDC_BASE_SCALING * (bvert.y + yd);
+ vXYZOut.z = frame.localOrigin.z + AI_MDC_BASE_SCALING * (bvert.z + zd);
+
+ // compute the normal vector .. ehm ... lookup it in the table :-)
+ vNorOut.x = mdcNormals[cvert.nd][0];
+ vNorOut.y = mdcNormals[cvert.nd][1];
+ vNorOut.z = mdcNormals[cvert.nd][2];
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MDCImporter::MDCImporter() : configFrameID(0), pcHeader(0), mBuffer(0), fileSize(0)
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MDCImporter::~MDCImporter()
+{
+}
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool MDCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+ if (extension == "mdc")
+ return true;
+
+ // if check for extension is not enough, check for the magic tokens
+ if (!extension.length() || checkSig) {
+ uint32_t tokens[1];
+ tokens[0] = AI_MDC_MAGIC_NUMBER_LE;
+ return CheckMagicToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MDCImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("mdc");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Validate the header of the given MDC file
+void MDCImporter::ValidateHeader()
+{
+ AI_SWAP4( this->pcHeader->ulVersion );
+ AI_SWAP4( this->pcHeader->ulFlags );
+ AI_SWAP4( this->pcHeader->ulNumFrames );
+ AI_SWAP4( this->pcHeader->ulNumTags );
+ AI_SWAP4( this->pcHeader->ulNumSurfaces );
+ AI_SWAP4( this->pcHeader->ulNumSkins );
+ AI_SWAP4( this->pcHeader->ulOffsetBorderFrames );
+
+ if (pcHeader->ulIdent != AI_MDC_MAGIC_NUMBER_BE &&
+ pcHeader->ulIdent != AI_MDC_MAGIC_NUMBER_LE)
+ {
+ char szBuffer[5];
+ szBuffer[0] = ((char*)&pcHeader->ulIdent)[0];
+ szBuffer[1] = ((char*)&pcHeader->ulIdent)[1];
+ szBuffer[2] = ((char*)&pcHeader->ulIdent)[2];
+ szBuffer[3] = ((char*)&pcHeader->ulIdent)[3];
+ szBuffer[4] = '\0';
+
+ throw DeadlyImportError("Invalid MDC magic word: should be IDPC, the "
+ "magic word found is " + std::string( szBuffer ));
+ }
+
+ if (pcHeader->ulVersion != AI_MDC_VERSION)
+ DefaultLogger::get()->warn("Unsupported MDC file version (2 (AI_MDC_VERSION) was expected)");
+
+ if (pcHeader->ulOffsetBorderFrames + pcHeader->ulNumFrames * sizeof(MDC::Frame) > this->fileSize ||
+ pcHeader->ulOffsetSurfaces + pcHeader->ulNumSurfaces * sizeof(MDC::Surface) > this->fileSize)
+ {
+ throw DeadlyImportError("Some of the offset values in the MDC header are invalid "
+ "and point to something behind the file.");
+ }
+
+ if (this->configFrameID >= this->pcHeader->ulNumFrames)
+ throw DeadlyImportError("The requested frame is not available");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Validate the header of a given MDC file surface
+void MDCImporter::ValidateSurfaceHeader(BE_NCONST MDC::Surface* pcSurf)
+{
+ AI_SWAP4(pcSurf->ulFlags);
+ AI_SWAP4(pcSurf->ulNumCompFrames);
+ AI_SWAP4(pcSurf->ulNumBaseFrames);
+ AI_SWAP4(pcSurf->ulNumShaders);
+ AI_SWAP4(pcSurf->ulNumVertices);
+ AI_SWAP4(pcSurf->ulNumTriangles);
+ AI_SWAP4(pcSurf->ulOffsetTriangles);
+ AI_SWAP4(pcSurf->ulOffsetTexCoords);
+ AI_SWAP4(pcSurf->ulOffsetBaseVerts);
+ AI_SWAP4(pcSurf->ulOffsetCompVerts);
+ AI_SWAP4(pcSurf->ulOffsetFrameBaseFrames);
+ AI_SWAP4(pcSurf->ulOffsetFrameCompFrames);
+ AI_SWAP4(pcSurf->ulOffsetEnd);
+
+ const unsigned int iMax = this->fileSize - (unsigned int)((int8_t*)pcSurf-(int8_t*)pcHeader);
+
+ if (pcSurf->ulOffsetBaseVerts + pcSurf->ulNumVertices * sizeof(MDC::BaseVertex) > iMax ||
+ (pcSurf->ulNumCompFrames && pcSurf->ulOffsetCompVerts + pcSurf->ulNumVertices * sizeof(MDC::CompressedVertex) > iMax) ||
+ pcSurf->ulOffsetTriangles + pcSurf->ulNumTriangles * sizeof(MDC::Triangle) > iMax ||
+ pcSurf->ulOffsetTexCoords + pcSurf->ulNumVertices * sizeof(MDC::TexturCoord) > iMax ||
+ pcSurf->ulOffsetShaders + pcSurf->ulNumShaders * sizeof(MDC::Shader) > iMax ||
+ pcSurf->ulOffsetFrameBaseFrames + pcSurf->ulNumBaseFrames * 2 > iMax ||
+ (pcSurf->ulNumCompFrames && pcSurf->ulOffsetFrameCompFrames + pcSurf->ulNumCompFrames * 2 > iMax))
+ {
+ throw DeadlyImportError("Some of the offset values in the MDC surface header "
+ "are invalid and point somewhere behind the file.");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void MDCImporter::SetupProperties(const Importer* pImp)
+{
+ // The AI_CONFIG_IMPORT_MDC_KEYFRAME option overrides the
+ // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
+ if (0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
+ AI_CONFIG_IMPORT_MDC_KEYFRAME,0xffffffff)))
+ {
+ this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void MDCImporter::InternReadFile(
+ const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open MDC file " + pFile + ".");
+
+ // check whether the mdc file is large enough to contain the file header
+ fileSize = (unsigned int)file->FileSize();
+ if ( fileSize < sizeof(MDC::Header))
+ throw DeadlyImportError( "MDC File is too small.");
+
+ std::vector<unsigned char> mBuffer2(fileSize);
+ file->Read( &mBuffer2[0], 1, fileSize);
+ mBuffer = &mBuffer2[0];
+
+ // validate the file header
+ this->pcHeader = (BE_NCONST MDC::Header*)this->mBuffer;
+ this->ValidateHeader();
+
+ std::vector<std::string> aszShaders;
+
+ // get a pointer to the frame we want to read
+ BE_NCONST MDC::Frame* pcFrame = (BE_NCONST MDC::Frame*)(this->mBuffer+
+ this->pcHeader->ulOffsetBorderFrames);
+
+ // no need to swap the other members, we won't need them
+ pcFrame += configFrameID;
+ AI_SWAP4( pcFrame->localOrigin[0] );
+ AI_SWAP4( pcFrame->localOrigin[1] );
+ AI_SWAP4( pcFrame->localOrigin[2] );
+
+ // get the number of valid surfaces
+ BE_NCONST MDC::Surface* pcSurface, *pcSurface2;
+ pcSurface = pcSurface2 = (BE_NCONST MDC::Surface*)(mBuffer + pcHeader->ulOffsetSurfaces);
+ unsigned int iNumShaders = 0;
+ for (unsigned int i = 0; i < pcHeader->ulNumSurfaces;++i)
+ {
+ // validate the surface header
+ this->ValidateSurfaceHeader(pcSurface2);
+
+ if (pcSurface2->ulNumVertices && pcSurface2->ulNumTriangles)++pScene->mNumMeshes;
+ iNumShaders += pcSurface2->ulNumShaders;
+ pcSurface2 = (BE_NCONST MDC::Surface*)((int8_t*)pcSurface2 + pcSurface2->ulOffsetEnd);
+ }
+ aszShaders.reserve(iNumShaders);
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+
+ // necessary that we don't crash if an exception occurs
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mMeshes[i] = NULL;
+
+ // now read all surfaces
+ unsigned int iDefaultMatIndex = 0xffffffff;
+ for (unsigned int i = 0, iNum = 0; i < pcHeader->ulNumSurfaces;++i)
+ {
+ if (!pcSurface->ulNumVertices || !pcSurface->ulNumTriangles)continue;
+ aiMesh* pcMesh = pScene->mMeshes[iNum++] = new aiMesh();
+
+ pcMesh->mNumFaces = pcSurface->ulNumTriangles;
+ pcMesh->mNumVertices = pcMesh->mNumFaces * 3;
+
+ // store the name of the surface for use as node name.
+ // FIX: make sure there is a 0 termination
+ const_cast<char&>(pcSurface->ucName[AI_MDC_MAXQPATH-1]) = '\0';
+ pcMesh->mTextureCoords[3] = (aiVector3D*)pcSurface->ucName;
+
+ // go to the first shader in the file. ignore the others.
+ if (pcSurface->ulNumShaders)
+ {
+ const MDC::Shader* pcShader = (const MDC::Shader*)((int8_t*)pcSurface + pcSurface->ulOffsetShaders);
+ pcMesh->mMaterialIndex = (unsigned int)aszShaders.size();
+
+ // create a new shader
+ aszShaders.push_back(std::string( pcShader->ucName, std::min(
+ ::strlen(pcShader->ucName),sizeof(pcShader->ucName)) ));
+ }
+ // need to create a default material
+ else if (0xffffffff == iDefaultMatIndex)
+ {
+ pcMesh->mMaterialIndex = iDefaultMatIndex = (unsigned int)aszShaders.size();
+ aszShaders.push_back(std::string());
+ }
+ // otherwise assign a reference to the default material
+ else pcMesh->mMaterialIndex = iDefaultMatIndex;
+
+ // allocate output storage for the mesh
+ aiVector3D* pcVertCur = pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+ aiVector3D* pcNorCur = pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+ aiVector3D* pcUVCur = pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ aiFace* pcFaceCur = pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+
+ // create all vertices/faces
+ BE_NCONST MDC::Triangle* pcTriangle = (BE_NCONST MDC::Triangle*)
+ ((int8_t*)pcSurface+pcSurface->ulOffsetTriangles);
+
+ BE_NCONST MDC::TexturCoord* const pcUVs = (BE_NCONST MDC::TexturCoord*)
+ ((int8_t*)pcSurface+pcSurface->ulOffsetTexCoords);
+
+ // get a pointer to the uncompressed vertices
+ int16_t iOfs = *((int16_t*) ((int8_t*) pcSurface +
+ pcSurface->ulOffsetFrameBaseFrames) + this->configFrameID);
+
+ AI_SWAP2(iOfs);
+
+ BE_NCONST MDC::BaseVertex* const pcVerts = (BE_NCONST MDC::BaseVertex*)
+ ((int8_t*)pcSurface+pcSurface->ulOffsetBaseVerts) +
+ ((int)iOfs * pcSurface->ulNumVertices * 4);
+
+ // do the main swapping stuff ...
+#if (defined AI_BUILD_BIG_ENDIAN)
+
+ // swap all triangles
+ for (unsigned int i = 0; i < pcSurface->ulNumTriangles;++i)
+ {
+ AI_SWAP4( pcTriangle[i].aiIndices[0] );
+ AI_SWAP4( pcTriangle[i].aiIndices[1] );
+ AI_SWAP4( pcTriangle[i].aiIndices[2] );
+ }
+
+ // swap all vertices
+ for (unsigned int i = 0; i < pcSurface->ulNumVertices*pcSurface->ulNumBaseFrames;++i)
+ {
+ AI_SWAP2( pcVerts->normal );
+ AI_SWAP2( pcVerts->x );
+ AI_SWAP2( pcVerts->y );
+ AI_SWAP2( pcVerts->z );
+ }
+
+ // swap all texture coordinates
+ for (unsigned int i = 0; i < pcSurface->ulNumVertices;++i)
+ {
+ AI_SWAP4( pcUVs->v );
+ AI_SWAP4( pcUVs->v );
+ }
+
+#endif
+
+ const MDC::CompressedVertex* pcCVerts = NULL;
+ int16_t* mdcCompVert = NULL;
+
+ // access compressed frames for large frame numbers, but never for the first
+ if ( this->configFrameID && pcSurface->ulNumCompFrames > 0 )
+ {
+ mdcCompVert = (int16_t*) ((int8_t*)pcSurface+pcSurface->ulOffsetFrameCompFrames) + this->configFrameID;
+ AI_SWAP2P(mdcCompVert);
+ if ( *mdcCompVert >= 0 )
+ {
+ pcCVerts = (const MDC::CompressedVertex*)((int8_t*)pcSurface +
+ pcSurface->ulOffsetCompVerts) + *mdcCompVert * pcSurface->ulNumVertices;
+ }
+ else mdcCompVert = NULL;
+ }
+
+ // copy all faces
+ for (unsigned int iFace = 0; iFace < pcSurface->ulNumTriangles;++iFace,
+ ++pcTriangle,++pcFaceCur)
+ {
+ const unsigned int iOutIndex = iFace*3;
+ pcFaceCur->mNumIndices = 3;
+ pcFaceCur->mIndices = new unsigned int[3];
+
+ for (unsigned int iIndex = 0; iIndex < 3;++iIndex,
+ ++pcVertCur,++pcUVCur,++pcNorCur)
+ {
+ uint32_t quak = pcTriangle->aiIndices[iIndex];
+ if (quak >= pcSurface->ulNumVertices)
+ {
+ DefaultLogger::get()->error("MDC vertex index is out of range");
+ quak = pcSurface->ulNumVertices-1;
+ }
+
+ // compressed vertices?
+ if (mdcCompVert)
+ {
+ MDC::BuildVertex(*pcFrame,pcVerts[quak],pcCVerts[quak],
+ *pcVertCur,*pcNorCur);
+ }
+ else
+ {
+ // copy position
+ pcVertCur->x = pcVerts[quak].x * AI_MDC_BASE_SCALING;
+ pcVertCur->y = pcVerts[quak].y * AI_MDC_BASE_SCALING;
+ pcVertCur->z = pcVerts[quak].z * AI_MDC_BASE_SCALING;
+
+ // copy normals
+ MD3::LatLngNormalToVec3( pcVerts[quak].normal, &pcNorCur->x );
+
+ // copy texture coordinates
+ pcUVCur->x = pcUVs[quak].u;
+ pcUVCur->y = 1.0f-pcUVs[quak].v; // DX to OGL
+ }
+ pcVertCur->x += pcFrame->localOrigin[0] ;
+ pcVertCur->y += pcFrame->localOrigin[1] ;
+ pcVertCur->z += pcFrame->localOrigin[2] ;
+ }
+
+ // swap the face order - DX to OGL
+ pcFaceCur->mIndices[0] = iOutIndex + 2;
+ pcFaceCur->mIndices[1] = iOutIndex + 1;
+ pcFaceCur->mIndices[2] = iOutIndex + 0;
+ }
+
+ pcSurface = (BE_NCONST MDC::Surface*)((int8_t*)pcSurface + pcSurface->ulOffsetEnd);
+ }
+
+ // create a flat node graph with a root node and one child for each surface
+ if (!pScene->mNumMeshes)
+ throw DeadlyImportError( "Invalid MDC file: File contains no valid mesh");
+ else if (1 == pScene->mNumMeshes)
+ {
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set(std::string((const char*)pScene->mMeshes[0]->mTextureCoords[3]));
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+ }
+ else
+ {
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mNumChildren = pScene->mNumMeshes;
+ pScene->mRootNode->mChildren = new aiNode*[pScene->mNumMeshes];
+ pScene->mRootNode->mName.Set("<root>");
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ {
+ aiNode* pcNode = pScene->mRootNode->mChildren[i] = new aiNode();
+ pcNode->mParent = pScene->mRootNode;
+ pcNode->mName.Set(std::string((const char*)pScene->mMeshes[i]->mTextureCoords[3]));
+ pcNode->mNumMeshes = 1;
+ pcNode->mMeshes = new unsigned int[1];
+ pcNode->mMeshes[0] = i;
+ }
+ }
+
+ // make sure we invalidate the pointer to the mesh name
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mMeshes[i]->mTextureCoords[3] = NULL;
+
+ // create materials
+ pScene->mNumMaterials = (unsigned int)aszShaders.size();
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+ {
+ MaterialHelper* pcMat = new MaterialHelper();
+ pScene->mMaterials[i] = pcMat;
+
+ const std::string& name = aszShaders[i];
+
+ int iMode = (int)aiShadingMode_Gouraud;
+ pcMat->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ // add a small ambient color value - RtCW seems to have one
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.05f;
+ pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ if (name.length())clr.b = clr.g = clr.r = 1.0f;
+ else clr.b = clr.g = clr.r = 0.6f;
+
+ pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ if (name.length())
+ {
+ aiString path;
+ path.Set(name);
+ pcMat->AddProperty(&path,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_MDC_IMPORTER
diff --git a/src/3rdparty/assimp/code/MDCLoader.h b/src/3rdparty/assimp/code/MDCLoader.h
new file mode 100644
index 000000000..f97efc73e
--- /dev/null
+++ b/src/3rdparty/assimp/code/MDCLoader.h
@@ -0,0 +1,132 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file MDCLoader.h
+ * @brief Definition of the MDC importer class.
+ */
+#ifndef AI_MDCLOADER_H_INCLUDED
+#define AI_MDCLOADER_H_INCLUDED
+
+#include "../include/aiTypes.h"
+
+#include "BaseImporter.h"
+#include "MDCFileData.h"
+#include "ByteSwap.h"
+
+namespace Assimp {
+using namespace MDC;
+
+// ---------------------------------------------------------------------------
+/** Importer class to load the RtCW MDC file format
+*/
+class MDCImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ MDCImporter();
+
+ /** Destructor, private as well */
+ ~MDCImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details. */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Validate the header of the file
+ */
+ void ValidateHeader();
+
+ // -------------------------------------------------------------------
+ /** Validate the header of a MDC surface
+ */
+ void ValidateSurfaceHeader(BE_NCONST MDC::Surface* pcSurf);
+
+protected:
+
+
+ /** Configuration option: frame to be loaded */
+ unsigned int configFrameID;
+
+ /** Header of the MDC file */
+ BE_NCONST MDC::Header* pcHeader;
+
+ /** Buffer to hold the loaded file */
+ unsigned char* mBuffer;
+
+ /** size of the file, in bytes */
+ unsigned int fileSize;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
+
diff --git a/src/3rdparty/assimp/code/MDCNormalTable.h b/src/3rdparty/assimp/code/MDCNormalTable.h
new file mode 100644
index 000000000..f47e97f33
--- /dev/null
+++ b/src/3rdparty/assimp/code/MDCNormalTable.h
@@ -0,0 +1,299 @@
+/* -----------------------------------------------------------------------------
+
+PicoModel Library
+
+Copyright (c) 2002, Randy Reddig & seaw0lf
+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 names of the copyright holders 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.
+
+----------------------------------------------------------------------------- */
+
+#if (!defined MDC_NORMAL_TABLE_INCLUDED)
+#define MDC_NORMAL_TABLE_INCLUDED
+
+/* mdc decoding normal table */
+float mdcNormals[ 256 ][ 3 ] =
+{
+ { 1.000000f, 0.000000f, 0.000000f },
+ { 0.980785f, 0.195090f, 0.000000f },
+ { 0.923880f, 0.382683f, 0.000000f },
+ { 0.831470f, 0.555570f, 0.000000f },
+ { 0.707107f, 0.707107f, 0.000000f },
+ { 0.555570f, 0.831470f, 0.000000f },
+ { 0.382683f, 0.923880f, 0.000000f },
+ { 0.195090f, 0.980785f, 0.000000f },
+ { -0.000000f, 1.000000f, 0.000000f },
+ { -0.195090f, 0.980785f, 0.000000f },
+ { -0.382683f, 0.923880f, 0.000000f },
+ { -0.555570f, 0.831470f, 0.000000f },
+ { -0.707107f, 0.707107f, 0.000000f },
+ { -0.831470f, 0.555570f, 0.000000f },
+ { -0.923880f, 0.382683f, 0.000000f },
+ { -0.980785f, 0.195090f, 0.000000f },
+ { -1.000000f, -0.000000f, 0.000000f },
+ { -0.980785f, -0.195090f, 0.000000f },
+ { -0.923880f, -0.382683f, 0.000000f },
+ { -0.831470f, -0.555570f, 0.000000f },
+ { -0.707107f, -0.707107f, 0.000000f },
+ { -0.555570f, -0.831469f, 0.000000f },
+ { -0.382684f, -0.923880f, 0.000000f },
+ { -0.195090f, -0.980785f, 0.000000f },
+ { 0.000000f, -1.000000f, 0.000000f },
+ { 0.195090f, -0.980785f, 0.000000f },
+ { 0.382684f, -0.923879f, 0.000000f },
+ { 0.555570f, -0.831470f, 0.000000f },
+ { 0.707107f, -0.707107f, 0.000000f },
+ { 0.831470f, -0.555570f, 0.000000f },
+ { 0.923880f, -0.382683f, 0.000000f },
+ { 0.980785f, -0.195090f, 0.000000f },
+ { 0.980785f, 0.000000f, -0.195090f },
+ { 0.956195f, 0.218245f, -0.195090f },
+ { 0.883657f, 0.425547f, -0.195090f },
+ { 0.766809f, 0.611510f, -0.195090f },
+ { 0.611510f, 0.766809f, -0.195090f },
+ { 0.425547f, 0.883657f, -0.195090f },
+ { 0.218245f, 0.956195f, -0.195090f },
+ { -0.000000f, 0.980785f, -0.195090f },
+ { -0.218245f, 0.956195f, -0.195090f },
+ { -0.425547f, 0.883657f, -0.195090f },
+ { -0.611510f, 0.766809f, -0.195090f },
+ { -0.766809f, 0.611510f, -0.195090f },
+ { -0.883657f, 0.425547f, -0.195090f },
+ { -0.956195f, 0.218245f, -0.195090f },
+ { -0.980785f, -0.000000f, -0.195090f },
+ { -0.956195f, -0.218245f, -0.195090f },
+ { -0.883657f, -0.425547f, -0.195090f },
+ { -0.766809f, -0.611510f, -0.195090f },
+ { -0.611510f, -0.766809f, -0.195090f },
+ { -0.425547f, -0.883657f, -0.195090f },
+ { -0.218245f, -0.956195f, -0.195090f },
+ { 0.000000f, -0.980785f, -0.195090f },
+ { 0.218245f, -0.956195f, -0.195090f },
+ { 0.425547f, -0.883657f, -0.195090f },
+ { 0.611510f, -0.766809f, -0.195090f },
+ { 0.766809f, -0.611510f, -0.195090f },
+ { 0.883657f, -0.425547f, -0.195090f },
+ { 0.956195f, -0.218245f, -0.195090f },
+ { 0.923880f, 0.000000f, -0.382683f },
+ { 0.892399f, 0.239118f, -0.382683f },
+ { 0.800103f, 0.461940f, -0.382683f },
+ { 0.653281f, 0.653281f, -0.382683f },
+ { 0.461940f, 0.800103f, -0.382683f },
+ { 0.239118f, 0.892399f, -0.382683f },
+ { -0.000000f, 0.923880f, -0.382683f },
+ { -0.239118f, 0.892399f, -0.382683f },
+ { -0.461940f, 0.800103f, -0.382683f },
+ { -0.653281f, 0.653281f, -0.382683f },
+ { -0.800103f, 0.461940f, -0.382683f },
+ { -0.892399f, 0.239118f, -0.382683f },
+ { -0.923880f, -0.000000f, -0.382683f },
+ { -0.892399f, -0.239118f, -0.382683f },
+ { -0.800103f, -0.461940f, -0.382683f },
+ { -0.653282f, -0.653281f, -0.382683f },
+ { -0.461940f, -0.800103f, -0.382683f },
+ { -0.239118f, -0.892399f, -0.382683f },
+ { 0.000000f, -0.923880f, -0.382683f },
+ { 0.239118f, -0.892399f, -0.382683f },
+ { 0.461940f, -0.800103f, -0.382683f },
+ { 0.653281f, -0.653282f, -0.382683f },
+ { 0.800103f, -0.461940f, -0.382683f },
+ { 0.892399f, -0.239117f, -0.382683f },
+ { 0.831470f, 0.000000f, -0.555570f },
+ { 0.790775f, 0.256938f, -0.555570f },
+ { 0.672673f, 0.488726f, -0.555570f },
+ { 0.488726f, 0.672673f, -0.555570f },
+ { 0.256938f, 0.790775f, -0.555570f },
+ { -0.000000f, 0.831470f, -0.555570f },
+ { -0.256938f, 0.790775f, -0.555570f },
+ { -0.488726f, 0.672673f, -0.555570f },
+ { -0.672673f, 0.488726f, -0.555570f },
+ { -0.790775f, 0.256938f, -0.555570f },
+ { -0.831470f, -0.000000f, -0.555570f },
+ { -0.790775f, -0.256938f, -0.555570f },
+ { -0.672673f, -0.488726f, -0.555570f },
+ { -0.488725f, -0.672673f, -0.555570f },
+ { -0.256938f, -0.790775f, -0.555570f },
+ { 0.000000f, -0.831470f, -0.555570f },
+ { 0.256938f, -0.790775f, -0.555570f },
+ { 0.488725f, -0.672673f, -0.555570f },
+ { 0.672673f, -0.488726f, -0.555570f },
+ { 0.790775f, -0.256938f, -0.555570f },
+ { 0.707107f, 0.000000f, -0.707107f },
+ { 0.653281f, 0.270598f, -0.707107f },
+ { 0.500000f, 0.500000f, -0.707107f },
+ { 0.270598f, 0.653281f, -0.707107f },
+ { -0.000000f, 0.707107f, -0.707107f },
+ { -0.270598f, 0.653282f, -0.707107f },
+ { -0.500000f, 0.500000f, -0.707107f },
+ { -0.653281f, 0.270598f, -0.707107f },
+ { -0.707107f, -0.000000f, -0.707107f },
+ { -0.653281f, -0.270598f, -0.707107f },
+ { -0.500000f, -0.500000f, -0.707107f },
+ { -0.270598f, -0.653281f, -0.707107f },
+ { 0.000000f, -0.707107f, -0.707107f },
+ { 0.270598f, -0.653281f, -0.707107f },
+ { 0.500000f, -0.500000f, -0.707107f },
+ { 0.653282f, -0.270598f, -0.707107f },
+ { 0.555570f, 0.000000f, -0.831470f },
+ { 0.481138f, 0.277785f, -0.831470f },
+ { 0.277785f, 0.481138f, -0.831470f },
+ { -0.000000f, 0.555570f, -0.831470f },
+ { -0.277785f, 0.481138f, -0.831470f },
+ { -0.481138f, 0.277785f, -0.831470f },
+ { -0.555570f, -0.000000f, -0.831470f },
+ { -0.481138f, -0.277785f, -0.831470f },
+ { -0.277785f, -0.481138f, -0.831470f },
+ { 0.000000f, -0.555570f, -0.831470f },
+ { 0.277785f, -0.481138f, -0.831470f },
+ { 0.481138f, -0.277785f, -0.831470f },
+ { 0.382683f, 0.000000f, -0.923880f },
+ { 0.270598f, 0.270598f, -0.923880f },
+ { -0.000000f, 0.382683f, -0.923880f },
+ { -0.270598f, 0.270598f, -0.923880f },
+ { -0.382683f, -0.000000f, -0.923880f },
+ { -0.270598f, -0.270598f, -0.923880f },
+ { 0.000000f, -0.382683f, -0.923880f },
+ { 0.270598f, -0.270598f, -0.923880f },
+ { 0.195090f, 0.000000f, -0.980785f },
+ { -0.000000f, 0.195090f, -0.980785f },
+ { -0.195090f, -0.000000f, -0.980785f },
+ { 0.000000f, -0.195090f, -0.980785f },
+ { 0.980785f, 0.000000f, 0.195090f },
+ { 0.956195f, 0.218245f, 0.195090f },
+ { 0.883657f, 0.425547f, 0.195090f },
+ { 0.766809f, 0.611510f, 0.195090f },
+ { 0.611510f, 0.766809f, 0.195090f },
+ { 0.425547f, 0.883657f, 0.195090f },
+ { 0.218245f, 0.956195f, 0.195090f },
+ { -0.000000f, 0.980785f, 0.195090f },
+ { -0.218245f, 0.956195f, 0.195090f },
+ { -0.425547f, 0.883657f, 0.195090f },
+ { -0.611510f, 0.766809f, 0.195090f },
+ { -0.766809f, 0.611510f, 0.195090f },
+ { -0.883657f, 0.425547f, 0.195090f },
+ { -0.956195f, 0.218245f, 0.195090f },
+ { -0.980785f, -0.000000f, 0.195090f },
+ { -0.956195f, -0.218245f, 0.195090f },
+ { -0.883657f, -0.425547f, 0.195090f },
+ { -0.766809f, -0.611510f, 0.195090f },
+ { -0.611510f, -0.766809f, 0.195090f },
+ { -0.425547f, -0.883657f, 0.195090f },
+ { -0.218245f, -0.956195f, 0.195090f },
+ { 0.000000f, -0.980785f, 0.195090f },
+ { 0.218245f, -0.956195f, 0.195090f },
+ { 0.425547f, -0.883657f, 0.195090f },
+ { 0.611510f, -0.766809f, 0.195090f },
+ { 0.766809f, -0.611510f, 0.195090f },
+ { 0.883657f, -0.425547f, 0.195090f },
+ { 0.956195f, -0.218245f, 0.195090f },
+ { 0.923880f, 0.000000f, 0.382683f },
+ { 0.892399f, 0.239118f, 0.382683f },
+ { 0.800103f, 0.461940f, 0.382683f },
+ { 0.653281f, 0.653281f, 0.382683f },
+ { 0.461940f, 0.800103f, 0.382683f },
+ { 0.239118f, 0.892399f, 0.382683f },
+ { -0.000000f, 0.923880f, 0.382683f },
+ { -0.239118f, 0.892399f, 0.382683f },
+ { -0.461940f, 0.800103f, 0.382683f },
+ { -0.653281f, 0.653281f, 0.382683f },
+ { -0.800103f, 0.461940f, 0.382683f },
+ { -0.892399f, 0.239118f, 0.382683f },
+ { -0.923880f, -0.000000f, 0.382683f },
+ { -0.892399f, -0.239118f, 0.382683f },
+ { -0.800103f, -0.461940f, 0.382683f },
+ { -0.653282f, -0.653281f, 0.382683f },
+ { -0.461940f, -0.800103f, 0.382683f },
+ { -0.239118f, -0.892399f, 0.382683f },
+ { 0.000000f, -0.923880f, 0.382683f },
+ { 0.239118f, -0.892399f, 0.382683f },
+ { 0.461940f, -0.800103f, 0.382683f },
+ { 0.653281f, -0.653282f, 0.382683f },
+ { 0.800103f, -0.461940f, 0.382683f },
+ { 0.892399f, -0.239117f, 0.382683f },
+ { 0.831470f, 0.000000f, 0.555570f },
+ { 0.790775f, 0.256938f, 0.555570f },
+ { 0.672673f, 0.488726f, 0.555570f },
+ { 0.488726f, 0.672673f, 0.555570f },
+ { 0.256938f, 0.790775f, 0.555570f },
+ { -0.000000f, 0.831470f, 0.555570f },
+ { -0.256938f, 0.790775f, 0.555570f },
+ { -0.488726f, 0.672673f, 0.555570f },
+ { -0.672673f, 0.488726f, 0.555570f },
+ { -0.790775f, 0.256938f, 0.555570f },
+ { -0.831470f, -0.000000f, 0.555570f },
+ { -0.790775f, -0.256938f, 0.555570f },
+ { -0.672673f, -0.488726f, 0.555570f },
+ { -0.488725f, -0.672673f, 0.555570f },
+ { -0.256938f, -0.790775f, 0.555570f },
+ { 0.000000f, -0.831470f, 0.555570f },
+ { 0.256938f, -0.790775f, 0.555570f },
+ { 0.488725f, -0.672673f, 0.555570f },
+ { 0.672673f, -0.488726f, 0.555570f },
+ { 0.790775f, -0.256938f, 0.555570f },
+ { 0.707107f, 0.000000f, 0.707107f },
+ { 0.653281f, 0.270598f, 0.707107f },
+ { 0.500000f, 0.500000f, 0.707107f },
+ { 0.270598f, 0.653281f, 0.707107f },
+ { -0.000000f, 0.707107f, 0.707107f },
+ { -0.270598f, 0.653282f, 0.707107f },
+ { -0.500000f, 0.500000f, 0.707107f },
+ { -0.653281f, 0.270598f, 0.707107f },
+ { -0.707107f, -0.000000f, 0.707107f },
+ { -0.653281f, -0.270598f, 0.707107f },
+ { -0.500000f, -0.500000f, 0.707107f },
+ { -0.270598f, -0.653281f, 0.707107f },
+ { 0.000000f, -0.707107f, 0.707107f },
+ { 0.270598f, -0.653281f, 0.707107f },
+ { 0.500000f, -0.500000f, 0.707107f },
+ { 0.653282f, -0.270598f, 0.707107f },
+ { 0.555570f, 0.000000f, 0.831470f },
+ { 0.481138f, 0.277785f, 0.831470f },
+ { 0.277785f, 0.481138f, 0.831470f },
+ { -0.000000f, 0.555570f, 0.831470f },
+ { -0.277785f, 0.481138f, 0.831470f },
+ { -0.481138f, 0.277785f, 0.831470f },
+ { -0.555570f, -0.000000f, 0.831470f },
+ { -0.481138f, -0.277785f, 0.831470f },
+ { -0.277785f, -0.481138f, 0.831470f },
+ { 0.000000f, -0.555570f, 0.831470f },
+ { 0.277785f, -0.481138f, 0.831470f },
+ { 0.481138f, -0.277785f, 0.831470f },
+ { 0.382683f, 0.000000f, 0.923880f },
+ { 0.270598f, 0.270598f, 0.923880f },
+ { -0.000000f, 0.382683f, 0.923880f },
+ { -0.270598f, 0.270598f, 0.923880f },
+ { -0.382683f, -0.000000f, 0.923880f },
+ { -0.270598f, -0.270598f, 0.923880f },
+ { 0.000000f, -0.382683f, 0.923880f },
+ { 0.270598f, -0.270598f, 0.923880f },
+ { 0.195090f, 0.000000f, 0.980785f },
+ { -0.000000f, 0.195090f, 0.980785f },
+ { -0.195090f, -0.000000f, 0.980785f },
+ { 0.000000f, -0.195090f, 0.980785f }
+};
+
+#endif // !! MDC_NORMAL_TABLE_INCLUDED
diff --git a/src/3rdparty/assimp/code/MDLDefaultColorMap.h b/src/3rdparty/assimp/code/MDLDefaultColorMap.h
new file mode 100644
index 000000000..a40da826c
--- /dev/null
+++ b/src/3rdparty/assimp/code/MDLDefaultColorMap.h
@@ -0,0 +1,118 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file Defines the default color map used for Quake 1 model textures
+ *
+ * The lib tries to load colormap.lmp from the model's directory.
+ * This table is only used when required.
+ */
+
+#ifndef AI_MDL_DEFAULTLMP_H_INC
+#define AI_MDL_DEFAULTLMP_H_INC
+
+const unsigned char g_aclrDefaultColorMap[256][3] = {
+{ 0, 0, 0}, { 15, 15, 15}, { 31, 31, 31}, { 47, 47, 47},
+{ 63, 63, 63}, { 75, 75, 75}, { 91, 91, 91}, {107, 107, 107},
+{123, 123, 123}, {139, 139, 139}, {155, 155, 155}, {171, 171, 171},
+{187, 187, 187}, {203, 203, 203}, {219, 219, 219}, {235, 235, 235},
+{ 15, 11, 7}, { 23, 15, 11}, { 31, 23, 11}, { 39, 27, 15},
+{ 47, 35, 19}, { 55, 43, 23}, { 63, 47, 23}, { 75, 55, 27},
+{ 83, 59, 27}, { 91, 67, 31}, { 99, 75, 31}, {107, 83, 31},
+{115, 87, 31}, {123, 95, 35}, {131, 103, 35}, {143, 111, 35},
+{ 11, 11, 15}, { 19, 19, 27}, { 27, 27, 39}, { 39, 39, 51},
+{ 47, 47, 63}, { 55, 55, 75}, { 63, 63, 87}, { 71, 71, 103},
+{ 79, 79, 115}, { 91, 91, 127}, { 99, 99, 139}, {107, 107, 151},
+{115, 115, 163}, {123, 123, 175}, {131, 131, 187}, {139, 139, 203},
+{ 0, 0, 0}, { 7, 7, 0}, { 11, 11, 0}, { 19, 19, 0},
+{ 27, 27, 0}, { 35, 35, 0}, { 43, 43, 7}, { 47, 47, 7},
+{ 55, 55, 7}, { 63, 63, 7}, { 71, 71, 7}, { 75, 75, 11},
+{ 83, 83, 11}, { 91, 91, 11}, { 99, 99, 11}, {107, 107, 15},
+{ 7, 0, 0}, { 15, 0, 0}, { 23, 0, 0}, { 31, 0, 0},
+{ 39, 0, 0}, { 47, 0, 0}, { 55, 0, 0}, { 63, 0, 0},
+{ 71, 0, 0}, { 79, 0, 0}, { 87, 0, 0}, { 95, 0, 0},
+{103, 0, 0}, {111, 0, 0}, {119, 0, 0}, {127, 0, 0},
+{ 19, 19, 0}, { 27, 27, 0}, { 35, 35, 0}, { 47, 43, 0},
+{ 55, 47, 0}, { 67, 55, 0}, { 75, 59, 7}, { 87, 67, 7},
+{ 95, 71, 7}, {107, 75, 11}, {119, 83, 15}, {131, 87, 19},
+{139, 91, 19}, {151, 95, 27}, {163, 99, 31}, {175, 103, 35},
+{ 35, 19, 7}, { 47, 23, 11}, { 59, 31, 15}, { 75, 35, 19},
+{ 87, 43, 23}, { 99, 47, 31}, {115, 55, 35}, {127, 59, 43},
+{143, 67, 51}, {159, 79, 51}, {175, 99, 47}, {191, 119, 47},
+{207, 143, 43}, {223, 171, 39}, {239, 203, 31}, {255, 243, 27},
+{ 11, 7, 0}, { 27, 19, 0}, { 43, 35, 15}, { 55, 43, 19},
+{ 71, 51, 27}, { 83, 55, 35}, { 99, 63, 43}, {111, 71, 51},
+{127, 83, 63}, {139, 95, 71}, {155, 107, 83}, {167, 123, 95},
+{183, 135, 107}, {195, 147, 123}, {211, 163, 139}, {227, 179, 151},
+{171, 139, 163}, {159, 127, 151}, {147, 115, 135}, {139, 103, 123},
+{127, 91, 111}, {119, 83, 99}, {107, 75, 87}, { 95, 63, 75},
+{ 87, 55, 67}, { 75, 47, 55}, { 67, 39, 47}, { 55, 31, 35},
+{ 43, 23, 27}, { 35, 19, 19}, { 23, 11, 11}, { 15, 7, 7},
+{187, 115, 159}, {175, 107, 143}, {163, 95, 131}, {151, 87, 119},
+{139, 79, 107}, {127, 75, 95}, {115, 67, 83}, {107, 59, 75},
+{ 95, 51, 63}, { 83, 43, 55}, { 71, 35, 43}, { 59, 31, 35},
+{ 47, 23, 27}, { 35, 19, 19}, { 23, 11, 11}, { 15, 7, 7},
+{219, 195, 187}, {203, 179, 167}, {191, 163, 155}, {175, 151, 139},
+{163, 135, 123}, {151, 123, 111}, {135, 111, 95}, {123, 99, 83},
+{107, 87, 71}, { 95, 75, 59}, { 83, 63, 51}, { 67, 51, 39},
+{ 55, 43, 31}, { 39, 31, 23}, { 27, 19, 15}, { 15, 11, 7},
+{111, 131, 123}, {103, 123, 111}, { 95, 115, 103}, { 87, 107, 95},
+{ 79, 99, 87}, { 71, 91, 79}, { 63, 83, 71}, { 55, 75, 63},
+{ 47, 67, 55}, { 43, 59, 47}, { 35, 51, 39}, { 31, 43, 31},
+{ 23, 35, 23}, { 15, 27, 19}, { 11, 19, 11}, { 7, 11, 7},
+{255, 243, 27}, {239, 223, 23}, {219, 203, 19}, {203, 183, 15},
+{187, 167, 15}, {171, 151, 11}, {155, 131, 7}, {139, 115, 7},
+{123, 99, 7}, {107, 83, 0}, { 91, 71, 0}, { 75, 55, 0},
+{ 59, 43, 0}, { 43, 31, 0}, { 27, 15, 0}, { 11, 7, 0},
+{ 0, 0, 255}, { 11, 11, 239}, { 19, 19, 223}, { 27, 27, 207},
+{ 35, 35, 191}, { 43, 43, 175}, { 47, 47, 159}, { 47, 47, 143},
+{ 47, 47, 127}, { 47, 47, 111}, { 47, 47, 95}, { 43, 43, 79},
+{ 35, 35, 63}, { 27, 27, 47}, { 19, 19, 31}, { 11, 11, 15},
+{ 43, 0, 0}, { 59, 0, 0}, { 75, 7, 0}, { 95, 7, 0},
+{111, 15, 0}, {127, 23, 7}, {147, 31, 7}, {163, 39, 11},
+{183, 51, 15}, {195, 75, 27}, {207, 99, 43}, {219, 127, 59},
+{227, 151, 79}, {231, 171, 95}, {239, 191, 119}, {247, 211, 139},
+{167, 123, 59}, {183, 155, 55}, {199, 195, 55}, {231, 227, 87},
+{127, 191, 255}, {171, 231, 255}, {215, 255, 255}, {103, 0, 0},
+{139, 0, 0}, {179, 0, 0}, {215, 0, 0}, {255, 0, 0},
+{255, 243, 147}, {255, 247, 199}, {255, 255, 255}, {159, 91, 83} };
+
+
+#endif // !! AI_MDL_DEFAULTLMP_H_INC
diff --git a/src/3rdparty/assimp/code/MDLFileData.h b/src/3rdparty/assimp/code/MDLFileData.h
new file mode 100644
index 000000000..68d3dda17
--- /dev/null
+++ b/src/3rdparty/assimp/code/MDLFileData.h
@@ -0,0 +1,962 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/**
+ * @file MDLFileData.h
+ * @brief Definition of in-memory structures for the MDL file format.
+ *
+ * The specification has been taken from various sources on the internet.
+ * - http://tfc.duke.free.fr/coding/mdl-specs-en.html
+ * - Conitec's MED SDK
+ * - Many quite long HEX-editor sessions
+ */
+
+#ifndef AI_MDLFILEHELPER_H_INC
+#define AI_MDLFILEHELPER_H_INC
+
+#include "./../include/Compiler/pushpack1.h"
+
+namespace Assimp {
+namespace MDL {
+
+// -------------------------------------------------------------------------------------
+// to make it easier for us, we test the magic word against both "endianesses"
+
+// magic bytes used in Quake 1 MDL meshes
+#define AI_MDL_MAGIC_NUMBER_BE AI_MAKE_MAGIC("IDPO")
+#define AI_MDL_MAGIC_NUMBER_LE AI_MAKE_MAGIC("OPDI")
+
+// magic bytes used in GameStudio A<very low> MDL meshes
+#define AI_MDL_MAGIC_NUMBER_BE_GS3 AI_MAKE_MAGIC("MDL2")
+#define AI_MDL_MAGIC_NUMBER_LE_GS3 AI_MAKE_MAGIC("2LDM")
+
+// magic bytes used in GameStudio A4 MDL meshes
+#define AI_MDL_MAGIC_NUMBER_BE_GS4 AI_MAKE_MAGIC("MDL3")
+#define AI_MDL_MAGIC_NUMBER_LE_GS4 AI_MAKE_MAGIC("3LDM")
+
+// magic bytes used in GameStudio A5+ MDL meshes
+#define AI_MDL_MAGIC_NUMBER_BE_GS5a AI_MAKE_MAGIC("MDL4")
+#define AI_MDL_MAGIC_NUMBER_LE_GS5a AI_MAKE_MAGIC("4LDM")
+#define AI_MDL_MAGIC_NUMBER_BE_GS5b AI_MAKE_MAGIC("MDL5")
+#define AI_MDL_MAGIC_NUMBER_LE_GS5b AI_MAKE_MAGIC("5LDM")
+
+// magic bytes used in GameStudio A7+ MDL meshes
+#define AI_MDL_MAGIC_NUMBER_BE_GS7 AI_MAKE_MAGIC("MDL7")
+#define AI_MDL_MAGIC_NUMBER_LE_GS7 AI_MAKE_MAGIC("7LDM")
+
+
+// common limitations for Quake1 meshes. The loader does not check them,
+// (however it warns) but models should not exceed these limits.
+#if (!defined AI_MDL_VERSION)
+# define AI_MDL_VERSION 6
+#endif
+#if (!defined AI_MDL_MAX_FRAMES)
+# define AI_MDL_MAX_FRAMES 256
+#endif
+#if (!defined AI_MDL_MAX_UVS)
+# define AI_MDL_MAX_UVS 1024
+#endif
+#if (!defined AI_MDL_MAX_VERTS)
+# define AI_MDL_MAX_VERTS 1024
+#endif
+#if (!defined AI_MDL_MAX_TRIANGLES)
+# define AI_MDL_MAX_TRIANGLES 2048
+#endif
+
+// material key that is set for dummy materials that are
+// just referencing another material
+#if (!defined AI_MDL7_REFERRER_MATERIAL)
+# define AI_MDL7_REFERRER_MATERIAL "&&&referrer&&&",0,0
+#endif
+
+// -------------------------------------------------------------------------------------
+/** \struct Header
+ * \brief Data structure for the MDL main header
+ */
+struct Header
+{
+ //! magic number: "IDPO"
+ uint32_t ident;
+
+ //! version number: 6
+ int32_t version;
+
+ //! scale factors for each axis
+ aiVector3D scale;
+
+ //! translation factors for each axis
+ aiVector3D translate;
+
+ //! bounding radius of the mesh
+ float boundingradius;
+
+ //! Position of the viewer's exe. Ignored
+ aiVector3D vEyePos;
+
+ //! Number of textures
+ int32_t num_skins;
+
+ //! Texture width in pixels
+ int32_t skinwidth;
+
+ //! Texture height in pixels
+ int32_t skinheight;
+
+ //! Number of vertices contained in the file
+ int32_t num_verts;
+
+ //! Number of triangles contained in the file
+ int32_t num_tris;
+
+ //! Number of frames contained in the file
+ int32_t num_frames;
+
+ //! 0 = synchron, 1 = random . Ignored
+ //! (MDLn formats: number of texture coordinates)
+ int32_t synctype;
+
+ //! State flag
+ int32_t flags;
+
+ //! Could be the total size of the file (and not a float)
+ float size;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct Header_MDL7
+ * \brief Data structure for the MDL 7 main header
+ */
+struct Header_MDL7
+{
+ //! magic number: "MDL7"
+ char ident[4];
+
+ //! Version number. Ignored
+ int32_t version;
+
+ //! Number of bones in file
+ uint32_t bones_num;
+
+ //! Number of groups in file
+ uint32_t groups_num;
+
+ //! Size of data in the file
+ uint32_t data_size;
+
+ //! Ignored. Used to store entity specific information
+ int32_t entlump_size;
+
+ //! Ignored. Used to store MED related data
+ int32_t medlump_size;
+
+ //! Size of the Bone_MDL7 data structure used in the file
+ uint16_t bone_stc_size;
+
+ //! Size of the Skin_MDL 7 data structure used in the file
+ uint16_t skin_stc_size;
+
+ //! Size of a single color (e.g. in a material)
+ uint16_t colorvalue_stc_size;
+
+ //! Size of the Material_MDL7 data structure used in the file
+ uint16_t material_stc_size;
+
+ //! Size of a texture coordinate set in the file
+ uint16_t skinpoint_stc_size;
+
+ //! Size of a triangle in the file
+ uint16_t triangle_stc_size;
+
+ //! Size of a normal vertex in the file
+ uint16_t mainvertex_stc_size;
+
+ //! Size of a per-frame animated vertex in the file
+ //! (this is not supported)
+ uint16_t framevertex_stc_size;
+
+ //! Size of a bone animation matrix
+ uint16_t bonetrans_stc_size;
+
+ //! Size of the Frame_MDL7 data structure used in the file
+ uint16_t frame_stc_size;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct Bone_MDL7
+ * \brief Data structure for a bone in a MDL7 file
+ */
+struct Bone_MDL7
+{
+ //! Index of the parent bone of *this* bone. 0xffff means:
+ //! "hey, I have no parent, I'm an orphan"
+ uint16_t parent_index;
+ uint8_t _unused_[2];
+
+ //! Relative position of the bone (relative to the
+ //! parent bone)
+ float x,y,z;
+
+ //! Optional name of the bone
+ char name[1 /* DUMMY SIZE */];
+} PACK_STRUCT;
+
+#if (!defined AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_20_CHARS)
+# define AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_20_CHARS (16 + 20)
+#endif
+
+#if (!defined AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_32_CHARS)
+# define AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_32_CHARS (16 + 32)
+#endif
+
+#if (!defined AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE)
+# define AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE (16)
+#endif
+
+#if (!defined AI_MDL7_MAX_GROUPNAMESIZE)
+# define AI_MDL7_MAX_GROUPNAMESIZE 16
+#endif // ! AI_MDL7_MAX_GROUPNAMESIZE
+
+// -------------------------------------------------------------------------------------
+/** \struct Group_MDL7
+ * \brief Group in a MDL7 file
+ */
+struct Group_MDL7
+{
+ //! = '1' -> triangle based Mesh
+ unsigned char typ;
+
+ int8_t deformers;
+ int8_t max_weights;
+ int8_t _unused_;
+
+ //! size of data for this group in bytes ( MD7_GROUP stc. included).
+ int32_t groupdata_size;
+ char name[AI_MDL7_MAX_GROUPNAMESIZE];
+
+ //! Number of skins
+ int32_t numskins;
+
+ //! Number of texture coordinates
+ int32_t num_stpts;
+
+ //! Number of triangles
+ int32_t numtris;
+
+ //! Number of vertices
+ int32_t numverts;
+
+ //! Number of frames
+ int32_t numframes;
+} PACK_STRUCT;
+
+#define AI_MDL7_SKINTYPE_MIPFLAG 0x08
+#define AI_MDL7_SKINTYPE_MATERIAL 0x10
+#define AI_MDL7_SKINTYPE_MATERIAL_ASCDEF 0x20
+#define AI_MDL7_SKINTYPE_RGBFLAG 0x80
+
+#if (!defined AI_MDL7_MAX_BONENAMESIZE)
+# define AI_MDL7_MAX_BONENAMESIZE 20
+#endif // !! AI_MDL7_MAX_BONENAMESIZE
+
+// -------------------------------------------------------------------------------------
+/** \struct Deformer_MDL7
+ * \brief Deformer in a MDL7 file
+ */
+struct Deformer_MDL7
+{
+ int8_t deformer_version; // 0
+ int8_t deformer_typ; // 0 - bones
+ int8_t _unused_[2];
+ int32_t group_index;
+ int32_t elements;
+ int32_t deformerdata_size;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct DeformerElement_MDL7
+ * \brief Deformer element in a MDL7 file
+ */
+struct DeformerElement_MDL7
+{
+ //! bei deformer_typ==0 (==bones) element_index == bone index
+ int32_t element_index;
+ char element_name[AI_MDL7_MAX_BONENAMESIZE];
+ int32_t weights;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct DeformerWeight_MDL7
+ * \brief Deformer weight in a MDL7 file
+ */
+struct DeformerWeight_MDL7
+{
+ //! for deformer_typ==0 (==bones) index == vertex index
+ int32_t index;
+ float weight;
+} PACK_STRUCT;
+
+
+// don't know why this was in the original headers ...
+typedef int32_t MD7_MATERIAL_ASCDEFSIZE;
+
+// -------------------------------------------------------------------------------------
+/** \struct ColorValue_MDL7
+ * \brief Data structure for a color value in a MDL7 file
+ */
+struct ColorValue_MDL7
+{
+ float r,g,b,a;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct Material_MDL7
+ * \brief Data structure for a Material in a MDL7 file
+ */
+struct Material_MDL7
+{
+ //! Diffuse base color of the material
+ ColorValue_MDL7 Diffuse;
+
+ //! Ambient base color of the material
+ ColorValue_MDL7 Ambient;
+
+ //! Specular base color of the material
+ ColorValue_MDL7 Specular;
+
+ //! Emissive base color of the material
+ ColorValue_MDL7 Emissive;
+
+ //! Phong power
+ float Power;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct Skin
+ * \brief Skin data structure #1 - used by Quake1, MDL2, MDL3 and MDL4
+ */
+struct Skin
+{
+ //! 0 = single (Skin), 1 = group (GroupSkin)
+ //! For MDL3-5: Defines the type of the skin and there
+ //! fore the size of the data to skip:
+ //-------------------------------------------------------
+ //! 2 for 565 RGB,
+ //! 3 for 4444 ARGB,
+ //! 10 for 565 mipmapped,
+ //! 11 for 4444 mipmapped (bpp = 2),
+ //! 12 for 888 RGB mipmapped (bpp = 3),
+ //! 13 for 8888 ARGB mipmapped (bpp = 4)
+ //-------------------------------------------------------
+ int32_t group;
+
+ //! Texture data
+ uint8_t *data;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct Skin
+ * \brief Skin data structure #2 - used by MDL5, MDL6 and MDL7
+ * \see Skin
+ */
+struct Skin_MDL5
+{
+ int32_t size, width, height;
+ uint8_t *data;
+} PACK_STRUCT;
+
+// maximum length of texture file name
+#if (!defined AI_MDL7_MAX_TEXNAMESIZE)
+# define AI_MDL7_MAX_TEXNAMESIZE 0x10
+#endif
+
+// ---------------------------------------------------------------------------
+/** \struct Skin_MDL7
+ * \brief Skin data structure #3 - used by MDL7 and HMP7
+ */
+struct Skin_MDL7
+{
+ uint8_t typ;
+ int8_t _unused_[3];
+ int32_t width;
+ int32_t height;
+ char texture_name[AI_MDL7_MAX_TEXNAMESIZE];
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct RGB565
+ * \brief Data structure for a RGB565 pixel in a texture
+ */
+struct RGB565
+{
+ uint16_t r : 5;
+ uint16_t g : 6;
+ uint16_t b : 5;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct ARGB4
+ * \brief Data structure for a ARGB4444 pixel in a texture
+ */
+struct ARGB4
+{
+ uint16_t a : 4;
+ uint16_t r : 4;
+ uint16_t g : 4;
+ uint16_t b : 4;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct GroupSkin
+ * \brief Skin data structure #2 (group of pictures)
+ */
+struct GroupSkin
+{
+ //! 0 = single (Skin), 1 = group (GroupSkin)
+ int32_t group;
+
+ //! Number of images
+ int32_t nb;
+
+ //! Time for each image
+ float *time;
+
+ //! Data of each image
+ uint8_t **data;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct TexCoord
+ * \brief Texture coordinate data structure used by the Quake1 MDL format
+ */
+struct TexCoord
+{
+ //! Is the vertex on the noundary between front and back piece?
+ int32_t onseam;
+
+ //! Texture coordinate in the tx direction
+ int32_t s;
+
+ //! Texture coordinate in the ty direction
+ int32_t t;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct TexCoord_MDL3
+ * \brief Data structure for an UV coordinate in the 3DGS MDL3 format
+ */
+struct TexCoord_MDL3
+{
+ //! position, horizontally in range 0..skinwidth-1
+ int16_t u;
+
+ //! position, vertically in range 0..skinheight-1
+ int16_t v;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct TexCoord_MDL7
+ * \brief Data structure for an UV coordinate in the 3DGS MDL7 format
+ */
+struct TexCoord_MDL7
+{
+ //! position, horizontally in range 0..1
+ float u;
+
+ //! position, vertically in range 0..1
+ float v;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct SkinSet_MDL7
+ * \brief Skin set data structure for the 3DGS MDL7 format
+ * MDL7 references UV coordinates per face via an index list.
+ * This allows the use of multiple skins per face with just one
+ * UV coordinate set.
+ */
+struct SkinSet_MDL7
+{
+ //! Index into the UV coordinate list
+ uint16_t st_index[3]; // size 6
+
+ //! Material index
+ int32_t material; // size 4
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct Triangle
+ * \brief Triangle data structure for the Quake1 MDL format
+ */
+struct Triangle
+{
+ //! 0 = backface, 1 = frontface
+ int32_t facesfront;
+
+ //! Vertex indices
+ int32_t vertex[3];
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct Triangle_MDL3
+ * \brief Triangle data structure for the 3DGS MDL3 format
+ */
+struct Triangle_MDL3
+{
+ //! Index of 3 3D vertices in range 0..numverts
+ uint16_t index_xyz[3];
+
+ //! Index of 3 skin vertices in range 0..numskinverts
+ uint16_t index_uv[3];
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct Triangle_MDL7
+ * \brief Triangle data structure for the 3DGS MDL7 format
+ */
+struct Triangle_MDL7
+{
+ //! Vertex indices
+ uint16_t v_index[3]; // size 6
+
+ //! Two skinsets. The second will be used for multi-texturing
+ SkinSet_MDL7 skinsets[2];
+} PACK_STRUCT;
+
+#if (!defined AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV)
+# define AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV (6+sizeof(SkinSet_MDL7)-sizeof(uint32_t))
+#endif
+#if (!defined AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV_WITH_MATINDEX)
+# define AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV_WITH_MATINDEX (6+sizeof(SkinSet_MDL7))
+#endif
+#if (!defined AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV)
+# define AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV (6+2*sizeof(SkinSet_MDL7))
+#endif
+
+// Helper constants for Triangle::facesfront
+#if (!defined AI_MDL_BACKFACE)
+# define AI_MDL_BACKFACE 0x0
+#endif
+#if (!defined AI_MDL_FRONTFACE)
+# define AI_MDL_FRONTFACE 0x1
+#endif
+
+// -------------------------------------------------------------------------------------
+/** \struct Vertex
+ * \brief Vertex data structure
+ */
+struct Vertex
+{
+ uint8_t v[3];
+ uint8_t normalIndex;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+struct Vertex_MDL4
+{
+ uint16_t v[3];
+ uint8_t normalIndex;
+ uint8_t unused;
+} PACK_STRUCT;
+
+#define AI_MDL7_FRAMEVERTEX120503_STCSIZE 16
+#define AI_MDL7_FRAMEVERTEX030305_STCSIZE 26
+
+// -------------------------------------------------------------------------------------
+/** \struct Vertex_MDL7
+ * \brief Vertex data structure used in MDL7 files
+ */
+struct Vertex_MDL7
+{
+ float x,y,z;
+ uint16_t vertindex; // = bone index
+ union {
+ uint8_t norm162index;
+ float norm[3];
+ };
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct BoneTransform_MDL7
+ * \brief bone transformation matrix structure used in MDL7 files
+ */
+struct BoneTransform_MDL7
+{
+ //! 4*3
+ float m [4*4];
+
+ //! the index of this vertex, 0.. header::bones_num - 1
+ uint16_t bone_index;
+
+ //! I HATE 3DGS AND THE SILLY DEVELOPER WHO DESIGNED
+ //! THIS STUPID FILE FORMAT!
+ int8_t _unused_[2];
+} PACK_STRUCT;
+
+
+#define AI_MDL7_MAX_FRAMENAMESIZE 16
+
+
+// -------------------------------------------------------------------------------------
+/** \struct Frame_MDL7
+ * \brief Frame data structure used by MDL7 files
+ */
+struct Frame_MDL7
+{
+ char frame_name[AI_MDL7_MAX_FRAMENAMESIZE];
+ uint32_t vertices_count;
+ uint32_t transmatrix_count;
+};
+
+
+// -------------------------------------------------------------------------------------
+/** \struct SimpleFrame
+ * \brief Data structure for a simple frame
+ */
+struct SimpleFrame
+{
+ //! Minimum vertex of the bounding box
+ Vertex bboxmin;
+
+ //! Maximum vertex of the bounding box
+ Vertex bboxmax;
+
+ //! Name of the frame
+ char name[16];
+
+ //! Vertex list of the frame
+ Vertex *verts;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct Frame
+ * \brief Model frame data structure
+ */
+struct Frame
+{
+ //! 0 = simple frame, !0 = group frame
+ int32_t type;
+
+ //! Frame data
+ SimpleFrame frame;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+struct SimpleFrame_MDLn_SP
+{
+ //! Minimum vertex of the bounding box
+ Vertex_MDL4 bboxmin;
+
+ //! Maximum vertex of the bounding box
+ Vertex_MDL4 bboxmax;
+
+ //! Name of the frame
+ char name[16];
+
+ //! Vertex list of the frame
+ Vertex_MDL4 *verts;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct GroupFrame
+ * \brief Data structure for a group of frames
+ */
+struct GroupFrame
+{
+ //! 0 = simple frame, !0 = group frame
+ int32_t type;
+
+ //! Minimum vertex for all single frames
+ Vertex min;
+
+ //! Maximum vertex for all single frames
+ Vertex max;
+
+ //! Time for all single frames
+ float *time;
+
+ //! List of single frames
+ SimpleFrame *frames;
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+// -------------------------------------------------------------------------------------
+/** \struct IntFace_MDL7
+ * \brief Internal data structure to temporarily represent a face
+ */
+struct IntFace_MDL7
+{
+ // provide a constructor for our own convenience
+ IntFace_MDL7()
+ {
+ // set everything to zero
+ mIndices[0] = mIndices[1] = mIndices[2] = 0;
+ iMatIndex[0] = iMatIndex[1] = 0;
+ }
+
+ //! Vertex indices
+ uint32_t mIndices[3];
+
+ //! Material index (maximally two channels, which are joined later)
+ unsigned int iMatIndex[2];
+};
+
+// -------------------------------------------------------------------------------------
+/** \struct IntMaterial_MDL7
+ * \brief Internal data structure to temporarily represent a material
+ * which has been created from two single materials along with the
+ * original material indices.
+ */
+struct IntMaterial_MDL7
+{
+ // provide a constructor for our own convenience
+ IntMaterial_MDL7()
+ {
+ pcMat = NULL;
+ iOldMatIndices[0] = iOldMatIndices[1] = 0;
+ }
+
+ //! Material instance
+ MaterialHelper* pcMat;
+
+ //! Old material indices
+ unsigned int iOldMatIndices[2];
+};
+
+// -------------------------------------------------------------------------------------
+/** \struct IntBone_MDL7
+ * \brief Internal data structure to represent a bone in a MDL7 file with
+ * all of its animation channels assigned to it.
+ */
+struct IntBone_MDL7 : aiBone
+{
+ //! Default constructor
+ IntBone_MDL7() : iParent (0xffff)
+ {
+ pkeyPositions.reserve(30);
+ pkeyScalings.reserve(30);
+ pkeyRotations.reserve(30);
+ }
+
+ //! Parent bone of the bone
+ uint64_t iParent;
+
+ //! Relative position of the bone
+ aiVector3D vPosition;
+
+ //! Array of position keys
+ std::vector<aiVectorKey> pkeyPositions;
+
+ //! Array of scaling keys
+ std::vector<aiVectorKey> pkeyScalings;
+
+ //! Array of rotation keys
+ std::vector<aiQuatKey> pkeyRotations;
+};
+
+// -------------------------------------------------------------------------------------
+//! Describes a MDL7 frame
+struct IntFrameInfo_MDL7
+{
+ //! Construction from an existing frame header
+ IntFrameInfo_MDL7(BE_NCONST MDL::Frame_MDL7* _pcFrame,unsigned int _iIndex)
+ : iIndex(_iIndex)
+ , pcFrame(_pcFrame)
+ {}
+
+ //! Index of the frame
+ unsigned int iIndex;
+
+ //! Points to the header of the frame
+ BE_NCONST MDL::Frame_MDL7* pcFrame;
+};
+
+// -------------------------------------------------------------------------------------
+//! Describes a MDL7 mesh group
+struct IntGroupInfo_MDL7
+{
+ //! Default constructor
+ IntGroupInfo_MDL7()
+ : iIndex(0)
+ , pcGroup(NULL)
+ , pcGroupUVs(NULL)
+ , pcGroupTris(NULL)
+ , pcGroupVerts(NULL)
+ {}
+
+ //! Construction from an existing group header
+ IntGroupInfo_MDL7(BE_NCONST MDL::Group_MDL7* _pcGroup, unsigned int _iIndex)
+ : iIndex(_iIndex)
+ , pcGroup(_pcGroup)
+ , pcGroupUVs(NULL)
+ , pcGroupTris(NULL)
+ , pcGroupVerts(NULL)
+ {}
+
+ //! Index of the group
+ unsigned int iIndex;
+
+ //! Points to the header of the group
+ BE_NCONST MDL::Group_MDL7* pcGroup;
+
+ //! Points to the beginning of the uv coordinate section
+ BE_NCONST MDL::TexCoord_MDL7* pcGroupUVs;
+
+ //! Points to the beginning of the triangle section
+ BE_NCONST MDL::Triangle_MDL7* pcGroupTris;
+
+ //! Points to the beginning of the vertex section
+ BE_NCONST MDL::Vertex_MDL7* pcGroupVerts;
+};
+
+// -------------------------------------------------------------------------------------
+//! Holds the data that belongs to a MDL7 mesh group
+struct IntGroupData_MDL7
+{
+ IntGroupData_MDL7()
+ : pcFaces(NULL), bNeed2UV(false)
+ {}
+
+ //! Array of faces that belong to the group
+ MDL::IntFace_MDL7* pcFaces;
+
+ //! Array of vertex positions
+ std::vector<aiVector3D> vPositions;
+
+ //! Array of vertex normals
+ std::vector<aiVector3D> vNormals;
+
+ //! Array of bones indices
+ std::vector<unsigned int> aiBones;
+
+ //! First UV coordinate set
+ std::vector<aiVector3D> vTextureCoords1;
+
+ //! Optional second UV coordinate set
+ std::vector<aiVector3D> vTextureCoords2;
+
+ //! Specifies whether there are two texture
+ //! coordinate sets required
+ bool bNeed2UV;
+};
+
+// -------------------------------------------------------------------------------------
+//! Holds data from an MDL7 file that is shared by all mesh groups
+struct IntSharedData_MDL7
+{
+ //! Default constructor
+ IntSharedData_MDL7() : apcOutBones(0), iNum(0)
+ {
+ abNeedMaterials.reserve(10);
+ }
+
+ //! Destruction: properly delete all allocated resources
+ ~IntSharedData_MDL7()
+ {
+ // kill all bones
+ if (this->apcOutBones)
+ {
+ for (unsigned int m = 0; m < iNum;++m)
+ delete this->apcOutBones[m];
+ delete[] this->apcOutBones;
+ }
+ }
+
+ //! Specifies which materials are used
+ std::vector<bool> abNeedMaterials;
+
+ //! List of all materials
+ std::vector<MaterialHelper*> pcMats;
+
+ //! List of all bones
+ IntBone_MDL7** apcOutBones;
+
+ //! number of bones
+ unsigned int iNum;
+};
+
+// -------------------------------------------------------------------------------------
+//! Contains input data for GenerateOutputMeshes_3DGS_MDL7
+struct IntSplittedGroupData_MDL7
+{
+ //! Construction from a given shared data set
+ IntSplittedGroupData_MDL7(IntSharedData_MDL7& _shared,
+ std::vector<aiMesh*>& _avOutList)
+
+ : aiSplit(0), shared(_shared), avOutList(_avOutList)
+ {
+ }
+
+ //! Destruction: properly delete all allocated resources
+ ~IntSplittedGroupData_MDL7()
+ {
+ // kill all face lists
+ if (this->aiSplit)
+ {
+ for (unsigned int m = 0; m < shared.pcMats.size();++m)
+ delete this->aiSplit[m];
+ delete[] this->aiSplit;
+ }
+ }
+
+ //! Contains a list of all faces per material
+ std::vector<unsigned int>** aiSplit;
+
+ //! Shared data for all groups of the model
+ IntSharedData_MDL7& shared;
+
+ //! List of meshes
+ std::vector<aiMesh*>& avOutList;
+};
+
+
+}
+} // end namespaces
+
+#endif // !! AI_MDLFILEHELPER_H_INC
diff --git a/src/3rdparty/assimp/code/MDLLoader.cpp b/src/3rdparty/assimp/code/MDLLoader.cpp
new file mode 100644
index 000000000..98f0dbcc8
--- /dev/null
+++ b/src/3rdparty/assimp/code/MDLLoader.cpp
@@ -0,0 +1,1933 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file MDLLoader.cpp
+ * @brief Implementation of the main parts of the MDL importer class
+ * *TODO* Cleanup and further testing of some parts necessary
+ */
+
+// internal headers
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
+
+#include "MDLLoader.h"
+#include "MDLDefaultColorMap.h"
+#include "MD2FileData.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Ugly stuff ... nevermind
+#define _AI_MDL7_ACCESS(_data, _index, _limit, _type) \
+ (*((const _type*)(((const char*)_data) + _index * _limit)))
+
+#define _AI_MDL7_ACCESS_PTR(_data, _index, _limit, _type) \
+ ((BE_NCONST _type*)(((const char*)_data) + _index * _limit))
+
+#define _AI_MDL7_ACCESS_VERT(_data, _index, _limit) \
+ _AI_MDL7_ACCESS(_data,_index,_limit,MDL::Vertex_MDL7)
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MDLImporter::MDLImporter() : configFrameID(0), mBuffer(0), iGSFileVersion(0), pIOHandler(0), pScene(0), iFileSize(0)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MDLImporter::~MDLImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool MDLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+
+ // if check for extension is not enough, check for the magic tokens
+ if (extension == "mdl" || !extension.length() || checkSig) {
+ uint32_t tokens[8];
+ tokens[0] = AI_MDL_MAGIC_NUMBER_LE_HL2a;
+ tokens[1] = AI_MDL_MAGIC_NUMBER_LE_HL2b;
+ tokens[2] = AI_MDL_MAGIC_NUMBER_LE_GS7;
+ tokens[3] = AI_MDL_MAGIC_NUMBER_LE_GS5b;
+ tokens[4] = AI_MDL_MAGIC_NUMBER_LE_GS5a;
+ tokens[5] = AI_MDL_MAGIC_NUMBER_LE_GS4;
+ tokens[6] = AI_MDL_MAGIC_NUMBER_LE_GS3;
+ tokens[7] = AI_MDL_MAGIC_NUMBER_LE;
+ return CheckMagicToken(pIOHandler,pFile,tokens,8,0);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void MDLImporter::SetupProperties(const Importer* pImp)
+{
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MDL_KEYFRAME,0xffffffff);
+
+ // The
+ // AI_CONFIG_IMPORT_MDL_KEYFRAME option overrides the
+ // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
+ if (0xffffffff == configFrameID) {
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+ }
+
+ // AI_CONFIG_IMPORT_MDL_COLORMAP - pallette file
+ configPalette = pImp->GetPropertyString(AI_CONFIG_IMPORT_MDL_COLORMAP,"colormap.lmp");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all supported extensions
+void MDLImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert( "mdl" );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void MDLImporter::InternReadFile( const std::string& pFile,
+ aiScene* _pScene, IOSystem* _pIOHandler)
+{
+ pScene = _pScene;
+ pIOHandler = _pIOHandler;
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open MDL file " + pFile + ".");
+ }
+
+ // This should work for all other types of MDL files, too ...
+ // the quake header is one of the smallest, afaik
+ iFileSize = (unsigned int)file->FileSize();
+ if ( iFileSize < sizeof(MDL::Header)) {
+ throw DeadlyImportError( "MDL File is too small.");
+ }
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ std::vector<unsigned char> buffer(iFileSize+1);
+ mBuffer = &buffer[0];
+ file->Read( (void*)mBuffer, 1, iFileSize);
+
+ // Append a binary zero to the end of the buffer.
+ // this is just for safety that string parsing routines
+ // find the end of the buffer ...
+ mBuffer[iFileSize] = '\0';
+ const uint32_t iMagicWord = *((uint32_t*)mBuffer);
+
+ // Determine the file subtype and call the appropriate member function
+
+ // Original Quake1 format
+ if (AI_MDL_MAGIC_NUMBER_BE == iMagicWord || AI_MDL_MAGIC_NUMBER_LE == iMagicWord) {
+ DefaultLogger::get()->debug("MDL subtype: Quake 1, magic word is IDPO");
+ iGSFileVersion = 0;
+ InternReadFile_Quake1();
+ }
+ // GameStudio A<old> MDL2 format - used by some test models that come with 3DGS
+ else if (AI_MDL_MAGIC_NUMBER_BE_GS3 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS3 == iMagicWord) {
+ DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A2, magic word is MDL2");
+ iGSFileVersion = 2;
+ InternReadFile_Quake1();
+ }
+ // GameStudio A4 MDL3 format
+ else if (AI_MDL_MAGIC_NUMBER_BE_GS4 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS4 == iMagicWord) {
+ DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A4, magic word is MDL3");
+ iGSFileVersion = 3;
+ InternReadFile_3DGS_MDL345();
+ }
+ // GameStudio A5+ MDL4 format
+ else if (AI_MDL_MAGIC_NUMBER_BE_GS5a == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS5a == iMagicWord) {
+ DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A4, magic word is MDL4");
+ iGSFileVersion = 4;
+ InternReadFile_3DGS_MDL345();
+ }
+ // GameStudio A5+ MDL5 format
+ else if (AI_MDL_MAGIC_NUMBER_BE_GS5b == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS5b == iMagicWord) {
+ DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A5, magic word is MDL5");
+ iGSFileVersion = 5;
+ InternReadFile_3DGS_MDL345();
+ }
+ // GameStudio A7 MDL7 format
+ else if (AI_MDL_MAGIC_NUMBER_BE_GS7 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS7 == iMagicWord) {
+ DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A7, magic word is MDL7");
+ iGSFileVersion = 7;
+ InternReadFile_3DGS_MDL7();
+ }
+ // IDST/IDSQ Format (CS:S/HL, etc ...)
+ else if (AI_MDL_MAGIC_NUMBER_BE_HL2a == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2a == iMagicWord ||
+ AI_MDL_MAGIC_NUMBER_BE_HL2b == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2b == iMagicWord)
+ {
+ DefaultLogger::get()->debug("MDL subtype: Source(tm) Engine, magic word is IDST/IDSQ");
+ iGSFileVersion = 0;
+ InternReadFile_HL2();
+ }
+ else {
+ // print the magic word to the log file
+ throw DeadlyImportError( "Unknown MDL subformat " + pFile +
+ ". Magic word (" + std::string((char*)&iMagicWord,4) + ") is not known");
+ }
+
+ // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
+ pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
+ 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
+
+ // delete the file buffer and cleanup
+ AI_DEBUG_INVALIDATE_PTR(mBuffer);
+ AI_DEBUG_INVALIDATE_PTR(pIOHandler);
+ AI_DEBUG_INVALIDATE_PTR(pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Check whether we're still inside the valid file range
+void MDLImporter::SizeCheck(const void* szPos)
+{
+ if (!szPos || (const unsigned char*)szPos > this->mBuffer + this->iFileSize)
+ {
+ throw DeadlyImportError("Invalid MDL file. The file is too small "
+ "or contains invalid data.");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Just for debgging purposes
+void MDLImporter::SizeCheck(const void* szPos, const char* szFile, unsigned int iLine)
+{
+ ai_assert(NULL != szFile);
+ if (!szPos || (const unsigned char*)szPos > mBuffer + iFileSize)
+ {
+ // remove a directory if there is one
+ const char* szFilePtr = ::strrchr(szFile,'\\');
+ if (!szFilePtr) {
+ if (!(szFilePtr = ::strrchr(szFile,'/')))
+ szFilePtr = szFile;
+ }
+ if (szFilePtr)++szFilePtr;
+
+ char szBuffer[1024];
+ ::sprintf(szBuffer,"Invalid MDL file. The file is too small "
+ "or contains invalid data (File: %s Line: %i)",szFilePtr,iLine);
+
+ throw DeadlyImportError(szBuffer);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Validate a quake file header
+void MDLImporter::ValidateHeader_Quake1(const MDL::Header* pcHeader)
+{
+ // some values may not be NULL
+ if (!pcHeader->num_frames)
+ throw DeadlyImportError( "[Quake 1 MDL] There are no frames in the file");
+
+ if (!pcHeader->num_verts)
+ throw DeadlyImportError( "[Quake 1 MDL] There are no vertices in the file");
+
+ if (!pcHeader->num_tris)
+ throw DeadlyImportError( "[Quake 1 MDL] There are no triangles in the file");
+
+ // check whether the maxima are exceeded ...however, this applies for Quake 1 MDLs only
+ if (!this->iGSFileVersion)
+ {
+ if (pcHeader->num_verts > AI_MDL_MAX_VERTS)
+ DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_VERTS vertices");
+
+ if (pcHeader->num_tris > AI_MDL_MAX_TRIANGLES)
+ DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_TRIANGLES triangles");
+
+ if (pcHeader->num_frames > AI_MDL_MAX_FRAMES)
+ DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_FRAMES frames");
+
+ // (this does not apply for 3DGS MDLs)
+ if (!this->iGSFileVersion && pcHeader->version != AI_MDL_VERSION)
+ DefaultLogger::get()->warn("Quake 1 MDL model has an unknown version: AI_MDL_VERSION (=6) is "
+ "the expected file format version");
+ if (pcHeader->num_skins && (!pcHeader->skinwidth || !pcHeader->skinheight))
+ DefaultLogger::get()->warn("Skin width or height are 0");
+ }
+}
+
+#ifdef AI_BUILD_BIG_ENDIAN
+// ------------------------------------------------------------------------------------------------
+void FlipQuakeHeader(BE_NCONST MDL::Header* pcHeader)
+{
+ AI_SWAP4( pcHeader->ident);
+ AI_SWAP4( pcHeader->version);
+ AI_SWAP4( pcHeader->boundingradius);
+ AI_SWAP4( pcHeader->flags);
+ AI_SWAP4( pcHeader->num_frames);
+ AI_SWAP4( pcHeader->num_skins);
+ AI_SWAP4( pcHeader->num_tris);
+ AI_SWAP4( pcHeader->num_verts);
+ for (unsigned int i = 0; i < 3;++i)
+ {
+ AI_SWAP4( pcHeader->scale[i]);
+ AI_SWAP4( pcHeader->translate[i]);
+ }
+ AI_SWAP4( pcHeader->size);
+ AI_SWAP4( pcHeader->skinheight);
+ AI_SWAP4( pcHeader->skinwidth);
+ AI_SWAP4( pcHeader->synctype);
+}
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Read a Quake 1 file
+void MDLImporter::InternReadFile_Quake1( )
+{
+ ai_assert(NULL != pScene);
+ BE_NCONST MDL::Header *pcHeader = (BE_NCONST MDL::Header*)this->mBuffer;
+
+#ifdef AI_BUILD_BIG_ENDIAN
+ FlipQuakeHeader(pcHeader);
+#endif
+
+ ValidateHeader_Quake1(pcHeader);
+
+ // current cursor position in the file
+ const unsigned char* szCurrent = (const unsigned char*)(pcHeader+1);
+
+ // need to read all textures
+ for (unsigned int i = 0; i < (unsigned int)pcHeader->num_skins;++i)
+ {
+ union{BE_NCONST MDL::Skin* pcSkin;BE_NCONST MDL::GroupSkin* pcGroupSkin;};
+ pcSkin = (BE_NCONST MDL::Skin*)szCurrent;
+
+ AI_SWAP4( pcSkin->group );
+
+ // Quake 1 groupskins
+ if (1 == pcSkin->group)
+ {
+ AI_SWAP4( pcGroupSkin->nb );
+
+ // need to skip multiple images
+ const unsigned int iNumImages = (unsigned int)pcGroupSkin->nb;
+ szCurrent += sizeof(uint32_t) * 2;
+
+ if (0 != iNumImages)
+ {
+ if (!i) {
+ // however, create only one output image (the first)
+ this->CreateTextureARGB8_3DGS_MDL3(szCurrent + iNumImages * sizeof(float));
+ }
+ // go to the end of the skin section / the beginning of the next skin
+ szCurrent += pcHeader->skinheight * pcHeader->skinwidth +
+ sizeof(float) * iNumImages;
+ }
+ }
+ // 3DGS has a few files that are using other 3DGS like texture formats here
+ else
+ {
+ szCurrent += sizeof(uint32_t);
+ unsigned int iSkip = i ? 0xffffffff : 0;
+ this->CreateTexture_3DGS_MDL4(szCurrent,pcSkin->group,&iSkip);
+ szCurrent += iSkip;
+ }
+ }
+ // get a pointer to the texture coordinates
+ BE_NCONST MDL::TexCoord* pcTexCoords = (BE_NCONST MDL::TexCoord*)szCurrent;
+ szCurrent += sizeof(MDL::TexCoord) * pcHeader->num_verts;
+
+ // get a pointer to the triangles
+ BE_NCONST MDL::Triangle* pcTriangles = (BE_NCONST MDL::Triangle*)szCurrent;
+ szCurrent += sizeof(MDL::Triangle) * pcHeader->num_tris;
+ VALIDATE_FILE_SIZE(szCurrent);
+
+ // now get a pointer to the first frame in the file
+ BE_NCONST MDL::Frame* pcFrames = (BE_NCONST MDL::Frame*)szCurrent;
+ BE_NCONST MDL::SimpleFrame* pcFirstFrame;
+
+ if (0 == pcFrames->type)
+ {
+ // get address of single frame
+ pcFirstFrame = &pcFrames->frame;
+ }
+ else
+ {
+ // get the first frame in the group
+ BE_NCONST MDL::GroupFrame* pcFrames2 = (BE_NCONST MDL::GroupFrame*)pcFrames;
+ pcFirstFrame = (BE_NCONST MDL::SimpleFrame*)(&pcFrames2->time + pcFrames->type);
+ }
+ BE_NCONST MDL::Vertex* pcVertices = (BE_NCONST MDL::Vertex*) ((pcFirstFrame->name) + sizeof(pcFirstFrame->name));
+ VALIDATE_FILE_SIZE((const unsigned char*)(pcVertices + pcHeader->num_verts));
+
+#ifdef AI_BUILD_BIG_ENDIAN
+ for (int i = 0; i<pcHeader->num_verts;++i)
+ {
+ AI_SWAP4( pcTexCoords[i].onseam );
+ AI_SWAP4( pcTexCoords[i].s );
+ AI_SWAP4( pcTexCoords[i].t );
+ }
+
+ for (int i = 0; i<pcHeader->num_tris;++i)
+ {
+ AI_SWAP4( pcTriangles[i].facesfront);
+ AI_SWAP4( pcTriangles[i].vertex[0]);
+ AI_SWAP4( pcTriangles[i].vertex[1]);
+ AI_SWAP4( pcTriangles[i].vertex[2]);
+ }
+#endif
+
+ // setup materials
+ SetupMaterialProperties_3DGS_MDL5_Quake1();
+
+ // allocate enough storage to hold all vertices and triangles
+ aiMesh* pcMesh = new aiMesh();
+
+ pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+ pcMesh->mNumVertices = pcHeader->num_tris * 3;
+ pcMesh->mNumFaces = pcHeader->num_tris;
+ pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+ pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNumUVComponents[0] = 2;
+
+ // there won't be more than one mesh inside the file
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes = new aiMesh*[1];
+ pScene->mMeshes[0] = pcMesh;
+
+ // now iterate through all triangles
+ unsigned int iCurrent = 0;
+ for (unsigned int i = 0; i < (unsigned int) pcHeader->num_tris;++i)
+ {
+ pcMesh->mFaces[i].mIndices = new unsigned int[3];
+ pcMesh->mFaces[i].mNumIndices = 3;
+
+ unsigned int iTemp = iCurrent;
+ for (unsigned int c = 0; c < 3;++c,++iCurrent)
+ {
+ pcMesh->mFaces[i].mIndices[c] = iCurrent;
+
+ // read vertices
+ unsigned int iIndex = pcTriangles->vertex[c];
+ if (iIndex >= (unsigned int)pcHeader->num_verts)
+ {
+ iIndex = pcHeader->num_verts-1;
+ DefaultLogger::get()->warn("Index overflow in Q1-MDL vertex list.");
+ }
+
+ aiVector3D& vec = pcMesh->mVertices[iCurrent];
+ vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0];
+ vec.x += pcHeader->translate[0];
+
+ vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1];
+ vec.y += pcHeader->translate[1];
+ //vec.y *= -1.0f;
+
+ vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2];
+ vec.z += pcHeader->translate[2];
+
+ // read the normal vector from the precalculated normal table
+ MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex,pcMesh->mNormals[iCurrent]);
+ //pcMesh->mNormals[iCurrent].y *= -1.0f;
+
+ // read texture coordinates
+ float s = (float)pcTexCoords[iIndex].s;
+ float t = (float)pcTexCoords[iIndex].t;
+
+ // translate texture coordinates
+ if (0 == pcTriangles->facesfront && 0 != pcTexCoords[iIndex].onseam) {
+ s += pcHeader->skinwidth * 0.5f;
+ }
+
+ // Scale s and t to range from 0.0 to 1.0
+ pcMesh->mTextureCoords[0][iCurrent].x = (s + 0.5f) / pcHeader->skinwidth;
+ pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-(t + 0.5f) / pcHeader->skinheight;
+
+ }
+ pcMesh->mFaces[i].mIndices[0] = iTemp+2;
+ pcMesh->mFaces[i].mIndices[1] = iTemp+1;
+ pcMesh->mFaces[i].mIndices[2] = iTemp+0;
+ pcTriangles++;
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup material properties for Quake and older GameStudio files
+void MDLImporter::SetupMaterialProperties_3DGS_MDL5_Quake1( )
+{
+ const MDL::Header* const pcHeader = (const MDL::Header*)this->mBuffer;
+
+ // allocate ONE material
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = new MaterialHelper();
+ pScene->mNumMaterials = 1;
+
+ // setup the material's properties
+ const int iMode = (int)aiShadingMode_Gouraud;
+ MaterialHelper* const pcHelper = (MaterialHelper*)pScene->mMaterials[0];
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ aiColor4D clr;
+ if (0 != pcHeader->num_skins && pScene->mNumTextures) {
+ // can we replace the texture with a single color?
+ clr = this->ReplaceTextureWithColor(pScene->mTextures[0]);
+ if (is_not_qnan(clr.r)) {
+ delete pScene->mTextures[0];
+ delete[] pScene->mTextures;
+
+ pScene->mTextures = NULL;
+ pScene->mNumTextures = 0;
+ }
+ else {
+ clr.b = clr.a = clr.g = clr.r = 1.0f;
+ aiString szString;
+ ::memcpy(szString.data,AI_MAKE_EMBEDDED_TEXNAME(0),3);
+ szString.length = 2;
+ pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ }
+
+ pcHelper->AddProperty<aiColor4D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor4D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.r *= 0.05f;clr.g *= 0.05f;
+ clr.b *= 0.05f;clr.a = 1.0f;
+ pcHelper->AddProperty<aiColor4D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a MDL 3,4,5 file
+void MDLImporter::InternReadFile_3DGS_MDL345( )
+{
+ ai_assert(NULL != pScene);
+
+ // the header of MDL 3/4/5 is nearly identical to the original Quake1 header
+ BE_NCONST MDL::Header *pcHeader = (BE_NCONST MDL::Header*)this->mBuffer;
+#ifdef AI_BUILD_BIG_ENDIAN
+ FlipQuakeHeader(pcHeader);
+#endif
+ ValidateHeader_Quake1(pcHeader);
+
+ // current cursor position in the file
+ const unsigned char* szCurrent = (const unsigned char*)(pcHeader+1);
+
+ // need to read all textures
+ for (unsigned int i = 0; i < (unsigned int)pcHeader->num_skins;++i) {
+ BE_NCONST MDL::Skin* pcSkin;
+ pcSkin = (BE_NCONST MDL::Skin*)szCurrent;
+ AI_SWAP4( pcSkin->group);
+ // create one output image
+ unsigned int iSkip = i ? 0xffffffff : 0;
+ if (5 <= iGSFileVersion)
+ {
+ // MDL5 format could contain MIPmaps
+ CreateTexture_3DGS_MDL5((unsigned char*)pcSkin + sizeof(uint32_t),
+ pcSkin->group,&iSkip);
+ }
+ else {
+ CreateTexture_3DGS_MDL4((unsigned char*)pcSkin + sizeof(uint32_t),
+ pcSkin->group,&iSkip);
+ }
+ // need to skip one image
+ szCurrent += iSkip + sizeof(uint32_t);
+
+ }
+ // get a pointer to the texture coordinates
+ BE_NCONST MDL::TexCoord_MDL3* pcTexCoords = (BE_NCONST MDL::TexCoord_MDL3*)szCurrent;
+ szCurrent += sizeof(MDL::TexCoord_MDL3) * pcHeader->synctype;
+
+ // NOTE: for MDLn formats "synctype" corresponds to the number of UV coords
+
+ // get a pointer to the triangles
+ BE_NCONST MDL::Triangle_MDL3* pcTriangles = (BE_NCONST MDL::Triangle_MDL3*)szCurrent;
+ szCurrent += sizeof(MDL::Triangle_MDL3) * pcHeader->num_tris;
+
+#ifdef AI_BUILD_BIG_ENDIAN
+
+ for (int i = 0; i<pcHeader->synctype;++i) {
+ AI_SWAP2( pcTexCoords[i].u );
+ AI_SWAP2( pcTexCoords[i].v );
+ }
+
+ for (int i = 0; i<pcHeader->num_tris;++i) {
+ AI_SWAP2( pcTriangles[i].index_xyz[0]);
+ AI_SWAP2( pcTriangles[i].index_xyz[1]);
+ AI_SWAP2( pcTriangles[i].index_xyz[2]);
+ AI_SWAP2( pcTriangles[i].index_uv[0]);
+ AI_SWAP2( pcTriangles[i].index_uv[1]);
+ AI_SWAP2( pcTriangles[i].index_uv[2]);
+ }
+
+#endif
+
+ VALIDATE_FILE_SIZE(szCurrent);
+
+ // setup materials
+ SetupMaterialProperties_3DGS_MDL5_Quake1();
+
+ // allocate enough storage to hold all vertices and triangles
+ aiMesh* pcMesh = new aiMesh();
+ pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ pcMesh->mNumVertices = pcHeader->num_tris * 3;
+ pcMesh->mNumFaces = pcHeader->num_tris;
+ pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+
+ // there won't be more than one mesh inside the file
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes = new aiMesh*[1];
+ pScene->mMeshes[0] = pcMesh;
+
+ // allocate output storage
+ pcMesh->mNumVertices = (unsigned int)pcHeader->num_tris*3;
+ pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+
+ if (pcHeader->synctype) {
+ pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNumUVComponents[0] = 2;
+ }
+
+ // now get a pointer to the first frame in the file
+ BE_NCONST MDL::Frame* pcFrames = (BE_NCONST MDL::Frame*)szCurrent;
+ AI_SWAP4(pcFrames->type);
+
+ // byte packed vertices
+ // FIXME: these two snippets below are almost identical ... join them?
+ /////////////////////////////////////////////////////////////////////////////////////
+ if (0 == pcFrames->type || 3 >= this->iGSFileVersion) {
+
+ const MDL::SimpleFrame* pcFirstFrame = (const MDL::SimpleFrame*)(szCurrent + sizeof(uint32_t));
+ const MDL::Vertex* pcVertices = (const MDL::Vertex*) ((pcFirstFrame->name) + sizeof(pcFirstFrame->name));
+
+ VALIDATE_FILE_SIZE(pcVertices + pcHeader->num_verts);
+
+ // now iterate through all triangles
+ unsigned int iCurrent = 0;
+ for (unsigned int i = 0; i < (unsigned int) pcHeader->num_tris;++i) {
+ pcMesh->mFaces[i].mIndices = new unsigned int[3];
+ pcMesh->mFaces[i].mNumIndices = 3;
+
+ unsigned int iTemp = iCurrent;
+ for (unsigned int c = 0; c < 3;++c,++iCurrent) {
+ // read vertices
+ unsigned int iIndex = pcTriangles->index_xyz[c];
+ if (iIndex >= (unsigned int)pcHeader->num_verts) {
+ iIndex = pcHeader->num_verts-1;
+ DefaultLogger::get()->warn("Index overflow in MDLn vertex list");
+ }
+
+ aiVector3D& vec = pcMesh->mVertices[iCurrent];
+ vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0];
+ vec.x += pcHeader->translate[0];
+
+ vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1];
+ vec.y += pcHeader->translate[1];
+ // vec.y *= -1.0f;
+
+ vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2];
+ vec.z += pcHeader->translate[2];
+
+ // read the normal vector from the precalculated normal table
+ MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex,pcMesh->mNormals[iCurrent]);
+ // pcMesh->mNormals[iCurrent].y *= -1.0f;
+
+ // read texture coordinates
+ if (pcHeader->synctype) {
+ ImportUVCoordinate_3DGS_MDL345(pcMesh->mTextureCoords[0][iCurrent],
+ pcTexCoords,pcTriangles->index_uv[c]);
+ }
+ }
+ pcMesh->mFaces[i].mIndices[0] = iTemp+2;
+ pcMesh->mFaces[i].mIndices[1] = iTemp+1;
+ pcMesh->mFaces[i].mIndices[2] = iTemp+0;
+ pcTriangles++;
+ }
+
+ }
+ // short packed vertices
+ /////////////////////////////////////////////////////////////////////////////////////
+ else {
+ // now get a pointer to the first frame in the file
+ const MDL::SimpleFrame_MDLn_SP* pcFirstFrame = (const MDL::SimpleFrame_MDLn_SP*) (szCurrent + sizeof(uint32_t));
+
+ // get a pointer to the vertices
+ const MDL::Vertex_MDL4* pcVertices = (const MDL::Vertex_MDL4*) ((pcFirstFrame->name) +
+ sizeof(pcFirstFrame->name));
+
+ VALIDATE_FILE_SIZE(pcVertices + pcHeader->num_verts);
+
+ // now iterate through all triangles
+ unsigned int iCurrent = 0;
+ for (unsigned int i = 0; i < (unsigned int) pcHeader->num_tris;++i) {
+ pcMesh->mFaces[i].mIndices = new unsigned int[3];
+ pcMesh->mFaces[i].mNumIndices = 3;
+
+ unsigned int iTemp = iCurrent;
+ for (unsigned int c = 0; c < 3;++c,++iCurrent) {
+ // read vertices
+ unsigned int iIndex = pcTriangles->index_xyz[c];
+ if (iIndex >= (unsigned int)pcHeader->num_verts) {
+ iIndex = pcHeader->num_verts-1;
+ DefaultLogger::get()->warn("Index overflow in MDLn vertex list");
+ }
+
+ aiVector3D& vec = pcMesh->mVertices[iCurrent];
+ vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0];
+ vec.x += pcHeader->translate[0];
+
+ vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1];
+ vec.y += pcHeader->translate[1];
+ // vec.y *= -1.0f;
+
+ vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2];
+ vec.z += pcHeader->translate[2];
+
+ // read the normal vector from the precalculated normal table
+ MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex,pcMesh->mNormals[iCurrent]);
+ // pcMesh->mNormals[iCurrent].y *= -1.0f;
+
+ // read texture coordinates
+ if (pcHeader->synctype) {
+ ImportUVCoordinate_3DGS_MDL345(pcMesh->mTextureCoords[0][iCurrent],
+ pcTexCoords,pcTriangles->index_uv[c]);
+ }
+ }
+ pcMesh->mFaces[i].mIndices[0] = iTemp+2;
+ pcMesh->mFaces[i].mIndices[1] = iTemp+1;
+ pcMesh->mFaces[i].mIndices[2] = iTemp+0;
+ pcTriangles++;
+ }
+ }
+
+ // For MDL5 we will need to build valid texture coordinates
+ // basing upon the file loaded (only support one file as skin)
+ if (0x5 == iGSFileVersion)
+ CalculateUVCoordinates_MDL5();
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a single UV coordinate for Quake and older GameStudio files
+void MDLImporter::ImportUVCoordinate_3DGS_MDL345(
+ aiVector3D& vOut,
+ const MDL::TexCoord_MDL3* pcSrc,
+ unsigned int iIndex)
+{
+ ai_assert(NULL != pcSrc);
+ const MDL::Header* const pcHeader = (const MDL::Header*)this->mBuffer;
+
+ // validate UV indices
+ if (iIndex >= (unsigned int) pcHeader->synctype) {
+ iIndex = pcHeader->synctype-1;
+ DefaultLogger::get()->warn("Index overflow in MDLn UV coord list");
+ }
+
+ float s = (float)pcSrc[iIndex].u;
+ float t = (float)pcSrc[iIndex].v;
+
+ // Scale s and t to range from 0.0 to 1.0
+ if (0x5 != iGSFileVersion) {
+ s = (s + 0.5f) / pcHeader->skinwidth;
+ t = 1.0f-(t + 0.5f) / pcHeader->skinheight;
+ }
+
+ vOut.x = s;
+ vOut.y = t;
+ vOut.z = 0.0f;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Compute UV coordinates for a MDL5 file
+void MDLImporter::CalculateUVCoordinates_MDL5()
+{
+ const MDL::Header* const pcHeader = (const MDL::Header*)this->mBuffer;
+ if (pcHeader->num_skins && this->pScene->mNumTextures) {
+ const aiTexture* pcTex = this->pScene->mTextures[0];
+
+ // if the file is loaded in DDS format: get the size of the
+ // texture from the header of the DDS file
+ // skip three DWORDs and read first height, then the width
+ unsigned int iWidth, iHeight;
+ if (!pcTex->mHeight) {
+ const uint32_t* piPtr = (uint32_t*)pcTex->pcData;
+
+ piPtr += 3;
+ iHeight = (unsigned int)*piPtr++;
+ iWidth = (unsigned int)*piPtr;
+ if (!iHeight || !iWidth)
+ {
+ DefaultLogger::get()->warn("Either the width or the height of the "
+ "embedded DDS texture is zero. Unable to compute final texture "
+ "coordinates. The texture coordinates remain in their original "
+ "0-x/0-y (x,y = texture size) range.");
+ iWidth = 1;
+ iHeight = 1;
+ }
+ }
+ else {
+ iWidth = pcTex->mWidth;
+ iHeight = pcTex->mHeight;
+ }
+
+ if (1 != iWidth || 1 != iHeight) {
+ const float fWidth = (float)iWidth;
+ const float fHeight = (float)iHeight;
+ aiMesh* pcMesh = this->pScene->mMeshes[0];
+ for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
+ {
+ pcMesh->mTextureCoords[0][i].x /= fWidth;
+ pcMesh->mTextureCoords[0][i].y /= fHeight;
+ pcMesh->mTextureCoords[0][i].y = 1.0f - pcMesh->mTextureCoords[0][i].y; // DX to OGL
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Validate the header of a MDL7 file
+void MDLImporter::ValidateHeader_3DGS_MDL7(const MDL::Header_MDL7* pcHeader)
+{
+ ai_assert(NULL != pcHeader);
+
+ // There are some fixed sizes ...
+ if (sizeof(MDL::ColorValue_MDL7) != pcHeader->colorvalue_stc_size) {
+ throw DeadlyImportError(
+ "[3DGS MDL7] sizeof(MDL::ColorValue_MDL7) != pcHeader->colorvalue_stc_size");
+ }
+ if (sizeof(MDL::TexCoord_MDL7) != pcHeader->skinpoint_stc_size) {
+ throw DeadlyImportError(
+ "[3DGS MDL7] sizeof(MDL::TexCoord_MDL7) != pcHeader->skinpoint_stc_size");
+ }
+ if (sizeof(MDL::Skin_MDL7) != pcHeader->skin_stc_size) {
+ throw DeadlyImportError(
+ "sizeof(MDL::Skin_MDL7) != pcHeader->skin_stc_size");
+ }
+
+ // if there are no groups ... how should we load such a file?
+ if (!pcHeader->groups_num) {
+ throw DeadlyImportError( "[3DGS MDL7] No frames found");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// resolve bone animation matrices
+void MDLImporter::CalcAbsBoneMatrices_3DGS_MDL7(MDL::IntBone_MDL7** apcOutBones)
+{
+ const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
+ const MDL::Bone_MDL7* pcBones = (const MDL::Bone_MDL7*)(pcHeader+1);
+ ai_assert(NULL != apcOutBones);
+
+ // first find the bone that has NO parent, calculate the
+ // animation matrix for it, then go on and search for the next parent
+ // index (0) and so on until we can't find a new node.
+ uint16_t iParent = 0xffff;
+ uint32_t iIterations = 0;
+ while (iIterations++ < pcHeader->bones_num) {
+ for (uint32_t iBone = 0; iBone < pcHeader->bones_num;++iBone) {
+ BE_NCONST MDL::Bone_MDL7* pcBone = _AI_MDL7_ACCESS_PTR(pcBones,iBone,
+ pcHeader->bone_stc_size,MDL::Bone_MDL7);
+
+ AI_SWAP2(pcBone->parent_index);
+ AI_SWAP4(pcBone->x);
+ AI_SWAP4(pcBone->y);
+ AI_SWAP4(pcBone->z);
+
+ if (iParent == pcBone->parent_index) {
+ // MDL7 readme
+ ////////////////////////////////////////////////////////////////
+ /*
+ The animation matrix is then calculated the following way:
+
+ vector3 bPos = <absolute bone position>
+ matrix44 laM; // local animation matrix
+ sphrvector key_rotate = <bone rotation>
+
+ matrix44 m1,m2;
+ create_trans_matrix(m1, -bPos.x, -bPos.y, -bPos.z);
+ create_trans_matrix(m2, -bPos.x, -bPos.y, -bPos.z);
+
+ create_rotation_matrix(laM,key_rotate);
+
+ laM = sm1 * laM;
+ laM = laM * sm2;
+ */
+ /////////////////////////////////////////////////////////////////
+
+ MDL::IntBone_MDL7* const pcOutBone = apcOutBones[iBone];
+
+ // store the parent index of the bone
+ pcOutBone->iParent = pcBone->parent_index;
+ if (0xffff != iParent) {
+ const MDL::IntBone_MDL7* pcParentBone = apcOutBones[iParent];
+ pcOutBone->mOffsetMatrix.a4 = -pcParentBone->vPosition.x;
+ pcOutBone->mOffsetMatrix.b4 = -pcParentBone->vPosition.y;
+ pcOutBone->mOffsetMatrix.c4 = -pcParentBone->vPosition.z;
+ }
+ pcOutBone->vPosition.x = pcBone->x;
+ pcOutBone->vPosition.y = pcBone->y;
+ pcOutBone->vPosition.z = pcBone->z;
+ pcOutBone->mOffsetMatrix.a4 -= pcBone->x;
+ pcOutBone->mOffsetMatrix.b4 -= pcBone->y;
+ pcOutBone->mOffsetMatrix.c4 -= pcBone->z;
+
+ if (AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE == pcHeader->bone_stc_size) {
+ // no real name for our poor bone is specified :-(
+ pcOutBone->mName.length = ::sprintf(pcOutBone->mName.data,
+ "UnnamedBone_%i",iBone);
+ }
+ else {
+ // Make sure we won't run over the buffer's end if there is no
+ // terminal 0 character (however the documentation says there
+ // should be one)
+ uint32_t iMaxLen = pcHeader->bone_stc_size-16;
+ for (uint32_t qq = 0; qq < iMaxLen;++qq) {
+ if (!pcBone->name[qq]) {
+ iMaxLen = qq;
+ break;
+ }
+ }
+
+ // store the name of the bone
+ pcOutBone->mName.length = (size_t)iMaxLen;
+ ::memcpy(pcOutBone->mName.data,pcBone->name,pcOutBone->mName.length);
+ pcOutBone->mName.data[pcOutBone->mName.length] = '\0';
+ }
+ }
+ }
+ ++iParent;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read bones from a MDL7 file
+MDL::IntBone_MDL7** MDLImporter::LoadBones_3DGS_MDL7()
+{
+ const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
+ if (pcHeader->bones_num) {
+ // validate the size of the bone data structure in the file
+ if (AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_20_CHARS != pcHeader->bone_stc_size &&
+ AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_32_CHARS != pcHeader->bone_stc_size &&
+ AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE != pcHeader->bone_stc_size)
+ {
+ DefaultLogger::get()->warn("Unknown size of bone data structure");
+ return NULL;
+ }
+
+ MDL::IntBone_MDL7** apcBonesOut = new MDL::IntBone_MDL7*[pcHeader->bones_num];
+ for (uint32_t crank = 0; crank < pcHeader->bones_num;++crank)
+ apcBonesOut[crank] = new MDL::IntBone_MDL7();
+
+ // and calculate absolute bone offset matrices ...
+ CalcAbsBoneMatrices_3DGS_MDL7(apcBonesOut);
+ return apcBonesOut;
+ }
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+// read faces from a MDL7 file
+void MDLImporter::ReadFaces_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
+ MDL::IntGroupData_MDL7& groupData)
+{
+ const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
+ BE_NCONST MDL::Triangle_MDL7* pcGroupTris = groupInfo.pcGroupTris;
+
+ // iterate through all triangles and build valid display lists
+ unsigned int iOutIndex = 0;
+ for (unsigned int iTriangle = 0; iTriangle < (unsigned int)groupInfo.pcGroup->numtris; ++iTriangle) {
+ AI_SWAP2(pcGroupTris->v_index[0]);
+ AI_SWAP2(pcGroupTris->v_index[1]);
+ AI_SWAP2(pcGroupTris->v_index[2]);
+
+ // iterate through all indices of the current triangle
+ for (unsigned int c = 0; c < 3;++c,++iOutIndex) {
+
+ // validate the vertex index
+ unsigned int iIndex = pcGroupTris->v_index[c];
+ if (iIndex > (unsigned int)groupInfo.pcGroup->numverts) {
+ // (we might need to read this section a second time - to process frame vertices correctly)
+ const_cast<MDL::Triangle_MDL7*>(pcGroupTris)->v_index[c] = iIndex = groupInfo.pcGroup->numverts-1;
+ DefaultLogger::get()->warn("Index overflow in MDL7 vertex list");
+ }
+
+ // write the output face index
+ groupData.pcFaces[iTriangle].mIndices[2-c] = iOutIndex;
+
+ aiVector3D& vPosition = groupData.vPositions[ iOutIndex ];
+ vPosition.x = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex, pcHeader->mainvertex_stc_size) .x;
+ vPosition.y = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .y;
+ vPosition.z = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .z;
+
+ // if we have bones, save the index
+ if (!groupData.aiBones.empty()) {
+ groupData.aiBones[iOutIndex] = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,
+ iIndex,pcHeader->mainvertex_stc_size).vertindex;
+ }
+
+ // now read the normal vector
+ if (AI_MDL7_FRAMEVERTEX030305_STCSIZE <= pcHeader->mainvertex_stc_size) {
+ // read the full normal vector
+ aiVector3D& vNormal = groupData.vNormals[ iOutIndex ];
+ vNormal.x = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[0];
+ AI_SWAP4(vNormal.x);
+ vNormal.y = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[1];
+ AI_SWAP4(vNormal.y);
+ vNormal.z = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[2];
+ AI_SWAP4(vNormal.z);
+ }
+ else if (AI_MDL7_FRAMEVERTEX120503_STCSIZE <= pcHeader->mainvertex_stc_size) {
+ // read the normal vector from Quake2's smart table
+ aiVector3D& vNormal = groupData.vNormals[ iOutIndex ];
+ MD2::LookupNormalIndex(_AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,
+ pcHeader->mainvertex_stc_size) .norm162index,vNormal);
+ }
+ // validate and process the first uv coordinate set
+ if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV) {
+
+ if (groupInfo.pcGroup->num_stpts) {
+ AI_SWAP2(pcGroupTris->skinsets[0].st_index[0]);
+ AI_SWAP2(pcGroupTris->skinsets[0].st_index[1]);
+ AI_SWAP2(pcGroupTris->skinsets[0].st_index[2]);
+
+ iIndex = pcGroupTris->skinsets[0].st_index[c];
+ if (iIndex > (unsigned int)groupInfo.pcGroup->num_stpts) {
+ iIndex = groupInfo.pcGroup->num_stpts-1;
+ DefaultLogger::get()->warn("Index overflow in MDL7 UV coordinate list (#1)");
+ }
+
+ float u = groupInfo.pcGroupUVs[iIndex].u;
+ float v = 1.0f-groupInfo.pcGroupUVs[iIndex].v; // DX to OGL
+
+ groupData.vTextureCoords1[iOutIndex].x = u;
+ groupData.vTextureCoords1[iOutIndex].y = v;
+ }
+ // assign the material index, but only if it is existing
+ if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV_WITH_MATINDEX){
+ AI_SWAP4(pcGroupTris->skinsets[0].material);
+ groupData.pcFaces[iTriangle].iMatIndex[0] = pcGroupTris->skinsets[0].material;
+ }
+ }
+ // validate and process the second uv coordinate set
+ if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV) {
+
+ if (groupInfo.pcGroup->num_stpts) {
+ AI_SWAP2(pcGroupTris->skinsets[1].st_index[0]);
+ AI_SWAP2(pcGroupTris->skinsets[1].st_index[1]);
+ AI_SWAP2(pcGroupTris->skinsets[1].st_index[2]);
+ AI_SWAP4(pcGroupTris->skinsets[1].material);
+
+ iIndex = pcGroupTris->skinsets[1].st_index[c];
+ if (iIndex > (unsigned int)groupInfo.pcGroup->num_stpts) {
+ iIndex = groupInfo.pcGroup->num_stpts-1;
+ DefaultLogger::get()->warn("Index overflow in MDL7 UV coordinate list (#2)");
+ }
+
+ float u = groupInfo.pcGroupUVs[ iIndex ].u;
+ float v = 1.0f-groupInfo.pcGroupUVs[ iIndex ].v;
+
+ groupData.vTextureCoords2[ iOutIndex ].x = u;
+ groupData.vTextureCoords2[ iOutIndex ].y = v; // DX to OGL
+
+ // check whether we do really need the second texture
+ // coordinate set ... wastes memory and loading time
+ if (0 != iIndex && (u != groupData.vTextureCoords1[ iOutIndex ].x ||
+ v != groupData.vTextureCoords1[ iOutIndex ].y ) )
+ groupData.bNeed2UV = true;
+
+ // if the material differs, we need a second skin, too
+ if (pcGroupTris->skinsets[ 1 ].material != pcGroupTris->skinsets[ 0 ].material)
+ groupData.bNeed2UV = true;
+ }
+ // assign the material index
+ groupData.pcFaces[ iTriangle ].iMatIndex[ 1 ] = pcGroupTris->skinsets[ 1 ].material;
+ }
+ }
+ // get the next triangle in the list
+ pcGroupTris = (BE_NCONST MDL::Triangle_MDL7*)((const char*)pcGroupTris + pcHeader->triangle_stc_size);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// handle frames in a MDL7 file
+bool MDLImporter::ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
+ MDL::IntGroupData_MDL7& groupData,
+ MDL::IntSharedData_MDL7& shared,
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut)
+{
+ ai_assert(NULL != szCurrent && NULL != szCurrentOut);
+ const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)mBuffer;
+
+ // if we have no bones we can simply skip all frames,
+ // otherwise we'll need to process them.
+ // FIX: If we need another frame than the first we must apply frame vertex replacements ...
+ for (unsigned int iFrame = 0; iFrame < (unsigned int)groupInfo.pcGroup->numframes;++iFrame) {
+ MDL::IntFrameInfo_MDL7 frame ((BE_NCONST MDL::Frame_MDL7*)szCurrent,iFrame);
+
+ AI_SWAP4(frame.pcFrame->vertices_count);
+ AI_SWAP4(frame.pcFrame->transmatrix_count);
+
+ const unsigned int iAdd = pcHeader->frame_stc_size +
+ frame.pcFrame->vertices_count * pcHeader->framevertex_stc_size +
+ frame.pcFrame->transmatrix_count * pcHeader->bonetrans_stc_size;
+
+ if (((const char*)szCurrent - (const char*)pcHeader) + iAdd > (unsigned int)pcHeader->data_size) {
+ DefaultLogger::get()->warn("Index overflow in frame area. "
+ "Ignoring all frames and all further mesh groups, too.");
+
+ // don't parse more groups if we can't even read one
+ // FIXME: sometimes this seems to occur even for valid files ...
+ *szCurrentOut = szCurrent;
+ return false;
+ }
+ // our output frame?
+ if (configFrameID == iFrame) {
+ BE_NCONST MDL::Vertex_MDL7* pcFrameVertices = (BE_NCONST MDL::Vertex_MDL7*)(szCurrent+pcHeader->frame_stc_size);
+
+ for (unsigned int qq = 0; qq < frame.pcFrame->vertices_count;++qq) {
+ // I assume this are simple replacements for normal vertices, the bone index serving
+ // as the index of the vertex to be replaced.
+ uint16_t iIndex = _AI_MDL7_ACCESS(pcFrameVertices,qq,pcHeader->framevertex_stc_size,MDL::Vertex_MDL7).vertindex;
+ AI_SWAP2(iIndex);
+ if (iIndex >= groupInfo.pcGroup->numverts) {
+ DefaultLogger::get()->warn("Invalid vertex index in frame vertex section");
+ continue;
+ }
+
+ aiVector3D vPosition,vNormal;
+
+ vPosition.x = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .x;
+ AI_SWAP4(vPosition.x);
+ vPosition.y = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .y;
+ AI_SWAP4(vPosition.y);
+ vPosition.z = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .z;
+ AI_SWAP4(vPosition.z);
+
+ // now read the normal vector
+ if (AI_MDL7_FRAMEVERTEX030305_STCSIZE <= pcHeader->mainvertex_stc_size) {
+ // read the full normal vector
+ vNormal.x = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[0];
+ AI_SWAP4(vNormal.x);
+ vNormal.y = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[1];
+ AI_SWAP4(vNormal.y);
+ vNormal.z = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[2];
+ AI_SWAP4(vNormal.z);
+ }
+ else if (AI_MDL7_FRAMEVERTEX120503_STCSIZE <= pcHeader->mainvertex_stc_size) {
+ // read the normal vector from Quake2's smart table
+ MD2::LookupNormalIndex(_AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,
+ pcHeader->framevertex_stc_size) .norm162index,vNormal);
+ }
+
+ // FIXME: O(n^2) at the moment ...
+ BE_NCONST MDL::Triangle_MDL7* pcGroupTris = groupInfo.pcGroupTris;
+ unsigned int iOutIndex = 0;
+ for (unsigned int iTriangle = 0; iTriangle < (unsigned int)groupInfo.pcGroup->numtris; ++iTriangle) {
+ // iterate through all indices of the current triangle
+ for (unsigned int c = 0; c < 3;++c,++iOutIndex) {
+ // replace the vertex with the new data
+ const unsigned int iCurIndex = pcGroupTris->v_index[c];
+ if (iCurIndex == iIndex) {
+ groupData.vPositions[iOutIndex] = vPosition;
+ groupData.vNormals[iOutIndex] = vNormal;
+ }
+ }
+ // get the next triangle in the list
+ pcGroupTris = (BE_NCONST MDL::Triangle_MDL7*)((const char*)
+ pcGroupTris + pcHeader->triangle_stc_size);
+ }
+ }
+ }
+ // parse bone trafo matrix keys (only if there are bones ...)
+ if (shared.apcOutBones) {
+ ParseBoneTrafoKeys_3DGS_MDL7(groupInfo,frame,shared);
+ }
+ szCurrent += iAdd;
+ }
+ *szCurrentOut = szCurrent;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Sort faces by material, handle multiple UVs correctly
+void MDLImporter::SortByMaterials_3DGS_MDL7(
+ const MDL::IntGroupInfo_MDL7& groupInfo,
+ MDL::IntGroupData_MDL7& groupData,
+ MDL::IntSplittedGroupData_MDL7& splittedGroupData)
+{
+ const unsigned int iNumMaterials = (unsigned int)splittedGroupData.shared.pcMats.size();
+ if (!groupData.bNeed2UV) {
+ // if we don't need a second set of texture coordinates there is no reason to keep it in memory ...
+ groupData.vTextureCoords2.clear();
+
+ // allocate the array
+ splittedGroupData.aiSplit = new std::vector<unsigned int>*[iNumMaterials];
+
+ for (unsigned int m = 0; m < iNumMaterials;++m)
+ splittedGroupData.aiSplit[m] = new std::vector<unsigned int>();
+
+ // iterate through all faces and sort by material
+ for (unsigned int iFace = 0; iFace < (unsigned int)groupInfo.pcGroup->numtris;++iFace) {
+ // check range
+ if (groupData.pcFaces[iFace].iMatIndex[0] >= iNumMaterials) {
+ // use the last material instead
+ splittedGroupData.aiSplit[iNumMaterials-1]->push_back(iFace);
+
+ // sometimes MED writes -1, but normally only if there is only
+ // one skin assigned. No warning in this case
+ if (0xFFFFFFFF != groupData.pcFaces[iFace].iMatIndex[0])
+ DefaultLogger::get()->warn("Index overflow in MDL7 material list [#0]");
+ }
+ else splittedGroupData.aiSplit[groupData.pcFaces[iFace].
+ iMatIndex[0]]->push_back(iFace);
+ }
+ }
+ else
+ {
+ // we need to build combined materials for each combination of
+ std::vector<MDL::IntMaterial_MDL7> avMats;
+ avMats.reserve(iNumMaterials*2);
+
+ // fixme: why on the heap?
+ std::vector<std::vector<unsigned int>* > aiTempSplit(iNumMaterials*2);
+ for (unsigned int m = 0; m < iNumMaterials;++m)
+ aiTempSplit[m] = new std::vector<unsigned int>();
+
+ // iterate through all faces and sort by material
+ for (unsigned int iFace = 0; iFace < (unsigned int)groupInfo.pcGroup->numtris;++iFace) {
+ // check range
+ unsigned int iMatIndex = groupData.pcFaces[iFace].iMatIndex[0];
+ if (iMatIndex >= iNumMaterials) {
+ // sometimes MED writes -1, but normally only if there is only
+ // one skin assigned. No warning in this case
+ if (0xffffffff != iMatIndex)
+ DefaultLogger::get()->warn("Index overflow in MDL7 material list [#1]");
+ iMatIndex = iNumMaterials-1;
+ }
+ unsigned int iMatIndex2 = groupData.pcFaces[iFace].iMatIndex[1];
+
+ unsigned int iNum = iMatIndex;
+ if (0xffffffff != iMatIndex2 && iMatIndex != iMatIndex2) {
+ if (iMatIndex2 >= iNumMaterials) {
+ // sometimes MED writes -1, but normally only if there is only
+ // one skin assigned. No warning in this case
+ DefaultLogger::get()->warn("Index overflow in MDL7 material list [#2]");
+ iMatIndex2 = iNumMaterials-1;
+ }
+
+ // do a slow seach in the list ...
+ iNum = 0;
+ bool bFound = false;
+ for (std::vector<MDL::IntMaterial_MDL7>::iterator i = avMats.begin();i != avMats.end();++i,++iNum){
+ if ((*i).iOldMatIndices[0] == iMatIndex && (*i).iOldMatIndices[1] == iMatIndex2) {
+ // reuse this material
+ bFound = true;
+ break;
+ }
+ }
+ if (!bFound) {
+ // build a new material ...
+ MDL::IntMaterial_MDL7 sHelper;
+ sHelper.pcMat = new MaterialHelper();
+ sHelper.iOldMatIndices[0] = iMatIndex;
+ sHelper.iOldMatIndices[1] = iMatIndex2;
+ JoinSkins_3DGS_MDL7(splittedGroupData.shared.pcMats[iMatIndex],
+ splittedGroupData.shared.pcMats[iMatIndex2],sHelper.pcMat);
+
+ // and add it to the list
+ avMats.push_back(sHelper);
+ iNum = (unsigned int)avMats.size()-1;
+ }
+ // adjust the size of the file array
+ if (iNum == aiTempSplit.size()) {
+ aiTempSplit.push_back(new std::vector<unsigned int>());
+ }
+ }
+ aiTempSplit[iNum]->push_back(iFace);
+ }
+
+ // now add the newly created materials to the old list
+ if (0 == groupInfo.iIndex) {
+ splittedGroupData.shared.pcMats.resize(avMats.size());
+ for (unsigned int o = 0; o < avMats.size();++o)
+ splittedGroupData.shared.pcMats[o] = avMats[o].pcMat;
+ }
+ else {
+ // This might result in redundant materials ...
+ splittedGroupData.shared.pcMats.resize(iNumMaterials + avMats.size());
+ for (unsigned int o = iNumMaterials; o < avMats.size();++o)
+ splittedGroupData.shared.pcMats[o] = avMats[o].pcMat;
+ }
+
+ // and build the final face-to-material array
+ splittedGroupData.aiSplit = new std::vector<unsigned int>*[aiTempSplit.size()];
+ for (unsigned int m = 0; m < iNumMaterials;++m)
+ splittedGroupData.aiSplit[m] = aiTempSplit[m];
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a MDL7 file
+void MDLImporter::InternReadFile_3DGS_MDL7( )
+{
+ ai_assert(NULL != pScene);
+
+ MDL::IntSharedData_MDL7 sharedData;
+
+ // current cursor position in the file
+ BE_NCONST MDL::Header_MDL7 *pcHeader = (BE_NCONST MDL::Header_MDL7*)this->mBuffer;
+ const unsigned char* szCurrent = (const unsigned char*)(pcHeader+1);
+
+ AI_SWAP4(pcHeader->version);
+ AI_SWAP4(pcHeader->bones_num);
+ AI_SWAP4(pcHeader->groups_num);
+ AI_SWAP4(pcHeader->data_size);
+ AI_SWAP4(pcHeader->entlump_size);
+ AI_SWAP4(pcHeader->medlump_size);
+ AI_SWAP2(pcHeader->bone_stc_size);
+ AI_SWAP2(pcHeader->skin_stc_size);
+ AI_SWAP2(pcHeader->colorvalue_stc_size);
+ AI_SWAP2(pcHeader->material_stc_size);
+ AI_SWAP2(pcHeader->skinpoint_stc_size);
+ AI_SWAP2(pcHeader->triangle_stc_size);
+ AI_SWAP2(pcHeader->mainvertex_stc_size);
+ AI_SWAP2(pcHeader->framevertex_stc_size);
+ AI_SWAP2(pcHeader->bonetrans_stc_size);
+ AI_SWAP2(pcHeader->frame_stc_size);
+
+ // validate the header of the file. There are some structure
+ // sizes that are expected by the loader to be constant
+ this->ValidateHeader_3DGS_MDL7(pcHeader);
+
+ // load all bones (they are shared by all groups, so
+ // we'll need to add them to all groups/meshes later)
+ // apcBonesOut is a list of all bones or NULL if they could not been loaded
+ szCurrent += pcHeader->bones_num * pcHeader->bone_stc_size;
+ sharedData.apcOutBones = this->LoadBones_3DGS_MDL7();
+
+ // vector to held all created meshes
+ std::vector<aiMesh*>* avOutList;
+
+ // 3 meshes per group - that should be OK for most models
+ avOutList = new std::vector<aiMesh*>[pcHeader->groups_num];
+ for (uint32_t i = 0; i < pcHeader->groups_num;++i)
+ avOutList[i].reserve(3);
+
+ // buffer to held the names of all groups in the file
+ char* aszGroupNameBuffer = new char[AI_MDL7_MAX_GROUPNAMESIZE*pcHeader->groups_num];
+
+ // read all groups
+ for (unsigned int iGroup = 0; iGroup < (unsigned int)pcHeader->groups_num;++iGroup) {
+ MDL::IntGroupInfo_MDL7 groupInfo((BE_NCONST MDL::Group_MDL7*)szCurrent,iGroup);
+ szCurrent = (const unsigned char*)(groupInfo.pcGroup+1);
+
+ VALIDATE_FILE_SIZE(szCurrent);
+
+ AI_SWAP4(groupInfo.pcGroup->groupdata_size);
+ AI_SWAP4(groupInfo.pcGroup->numskins);
+ AI_SWAP4(groupInfo.pcGroup->num_stpts);
+ AI_SWAP4(groupInfo.pcGroup->numtris);
+ AI_SWAP4(groupInfo.pcGroup->numverts);
+ AI_SWAP4(groupInfo.pcGroup->numframes);
+
+ if (1 != groupInfo.pcGroup->typ) {
+ // Not a triangle-based mesh
+ DefaultLogger::get()->warn("[3DGS MDL7] Not a triangle mesh group. Continuing happily");
+ }
+
+ // store the name of the group
+ const unsigned int ofs = iGroup*AI_MDL7_MAX_GROUPNAMESIZE;
+ ::memcpy(&aszGroupNameBuffer[ofs],
+ groupInfo.pcGroup->name,AI_MDL7_MAX_GROUPNAMESIZE);
+
+ // make sure '\0' is at the end
+ aszGroupNameBuffer[ofs+AI_MDL7_MAX_GROUPNAMESIZE-1] = '\0';
+
+ // read all skins
+ sharedData.pcMats.reserve(sharedData.pcMats.size() + groupInfo.pcGroup->numskins);
+ sharedData.abNeedMaterials.resize(sharedData.abNeedMaterials.size() +
+ groupInfo.pcGroup->numskins,false);
+
+ for (unsigned int iSkin = 0; iSkin < (unsigned int)groupInfo.pcGroup->numskins;++iSkin) {
+ ParseSkinLump_3DGS_MDL7(szCurrent,&szCurrent,sharedData.pcMats);
+ }
+ // if we have absolutely no skin loaded we need to generate a default material
+ if (sharedData.pcMats.empty()) {
+ const int iMode = (int)aiShadingMode_Gouraud;
+ sharedData.pcMats.push_back(new MaterialHelper());
+ MaterialHelper* pcHelper = (MaterialHelper*)sharedData.pcMats[0];
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.6f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ aiString szName;
+ szName.Set(AI_DEFAULT_MATERIAL_NAME);
+ pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
+
+ sharedData.abNeedMaterials.resize(1,false);
+ }
+
+ // now get a pointer to all texture coords in the group
+ groupInfo.pcGroupUVs = (BE_NCONST MDL::TexCoord_MDL7*)szCurrent;
+ for (int i = 0; i < groupInfo.pcGroup->num_stpts; ++i){
+ AI_SWAP4(groupInfo.pcGroupUVs[i].u);
+ AI_SWAP4(groupInfo.pcGroupUVs[i].v);
+ }
+ szCurrent += pcHeader->skinpoint_stc_size * groupInfo.pcGroup->num_stpts;
+
+ // now get a pointer to all triangle in the group
+ groupInfo.pcGroupTris = (BE_NCONST MDL::Triangle_MDL7*)szCurrent;
+ szCurrent += pcHeader->triangle_stc_size * groupInfo.pcGroup->numtris;
+
+ // now get a pointer to all vertices in the group
+ groupInfo.pcGroupVerts = (BE_NCONST MDL::Vertex_MDL7*)szCurrent;
+ for (int i = 0; i < groupInfo.pcGroup->numverts; ++i){
+ AI_SWAP4(groupInfo.pcGroupVerts[i].x);
+ AI_SWAP4(groupInfo.pcGroupVerts[i].y);
+ AI_SWAP4(groupInfo.pcGroupVerts[i].z);
+
+ AI_SWAP2(groupInfo.pcGroupVerts[i].vertindex);
+ //We can not swap the normal information now as we don't know which of the two kinds it is
+ }
+ szCurrent += pcHeader->mainvertex_stc_size * groupInfo.pcGroup->numverts;
+ VALIDATE_FILE_SIZE(szCurrent);
+
+ MDL::IntSplittedGroupData_MDL7 splittedGroupData(sharedData,avOutList[iGroup]);
+ MDL::IntGroupData_MDL7 groupData;
+ if (groupInfo.pcGroup->numtris && groupInfo.pcGroup->numverts)
+ {
+ // build output vectors
+ const unsigned int iNumVertices = groupInfo.pcGroup->numtris*3;
+ groupData.vPositions.resize(iNumVertices);
+ groupData.vNormals.resize(iNumVertices);
+
+ if (sharedData.apcOutBones)groupData.aiBones.resize(iNumVertices,0xffffffff);
+
+ // it is also possible that there are 0 UV coordinate sets
+ if (groupInfo.pcGroup->num_stpts){
+ groupData.vTextureCoords1.resize(iNumVertices,aiVector3D());
+
+ // check whether the triangle data structure is large enough
+ // to contain a second UV coodinate set
+ if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV) {
+ groupData.vTextureCoords2.resize(iNumVertices,aiVector3D());
+ groupData.bNeed2UV = true;
+ }
+ }
+ groupData.pcFaces = new MDL::IntFace_MDL7[groupInfo.pcGroup->numtris];
+
+ // read all faces into the preallocated arrays
+ ReadFaces_3DGS_MDL7(groupInfo, groupData);
+
+ // sort by materials
+ SortByMaterials_3DGS_MDL7(groupInfo, groupData,
+ splittedGroupData);
+
+ for (unsigned int qq = 0; qq < sharedData.pcMats.size();++qq) {
+ if (!splittedGroupData.aiSplit[qq]->empty())
+ sharedData.abNeedMaterials[qq] = true;
+ }
+ }
+ else DefaultLogger::get()->warn("[3DGS MDL7] Mesh group consists of 0 "
+ "vertices or faces. It will be skipped.");
+
+ // process all frames and generate output meshes
+ ProcessFrames_3DGS_MDL7(groupInfo,groupData, sharedData,szCurrent,&szCurrent);
+ GenerateOutputMeshes_3DGS_MDL7(groupData,splittedGroupData);
+ }
+
+ // generate a nodegraph and subnodes for each group
+ pScene->mRootNode = new aiNode();
+
+ // now we need to build a final mesh list
+ for (uint32_t i = 0; i < pcHeader->groups_num;++i)
+ pScene->mNumMeshes += (unsigned int)avOutList[i].size();
+
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; {
+ unsigned int p = 0,q = 0;
+ for (uint32_t i = 0; i < pcHeader->groups_num;++i) {
+ for (unsigned int a = 0; a < avOutList[i].size();++a) {
+ pScene->mMeshes[p++] = avOutList[i][a];
+ }
+ if (!avOutList[i].empty())++pScene->mRootNode->mNumChildren;
+ }
+ // we will later need an extra node to serve as parent for all bones
+ if (sharedData.apcOutBones)++pScene->mRootNode->mNumChildren;
+ this->pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
+ p = 0;
+ for (uint32_t i = 0; i < pcHeader->groups_num;++i) {
+ if (avOutList[i].empty())continue;
+
+ aiNode* const pcNode = pScene->mRootNode->mChildren[p] = new aiNode();
+ pcNode->mNumMeshes = (unsigned int)avOutList[i].size();
+ pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
+ pcNode->mParent = this->pScene->mRootNode;
+ for (unsigned int a = 0; a < pcNode->mNumMeshes;++a)
+ pcNode->mMeshes[a] = q + a;
+ q += (unsigned int)avOutList[i].size();
+
+ // setup the name of the node
+ char* const szBuffer = &aszGroupNameBuffer[i*AI_MDL7_MAX_GROUPNAMESIZE];
+ if ('\0' == *szBuffer)
+ pcNode->mName.length = ::sprintf(szBuffer,"Group_%i",p);
+ else pcNode->mName.length = ::strlen(szBuffer);
+ ::strcpy(pcNode->mName.data,szBuffer);
+ ++p;
+ }
+ }
+
+ // if there is only one root node with a single child we can optimize it a bit ...
+ if (1 == pScene->mRootNode->mNumChildren && !sharedData.apcOutBones) {
+ aiNode* pcOldRoot = this->pScene->mRootNode;
+ pScene->mRootNode = pcOldRoot->mChildren[0];
+ pcOldRoot->mChildren[0] = NULL;
+ delete pcOldRoot;
+ pScene->mRootNode->mParent = NULL;
+ }
+ else pScene->mRootNode->mName.Set("<mesh_root>");
+
+ delete[] avOutList;
+ delete[] aszGroupNameBuffer;
+ AI_DEBUG_INVALIDATE_PTR(avOutList);
+ AI_DEBUG_INVALIDATE_PTR(aszGroupNameBuffer);
+
+ // build a final material list.
+ CopyMaterials_3DGS_MDL7(sharedData);
+ HandleMaterialReferences_3DGS_MDL7();
+
+ // generate output bone animations and add all bones to the scenegraph
+ if (sharedData.apcOutBones) {
+ // this step adds empty dummy bones to the nodegraph
+ // insert another dummy node to avoid name conflicts
+ aiNode* const pc = pScene->mRootNode->mChildren[pScene->mRootNode->mNumChildren-1] = new aiNode();
+
+ pc->mName.Set("<skeleton_root>");
+
+ // add bones to the nodegraph
+ AddBonesToNodeGraph_3DGS_MDL7((const Assimp::MDL::IntBone_MDL7 **)
+ sharedData.apcOutBones,pc,0xffff);
+
+ // this steps build a valid output animation
+ BuildOutputAnims_3DGS_MDL7((const Assimp::MDL::IntBone_MDL7 **)
+ sharedData.apcOutBones);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Copy materials
+void MDLImporter::CopyMaterials_3DGS_MDL7(MDL::IntSharedData_MDL7 &shared)
+{
+ pScene->mNumMaterials = (unsigned int)shared.pcMats.size();
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+ pScene->mMaterials[i] = shared.pcMats[i];
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Process material references
+void MDLImporter::HandleMaterialReferences_3DGS_MDL7()
+{
+ // search for referrer materials
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
+ int iIndex = 0;
+ if (AI_SUCCESS == aiGetMaterialInteger(pScene->mMaterials[i],AI_MDL7_REFERRER_MATERIAL, &iIndex) ) {
+ for (unsigned int a = 0; a < pScene->mNumMeshes;++a) {
+ aiMesh* const pcMesh = pScene->mMeshes[a];
+ if (i == pcMesh->mMaterialIndex) {
+ pcMesh->mMaterialIndex = iIndex;
+ }
+ }
+ // collapse the rest of the array
+ delete pScene->mMaterials[i];
+ for (unsigned int pp = i; pp < pScene->mNumMaterials-1;++pp) {
+
+ pScene->mMaterials[pp] = pScene->mMaterials[pp+1];
+ for (unsigned int a = 0; a < pScene->mNumMeshes;++a) {
+ aiMesh* const pcMesh = pScene->mMeshes[a];
+ if (pcMesh->mMaterialIndex > i)--pcMesh->mMaterialIndex;
+ }
+ }
+ --pScene->mNumMaterials;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read bone transformation keys
+void MDLImporter::ParseBoneTrafoKeys_3DGS_MDL7(
+ const MDL::IntGroupInfo_MDL7& groupInfo,
+ IntFrameInfo_MDL7& frame,
+ MDL::IntSharedData_MDL7& shared)
+{
+ const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
+
+ // only the first group contains bone animation keys
+ if (frame.pcFrame->transmatrix_count) {
+ if (!groupInfo.iIndex) {
+ // skip all frames vertices. We can't support them
+ const MDL::BoneTransform_MDL7* pcBoneTransforms = (const MDL::BoneTransform_MDL7*)
+ (((const char*)frame.pcFrame) + pcHeader->frame_stc_size +
+ frame.pcFrame->vertices_count * pcHeader->framevertex_stc_size);
+
+ // read all transformation matrices
+ for (unsigned int iTrafo = 0; iTrafo < frame.pcFrame->transmatrix_count;++iTrafo) {
+ if (pcBoneTransforms->bone_index >= pcHeader->bones_num) {
+ DefaultLogger::get()->warn("Index overflow in frame area. "
+ "Unable to parse this bone transformation");
+ }
+ else {
+ AddAnimationBoneTrafoKey_3DGS_MDL7(frame.iIndex,
+ pcBoneTransforms,shared.apcOutBones);
+ }
+ pcBoneTransforms = (const MDL::BoneTransform_MDL7*)(
+ (const char*)pcBoneTransforms + pcHeader->bonetrans_stc_size);
+ }
+ }
+ else {
+ DefaultLogger::get()->warn("Ignoring animation keyframes in groups != 0");
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Attach bones to the output nodegraph
+void MDLImporter::AddBonesToNodeGraph_3DGS_MDL7(const MDL::IntBone_MDL7** apcBones,
+ aiNode* pcParent,uint16_t iParentIndex)
+{
+ ai_assert(NULL != apcBones && NULL != pcParent);
+
+ // get a pointer to the header ...
+ const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
+
+ const MDL::IntBone_MDL7** apcBones2 = apcBones;
+ for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
+
+ const MDL::IntBone_MDL7* const pcBone = *apcBones2++;
+ if (pcBone->iParent == iParentIndex) {
+ ++pcParent->mNumChildren;
+ }
+ }
+ pcParent->mChildren = new aiNode*[pcParent->mNumChildren];
+ unsigned int qq = 0;
+ for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
+
+ const MDL::IntBone_MDL7* const pcBone = *apcBones++;
+ if (pcBone->iParent != iParentIndex)continue;
+
+ aiNode* pcNode = pcParent->mChildren[qq++] = new aiNode();
+ pcNode->mName = aiString( pcBone->mName );
+
+ AddBonesToNodeGraph_3DGS_MDL7(apcBones,pcNode,(uint16_t)i);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build output animations
+void MDLImporter::BuildOutputAnims_3DGS_MDL7(
+ const MDL::IntBone_MDL7** apcBonesOut)
+{
+ ai_assert(NULL != apcBonesOut);
+ const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)mBuffer;
+
+ // one animation ...
+ aiAnimation* pcAnim = new aiAnimation();
+ for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
+ if (!apcBonesOut[i]->pkeyPositions.empty()) {
+
+ // get the last frame ... (needn't be equal to pcHeader->frames_num)
+ for (size_t qq = 0; qq < apcBonesOut[i]->pkeyPositions.size();++qq) {
+ pcAnim->mDuration = std::max(pcAnim->mDuration, (double)
+ apcBonesOut[i]->pkeyPositions[qq].mTime);
+ }
+ ++pcAnim->mNumChannels;
+ }
+ }
+ if (pcAnim->mDuration) {
+ pcAnim->mChannels = new aiNodeAnim*[pcAnim->mNumChannels];
+
+ unsigned int iCnt = 0;
+ for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
+ if (!apcBonesOut[i]->pkeyPositions.empty()) {
+ const MDL::IntBone_MDL7* const intBone = apcBonesOut[i];
+
+ aiNodeAnim* const pcNodeAnim = pcAnim->mChannels[iCnt++] = new aiNodeAnim();
+ pcNodeAnim->mNodeName = aiString( intBone->mName );
+
+ // allocate enough storage for all keys
+ pcNodeAnim->mNumPositionKeys = (unsigned int)intBone->pkeyPositions.size();
+ pcNodeAnim->mNumScalingKeys = (unsigned int)intBone->pkeyPositions.size();
+ pcNodeAnim->mNumRotationKeys = (unsigned int)intBone->pkeyPositions.size();
+
+ pcNodeAnim->mPositionKeys = new aiVectorKey[pcNodeAnim->mNumPositionKeys];
+ pcNodeAnim->mScalingKeys = new aiVectorKey[pcNodeAnim->mNumPositionKeys];
+ pcNodeAnim->mRotationKeys = new aiQuatKey[pcNodeAnim->mNumPositionKeys];
+
+ // copy all keys
+ for (unsigned int qq = 0; qq < pcNodeAnim->mNumPositionKeys;++qq) {
+ pcNodeAnim->mPositionKeys[qq] = intBone->pkeyPositions[qq];
+ pcNodeAnim->mScalingKeys[qq] = intBone->pkeyScalings[qq];
+ pcNodeAnim->mRotationKeys[qq] = intBone->pkeyRotations[qq];
+ }
+ }
+ }
+
+ // store the output animation
+ pScene->mNumAnimations = 1;
+ pScene->mAnimations = new aiAnimation*[1];
+ pScene->mAnimations[0] = pcAnim;
+ }
+ else delete pcAnim;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MDLImporter::AddAnimationBoneTrafoKey_3DGS_MDL7(unsigned int iTrafo,
+ const MDL::BoneTransform_MDL7* pcBoneTransforms,
+ MDL::IntBone_MDL7** apcBonesOut)
+{
+ ai_assert(NULL != pcBoneTransforms);
+ ai_assert(NULL != apcBonesOut);
+
+ // first .. get the transformation matrix
+ aiMatrix4x4 mTransform;
+ mTransform.a1 = pcBoneTransforms->m[0];
+ mTransform.b1 = pcBoneTransforms->m[1];
+ mTransform.c1 = pcBoneTransforms->m[2];
+ mTransform.d1 = pcBoneTransforms->m[3];
+
+ mTransform.a2 = pcBoneTransforms->m[4];
+ mTransform.b2 = pcBoneTransforms->m[5];
+ mTransform.c2 = pcBoneTransforms->m[6];
+ mTransform.d2 = pcBoneTransforms->m[7];
+
+ mTransform.a3 = pcBoneTransforms->m[8];
+ mTransform.b3 = pcBoneTransforms->m[9];
+ mTransform.c3 = pcBoneTransforms->m[10];
+ mTransform.d3 = pcBoneTransforms->m[11];
+
+ // now decompose the transformation matrix into separate
+ // scaling, rotation and translation
+ aiVectorKey vScaling,vPosition;
+ aiQuatKey qRotation;
+
+ // FIXME: Decompose will assert in debug builds if the matrix is invalid ...
+ mTransform.Decompose(vScaling.mValue,qRotation.mValue,vPosition.mValue);
+
+ // now generate keys
+ vScaling.mTime = qRotation.mTime = vPosition.mTime = (double)iTrafo;
+
+ // add the keys to the bone
+ MDL::IntBone_MDL7* const pcBoneOut = apcBonesOut[pcBoneTransforms->bone_index];
+ pcBoneOut->pkeyPositions.push_back ( vPosition );
+ pcBoneOut->pkeyScalings.push_back ( vScaling );
+ pcBoneOut->pkeyRotations.push_back ( qRotation );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Construct output meshes
+void MDLImporter::GenerateOutputMeshes_3DGS_MDL7(
+ MDL::IntGroupData_MDL7& groupData,
+ MDL::IntSplittedGroupData_MDL7& splittedGroupData)
+{
+ const MDL::IntSharedData_MDL7& shared = splittedGroupData.shared;
+
+ // get a pointer to the header ...
+ const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
+ const unsigned int iNumOutBones = pcHeader->bones_num;
+
+ for (std::vector<MaterialHelper*>::size_type i = 0; i < shared.pcMats.size();++i) {
+ if (!splittedGroupData.aiSplit[i]->empty()) {
+
+ // allocate the output mesh
+ aiMesh* pcMesh = new aiMesh();
+
+ pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+ pcMesh->mMaterialIndex = (unsigned int)i;
+
+ // allocate output storage
+ pcMesh->mNumFaces = (unsigned int)splittedGroupData.aiSplit[i]->size();
+ pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+
+ pcMesh->mNumVertices = pcMesh->mNumFaces*3;
+ pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+
+ if (!groupData.vTextureCoords1.empty()) {
+ pcMesh->mNumUVComponents[0] = 2;
+ pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ if (!groupData.vTextureCoords2.empty()) {
+ pcMesh->mNumUVComponents[1] = 2;
+ pcMesh->mTextureCoords[1] = new aiVector3D[pcMesh->mNumVertices];
+ }
+ }
+
+ // iterate through all faces and build an unique set of vertices
+ unsigned int iCurrent = 0;
+ for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace) {
+ pcMesh->mFaces[iFace].mNumIndices = 3;
+ pcMesh->mFaces[iFace].mIndices = new unsigned int[3];
+
+ unsigned int iSrcFace = splittedGroupData.aiSplit[i]->operator[](iFace);
+ const MDL::IntFace_MDL7& oldFace = groupData.pcFaces[iSrcFace];
+
+ // iterate through all face indices
+ for (unsigned int c = 0; c < 3;++c) {
+ const uint32_t iIndex = oldFace.mIndices[c];
+ pcMesh->mVertices[iCurrent] = groupData.vPositions[iIndex];
+ pcMesh->mNormals[iCurrent] = groupData.vNormals[iIndex];
+
+ if (!groupData.vTextureCoords1.empty()) {
+
+ pcMesh->mTextureCoords[0][iCurrent] = groupData.vTextureCoords1[iIndex];
+ if (!groupData.vTextureCoords2.empty()) {
+ pcMesh->mTextureCoords[1][iCurrent] = groupData.vTextureCoords2[iIndex];
+ }
+ }
+ pcMesh->mFaces[iFace].mIndices[c] = iCurrent++;
+ }
+ }
+
+ // if we have bones in the mesh we'll need to generate
+ // proper vertex weights for them
+ if (!groupData.aiBones.empty()) {
+ std::vector<std::vector<unsigned int> > aaiVWeightList;
+ aaiVWeightList.resize(iNumOutBones);
+
+ int iCurrent = 0;
+ for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace) {
+ unsigned int iSrcFace = splittedGroupData.aiSplit[i]->operator[](iFace);
+ const MDL::IntFace_MDL7& oldFace = groupData.pcFaces[iSrcFace];
+
+ // iterate through all face indices
+ for (unsigned int c = 0; c < 3;++c) {
+ unsigned int iBone = groupData.aiBones[ oldFace.mIndices[c] ];
+ if (0xffffffff != iBone) {
+ if (iBone >= iNumOutBones) {
+ DefaultLogger::get()->error("Bone index overflow. "
+ "The bone index of a vertex exceeds the allowed range. ");
+ iBone = iNumOutBones-1;
+ }
+ aaiVWeightList[ iBone ].push_back ( iCurrent );
+ }
+ ++iCurrent;
+ }
+ }
+ // now check which bones are required ...
+ for (std::vector<std::vector<unsigned int> >::const_iterator k = aaiVWeightList.begin();k != aaiVWeightList.end();++k) {
+ if (!(*k).empty()) {
+ ++pcMesh->mNumBones;
+ }
+ }
+ pcMesh->mBones = new aiBone*[pcMesh->mNumBones];
+ iCurrent = 0;
+ for (std::vector<std::vector<unsigned int> >::const_iterator k = aaiVWeightList.begin();k!= aaiVWeightList.end();++k,++iCurrent)
+ {
+ if ((*k).empty())
+ continue;
+
+ // seems we'll need this node
+ aiBone* pcBone = pcMesh->mBones[ iCurrent ] = new aiBone();
+ pcBone->mName = aiString(shared.apcOutBones[ iCurrent ]->mName);
+ pcBone->mOffsetMatrix = shared.apcOutBones[ iCurrent ]->mOffsetMatrix;
+
+ // setup vertex weights
+ pcBone->mNumWeights = (unsigned int)(*k).size();
+ pcBone->mWeights = new aiVertexWeight[pcBone->mNumWeights];
+
+ for (unsigned int weight = 0; weight < pcBone->mNumWeights;++weight) {
+ pcBone->mWeights[weight].mVertexId = (*k)[weight];
+ pcBone->mWeights[weight].mWeight = 1.0f;
+ }
+ }
+ }
+ // add the mesh to the list of output meshes
+ splittedGroupData.avOutList.push_back(pcMesh);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Join to materials
+void MDLImporter::JoinSkins_3DGS_MDL7(
+ MaterialHelper* pcMat1,
+ MaterialHelper* pcMat2,
+ MaterialHelper* pcMatOut)
+{
+ ai_assert(NULL != pcMat1 && NULL != pcMat2 && NULL != pcMatOut);
+
+ // first create a full copy of the first skin property set
+ // and assign it to the output material
+ MaterialHelper::CopyPropertyList(pcMatOut,pcMat1);
+
+ int iVal = 0;
+ pcMatOut->AddProperty<int>(&iVal,1,AI_MATKEY_UVWSRC_DIFFUSE(0));
+
+ // then extract the diffuse texture from the second skin,
+ // setup 1 as UV source and we have it
+ aiString sString;
+ if (AI_SUCCESS == aiGetMaterialString ( pcMat2, AI_MATKEY_TEXTURE_DIFFUSE(0),&sString )) {
+ iVal = 1;
+ pcMatOut->AddProperty<int>(&iVal,1,AI_MATKEY_UVWSRC_DIFFUSE(1));
+ pcMatOut->AddProperty(&sString,AI_MATKEY_TEXTURE_DIFFUSE(1));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a half-life 2 MDL
+void MDLImporter::InternReadFile_HL2( )
+{
+ //const MDL::Header_HL2* pcHeader = (const MDL::Header_HL2*)this->mBuffer;
+ throw DeadlyImportError("HL2 MDLs are not implemented");
+}
+
+#endif // !! ASSIMP_BUILD_NO_MDL_IMPORTER
diff --git a/src/3rdparty/assimp/code/MDLLoader.h b/src/3rdparty/assimp/code/MDLLoader.h
new file mode 100644
index 000000000..34c101fcb
--- /dev/null
+++ b/src/3rdparty/assimp/code/MDLLoader.h
@@ -0,0 +1,460 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file MDLLoader.h
+ * @brief Declaration of the loader for MDL files
+ */
+
+#ifndef AI_MDLLOADER_H_INCLUDED
+#define AI_MDLLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+
+struct aiNode;
+#include "MDLFileData.h"
+#include "HalfLifeFileData.h"
+
+namespace Assimp {
+class MaterialHelper;
+
+using namespace MDL;
+
+// --------------------------------------------------------------------------------------
+// Include file/line information in debug builds
+#ifdef ASSIMP_BUILD_DEBUG
+# define VALIDATE_FILE_SIZE(msg) SizeCheck(msg,__FILE__,__LINE__)
+#else
+# define VALIDATE_FILE_SIZE(msg) SizeCheck(msg)
+#endif
+
+// --------------------------------------------------------------------------------------
+/** @brief Class to load MDL files.
+ *
+ * Several subformats exist:
+ * <ul>
+ * <li>Quake I</li>
+ * <li>3D Game Studio MDL3, MDL4</li>
+ * <li>3D Game Studio MDL5</li>
+ * <li>3D Game Studio MDL7</li>
+ * <li>Halflife 2</li>
+ * </ul>
+ * These formats are partially identical and it would be possible to load
+ * them all with a single 1000-line function-beast. However, it has been
+ * splitted to several code paths to make the code easier to read and maintain.
+*/
+class MDLImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ MDLImporter();
+
+ /** Destructor, private as well */
+ ~MDLImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details. */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Import a quake 1 MDL file (IDPO)
+ */
+ void InternReadFile_Quake1( );
+
+ // -------------------------------------------------------------------
+ /** Import a GameStudio A4/A5 file (MDL 3,4,5)
+ */
+ void InternReadFile_3DGS_MDL345( );
+
+ // -------------------------------------------------------------------
+ /** Import a GameStudio A7 file (MDL 7)
+ */
+ void InternReadFile_3DGS_MDL7( );
+
+ // -------------------------------------------------------------------
+ /** Import a CS:S/HL2 MDL file (not fully implemented)
+ */
+ void InternReadFile_HL2( );
+
+ // -------------------------------------------------------------------
+ /** Check whether a given position is inside the valid range
+ * Throw a DeadlyImportError if it is not
+ * \param szPos Cursor position
+ * \param szFile Name of the source file from which the function was called
+ * \param iLine Source code line from which the function was called
+ */
+ void SizeCheck(const void* szPos);
+ void SizeCheck(const void* szPos, const char* szFile, unsigned int iLine);
+
+
+ // -------------------------------------------------------------------
+ /** Validate the header data structure of a game studio MDL7 file
+ * \param pcHeader Input header to be validated
+ */
+ void ValidateHeader_3DGS_MDL7(const MDL::Header_MDL7* pcHeader);
+
+ // -------------------------------------------------------------------
+ /** Validate the header data structure of a Quake 1 model
+ * \param pcHeader Input header to be validated
+ */
+ void ValidateHeader_Quake1(const MDL::Header* pcHeader);
+
+
+ // -------------------------------------------------------------------
+ /** Try to load a palette from the current directory (colormap.lmp)
+ * If it is not found the default palette of Quake1 is returned
+ */
+ void SearchPalette(const unsigned char** pszColorMap);
+
+ // -------------------------------------------------------------------
+ /** Free a palette created with a previous call to SearchPalette()
+ */
+ void FreePalette(const unsigned char* pszColorMap);
+
+
+ // -------------------------------------------------------------------
+ /** Load a paletized texture from the file and convert it to 32bpp
+ */
+ void CreateTextureARGB8_3DGS_MDL3(const unsigned char* szData);
+
+ // -------------------------------------------------------------------
+ /** Used to load textures from MDL3/4
+ * \param szData Input data
+ * \param iType Color data type
+ * \param piSkip Receive: Size to skip, in bytes
+ */
+ void CreateTexture_3DGS_MDL4(const unsigned char* szData,
+ unsigned int iType,
+ unsigned int* piSkip);
+
+
+ // -------------------------------------------------------------------
+ /** Used to load textures from MDL5
+ * \param szData Input data
+ * \param iType Color data type
+ * \param piSkip Receive: Size to skip, in bytes
+ */
+ void CreateTexture_3DGS_MDL5(const unsigned char* szData,
+ unsigned int iType,
+ unsigned int* piSkip);
+
+
+ // -------------------------------------------------------------------
+ /** Checks whether a texture can be replaced with a single color
+ * This is useful for all file formats before MDL7 (all those
+ * that are not containing material colors separate from textures).
+ * MED seems to write dummy 8x8 monochrome images instead.
+ * \param pcTexture Input texture
+ * \return aiColor.r is set to qnan if the function fails and no
+ * color can be found.
+ */
+ aiColor4D ReplaceTextureWithColor(const aiTexture* pcTexture);
+
+
+ // -------------------------------------------------------------------
+ /** Converts the absolute texture coordinates in MDL5 files to
+ * relative in a range between 0 and 1
+ */
+ void CalculateUVCoordinates_MDL5();
+
+
+ // -------------------------------------------------------------------
+ /** Read an UV coordinate from the file. If the file format is not
+ * MDL5, the function calculates relative texture coordinates
+ * \param vOut Receives the output UV coord
+ * \param pcSrc UV coordinate buffer
+ * \param UV coordinate index
+ */
+ void ImportUVCoordinate_3DGS_MDL345( aiVector3D& vOut,
+ const MDL::TexCoord_MDL3* pcSrc,
+ unsigned int iIndex);
+
+ // -------------------------------------------------------------------
+ /** Setup the material properties for Quake and MDL<7 models.
+ * These formats don't support more than one material per mesh,
+ * therefore the method processes only ONE skin and removes
+ * all others.
+ */
+ void SetupMaterialProperties_3DGS_MDL5_Quake1( );
+
+
+ // -------------------------------------------------------------------
+ /** Parse a skin lump in a MDL7/HMP7 file with all of its features
+ * variant 1: Current cursor position is the beginning of the skin header
+ * \param szCurrent Current data pointer
+ * \param szCurrentOut Output data pointer
+ * \param pcMats Material list for this group. To be filled ...
+ */
+ void ParseSkinLump_3DGS_MDL7(
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut,
+ std::vector<MaterialHelper*>& pcMats);
+
+ // -------------------------------------------------------------------
+ /** Parse a skin lump in a MDL7/HMP7 file with all of its features
+ * variant 2: Current cursor position is the beginning of the skin data
+ * \param szCurrent Current data pointer
+ * \param szCurrentOut Output data pointer
+ * \param pcMatOut Output material
+ * \param iType header.typ
+ * \param iWidth header.width
+ * \param iHeight header.height
+ */
+ void ParseSkinLump_3DGS_MDL7(
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut,
+ MaterialHelper* pcMatOut,
+ unsigned int iType,
+ unsigned int iWidth,
+ unsigned int iHeight);
+
+ // -------------------------------------------------------------------
+ /** Skip a skin lump in a MDL7/HMP7 file
+ * \param szCurrent Current data pointer
+ * \param szCurrentOut Output data pointer. Points to the byte just
+ * behind the last byte of the skin.
+ * \param iType header.typ
+ * \param iWidth header.width
+ * \param iHeight header.height
+ */
+ void SkipSkinLump_3DGS_MDL7(const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut,
+ unsigned int iType,
+ unsigned int iWidth,
+ unsigned int iHeight);
+
+ // -------------------------------------------------------------------
+ /** Parse texture color data for MDL5, MDL6 and MDL7 formats
+ * \param szData Current data pointer
+ * \param iType type of the texture data. No DDS or external
+ * \param piSkip Receive the number of bytes to skip
+ * \param pcNew Must point to fully initialized data. Width and
+ * height must be set. If pcNew->pcData is set to 0xffffffff,
+ * piSkip will receive the size of the texture, in bytes, but no
+ * color data will be read.
+ */
+ void ParseTextureColorData(const unsigned char* szData,
+ unsigned int iType,
+ unsigned int* piSkip,
+ aiTexture* pcNew);
+
+ // -------------------------------------------------------------------
+ /** Join two materials / skins. Setup UV source ... etc
+ * \param pcMat1 First input material
+ * \param pcMat2 Second input material
+ * \param pcMatOut Output material instance to be filled. Must be empty
+ */
+ void JoinSkins_3DGS_MDL7(MaterialHelper* pcMat1,
+ MaterialHelper* pcMat2,
+ MaterialHelper* pcMatOut);
+
+ // -------------------------------------------------------------------
+ /** Add a bone transformation key to an animation
+ * \param iTrafo Index of the transformation (always==frame index?)
+ * No need to validate this index, it is always valid.
+ * \param pcBoneTransforms Bone transformation for this index
+ * \param apcOutBones Output bones array
+ */
+ void AddAnimationBoneTrafoKey_3DGS_MDL7(unsigned int iTrafo,
+ const MDL::BoneTransform_MDL7* pcBoneTransforms,
+ MDL::IntBone_MDL7** apcBonesOut);
+
+ // -------------------------------------------------------------------
+ /** Load the bone list of a MDL7 file
+ * \return If the bones could be loaded successfully, a valid
+ * array containing pointers to a temporary bone
+ * representation. NULL if the bones could not be loaded.
+ */
+ MDL::IntBone_MDL7** LoadBones_3DGS_MDL7();
+
+ // -------------------------------------------------------------------
+ /** Load bone transformation keyframes from a file chunk
+ * \param groupInfo -> doc of data structure
+ * \param frame -> doc of data structure
+ * \param shared -> doc of data structure
+ */
+ void ParseBoneTrafoKeys_3DGS_MDL7(
+ const MDL::IntGroupInfo_MDL7& groupInfo,
+ IntFrameInfo_MDL7& frame,
+ MDL::IntSharedData_MDL7& shared);
+
+ // -------------------------------------------------------------------
+ /** Calculate absolute bone animation matrices for each bone
+ * \param apcOutBones Output bones array
+ */
+ void CalcAbsBoneMatrices_3DGS_MDL7(MDL::IntBone_MDL7** apcOutBones);
+
+ // -------------------------------------------------------------------
+ /** Add all bones to the nodegraph (as children of the root node)
+ * \param apcBonesOut List of bones
+ * \param pcParent Parent node. New nodes will be added to this node
+ * \param iParentIndex Index of the parent bone
+ */
+ void AddBonesToNodeGraph_3DGS_MDL7(const MDL::IntBone_MDL7** apcBonesOut,
+ aiNode* pcParent,uint16_t iParentIndex);
+
+ // -------------------------------------------------------------------
+ /** Build output animations
+ * \param apcBonesOut List of bones
+ */
+ void BuildOutputAnims_3DGS_MDL7(const MDL::IntBone_MDL7** apcBonesOut);
+
+ // -------------------------------------------------------------------
+ /** Handles materials that are just referencing another material
+ * There is no test file for this feature, but Conitec's doc
+ * say it is used.
+ */
+ void HandleMaterialReferences_3DGS_MDL7();
+
+ // -------------------------------------------------------------------
+ /** Copies only the material that are referenced by at least one
+ * mesh to the final output material list. All other materials
+ * will be discarded.
+ * \param shared -> doc of data structure
+ */
+ void CopyMaterials_3DGS_MDL7(MDL::IntSharedData_MDL7 &shared);
+
+ // -------------------------------------------------------------------
+ /** Process the frame section at the end of a group
+ * \param groupInfo -> doc of data structure
+ * \param shared -> doc of data structure
+ * \param szCurrent Pointer to the start of the frame section
+ * \param szCurrentOut Receives a pointer to the first byte of the
+ * next data section.
+ * \return false to read no further groups (a small workaround for
+ * some tiny and unsolved problems ... )
+ */
+ bool ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
+ MDL::IntGroupData_MDL7& groupData,
+ MDL::IntSharedData_MDL7& shared,
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut);
+
+ // -------------------------------------------------------------------
+ /** Sort all faces by their materials. If the mesh is using
+ * multiple materials per face (that are blended together) the function
+ * might create new materials.
+ * \param groupInfo -> doc of data structure
+ * \param groupData -> doc of data structure
+ * \param splittedGroupData -> doc of data structure
+ */
+ void SortByMaterials_3DGS_MDL7(
+ const MDL::IntGroupInfo_MDL7& groupInfo,
+ MDL::IntGroupData_MDL7& groupData,
+ MDL::IntSplittedGroupData_MDL7& splittedGroupData);
+
+ // -------------------------------------------------------------------
+ /** Read all faces and vertices from a MDL7 group. The function fills
+ * preallocated memory buffers.
+ * \param groupInfo -> doc of data structure
+ * \param groupData -> doc of data structure
+ */
+ void ReadFaces_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
+ MDL::IntGroupData_MDL7& groupData);
+
+ // -------------------------------------------------------------------
+ /** Generate the final output meshes for a7 models
+ * \param groupData -> doc of data structure
+ * \param splittedGroupData -> doc of data structure
+ */
+ void GenerateOutputMeshes_3DGS_MDL7(
+ MDL::IntGroupData_MDL7& groupData,
+ MDL::IntSplittedGroupData_MDL7& splittedGroupData);
+
+protected:
+
+ /** Configuration option: frame to be loaded */
+ unsigned int configFrameID;
+
+ /** Configuration option: palette to be used to decode palletized images*/
+ std::string configPalette;
+
+ /** Buffer to hold the loaded file */
+ unsigned char* mBuffer;
+
+ /** For GameStudio MDL files: The number in the magic word, either 3,4 or 5
+ * (MDL7 doesn't need this, the format has a separate loader) */
+ unsigned int iGSFileVersion;
+
+ /** Output I/O handler. used to load external lmp files */
+ IOSystem* pIOHandler;
+
+ /** Output scene to be filled */
+ aiScene* pScene;
+
+ /** Size of the input file in bytes */
+ unsigned int iFileSize;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/MDLMaterialLoader.cpp b/src/3rdparty/assimp/code/MDLMaterialLoader.cpp
new file mode 100644
index 000000000..d7b5f564e
--- /dev/null
+++ b/src/3rdparty/assimp/code/MDLMaterialLoader.cpp
@@ -0,0 +1,827 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the material part of the MDL importer class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
+
+// internal headers
+#include "MDLLoader.h"
+#include "MDLDefaultColorMap.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Find a suitable pallette file or take teh default one
+void MDLImporter::SearchPalette(const unsigned char** pszColorMap)
+{
+ // now try to find the color map in the current directory
+ IOStream* pcStream = pIOHandler->Open(configPalette,"rb");
+
+ const unsigned char* szColorMap = (const unsigned char*)::g_aclrDefaultColorMap;
+ if (pcStream)
+ {
+ if (pcStream->FileSize() >= 768)
+ {
+ szColorMap = new unsigned char[256*3];
+ pcStream->Read(const_cast<unsigned char*>(szColorMap),256*3,1);
+
+ DefaultLogger::get()->info("Found valid colormap.lmp in directory. "
+ "It will be used to decode embedded textures in palletized formats.");
+ }
+ delete pcStream;
+ pcStream = NULL;
+ }
+ *pszColorMap = szColorMap;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Free the palette again
+void MDLImporter::FreePalette(const unsigned char* szColorMap)
+{
+ if (szColorMap != (const unsigned char*)::g_aclrDefaultColorMap)
+ delete[] szColorMap;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Check whether we can replace a texture with a single color
+aiColor4D MDLImporter::ReplaceTextureWithColor(const aiTexture* pcTexture)
+{
+ ai_assert(NULL != pcTexture);
+
+ aiColor4D clrOut;
+ clrOut.r = get_qnan();
+ if (!pcTexture->mHeight || !pcTexture->mWidth)
+ return clrOut;
+
+ const unsigned int iNumPixels = pcTexture->mHeight*pcTexture->mWidth;
+ const aiTexel* pcTexel = pcTexture->pcData+1;
+ const aiTexel* const pcTexelEnd = &pcTexture->pcData[iNumPixels];
+
+ while (pcTexel != pcTexelEnd)
+ {
+ if (*pcTexel != *(pcTexel-1))
+ {
+ pcTexel = NULL;
+ break;
+ }
+ ++pcTexel;
+ }
+ if (pcTexel)
+ {
+ clrOut.r = pcTexture->pcData->r / 255.0f;
+ clrOut.g = pcTexture->pcData->g / 255.0f;
+ clrOut.b = pcTexture->pcData->b / 255.0f;
+ clrOut.a = pcTexture->pcData->a / 255.0f;
+ }
+ return clrOut;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a texture from a MDL3 file
+void MDLImporter::CreateTextureARGB8_3DGS_MDL3(const unsigned char* szData)
+{
+ const MDL::Header *pcHeader = (const MDL::Header*)mBuffer; //the endianess is allready corrected in the InternReadFile_3DGS_MDL345 function
+
+ VALIDATE_FILE_SIZE(szData + pcHeader->skinwidth *
+ pcHeader->skinheight);
+
+ // allocate a new texture object
+ aiTexture* pcNew = new aiTexture();
+ pcNew->mWidth = pcHeader->skinwidth;
+ pcNew->mHeight = pcHeader->skinheight;
+
+ pcNew->pcData = new aiTexel[pcNew->mWidth * pcNew->mHeight];
+
+ const unsigned char* szColorMap;
+ this->SearchPalette(&szColorMap);
+
+ // copy texture data
+ for (unsigned int i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+ {
+ const unsigned char val = szData[i];
+ const unsigned char* sz = &szColorMap[val*3];
+
+ pcNew->pcData[i].a = 0xFF;
+ pcNew->pcData[i].r = *sz++;
+ pcNew->pcData[i].g = *sz++;
+ pcNew->pcData[i].b = *sz;
+ }
+
+ FreePalette(szColorMap);
+
+ // store the texture
+ aiTexture** pc = this->pScene->mTextures;
+ this->pScene->mTextures = new aiTexture*[pScene->mNumTextures+1];
+ for (unsigned int i = 0; i <pScene->mNumTextures;++i)
+ pScene->mTextures[i] = pc[i];
+
+ pScene->mTextures[this->pScene->mNumTextures] = pcNew;
+ pScene->mNumTextures++;
+ delete[] pc;
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a texture from a MDL4 file
+void MDLImporter::CreateTexture_3DGS_MDL4(const unsigned char* szData,
+ unsigned int iType,
+ unsigned int* piSkip)
+{
+ ai_assert(NULL != piSkip);
+
+ const MDL::Header *pcHeader = (const MDL::Header*)mBuffer; //the endianess is allready corrected in the InternReadFile_3DGS_MDL345 function
+
+ if (iType == 1 || iType > 3)
+ {
+ DefaultLogger::get()->error("Unsupported texture file format");
+ return;
+ }
+
+ bool bNoRead = *piSkip == 0xffffffff;
+
+ // allocate a new texture object
+ aiTexture* pcNew = new aiTexture();
+ pcNew->mWidth = pcHeader->skinwidth;
+ pcNew->mHeight = pcHeader->skinheight;
+
+ if (bNoRead)pcNew->pcData = (aiTexel*)0xffffffff;
+ ParseTextureColorData(szData,iType,piSkip,pcNew);
+
+ // store the texture
+ if (!bNoRead)
+ {
+ if (!this->pScene->mNumTextures)
+ {
+ pScene->mNumTextures = 1;
+ pScene->mTextures = new aiTexture*[1];
+ pScene->mTextures[0] = pcNew;
+ }
+ else
+ {
+ aiTexture** pc = pScene->mTextures;
+ pScene->mTextures = new aiTexture*[pScene->mNumTextures+1];
+ for (unsigned int i = 0; i < this->pScene->mNumTextures;++i)
+ pScene->mTextures[i] = pc[i];
+ pScene->mTextures[pScene->mNumTextures] = pcNew;
+ pScene->mNumTextures++;
+ delete[] pc;
+ }
+ }
+ else {
+ pcNew->pcData = NULL;
+ delete pcNew;
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load color data of a texture and convert it to our output format
+void MDLImporter::ParseTextureColorData(const unsigned char* szData,
+ unsigned int iType,
+ unsigned int* piSkip,
+ aiTexture* pcNew)
+{
+ // allocate storage for the texture image
+ if ((aiTexel*)0xffffffff != pcNew->pcData)
+ pcNew->pcData = new aiTexel[pcNew->mWidth * pcNew->mHeight];
+
+ // R5G6B5 format (with or without MIPs)
+ // ****************************************************************
+ if (2 == iType || 10 == iType)
+ {
+ VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*2);
+
+ // copy texture data
+ unsigned int i;
+ if ((aiTexel*)0xffffffff != pcNew->pcData)
+ {
+ for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+ {
+ MDL::RGB565 val = ((MDL::RGB565*)szData)[i];
+ AI_SWAP2(val);
+
+ pcNew->pcData[i].a = 0xFF;
+ pcNew->pcData[i].r = (unsigned char)val.b << 3;
+ pcNew->pcData[i].g = (unsigned char)val.g << 2;
+ pcNew->pcData[i].b = (unsigned char)val.r << 3;
+ }
+ }
+ else i = pcNew->mWidth*pcNew->mHeight;
+ *piSkip = i * 2;
+
+ // apply MIP maps
+ if (10 == iType)
+ {
+ *piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 1;
+ VALIDATE_FILE_SIZE(szData + *piSkip);
+ }
+ }
+ // ARGB4 format (with or without MIPs)
+ // ****************************************************************
+ else if (3 == iType || 11 == iType)
+ {
+ VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*4);
+
+ // copy texture data
+ unsigned int i;
+ if ((aiTexel*)0xffffffff != pcNew->pcData)
+ {
+ for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+ {
+ MDL::ARGB4 val = ((MDL::ARGB4*)szData)[i];
+ AI_SWAP2(val);
+
+ pcNew->pcData[i].a = (unsigned char)val.a << 4;
+ pcNew->pcData[i].r = (unsigned char)val.r << 4;
+ pcNew->pcData[i].g = (unsigned char)val.g << 4;
+ pcNew->pcData[i].b = (unsigned char)val.b << 4;
+ }
+ }
+ else i = pcNew->mWidth*pcNew->mHeight;
+ *piSkip = i * 2;
+
+ // apply MIP maps
+ if (11 == iType)
+ {
+ *piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 1;
+ VALIDATE_FILE_SIZE(szData + *piSkip);
+ }
+ }
+ // RGB8 format (with or without MIPs)
+ // ****************************************************************
+ else if (4 == iType || 12 == iType)
+ {
+ VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*3);
+
+ // copy texture data
+ unsigned int i;
+ if ((aiTexel*)0xffffffff != pcNew->pcData)
+ {
+ for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+ {
+ const unsigned char* _szData = &szData[i*3];
+
+ pcNew->pcData[i].a = 0xFF;
+ pcNew->pcData[i].b = *_szData++;
+ pcNew->pcData[i].g = *_szData++;
+ pcNew->pcData[i].r = *_szData;
+ }
+ }
+ else i = pcNew->mWidth*pcNew->mHeight;
+
+
+ // apply MIP maps
+ *piSkip = i * 3;
+ if (12 == iType)
+ {
+ *piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) *3;
+ VALIDATE_FILE_SIZE(szData + *piSkip);
+ }
+ }
+ // ARGB8 format (with ir without MIPs)
+ // ****************************************************************
+ else if (5 == iType || 13 == iType)
+ {
+ VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*4);
+
+ // copy texture data
+ unsigned int i;
+ if ((aiTexel*)0xffffffff != pcNew->pcData)
+ {
+ for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+ {
+ const unsigned char* _szData = &szData[i*4];
+
+ pcNew->pcData[i].b = *_szData++;
+ pcNew->pcData[i].g = *_szData++;
+ pcNew->pcData[i].r = *_szData++;
+ pcNew->pcData[i].a = *_szData;
+ }
+ }
+ else i = pcNew->mWidth*pcNew->mHeight;
+
+ // apply MIP maps
+ *piSkip = i << 2;
+ if (13 == iType)
+ {
+ *piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 2;
+ }
+ }
+ // palletized 8 bit texture. As for Quake 1
+ // ****************************************************************
+ else if (0 == iType)
+ {
+ VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight);
+
+ // copy texture data
+ unsigned int i;
+ if ((aiTexel*)0xffffffff != pcNew->pcData)
+ {
+
+ const unsigned char* szColorMap;
+ this->SearchPalette(&szColorMap);
+
+ for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+ {
+ const unsigned char val = szData[i];
+ const unsigned char* sz = &szColorMap[val*3];
+
+ pcNew->pcData[i].a = 0xFF;
+ pcNew->pcData[i].r = *sz++;
+ pcNew->pcData[i].g = *sz++;
+ pcNew->pcData[i].b = *sz;
+ }
+ this->FreePalette(szColorMap);
+
+ }
+ else i = pcNew->mWidth*pcNew->mHeight;
+ *piSkip = i;
+
+ // FIXME: Also support for MIP maps?
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a texture from a MDL5 file
+void MDLImporter::CreateTexture_3DGS_MDL5(const unsigned char* szData,
+ unsigned int iType,
+ unsigned int* piSkip)
+{
+ ai_assert(NULL != piSkip);
+ bool bNoRead = *piSkip == 0xffffffff;
+
+ // allocate a new texture object
+ aiTexture* pcNew = new aiTexture();
+
+ VALIDATE_FILE_SIZE(szData+8);
+
+ // first read the size of the texture
+ pcNew->mWidth = *((uint32_t*)szData);
+ AI_SWAP4(pcNew->mWidth);
+ szData += sizeof(uint32_t);
+
+ pcNew->mHeight = *((uint32_t*)szData);
+ AI_SWAP4(pcNew->mHeight);
+ szData += sizeof(uint32_t);
+
+ if (bNoRead)pcNew->pcData = (aiTexel*)0xffffffff;
+
+ // this should not occur - at least the docs say it shouldn't
+ // however, you can easily try out what MED does if you have
+ // a model with a DDS texture and export it to MDL5 ...
+ // yes, you're right. It embedds the DDS texture ... :cry:
+ if (6 == iType)
+ {
+ // this is a compressed texture in DDS format
+ *piSkip = pcNew->mWidth;
+ VALIDATE_FILE_SIZE(szData + *piSkip);
+
+ if (!bNoRead)
+ {
+ // place a hint and let the application know that it's
+ // a DDS file
+ pcNew->mHeight = 0;
+ pcNew->achFormatHint[0] = 'd';
+ pcNew->achFormatHint[1] = 'd';
+ pcNew->achFormatHint[2] = 's';
+ pcNew->achFormatHint[3] = '\0';
+
+ pcNew->pcData = (aiTexel*) new unsigned char[pcNew->mWidth];
+ ::memcpy(pcNew->pcData,szData,pcNew->mWidth);
+ }
+ }
+ else
+ {
+ // parse the color data of the texture
+ ParseTextureColorData(szData,iType,
+ piSkip,pcNew);
+ }
+ *piSkip += sizeof(uint32_t) * 2;
+
+ if (!bNoRead)
+ {
+ // store the texture
+ if (!this->pScene->mNumTextures)
+ {
+ pScene->mNumTextures = 1;
+ pScene->mTextures = new aiTexture*[1];
+ pScene->mTextures[0] = pcNew;
+ }
+ else
+ {
+ aiTexture** pc = pScene->mTextures;
+ pScene->mTextures = new aiTexture*[pScene->mNumTextures+1];
+ for (unsigned int i = 0; i < pScene->mNumTextures;++i)
+ this->pScene->mTextures[i] = pc[i];
+
+ pScene->mTextures[pScene->mNumTextures] = pcNew;
+ pScene->mNumTextures++;
+ delete[] pc;
+ }
+ }
+ else {
+ pcNew->pcData = NULL;
+ delete pcNew;
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a skin from a MDL7 file - more complex than all other subformats
+void MDLImporter::ParseSkinLump_3DGS_MDL7(
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut,
+ MaterialHelper* pcMatOut,
+ unsigned int iType,
+ unsigned int iWidth,
+ unsigned int iHeight)
+{
+ aiTexture* pcNew = NULL;
+
+ // get the type of the skin
+ unsigned int iMasked = (unsigned int)(iType & 0xF);
+
+ if (0x1 == iMasked)
+ {
+ // ***** REFERENCE TO ANOTHER SKIN INDEX *****
+ int referrer = (int)iWidth;
+ pcMatOut->AddProperty<int>(&referrer,1,AI_MDL7_REFERRER_MATERIAL);
+ }
+ else if (0x6 == iMasked)
+ {
+ // ***** EMBEDDED DDS FILE *****
+ if (1 != iHeight)
+ {
+ DefaultLogger::get()->warn("Found a reference to an embedded DDS texture, "
+ "but texture height is not equal to 1, which is not supported by MED");
+ }
+
+ pcNew = new aiTexture();
+ pcNew->mHeight = 0;
+ pcNew->mWidth = iWidth;
+
+ // place a proper format hint
+ pcNew->achFormatHint[0] = 'd';
+ pcNew->achFormatHint[1] = 'd';
+ pcNew->achFormatHint[2] = 's';
+ pcNew->achFormatHint[3] = '\0';
+
+ pcNew->pcData = (aiTexel*) new unsigned char[pcNew->mWidth];
+ memcpy(pcNew->pcData,szCurrent,pcNew->mWidth);
+ szCurrent += iWidth;
+ }
+ if (0x7 == iMasked)
+ {
+ // ***** REFERENCE TO EXTERNAL FILE *****
+ if (1 != iHeight)
+ {
+ DefaultLogger::get()->warn("Found a reference to an external texture, "
+ "but texture height is not equal to 1, which is not supported by MED");
+ }
+
+ aiString szFile;
+ const size_t iLen = strlen((const char*)szCurrent);
+ size_t iLen2 = iLen+1;
+ iLen2 = iLen2 > MAXLEN ? MAXLEN : iLen2;
+ memcpy(szFile.data,(const char*)szCurrent,iLen2);
+ szFile.length = iLen;
+
+ szCurrent += iLen2;
+
+ // place this as diffuse texture
+ pcMatOut->AddProperty(&szFile,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ else if (iMasked || !iType || (iType && iWidth && iHeight))
+ {
+ // ***** STANDARD COLOR TEXTURE *****
+ pcNew = new aiTexture();
+ if (!iHeight || !iWidth)
+ {
+ DefaultLogger::get()->warn("Found embedded texture, but its width "
+ "an height are both 0. Is this a joke?");
+
+ // generate an empty chess pattern
+ pcNew->mWidth = pcNew->mHeight = 8;
+ pcNew->pcData = new aiTexel[64];
+ for (unsigned int x = 0; x < 8;++x)
+ {
+ for (unsigned int y = 0; y < 8;++y)
+ {
+ const bool bSet = ((0 == x % 2 && 0 != y % 2) ||
+ (0 != x % 2 && 0 == y % 2));
+
+ aiTexel* pc = &pcNew->pcData[y * 8 + x];
+ pc->r = pc->b = pc->g = (bSet?0xFF:0);
+ pc->a = 0xFF;
+ }
+ }
+ }
+ else
+ {
+ // it is a standard color texture. Fill in width and height
+ // and call the same function we used for loading MDL5 files
+
+ pcNew->mWidth = iWidth;
+ pcNew->mHeight = iHeight;
+
+ unsigned int iSkip = 0;
+ this->ParseTextureColorData(szCurrent,iMasked,&iSkip,pcNew);
+
+ // skip length of texture data
+ szCurrent += iSkip;
+ }
+ }
+
+ // sometimes there are MDL7 files which have a monochrome
+ // texture instead of material colors ... posssible they have
+ // been converted to MDL7 from other formats, such as MDL5
+ aiColor4D clrTexture;
+ if (pcNew)clrTexture = this->ReplaceTextureWithColor(pcNew);
+ else clrTexture.r = get_qnan();
+
+ // check whether a material definition is contained in the skin
+ if (iType & AI_MDL7_SKINTYPE_MATERIAL)
+ {
+ BE_NCONST MDL::Material_MDL7* pcMatIn = (BE_NCONST MDL::Material_MDL7*)szCurrent;
+ szCurrent = (unsigned char*)(pcMatIn+1);
+ VALIDATE_FILE_SIZE(szCurrent);
+
+ aiColor3D clrTemp;
+
+#define COLOR_MULTIPLY_RGB() \
+ if (is_not_qnan(clrTexture.r)) \
+ { \
+ clrTemp.r *= clrTexture.r; \
+ clrTemp.g *= clrTexture.g; \
+ clrTemp.b *= clrTexture.b; \
+ }
+
+ // read diffuse color
+ clrTemp.r = pcMatIn->Diffuse.r;
+ AI_SWAP4(clrTemp.r);
+ clrTemp.g = pcMatIn->Diffuse.g;
+ AI_SWAP4(clrTemp.g);
+ clrTemp.b = pcMatIn->Diffuse.b;
+ AI_SWAP4(clrTemp.b);
+ COLOR_MULTIPLY_RGB();
+ pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_DIFFUSE);
+
+ // read specular color
+ clrTemp.r = pcMatIn->Specular.r;
+ AI_SWAP4(clrTemp.r);
+ clrTemp.g = pcMatIn->Specular.g;
+ AI_SWAP4(clrTemp.g);
+ clrTemp.b = pcMatIn->Specular.b;
+ AI_SWAP4(clrTemp.b);
+ COLOR_MULTIPLY_RGB();
+ pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_SPECULAR);
+
+ // read ambient color
+ clrTemp.r = pcMatIn->Ambient.r;
+ AI_SWAP4(clrTemp.r);
+ clrTemp.g = pcMatIn->Ambient.g;
+ AI_SWAP4(clrTemp.g);
+ clrTemp.b = pcMatIn->Ambient.b;
+ AI_SWAP4(clrTemp.b);
+ COLOR_MULTIPLY_RGB();
+ pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_AMBIENT);
+
+ // read emissive color
+ clrTemp.r = pcMatIn->Emissive.r;
+ AI_SWAP4(clrTemp.r);
+ clrTemp.g = pcMatIn->Emissive.g;
+ AI_SWAP4(clrTemp.g);
+ clrTemp.b = pcMatIn->Emissive.b;
+ AI_SWAP4(clrTemp.b);
+ pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_EMISSIVE);
+
+#undef COLOR_MULITPLY_RGB
+
+ // FIX: Take the opacity from the ambient color
+ // the doc says something else, but it is fact that MED exports the
+ // opacity like this .... ARRRGGHH!
+ clrTemp.r = pcMatIn->Ambient.a;
+ AI_SWAP4(clrTemp.r);
+ if (is_not_qnan(clrTexture.r)) {
+ clrTemp.r *= clrTexture.a;
+ }
+ pcMatOut->AddProperty<float>(&clrTemp.r,1,AI_MATKEY_OPACITY);
+
+ // read phong power
+ int iShadingMode = (int)aiShadingMode_Gouraud;
+ AI_SWAP4(pcMatIn->Power);
+ if (0.0f != pcMatIn->Power)
+ {
+ iShadingMode = (int)aiShadingMode_Phong;
+ pcMatOut->AddProperty<float>(&pcMatIn->Power,1,AI_MATKEY_SHININESS);
+ }
+ pcMatOut->AddProperty<int>(&iShadingMode,1,AI_MATKEY_SHADING_MODEL);
+ }
+ else if (is_not_qnan(clrTexture.r))
+ {
+ pcMatOut->AddProperty<aiColor4D>(&clrTexture,1,AI_MATKEY_COLOR_DIFFUSE);
+ pcMatOut->AddProperty<aiColor4D>(&clrTexture,1,AI_MATKEY_COLOR_SPECULAR);
+ }
+ // if the texture could be replaced by a single material color
+ // we don't need the texture anymore
+ if (is_not_qnan(clrTexture.r))
+ {
+ delete pcNew;
+ pcNew = NULL;
+ }
+
+ // if an ASCII effect description (HLSL?) is contained in the file,
+ // we can simply ignore it ...
+ if (iType & AI_MDL7_SKINTYPE_MATERIAL_ASCDEF)
+ {
+ VALIDATE_FILE_SIZE(szCurrent);
+ int32_t iMe = *((int32_t*)szCurrent);
+ AI_SWAP4(iMe);
+ szCurrent += sizeof(char) * iMe + sizeof(int32_t);
+ VALIDATE_FILE_SIZE(szCurrent);
+ }
+
+ // If an embedded texture has been loaded setup the corresponding
+ // data structures in the aiScene instance
+ if (pcNew && this->pScene->mNumTextures <= 999)
+ {
+
+ // place this as diffuse texture
+ char szCurrent[5];
+ ::sprintf(szCurrent,"*%i",this->pScene->mNumTextures);
+
+ aiString szFile;
+ const size_t iLen = strlen((const char*)szCurrent);
+ ::memcpy(szFile.data,(const char*)szCurrent,iLen+1);
+ szFile.length = iLen;
+
+ pcMatOut->AddProperty(&szFile,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ // store the texture
+ if (!this->pScene->mNumTextures)
+ {
+ this->pScene->mNumTextures = 1;
+ this->pScene->mTextures = new aiTexture*[1];
+ this->pScene->mTextures[0] = pcNew;
+ }
+ else
+ {
+ aiTexture** pc = this->pScene->mTextures;
+ this->pScene->mTextures = new aiTexture*[this->pScene->mNumTextures+1];
+ for (unsigned int i = 0; i < this->pScene->mNumTextures;++i)
+ this->pScene->mTextures[i] = pc[i];
+
+ this->pScene->mTextures[this->pScene->mNumTextures] = pcNew;
+ this->pScene->mNumTextures++;
+ delete[] pc;
+ }
+ }
+ VALIDATE_FILE_SIZE(szCurrent);
+ *szCurrentOut = szCurrent;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skip a skin lump
+void MDLImporter::SkipSkinLump_3DGS_MDL7(
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut,
+ unsigned int iType,
+ unsigned int iWidth,
+ unsigned int iHeight)
+{
+ // get the type of the skin
+ unsigned int iMasked = (unsigned int)(iType & 0xF);
+
+ if (0x6 == iMasked)
+ {
+ szCurrent += iWidth;
+ }
+ if (0x7 == iMasked)
+ {
+ const size_t iLen = ::strlen((const char*)szCurrent);
+ szCurrent += iLen+1;
+ }
+ else if (iMasked || !iType)
+ {
+ if (iMasked || !iType || (iType && iWidth && iHeight))
+ {
+ // ParseTextureColorData(..., aiTexture::pcData == 0xffffffff) will simply
+ // return the size of the color data in bytes in iSkip
+ unsigned int iSkip = 0;
+
+ aiTexture tex;
+ tex.pcData = reinterpret_cast<aiTexel*>(0xffffffff);
+ tex.mHeight = iHeight;
+ tex.mWidth = iWidth;
+ this->ParseTextureColorData(szCurrent,iMasked,&iSkip,&tex);
+
+ // FIX: Important, otherwise the destructor will crash
+ tex.pcData = NULL;
+
+ // skip length of texture data
+ szCurrent += iSkip;
+ }
+ }
+
+ // check whether a material definition is contained in the skin
+ if (iType & AI_MDL7_SKINTYPE_MATERIAL)
+ {
+ BE_NCONST MDL::Material_MDL7* pcMatIn = (BE_NCONST MDL::Material_MDL7*)szCurrent;
+ szCurrent = (unsigned char*)(pcMatIn+1);
+ }
+
+ // if an ASCII effect description (HLSL?) is contained in the file,
+ // we can simply ignore it ...
+ if (iType & AI_MDL7_SKINTYPE_MATERIAL_ASCDEF)
+ {
+ int32_t iMe = *((int32_t*)szCurrent);
+ AI_SWAP4(iMe);
+ szCurrent += sizeof(char) * iMe + sizeof(int32_t);
+ }
+ *szCurrentOut = szCurrent;
+}
+
+// ------------------------------------------------------------------------------------------------
+// What does this function do? Can't remember
+void MDLImporter::ParseSkinLump_3DGS_MDL7(
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut,
+ std::vector<MaterialHelper*>& pcMats)
+{
+ ai_assert(NULL != szCurrent);
+ ai_assert(NULL != szCurrentOut);
+
+ *szCurrentOut = szCurrent;
+ BE_NCONST MDL::Skin_MDL7* pcSkin = (BE_NCONST MDL::Skin_MDL7*)szCurrent;
+ AI_SWAP4(pcSkin->width);
+ AI_SWAP4(pcSkin->height);
+ szCurrent += 12;
+
+ // allocate an output material
+ MaterialHelper* pcMatOut = new MaterialHelper();
+ pcMats.push_back(pcMatOut);
+
+ // skip length of file name
+ szCurrent += AI_MDL7_MAX_TEXNAMESIZE;
+
+ ParseSkinLump_3DGS_MDL7(szCurrent,szCurrentOut,pcMatOut,
+ pcSkin->typ,pcSkin->width,pcSkin->height);
+
+ // place the name of the skin in the material
+ if (pcSkin->texture_name[0])
+ {
+ // the 0 termination could be there or not - we can't know
+ aiString szFile;
+ ::memcpy(szFile.data,pcSkin->texture_name,sizeof(pcSkin->texture_name));
+ szFile.data[sizeof(pcSkin->texture_name)] = '\0';
+ szFile.length = ::strlen(szFile.data);
+
+ pcMatOut->AddProperty(&szFile,AI_MATKEY_NAME);
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_MDL_IMPORTER
diff --git a/src/3rdparty/assimp/code/MS3DLoader.cpp b/src/3rdparty/assimp/code/MS3DLoader.cpp
new file mode 100644
index 000000000..3d5abcc47
--- /dev/null
+++ b/src/3rdparty/assimp/code/MS3DLoader.cpp
@@ -0,0 +1,643 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file MS3DLoader.cpp
+ * @brief Implementation of the Ms3D importer class.
+ * Written against http://chumbalum.swissquake.ch/ms3d/ms3dspec.txt
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER
+
+// internal headers
+#include "MS3DLoader.h"
+#include "StreamReader.h"
+using namespace Assimp;
+
+// ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
+// (enable old code path, which generates extra nodes per mesh while
+// the newer code uses aiMesh::mName to express the name of the
+// meshes (a.k.a. groups in MS3D))
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MS3DImporter::MS3DImporter() : mScene(0)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MS3DImporter::~MS3DImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool MS3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ // first call - simple extension check
+ const std::string extension = GetExtension(pFile);
+ if (extension == "ms3d") {
+ return true;
+ }
+
+ // second call - check for magic identifiers
+ else if (!extension.length() || checkSig) {
+ if (!pIOHandler) {
+ return true;
+ }
+ const char* tokens[] = {"MS3D000000"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MS3DImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("ms3d");
+}
+
+// ------------------------------------------------------------------------------------------------
+void ReadColor(StreamReaderLE& stream, aiColor4D& ambient)
+{
+ // aiColor4D is packed on gcc, implicit binding to float& fails therefore.
+ stream >> (float&)ambient.r >> (float&)ambient.g >> (float&)ambient.b >> (float&)ambient.a;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ReadVector(StreamReaderLE& stream, aiVector3D& pos)
+{
+ // See note in ReadColor()
+ stream >> (float&)pos.x >> (float&)pos.y >> (float&)pos.z;
+}
+
+// ------------------------------------------------------------------------------------------------
+template<typename T>
+void MS3DImporter :: ReadComments(StreamReaderLE& stream, std::vector<T>& outp)
+{
+ uint16_t cnt;
+ stream >> cnt;
+
+ for (unsigned int i = 0; i < cnt; ++i) {
+ uint32_t index, clength;
+ stream >> index >> clength;
+
+ if (index >= outp.size()) {
+ DefaultLogger::get()->warn("MS3D: Invalid index in comment section");
+ }
+ else if (clength > stream.GetRemainingSize()) {
+ throw DeadlyImportError("MS3D: Failure reading comment, length field is out of range");
+ }
+ else {
+ outp[index].comment = std::string(reinterpret_cast<char*>(stream.GetPtr()),clength);
+ }
+ stream.IncPtr(clength);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T, typename T2, typename T3> bool inrange(const T& in, const T2& lower, const T3& higher)
+{
+ return in > lower && in <= higher;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MS3DImporter :: CollectChildJoints(const std::vector<TempJoint>& joints,
+ std::vector<bool>& hadit,
+ aiNode* nd,
+ const aiMatrix4x4& absTrafo)
+{
+ unsigned int cnt = 0;
+ for (size_t i = 0; i < joints.size(); ++i) {
+ if (!hadit[i] && !strcmp(joints[i].parentName,nd->mName.data)) {
+ ++cnt;
+ }
+ }
+
+ nd->mChildren = new aiNode*[nd->mNumChildren = cnt];
+ cnt = 0;
+ for (size_t i = 0; i < joints.size(); ++i) {
+ if (!hadit[i] && !strcmp(joints[i].parentName,nd->mName.data)) {
+ aiNode* ch = nd->mChildren[cnt++] = new aiNode(joints[i].name);
+ ch->mParent = nd;
+
+ ch->mTransformation = aiMatrix4x4::Translation(joints[i].position,aiMatrix4x4()=aiMatrix4x4())*
+ // XXX actually, I don't *know* why we need the inverse here. Probably column vs. row order?
+ aiMatrix4x4().FromEulerAnglesXYZ(joints[i].rotation).Transpose();
+
+ const aiMatrix4x4 abs = absTrafo*ch->mTransformation;
+ for (unsigned int a = 0; a < mScene->mNumMeshes; ++a) {
+ aiMesh* const msh = mScene->mMeshes[a];
+ for (unsigned int n = 0; n < msh->mNumBones; ++n) {
+ aiBone* const bone = msh->mBones[n];
+
+ if (bone->mName == ch->mName) {
+ bone->mOffsetMatrix = aiMatrix4x4(abs).Inverse();
+ }
+ }
+ }
+
+ hadit[i] = true;
+ CollectChildJoints(joints,hadit,ch,abs);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void MS3DImporter :: CollectChildJoints(const std::vector<TempJoint>& joints, aiNode* nd)
+{
+ std::vector<bool> hadit(joints.size(),false);
+ aiMatrix4x4 trafo;
+
+ CollectChildJoints(joints,hadit,nd,trafo);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void MS3DImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ StreamReaderLE stream(pIOHandler->Open(pFile,"rb"));
+
+ // CanRead() should have done this already
+ char head[10];
+ int32_t version;
+
+ mScene = pScene;
+
+
+ // 1 ------------ read into temporary data structures mirroring the original file
+
+ stream.CopyAndAdvance(head,10);
+ stream >> version;
+ if (strncmp(head,"MS3D000000",10)) {
+ throw DeadlyImportError("Not a MS3D file, magic string MS3D000000 not found: "+pFile);
+ }
+
+ if (version != 4) {
+ throw DeadlyImportError("MS3D: Unsupported file format version, 4 was expected");
+ }
+
+ uint16_t verts;
+ stream >> verts;
+
+ std::vector<TempVertex> vertices(verts);
+ for (unsigned int i = 0; i < verts; ++i) {
+ TempVertex& v = vertices[i];
+
+ stream.IncPtr(1);
+ ReadVector(stream,v.pos);
+ v.bone_id[0] = stream.GetI1();
+ v.ref_cnt = stream.GetI1();
+
+ v.bone_id[1] = v.bone_id[2] = v.bone_id[3] = 0xffffffff;
+ v.weights[1] = v.weights[2] = v.weights[3] = 0.f;
+ v.weights[0] = 1.f;
+ }
+
+ uint16_t tris;
+ stream >> tris;
+
+ std::vector<TempTriangle> triangles(tris);
+ for (unsigned int i = 0;i < tris; ++i) {
+ TempTriangle& t = triangles[i];
+
+ stream.IncPtr(2);
+ for (unsigned int i = 0; i < 3; ++i) {
+ t.indices[i] = stream.GetI2();
+ }
+
+ for (unsigned int i = 0; i < 3; ++i) {
+ ReadVector(stream,t.normals[i]);
+ }
+
+ for (unsigned int i = 0; i < 3; ++i) {
+ stream >> (float&)(t.uv[i].x); // see note in ReadColor()
+ }
+ for (unsigned int i = 0; i < 3; ++i) {
+ stream >> (float&)(t.uv[i].y);
+ }
+
+ t.sg = stream.GetI1();
+ t.group = stream.GetI1();
+ }
+
+ uint16_t grp;
+ stream >> grp;
+
+ bool need_default = false;
+ std::vector<TempGroup> groups(grp);
+ for (unsigned int i = 0;i < grp; ++i) {
+ TempGroup& t = groups[i];
+
+ stream.IncPtr(1);
+ stream.CopyAndAdvance(t.name,32);
+
+ t.name[32] = '\0';
+ uint16_t num;
+ stream >> num;
+
+ t.triangles.resize(num);
+ for (unsigned int i = 0; i < num; ++i) {
+ t.triangles[i] = stream.GetI2();
+ }
+ t.mat = stream.GetI1();
+ if (t.mat == 0xffffffff) {
+ need_default = true;
+ }
+ }
+
+ uint16_t mat;
+ stream >> mat;
+
+ std::vector<TempMaterial> materials(mat);
+ for (unsigned int i = 0;i < mat; ++i) {
+ TempMaterial& t = materials[i];
+
+ stream.CopyAndAdvance(t.name,32);
+ t.name[32] = '\0';
+
+ ReadColor(stream,t.ambient);
+ ReadColor(stream,t.diffuse);
+ ReadColor(stream,t.specular);
+ ReadColor(stream,t.emissive);
+ stream >> t.shininess >> t.transparency;
+
+ stream.IncPtr(1);
+
+ stream.CopyAndAdvance(t.texture,128);
+ t.texture[128] = '\0';
+
+ stream.CopyAndAdvance(t.alphamap,128);
+ t.alphamap[128] = '\0';
+ }
+
+ float animfps, currenttime;
+ uint32_t totalframes;
+ stream >> animfps >> currenttime >> totalframes;
+
+ uint16_t joint;
+ stream >> joint;
+
+ std::vector<TempJoint> joints(joint);
+ for (unsigned int i = 0; i < joint; ++i) {
+ TempJoint& j = joints[i];
+
+ stream.IncPtr(1);
+ stream.CopyAndAdvance(j.name,32);
+ j.name[32] = '\0';
+
+ stream.CopyAndAdvance(j.parentName,32);
+ j.parentName[32] = '\0';
+
+ // DefaultLogger::get()->debug(j.name);
+ // DefaultLogger::get()->debug(j.parentName);
+
+ ReadVector(stream,j.rotation);
+ ReadVector(stream,j.position);
+
+ j.rotFrames.resize(stream.GetI2());
+ j.posFrames.resize(stream.GetI2());
+
+ for (unsigned int a = 0; a < j.rotFrames.size(); ++a) {
+ TempKeyFrame& kf = j.rotFrames[a];
+ stream >> kf.time;
+ ReadVector(stream,kf.value);
+ }
+ for (unsigned int a = 0; a < j.posFrames.size(); ++a) {
+ TempKeyFrame& kf = j.posFrames[a];
+ stream >> kf.time;
+ ReadVector(stream,kf.value);
+ }
+ }
+
+ if (stream.GetRemainingSize() > 4) {
+ uint32_t subversion;
+ stream >> subversion;
+ if (subversion == 1) {
+ ReadComments<TempGroup>(stream,groups);
+ ReadComments<TempMaterial>(stream,materials);
+ ReadComments<TempJoint>(stream,joints);
+
+ // model comment - print it for we have such a nice log.
+ if (stream.GetI4()) {
+ const size_t len = static_cast<size_t>(stream.GetI4());
+ if (len > stream.GetRemainingSize()) {
+ throw DeadlyImportError("MS3D: Model comment is too long");
+ }
+ }
+
+ if (stream.GetRemainingSize() > 4 && inrange((stream >> subversion,subversion),1u,3u)) {
+ for (unsigned int i = 0; i < verts; ++i) {
+ TempVertex& v = vertices[i];
+ v.weights[3]=1.f;
+ for (unsigned int n = 0; n < 3; v.weights[3]-=v.weights[n++]) {
+ v.bone_id[n+1] = stream.GetI1();
+ v.weights[n] = static_cast<float>(static_cast<unsigned int>(stream.GetI1()))/255.f;
+ }
+ stream.IncPtr((subversion-1)<<2u);
+ }
+
+ // even further extra data is not of interest for us, at least now now.
+ }
+ }
+ }
+
+ // 2 ------------ convert to proper aiXX data structures -----------------------------------
+
+ if (need_default && materials.size()) {
+ DefaultLogger::get()->warn("MS3D: Found group with no material assigned, spawning default material");
+ // if one of the groups has no material assigned, but there are other
+ // groups with materials, a default material needs to be added (
+ // scenepreprocessor adds a default material only if nummat==0).
+ materials.push_back(TempMaterial());
+ TempMaterial& m = materials.back();
+
+ strcpy(m.name,"<MS3D_DefaultMat>");
+ m.diffuse = aiColor4D(0.6f,0.6f,0.6f,1.0);
+ m.transparency = 1.f;
+ m.shininess = 0.f;
+
+ // this is because these TempXXX struct's have no c'tors.
+ m.texture[0] = m.alphamap[0] = '\0';
+
+ for (unsigned int i = 0; i < groups.size(); ++i) {
+ TempGroup& g = groups[i];
+ if (g.mat == 0xffffffff) {
+ g.mat = materials.size()-1;
+ }
+ }
+ }
+
+ // convert materials to our generic key-value dict-alike
+ if (materials.size()) {
+ pScene->mMaterials = new aiMaterial*[materials.size()];
+ for (size_t i = 0; i < materials.size(); ++i) {
+
+ MaterialHelper* mo = new MaterialHelper();
+ pScene->mMaterials[pScene->mNumMaterials++] = mo;
+
+ const TempMaterial& mi = materials[i];
+
+ aiString tmp;
+ if (0[mi.alphamap]) {
+ tmp = aiString(mi.alphamap);
+ mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_OPACITY(0));
+ }
+ if (0[mi.texture]) {
+ tmp = aiString(mi.texture);
+ mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ if (0[mi.name]) {
+ tmp = aiString(mi.name);
+ mo->AddProperty(&tmp,AI_MATKEY_NAME);
+ }
+
+ mo->AddProperty(&mi.ambient,1,AI_MATKEY_COLOR_AMBIENT);
+ mo->AddProperty(&mi.diffuse,1,AI_MATKEY_COLOR_DIFFUSE);
+ mo->AddProperty(&mi.specular,1,AI_MATKEY_COLOR_SPECULAR);
+ mo->AddProperty(&mi.emissive,1,AI_MATKEY_COLOR_EMISSIVE);
+
+ mo->AddProperty(&mi.shininess,1,AI_MATKEY_SHININESS);
+ mo->AddProperty(&mi.transparency,1,AI_MATKEY_OPACITY);
+
+ const int sm = mi.shininess>0.f?aiShadingMode_Phong:aiShadingMode_Gouraud;
+ mo->AddProperty(&sm,1,AI_MATKEY_SHADING_MODEL);
+ }
+ }
+
+ // convert groups to meshes
+ if (groups.empty()) {
+ throw DeadlyImportError("MS3D: Didn't get any group records, file is malformed");
+ }
+
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes=static_cast<unsigned int>(groups.size())]();
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+
+ aiMesh* m = pScene->mMeshes[i] = new aiMesh();
+ const TempGroup& g = groups[i];
+
+ if (pScene->mNumMaterials && g.mat > pScene->mNumMaterials) {
+ throw DeadlyImportError("MS3D: Encountered invalid material index, file is malformed");
+ } // no error if no materials at all - scenepreprocessor adds one then
+
+ m->mMaterialIndex = g.mat;
+ m->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ m->mFaces = new aiFace[m->mNumFaces = g.triangles.size()];
+ m->mNumVertices = m->mNumFaces*3;
+
+ // storage for vertices - verbose format, as requested by the postprocessing pipeline
+ m->mVertices = new aiVector3D[m->mNumVertices];
+ m->mNormals = new aiVector3D[m->mNumVertices];
+ m->mTextureCoords[0] = new aiVector3D[m->mNumVertices];
+ m->mNumUVComponents[0] = 2;
+
+ typedef std::map<unsigned int,unsigned int> BoneSet;
+ BoneSet mybones;
+
+ for (unsigned int i = 0,n = 0; i < m->mNumFaces; ++i) {
+ aiFace& f = m->mFaces[i];
+ if (g.triangles[i]>triangles.size()) {
+ throw DeadlyImportError("MS3D: Encountered invalid triangle index, file is malformed");
+ }
+
+ TempTriangle& t = triangles[g.triangles[i]];
+ f.mIndices = new unsigned int[f.mNumIndices=3];
+
+ for (unsigned int i = 0; i < 3; ++i,++n) {
+ if (t.indices[i]>vertices.size()) {
+ throw DeadlyImportError("MS3D: Encountered invalid vertex index, file is malformed");
+ }
+
+ const TempVertex& v = vertices[t.indices[i]];
+ for (unsigned int a = 0; a < 4; ++a) {
+ if (v.bone_id[a] != 0xffffffff) {
+ if (v.bone_id[a] >= joints.size()) {
+ throw DeadlyImportError("MS3D: Encountered invalid bone index, file is malformed");
+ }
+ if (mybones.find(v.bone_id[a]) == mybones.end()) {
+ mybones[v.bone_id[a]] = 1;
+ }
+ else ++mybones[v.bone_id[a]];
+ }
+ }
+
+ // collect vertex components
+ m->mVertices[n] = v.pos;
+
+ m->mNormals[n] = t.normals[i];
+ m->mTextureCoords[0][n] = aiVector3D(t.uv[i].x,1.f-t.uv[i].y,0.0);
+ f.mIndices[i] = n;
+ }
+ }
+
+ // allocate storage for bones
+ if (mybones.size()) {
+ std::vector<unsigned int> bmap(joints.size());
+ m->mBones = new aiBone*[mybones.size()]();
+ for (BoneSet::const_iterator it = mybones.begin(); it != mybones.end(); ++it) {
+ aiBone* const bn = m->mBones[m->mNumBones] = new aiBone();
+ const TempJoint& jnt = joints[(*it).first];
+
+ bn->mName.Set(jnt.name);
+ bn->mWeights = new aiVertexWeight[(*it).second];
+
+ bmap[(*it).first] = m->mNumBones++;
+ }
+
+ // .. and collect bone weights
+ for (unsigned int i = 0,n = 0; i < m->mNumFaces; ++i) {
+ TempTriangle& t = triangles[g.triangles[i]];
+
+ for (unsigned int i = 0; i < 3; ++i,++n) {
+ const TempVertex& v = vertices[t.indices[i]];
+ for (unsigned int a = 0; a < 4; ++a) {
+ const unsigned int bone = v.bone_id[a];
+ if (bone==0xffffffff){
+ continue;
+ }
+
+ aiBone* const outbone = m->mBones[bmap[bone]];
+ aiVertexWeight& outwght = outbone->mWeights[outbone->mNumWeights++];
+
+ outwght.mVertexId = n;
+ outwght.mWeight = v.weights[a];
+ }
+ }
+ }
+ }
+ }
+
+ // ... add dummy nodes under a single root, each holding a reference to one
+ // mesh. If we didn't do this, we'd loose the group name.
+ aiNode* rt = pScene->mRootNode = new aiNode("<MS3DRoot>");
+
+#ifdef ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
+ rt->mChildren = new aiNode*[rt->mNumChildren=pScene->mNumMeshes+(joints.size()?1:0)]();
+
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+ aiNode* nd = rt->mChildren[i] = new aiNode();
+
+ const TempGroup& g = groups[i];
+
+ // we need to generate an unique name for all mesh nodes.
+ // since we want to keep the group name, a prefix is
+ // prepended.
+ nd->mName = aiString("<MS3DMesh>_");
+ nd->mName.Append(g.name);
+ nd->mParent = rt;
+
+ nd->mMeshes = new unsigned int[nd->mNumMeshes = 1];
+ nd->mMeshes[0] = i;
+ }
+#else
+ rt->mMeshes = new unsigned int[pScene->mNumMeshes];
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+ rt->mMeshes[rt->mNumMeshes++] = i;
+ }
+#endif
+
+ // convert animations as well
+ if (joints.size()) {
+#ifndef ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
+ rt->mChildren = new aiNode*[1]();
+#endif
+ aiNode* jt = rt->mChildren[pScene->mNumMeshes] = new aiNode();
+ jt->mParent = rt;
+ CollectChildJoints(joints,jt);
+ jt->mName.Set("<MS3DJointRoot>");
+
+ pScene->mAnimations = new aiAnimation*[ pScene->mNumAnimations = 1 ];
+ aiAnimation* const anim = pScene->mAnimations[0] = new aiAnimation();
+
+ anim->mName.Set("<MS3DMasterAnim>");
+
+ // carry the fps info to the user by scaling all times with it
+ anim->mTicksPerSecond = animfps;
+
+ // leave duration at its default, so ScenePreprocessor will fill an appropriate
+ // value (the values taken from some MS3D files seem to be too unreliable
+ // to pass the validation)
+ // anim->mDuration = totalframes/animfps;
+
+ anim->mChannels = new aiNodeAnim*[joints.size()]();
+ for (std::vector<TempJoint>::const_iterator it = joints.begin(); it != joints.end(); ++it) {
+ if ((*it).rotFrames.empty() && (*it).posFrames.empty()) {
+ continue;
+ }
+
+ aiNodeAnim* nd = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
+ nd->mNodeName.Set((*it).name);
+
+ if ((*it).rotFrames.size()) {
+ nd->mRotationKeys = new aiQuatKey[(*it).rotFrames.size()];
+ for (std::vector<TempKeyFrame>::const_iterator rot = (*it).rotFrames.begin(); rot != (*it).rotFrames.end(); ++rot) {
+ aiQuatKey& q = nd->mRotationKeys[nd->mNumRotationKeys++];
+
+ q.mTime = (*rot).time*animfps;
+
+ // XXX it seems our matrix&quaternion code has faults in its conversion routines --
+ // aiQuaternion(x,y,z) seems to besomething different as quat(matrix.fromeuler(x,y,z)).
+ q.mValue = aiQuaternion(aiMatrix3x3(aiMatrix4x4().FromEulerAnglesXYZ((*rot).value)*
+ aiMatrix4x4().FromEulerAnglesXYZ((*it).rotation)).Transpose());
+ }
+ }
+
+ if ((*it).posFrames.size()) {
+ nd->mPositionKeys = new aiVectorKey[(*it).posFrames.size()];
+
+ aiQuatKey* qu = nd->mRotationKeys;
+ for (std::vector<TempKeyFrame>::const_iterator pos = (*it).posFrames.begin(); pos != (*it).posFrames.end(); ++pos,++qu) {
+ aiVectorKey& v = nd->mPositionKeys[nd->mNumPositionKeys++];
+
+ v.mTime = (*pos).time*animfps;
+ v.mValue = (*it).position + (*pos).value;
+ }
+ }
+ }
+ // fixup to pass the validation if not a single animation channel is non-trivial
+ if (!anim->mNumChannels) {
+ anim->mChannels = NULL;
+ }
+ }
+}
+
+#endif
diff --git a/src/3rdparty/assimp/code/MS3DLoader.h b/src/3rdparty/assimp/code/MS3DLoader.h
new file mode 100644
index 000000000..6725dda32
--- /dev/null
+++ b/src/3rdparty/assimp/code/MS3DLoader.h
@@ -0,0 +1,158 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file MS3DLoader.h
+ * @brief Declaration of the MS3D importer class.
+ */
+#ifndef AI_MS3DLOADER_H_INCLUDED
+#define AI_MS3DLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------------------
+/** Milkshape 3D importer implementation */
+// ----------------------------------------------------------------------------------------------
+class MS3DImporter
+ : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+
+ MS3DImporter();
+ ~MS3DImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details. */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+
+private:
+
+ struct TempJoint;
+ void CollectChildJoints(const std::vector<TempJoint>& joints, std::vector<bool>& hadit, aiNode* nd,const aiMatrix4x4& absTrafo);
+ void CollectChildJoints(const std::vector<TempJoint>& joints, aiNode* nd);
+
+ template<typename T> void ReadComments(StreamReaderLE& stream, std::vector<T>& outp);
+private:
+
+ aiScene* mScene;
+
+private:
+
+ struct TempVertex
+ {
+ aiVector3D pos;
+ unsigned int bone_id[4], ref_cnt;
+ float weights[4];
+ };
+
+ struct TempTriangle
+ {
+ unsigned int indices[3];
+ aiVector3D normals[3];
+ aiVector2D uv[3];
+
+ unsigned int sg, group;
+ };
+
+ struct TempGroup
+ {
+ char name[33]; // +0
+ std::vector<unsigned int> triangles;
+ unsigned int mat; // 0xff is no material
+ std::string comment;
+ };
+
+ struct TempMaterial
+ {
+ // again, add an extra 0 character to all strings -
+ char name[33];
+ char texture[129];
+ char alphamap[129];
+
+ aiColor4D diffuse,specular,ambient,emissive;
+ float shininess,transparency;
+ std::string comment;
+ };
+
+ struct TempKeyFrame
+ {
+ float time;
+ aiVector3D value;
+ };
+
+ struct TempJoint
+ {
+ char name[33];
+ char parentName[33];
+ aiVector3D rotation, position;
+
+ std::vector<TempKeyFrame> rotFrames;
+ std::vector<TempKeyFrame> posFrames;
+ std::string comment;
+ };
+
+ //struct TempModel {
+ // std::string comment;
+ //};
+};
+
+}
+#endif
diff --git a/src/3rdparty/assimp/code/MakeVerboseFormat.cpp b/src/3rdparty/assimp/code/MakeVerboseFormat.cpp
new file mode 100644
index 000000000..b232146de
--- /dev/null
+++ b/src/3rdparty/assimp/code/MakeVerboseFormat.cpp
@@ -0,0 +1,218 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file Implementation of the post processing step "MakeVerboseFormat"
+*/
+
+#include "AssimpPCH.h"
+#include "MakeVerboseFormat.h"
+
+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);
+ DefaultLogger::get()->debug("MakeVerboseFormatProcess begin");
+
+ bool bHas = false;
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ {
+ if ( MakeVerboseFormat( pScene->mMeshes[a]))
+ bHas = true;
+ }
+ if (bHas) DefaultLogger::get()->info("MakeVerboseFormatProcess finished. There was much work to do ...");
+ else DefaultLogger::get()->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];
+ }
+
+ ai_assert(AI_MAX_NUMBER_OF_TEXTURECOORDS == 4);
+ ai_assert(AI_MAX_NUMBER_OF_COLOR_SETS == 4);
+
+ aiVector3D* apvTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS] = {NULL,NULL,NULL,NULL};
+ aiColor4D* apvColorSets[AI_MAX_NUMBER_OF_COLOR_SETS] = {NULL,NULL,NULL,NULL};
+
+ 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()];
+ memcpy(pcMesh->mBones[i]->mWeights,&newWeights[i][0],
+ sizeof(aiVertexWeight) * newWeights[i].size());
+ }
+ else pcMesh->mBones[i]->mWeights = NULL;
+ }
+
+ // 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/src/3rdparty/assimp/code/MakeVerboseFormat.h b/src/3rdparty/assimp/code/MakeVerboseFormat.h
new file mode 100644
index 000000000..6ecb2ed59
--- /dev/null
+++ b/src/3rdparty/assimp/code/MakeVerboseFormat.h
@@ -0,0 +1,106 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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"
+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 MakeVerboseFormatProcess : public BaseProcess
+{
+ friend class Importer;
+
+protected:
+
+ /** Constructor to be privately used by Importer, or by applications
+ which know what they are doing if they modify the aiScene object */
+ MakeVerboseFormatProcess();
+
+ /** Destructor, private as well */
+ ~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/src/3rdparty/assimp/code/MaterialSystem.cpp b/src/3rdparty/assimp/code/MaterialSystem.cpp
new file mode 100644
index 000000000..0df12ba0b
--- /dev/null
+++ b/src/3rdparty/assimp/code/MaterialSystem.cpp
@@ -0,0 +1,612 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file MaterialSystem.cpp
+ * @brief Implementation of the material system of the library
+ */
+
+#include "AssimpPCH.h"
+
+#include "Hash.h"
+#include "fast_atof.h"
+#include "ParsingUtils.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 )
+ && (0xffffffff == type || prop->mSemantic == type) /* 0xffffffff is a wildcard, but this is undocumented :-) */
+ && (0xffffffff == 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,
+ float* 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, simply copy it
+ unsigned int iWrite;
+ 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<float> ( reinterpret_cast<float*>(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<float> ( 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 && !prop->mData[prop->mDataLength-1]);
+ for (unsigned int a = 0; ;++a) {
+ cur = fast_atof_move(cur,pOut[a]);
+ if (a==iWrite-1) {
+ break;
+ }
+ if (!IsSpace(*cur)) {
+ DefaultLogger::get()->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;
+ if ( aiPTI_Integer == prop->mType || aiPTI_Buffer == 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<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 && !prop->mData[prop->mDataLength-1]);
+ for (unsigned int a = 0; ;++a) {
+ pOut[a] = strtol10s(cur,&cur);
+ if (a==iWrite-1) {
+ break;
+ }
+ if (!IsSpace(*cur)) {
+ DefaultLogger::get()->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,(float*)pOut,&iMax);
+
+ // if no alpha channel is defined: set it to 1.0
+ if (3 == iMax) {
+ pOut->a = 1.0f;
+ }
+
+ return eRet;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 && !prop->mData[prop->mDataLength-1]);
+ memcpy(pOut->data,prop->mData+4,pOut->length+1);
+ }
+ else {
+ // TODO - implement lexical cast as well
+ DefaultLogger::get()->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
+ASSIMP_API 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*/,
+ float* blend /*= NULL*/,
+ aiTextureOp* op /*= NULL*/,
+ aiTextureMapMode* mapmode /*= NULL*/,
+ unsigned int* flags /*= NULL*/
+ )
+{
+ ai_assert(NULL != mat && NULL != path);
+
+ // Get the path to the texture
+ if (AI_SUCCESS != aiGetMaterialString(mat,AI_MATKEY_TEXTURE(type,index),path)) {
+ return AI_FAILURE;
+ }
+ // Determine mapping type
+ aiTextureMapping mapping = aiTextureMapping_UV;
+ aiGetMaterialInteger(mat,AI_MATKEY_MAPPING(type,index),(int*)&mapping);
+ 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;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Construction. Actually the one and only way to get an aiMaterial instance
+MaterialHelper::MaterialHelper()
+{
+ // Allocate 5 entries by default
+ mNumProperties = 0;
+ mNumAllocated = 5;
+ mProperties = new aiMaterialProperty*[5];
+}
+
+// ------------------------------------------------------------------------------------------------
+MaterialHelper::~MaterialHelper()
+{
+ _InternDestruct();
+}
+
+// ------------------------------------------------------------------------------------------------
+aiMaterial::~aiMaterial()
+{
+ // HACK (Aramis): This is safe: aiMaterial has a private constructor,
+ // so instances must be created indirectly via MaterialHelper. We can't
+ // use a virtual d'tor because we need to preserve binary compatibility
+ // with good old C ...
+ ((MaterialHelper*)this)->_InternDestruct();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Manual destructor
+void MaterialHelper::_InternDestruct()
+{
+ // First clean up all properties
+ Clear();
+
+ // Then delete the array that stored them
+ delete[] mProperties;
+ AI_DEBUG_INVALIDATE_PTR(mProperties);
+
+ // Update members
+ mNumAllocated = 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MaterialHelper::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
+}
+
+// ------------------------------------------------------------------------------------------------
+uint32_t MaterialHelper::ComputeHash(bool includeMatName /*= false*/)
+{
+ uint32_t hash = 1503; // magic start value, choosen to be my birthday :-)
+ for (unsigned int i = 0; i < mNumProperties;++i) {
+ aiMaterialProperty* prop;
+
+ // Exclude all properties whose first character is '?' from the hash
+ // See doc for aiMaterialProperty.
+ if ((prop = 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;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiReturn MaterialHelper::RemoveProperty (const char* pKey,unsigned int type,
+ unsigned int index
+ )
+{
+ ai_assert(NULL != 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 MaterialHelper::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);
+
+ // first search the list whether there is already an entry with this key
+ unsigned int iOutIndex = 0xffffffff;
+ 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 (0xffffffff != 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&) {
+ 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 MaterialHelper::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,
+ pInput->length+1+4,
+ pKey,
+ type,
+ index,
+ aiPTI_String);
+ }
+ ai_assert(sizeof(size_t)==4);
+ return AddBinaryProperty(pInput,
+ pInput->length+1+4,
+ pKey,
+ type,
+ index,
+ aiPTI_String);
+}
+
+// ------------------------------------------------------------------------------------------------
+void MaterialHelper::CopyPropertyList(MaterialHelper* pcDest,
+ const MaterialHelper* 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];
+ }
+
+ 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);
+ }
+ return;
+}
+
diff --git a/src/3rdparty/assimp/code/MaterialSystem.h b/src/3rdparty/assimp/code/MaterialSystem.h
new file mode 100644
index 000000000..1504b6888
--- /dev/null
+++ b/src/3rdparty/assimp/code/MaterialSystem.h
@@ -0,0 +1,246 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file MaterialSystem.h
+ * Definition of the #MaterialHelper utility class.
+ */
+#ifndef AI_MATERIALSYSTEM_H_INC
+#define AI_MATERIALSYSTEM_H_INC
+
+#include "../include/aiMaterial.h"
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------------
+/** Internal material helper class deriving from aiMaterial.
+ *
+ * Intended to be used to fill an aiMaterial structure more easily.
+ */
+class ASSIMP_API MaterialHelper : public ::aiMaterial
+{
+public:
+
+ // Construction and destruction
+ MaterialHelper();
+ ~MaterialHelper();
+
+ // ------------------------------------------------------------------------------
+ /** @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);
+
+ // ------------------------------------------------------------------------------
+ /** @brief Remove a given key from the list.
+ *
+ * The function fails if the key isn't found
+ * @param pKey Key to be deleted
+ */
+ 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();
+
+ // ------------------------------------------------------------------------------
+ /** Computes a hash (hopefully unique) from all material properties
+ * The hash value reflects the current property state, so if you add any
+ * proprty and call this method again, the resulting hash value will be
+ * different.
+ *
+ * @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 Unique hash
+ */
+ uint32_t ComputeHash(bool includeMatName = false);
+
+ // ------------------------------------------------------------------------------
+ /** Copy the property list of a material
+ * @param pcDest Destination material
+ * @param pcSrc Source material
+ */
+ static void CopyPropertyList(MaterialHelper* pcDest,
+ const MaterialHelper* pcSrc);
+
+public:
+ // For internal use. That's why it's public.
+ void _InternDestruct();
+};
+
+
+// ----------------------------------------------------------------------------------------
+template<class TYPE>
+aiReturn MaterialHelper::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);
+}
+
+// ----------------------------------------------------------------------------------------
+template<>
+inline aiReturn MaterialHelper::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 MaterialHelper::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 MaterialHelper::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 MaterialHelper::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 MaterialHelper::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 MaterialHelper::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);
+}
+} // ! namespace Assimp
+
+#endif //!! AI_MATERIALSYSTEM_H_INC
diff --git a/src/3rdparty/assimp/code/MemoryIOWrapper.h b/src/3rdparty/assimp/code/MemoryIOWrapper.h
new file mode 100644
index 000000000..eb08aefb0
--- /dev/null
+++ b/src/3rdparty/assimp/code/MemoryIOWrapper.h
@@ -0,0 +1,181 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file MemoryIOWrapper.h
+ * Handy IOStream/IOSystem implemetation to read directly from a memory buffer */
+#ifndef AI_MEMORYIOSTREAM_H_INC
+#define AI_MEMORYIOSTREAM_H_INC
+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
+{
+ //friend class MemoryIOSystem;
+public:
+ MemoryIOStream (const uint8_t* buff, size_t len)
+ : buffer (buff), length(len), pos((size_t)0) {
+ }
+
+public:
+ ~MemoryIOStream () {
+ }
+
+ // -------------------------------------------------------------------
+ // Read from stream
+ size_t Read(void* pvBuffer, size_t pSize, size_t pCount) {
+ const size_t cnt = std::min(pCount,(length-pos)/pSize),ofs = pSize*cnt;
+
+ memcpy(pvBuffer,buffer+pos,ofs);
+ pos += ofs;
+
+ return cnt;
+ }
+
+ // -------------------------------------------------------------------
+ // Write to stream
+ size_t Write(const void*, size_t,size_t) {
+ 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;
+};
+
+// ---------------------------------------------------------------------------
+/** Dummy IO system to read from a memory buffer */
+class MemoryIOSystem : public IOSystem
+{
+public:
+ /** Constructor. */
+ MemoryIOSystem (const uint8_t* buff, size_t len)
+ : buffer (buff), length(len) {
+ }
+
+ /** Destructor. */
+ ~MemoryIOSystem() {
+ }
+
+ // -------------------------------------------------------------------
+ /** Tests for the existence of a file at the given path. */
+ bool Exists( const char* pFile) const {
+ return !strncmp(pFile,AI_MEMORYIO_MAGIC_FILENAME,AI_MEMORYIO_MAGIC_FILENAME_LENGTH);
+ }
+
+ // -------------------------------------------------------------------
+ /** Returns the directory separator. */
+ char getOsSeparator() const {
+ return '/'; // why not? it doesn't care
+ }
+
+ // -------------------------------------------------------------------
+ /** Open a new file with a given path. */
+ IOStream* Open( const char* pFile, const char* /* pMode */ = "rb") {
+ if (strncmp(pFile,AI_MEMORYIO_MAGIC_FILENAME,AI_MEMORYIO_MAGIC_FILENAME_LENGTH)) {
+ return NULL;
+ }
+ return new MemoryIOStream(buffer,length);
+ }
+
+ // -------------------------------------------------------------------
+ /** Closes the given file and releases all resources associated with it. */
+ void Close( IOStream* /* pFile */) {
+ }
+
+ // -------------------------------------------------------------------
+ /** Compare two paths */
+ bool ComparePaths (const char*, const char*) const {
+ return false;
+ }
+
+private:
+ const uint8_t* buffer;
+ size_t length;
+};
+} // end namespace Assimp
+
+#endif
diff --git a/src/3rdparty/assimp/code/NDOLoader.cpp b/src/3rdparty/assimp/code/NDOLoader.cpp
new file mode 100644
index 000000000..b1eb62141
--- /dev/null
+++ b/src/3rdparty/assimp/code/NDOLoader.cpp
@@ -0,0 +1,289 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2009, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file NDOLoader.cpp
+ * Implementation of the NDO importer class.
+ */
+
+#include "AssimpPCH.h"
+#ifndef AI_BUILD_NO_NDO_IMPORTER
+#include "NDOLoader.h"
+
+using namespace Assimp;
+#define for_each BOOST_FOREACH
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+NDOImporter::NDOImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+NDOImporter::~NDOImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool NDOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ // check file extension
+ const std::string extension = GetExtension(pFile);
+
+ if ( extension == "ndo")
+ return true;
+
+ if ((checkSig || !extension.length()) && pIOHandler) {
+ const char* tokens[] = {"nendo"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1,5);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a string of all file extensions supported
+void NDOImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("ndo");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void NDOImporter::SetupProperties(const Importer* /* pImp */)
+{
+ // nothing to be done for the moment
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void NDOImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ StreamReaderBE reader(pIOHandler->Open( pFile, "rb"));
+
+ // first 9 bytes are nendo file format ("nendo 1.n")
+ const char* head = (const char*)reader.GetPtr();
+ reader.IncPtr(9);
+
+ if (strncmp("nendo ",head,6)) {
+ throw DeadlyImportError("Not a Nendo file; magic signature missing");
+ }
+ // check if this is a supported version. if not, continue, too -- users,
+ // please don't complain if it doesn't work then ...
+ unsigned int file_format = 12;
+ if (!strncmp("1.0",head+6,3)) {
+ file_format = 10;
+ DefaultLogger::get()->info("NDO file format is 1.0");
+ }
+ else if (!strncmp("1.1",head+6,3)) {
+ file_format = 11;
+ DefaultLogger::get()->info("NDO file format is 1.1");
+ }
+ else if (!strncmp("1.2",head+6,3)) {
+ file_format = 12;
+ DefaultLogger::get()->info("NDO file format is 1.2");
+ }
+ else {
+ DefaultLogger::get()->warn(std::string("Unrecognized nendo file format version, continuing happily ... :") + (head+6));
+ }
+
+ reader.IncPtr(2); /* skip flags */
+ if (file_format >= 12) {
+ reader.IncPtr(2);
+ }
+ unsigned int temp = reader.GetU1();
+
+ std::vector<Object> objects(temp); /* buffer to store all the loaded objects in */
+
+ // read all objects
+ for (unsigned int o = 0; o < objects.size(); ++o) {
+
+// if (file_format < 12) {
+ if (!reader.GetI1()) {
+ continue; /* skip over empty object */
+ }
+ // reader.GetI2();
+// }
+ Object& obj = objects[o];
+
+ temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ head = (const char*)reader.GetPtr();
+ reader.IncPtr(temp + 76); /* skip unknown stuff */
+
+ obj.name = std::string(head, temp);
+
+ // read edge table
+ temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ obj.edges.reserve(temp);
+ for (unsigned int e = 0; e < temp; ++e) {
+
+ obj.edges.push_back(Edge());
+ Edge& edge = obj.edges.back();
+
+ for (unsigned int i = 0; i< 8; ++i) {
+ edge.edge[i] = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ }
+ edge.hard = file_format >= 11 ? reader.GetU1() : 0;
+ for (unsigned int i = 0; i< 8; ++i) {
+ edge.color[i] = reader.GetU1();
+ }
+ }
+
+ // read face table
+ temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ obj.faces.reserve(temp);
+ for (unsigned int e = 0; e < temp; ++e) {
+
+ obj.faces.push_back(Face());
+ Face& face = obj.faces.back();
+
+ face.elem = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ }
+
+ // read vertex table
+ temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ obj.vertices.reserve(temp);
+ for (unsigned int e = 0; e < temp; ++e) {
+
+ obj.vertices.push_back(Vertex());
+ Vertex& v = obj.vertices.back();
+
+ v.num = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ v.val.x = reader.GetF4();
+ v.val.y = reader.GetF4();
+ v.val.z = reader.GetF4();
+ }
+
+ // read UVs
+ temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ for (unsigned int e = 0; e < temp; ++e) {
+ file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ }
+
+ temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ for (unsigned int e = 0; e < temp; ++e) {
+ file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ }
+
+ if (reader.GetU1()) {
+ const unsigned int x = reader.GetU2(), y = reader.GetU2();
+ temp = 0;
+ while (temp < x*y) {
+ unsigned int repeat = reader.GetU1();
+ reader.GetU1();
+ reader.GetU1();
+ reader.GetU1();
+ temp += repeat;
+ }
+ }
+ }
+
+ // construct a dummy node graph and add all named objects as child nodes
+ aiNode* root = pScene->mRootNode = new aiNode("$NDODummyRoot");
+ aiNode** cc = root->mChildren = new aiNode* [ root->mNumChildren = static_cast<unsigned int>( objects.size()) ] ();
+ pScene->mMeshes = new aiMesh* [ root->mNumChildren] ();
+
+ std::vector<aiVector3D> vertices;
+ std::vector<unsigned int> indices;
+
+ for_each(const Object& obj,objects) {
+ aiNode* nd = *cc++ = new aiNode(obj.name);
+ nd->mParent = root;
+
+ // translated from a python dict() - a vector might be sufficient as well
+ typedef std::map<unsigned int, unsigned int> FaceTable;
+ FaceTable face_table;
+
+ unsigned int n = 0;
+ for_each(const Edge& edge, obj.edges) {
+
+ face_table[edge.edge[2]] = n;
+ face_table[edge.edge[3]] = n;
+
+ ++n;
+ }
+
+ aiMesh* mesh = new aiMesh();
+ aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces=face_table.size()];
+
+ vertices.clear();
+ vertices.reserve(4 * face_table.size()); // arbitrarily choosen
+ for_each(FaceTable::value_type& v, face_table) {
+ indices.clear();
+
+ aiFace& f = *faces++;
+
+ const unsigned int key = v.first;
+ unsigned int cur_edge = v.second;
+ while (1) {
+ unsigned int next_edge, next_vert;
+ if (key == obj.edges[cur_edge].edge[3]) {
+ next_edge = obj.edges[cur_edge].edge[5];
+ next_vert = obj.edges[cur_edge].edge[1];
+ }
+ else {
+ next_edge = obj.edges[cur_edge].edge[4];
+ next_vert = obj.edges[cur_edge].edge[0];
+ }
+ indices.push_back( vertices.size() );
+ vertices.push_back(obj.vertices[ next_vert ].val);
+
+ cur_edge = next_edge;
+ if (cur_edge == v.second) {
+ break;
+ }
+ }
+
+ f.mIndices = new unsigned int[f.mNumIndices = indices.size()];
+ std::copy(indices.begin(),indices.end(),f.mIndices);
+ }
+
+ mesh->mVertices = new aiVector3D[mesh->mNumVertices = vertices.size()];
+ std::copy(vertices.begin(),vertices.end(),mesh->mVertices);
+
+ if (mesh->mNumVertices) {
+ pScene->mMeshes[pScene->mNumMeshes] = mesh;
+
+ (nd->mMeshes = new unsigned int[nd->mNumMeshes=1])[0]=pScene->mNumMeshes++;
+ }
+ }
+}
+
+#endif
diff --git a/src/3rdparty/assimp/code/NDOLoader.h b/src/3rdparty/assimp/code/NDOLoader.h
new file mode 100644
index 000000000..1bae9c653
--- /dev/null
+++ b/src/3rdparty/assimp/code/NDOLoader.h
@@ -0,0 +1,116 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file NDOLoader.h
+ * Declaration of the Nendo importer class.
+ */
+#ifndef INCLUDED_AI_NDO_LOADER_H
+#define INCLUDED_AI_NDO_LOADER_H
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** @brief Importer class to load meshes from Nendo.
+ *
+ * Basing on
+ * <blender>/blender/release/scripts/nendo_import.py by Anthony D'Agostino.
+*/
+class NDOImporter : public BaseImporter
+{
+ friend class Importer;
+protected:
+ /** Constructor to be privately used by Importer */
+ NDOImporter();
+
+ /** Destructor, private as well */
+ ~NDOImporter();
+
+public:
+
+ //! Represents a single edge
+ struct Edge
+ {
+ unsigned int edge[8];
+ unsigned int hard;
+ uint8_t color[8];
+ };
+
+ //! Represents a single face
+ struct Face
+ {
+ unsigned int elem;
+ };
+
+ struct Vertex
+ {
+ unsigned int num;
+ aiVector3D val;
+ };
+
+ //! Represents a single object
+ struct Object
+ {
+ std::string name;
+
+ std::vector<Edge> edges;
+ std::vector<Face> faces;
+ std::vector<Vertex> vertices;
+ };
+
+ // -------------------------------------------------------------------
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+}; // end of class NDOImporter
+} // end of namespace Assimp
+#endif // INCLUDED_AI_NDO_LOADER_H
diff --git a/src/3rdparty/assimp/code/NFFLoader.cpp b/src/3rdparty/assimp/code/NFFLoader.cpp
new file mode 100644
index 000000000..b061e7395
--- /dev/null
+++ b/src/3rdparty/assimp/code/NFFLoader.cpp
@@ -0,0 +1,1276 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the STL importer class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER
+
+// internal headers
+#include "NFFLoader.h"
+#include "ParsingUtils.h"
+#include "StandardShapes.h"
+#include "fast_atof.h"
+#include "RemoveComments.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+NFFImporter::NFFImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+NFFImporter::~NFFImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool NFFImporter::CanRead( const std::string& pFile, IOSystem* /* pIOHandler */, bool /* checkSig */) const
+{
+ return SimpleExtensionCheck(pFile,"nff","enff");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the list of all supported file extensions
+void NFFImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("enff");
+ extensions.insert("nff");
+}
+
+// ------------------------------------------------------------------------------------------------
+#define AI_NFF_PARSE_FLOAT(f) \
+ SkipSpaces(&sz); \
+ if (!::IsLineEnd(*sz))sz = fast_atof_move(sz, (float&)f);
+
+// ------------------------------------------------------------------------------------------------
+#define AI_NFF_PARSE_TRIPLE(v) \
+ AI_NFF_PARSE_FLOAT(v[0]) \
+ AI_NFF_PARSE_FLOAT(v[1]) \
+ AI_NFF_PARSE_FLOAT(v[2])
+
+// ------------------------------------------------------------------------------------------------
+#define AI_NFF_PARSE_SHAPE_INFORMATION() \
+ aiVector3D center, radius(1.0f,get_qnan(),get_qnan()); \
+ AI_NFF_PARSE_TRIPLE(center); \
+ AI_NFF_PARSE_TRIPLE(radius); \
+ if (is_qnan(radius.z))radius.z = radius.x; \
+ if (is_qnan(radius.y))radius.y = radius.x; \
+ currentMesh.radius = radius; \
+ currentMesh.center = center;
+
+// ------------------------------------------------------------------------------------------------
+#define AI_NFF2_GET_NEXT_TOKEN() \
+ do \
+ { \
+ if (!GetNextLine(buffer,line)) \
+ {DefaultLogger::get()->warn("NFF2: Unexpected EOF, can't read next token");break;} \
+ SkipSpaces(line,&sz); \
+ } \
+ while (IsLineEnd(*sz))
+
+
+// ------------------------------------------------------------------------------------------------
+// Loads the materail table for the NFF2 file format from an external file
+void NFFImporter::LoadNFF2MaterialTable(std::vector<ShadingInfo>& output,
+ const std::string& path, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( path, "rb"));
+
+ // Check whether we can read from the file
+ if ( !file.get()) {
+ DefaultLogger::get()->error("NFF2: Unable to open material library " + path + ".");
+ return;
+ }
+
+ // get the size of the file
+ const unsigned int m = (unsigned int)file->FileSize();
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ // (terminate it with zero)
+ std::vector<char> mBuffer2(m+1);
+ TextFileToBuffer(file.get(),mBuffer2);
+ const char* buffer = &mBuffer2[0];
+
+ // First of all: remove all comments from the file
+ CommentRemover::RemoveLineComments("//",&mBuffer2[0]);
+
+ // The file should start with the magic sequence "mat"
+ if (!TokenMatch(buffer,"mat",3)) {
+ DefaultLogger::get()->error("NFF2: Not a valid material library " + path + ".");
+ return;
+ }
+
+ ShadingInfo* curShader = NULL;
+
+ // No read the file line per line
+ char line[4096];
+ const char* sz;
+ while (GetNextLine(buffer,line))
+ {
+ SkipSpaces(line,&sz);
+
+ // 'version' defines the version of the file format
+ if (TokenMatch(sz,"version",7))
+ {
+ DefaultLogger::get()->info("NFF (Sense8) material library file format: " + std::string(sz));
+ }
+ // 'matdef' starts a new material in the file
+ else if (TokenMatch(sz,"matdef",6))
+ {
+ // add a new material to the list
+ output.push_back( ShadingInfo() );
+ curShader = & output.back();
+
+ // parse the name of the material
+ }
+ else if (!TokenMatch(sz,"valid",5))
+ {
+ // check whether we have an active material at the moment
+ if (!IsLineEnd(*sz))
+ {
+ if (!curShader)
+ {
+ DefaultLogger::get()->error(std::string("NFF2 material library: Found element ") +
+ sz + "but there is no active material");
+ continue;
+ }
+ }
+ else continue;
+
+ // now read the material property and determine its type
+ aiColor3D c;
+ if (TokenMatch(sz,"ambient",7))
+ {
+ AI_NFF_PARSE_TRIPLE(c);
+ curShader->ambient = c;
+ }
+ else if (TokenMatch(sz,"diffuse",7) || TokenMatch(sz,"ambientdiffuse",14) /* correct? */)
+ {
+ AI_NFF_PARSE_TRIPLE(c);
+ curShader->diffuse = curShader->ambient = c;
+ }
+ else if (TokenMatch(sz,"specular",8))
+ {
+ AI_NFF_PARSE_TRIPLE(c);
+ curShader->specular = c;
+ }
+ else if (TokenMatch(sz,"emission",8))
+ {
+ AI_NFF_PARSE_TRIPLE(c);
+ curShader->emissive = c;
+ }
+ else if (TokenMatch(sz,"shininess",9))
+ {
+ AI_NFF_PARSE_FLOAT(curShader->shininess);
+ }
+ else if (TokenMatch(sz,"opacity",7))
+ {
+ AI_NFF_PARSE_FLOAT(curShader->opacity);
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void NFFImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( !file.get())
+ throw DeadlyImportError( "Failed to open NFF file " + pFile + ".");
+
+ unsigned int m = (unsigned int)file->FileSize();
+
+ // 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];
+
+ // mesh arrays - separate here to make the handling of the pointers below easier.
+ std::vector<MeshInfo> meshes;
+ std::vector<MeshInfo> meshesWithNormals;
+ std::vector<MeshInfo> meshesWithUVCoords;
+ std::vector<MeshInfo> meshesLocked;
+
+ char line[4096];
+ const char* sz;
+
+ // camera parameters
+ aiVector3D camPos, camUp(0.f,1.f,0.f), camLookAt(0.f,0.f,1.f);
+ float angle = 45.f;
+ aiVector2D resolution;
+
+ bool hasCam = false;
+
+ MeshInfo* currentMeshWithNormals = NULL;
+ MeshInfo* currentMesh = NULL;
+ MeshInfo* currentMeshWithUVCoords = NULL;
+
+ ShadingInfo s; // current material info
+
+ // degree of tesselation
+ unsigned int iTesselation = 4;
+
+ // some temporary variables we need to parse the file
+ unsigned int sphere = 0,
+ cylinder = 0,
+ cone = 0,
+ numNamed = 0,
+ dodecahedron = 0,
+ octahedron = 0,
+ tetrahedron = 0,
+ hexahedron = 0;
+
+ // lights imported from the file
+ std::vector<Light> lights;
+
+ // check whether this is the NFF2 file format
+ if (TokenMatch(buffer,"nff",3))
+ {
+ const float qnan = get_qnan();
+ const aiColor4D cQNAN = aiColor4D (qnan,0.f,0.f,1.f);
+ const aiVector3D vQNAN = aiVector3D(qnan,0.f,0.f);
+
+ // another NFF file format ... just a raw parser has been implemented
+ // no support for further details, I don't think it is worth the effort
+ // http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/nff/nff2.html
+ // http://www.netghost.narod.ru/gff/graphics/summary/sense8.htm
+
+ // First of all: remove all comments from the file
+ CommentRemover::RemoveLineComments("//",&mBuffer2[0]);
+
+ while (GetNextLine(buffer,line))
+ {
+ SkipSpaces(line,&sz);
+ if (TokenMatch(sz,"version",7))
+ {
+ DefaultLogger::get()->info("NFF (Sense8) file format: " + std::string(sz));
+ }
+ else if (TokenMatch(sz,"viewpos",7))
+ {
+ AI_NFF_PARSE_TRIPLE(camPos);
+ hasCam = true;
+ }
+ else if (TokenMatch(sz,"viewdir",7))
+ {
+ AI_NFF_PARSE_TRIPLE(camLookAt);
+ hasCam = true;
+ }
+ // This starts a new object section
+ else if (!IsSpaceOrNewLine(*sz))
+ {
+ unsigned int subMeshIdx = 0;
+
+ // read the name of the object, skip all spaces
+ // at the end of it.
+ const char* sz3 = sz;
+ while (!IsSpaceOrNewLine(*sz))++sz;
+ std::string objectName = std::string(sz3,(unsigned int)(sz-sz3));
+
+ const unsigned int objStart = (unsigned int)meshes.size();
+
+ // There could be a material table in a separate file
+ std::vector<ShadingInfo> materialTable;
+ while (true)
+ {
+ AI_NFF2_GET_NEXT_TOKEN();
+
+ // material table - an external file
+ if (TokenMatch(sz,"mtable",6))
+ {
+ SkipSpaces(&sz);
+ sz3 = sz;
+ while (!IsSpaceOrNewLine(*sz))++sz;
+ const unsigned int diff = (unsigned int)(sz-sz3);
+ if (!diff)DefaultLogger::get()->warn("NFF2: Found empty mtable token");
+ else
+ {
+ // The material table has the file extension .mat.
+ // If it is not there, we need to append it
+ std::string path = std::string(sz3,diff);
+ if (std::string::npos == path.find_last_of(".mat"))
+ {
+ path.append(".mat");
+ }
+
+ // Now extract the working directory from the path to
+ // this file and append the material library filename
+ // to it.
+ std::string::size_type s;
+ if ((std::string::npos == (s = path.find_last_of('\\')) || !s) &&
+ (std::string::npos == (s = path.find_last_of('/')) || !s) )
+ {
+ s = pFile.find_last_of('\\');
+ if (std::string::npos == s)s = pFile.find_last_of('/');
+ if (std::string::npos != s)
+ {
+ path = pFile.substr(0,s+1) + path;
+ }
+ }
+ LoadNFF2MaterialTable(materialTable,path,pIOHandler);
+ }
+ }
+ else break;
+ }
+
+ // read the numbr of vertices
+ unsigned int num = ::strtol10(sz,&sz);
+
+ // temporary storage
+ std::vector<aiColor4D> tempColors;
+ std::vector<aiVector3D> tempPositions,tempTextureCoords,tempNormals;
+
+ bool hasNormals = false,hasUVs = false,hasColor = false;
+
+ tempPositions.reserve (num);
+ tempColors.reserve (num);
+ tempNormals.reserve (num);
+ tempTextureCoords.reserve (num);
+ for (unsigned int i = 0; i < num; ++i)
+ {
+ AI_NFF2_GET_NEXT_TOKEN();
+ aiVector3D v;
+ AI_NFF_PARSE_TRIPLE(v);
+ tempPositions.push_back(v);
+
+ // parse all other attributes in the line
+ while (true)
+ {
+ SkipSpaces(&sz);
+ if (IsLineEnd(*sz))break;
+
+ // color definition
+ if (TokenMatch(sz,"0x",2))
+ {
+ hasColor = true;
+ register unsigned int numIdx = ::strtol16(sz,&sz);
+ aiColor4D clr;
+ clr.a = 1.f;
+
+ // 0xRRGGBB
+ clr.r = ((numIdx >> 16u) & 0xff) / 255.f;
+ clr.g = ((numIdx >> 8u) & 0xff) / 255.f;
+ clr.b = ((numIdx) & 0xff) / 255.f;
+ tempColors.push_back(clr);
+ }
+ // normal vector
+ else if (TokenMatch(sz,"norm",4))
+ {
+ hasNormals = true;
+ AI_NFF_PARSE_TRIPLE(v);
+ tempNormals.push_back(v);
+ }
+ // UV coordinate
+ else if (TokenMatch(sz,"uv",2))
+ {
+ hasUVs = true;
+ AI_NFF_PARSE_FLOAT(v.x);
+ AI_NFF_PARSE_FLOAT(v.y);
+ v.z = 0.f;
+ tempTextureCoords.push_back(v);
+ }
+ }
+
+ // fill in dummies for all attributes that have not been set
+ if (tempNormals.size() != tempPositions.size())
+ tempNormals.push_back(vQNAN);
+
+ if (tempTextureCoords.size() != tempPositions.size())
+ tempTextureCoords.push_back(vQNAN);
+
+ if (tempColors.size() != tempPositions.size())
+ tempColors.push_back(cQNAN);
+ }
+
+ AI_NFF2_GET_NEXT_TOKEN();
+ if (!num)throw DeadlyImportError("NFF2: There are zero vertices");
+ num = ::strtol10(sz,&sz);
+
+ std::vector<unsigned int> tempIdx;
+ tempIdx.reserve(10);
+ for (unsigned int i = 0; i < num; ++i)
+ {
+ AI_NFF2_GET_NEXT_TOKEN();
+ SkipSpaces(line,&sz);
+ unsigned int numIdx = ::strtol10(sz,&sz);
+
+ // read all faces indices
+ if (numIdx)
+ {
+ // mesh.faces.push_back(numIdx);
+ // tempIdx.erase(tempIdx.begin(),tempIdx.end());
+ tempIdx.resize(numIdx);
+
+ for (unsigned int a = 0; a < numIdx;++a)
+ {
+ SkipSpaces(sz,&sz);
+ m = ::strtol10(sz,&sz);
+ if (m >= (unsigned int)tempPositions.size())
+ {
+ DefaultLogger::get()->error("NFF2: Vertex index overflow");
+ m= 0;
+ }
+ // mesh.vertices.push_back (tempPositions[idx]);
+ tempIdx[a] = m;
+ }
+ }
+
+ // build a temporary shader object for the face.
+ ShadingInfo shader;
+ unsigned int matIdx = 0;
+
+ // white material color - we have vertex colors
+ shader.color = aiColor3D(1.f,1.f,1.f);
+ aiColor4D c = aiColor4D(1.f,1.f,1.f,1.f);
+ while (true)
+ {
+ SkipSpaces(sz,&sz);
+ if (IsLineEnd(*sz))break;
+
+ // per-polygon colors
+ if (TokenMatch(sz,"0x",2))
+ {
+ hasColor = true;
+ const char* sz2 = sz;
+ numIdx = ::strtol16(sz,&sz);
+ const unsigned int diff = (unsigned int)(sz-sz2);
+
+ // 0xRRGGBB
+ if (diff > 3)
+ {
+ c.r = ((numIdx >> 16u) & 0xff) / 255.f;
+ c.g = ((numIdx >> 8u) & 0xff) / 255.f;
+ c.b = ((numIdx) & 0xff) / 255.f;
+ }
+ // 0xRGB
+ else
+ {
+ c.r = ((numIdx >> 8u) & 0xf) / 16.f;
+ c.g = ((numIdx >> 4u) & 0xf) / 16.f;
+ c.b = ((numIdx) & 0xf) / 16.f;
+ }
+ }
+ // TODO - implement texture mapping here
+#if 0
+ // mirror vertex texture coordinate?
+ else if (TokenMatch(sz,"mirror",6))
+ {
+ }
+ // texture coordinate scaling
+ else if (TokenMatch(sz,"scale",5))
+ {
+ }
+ // texture coordinate translation
+ else if (TokenMatch(sz,"trans",5))
+ {
+ }
+ // texture coordinate rotation angle
+ else if (TokenMatch(sz,"rot",3))
+ {
+ }
+#endif
+
+ // texture file name for this polygon + mapping information
+ else if ('_' == sz[0])
+ {
+ // get mapping information
+ switch (sz[1])
+ {
+ case 'v':
+ case 'V':
+
+ shader.shaded = false;
+ break;
+
+ case 't':
+ case 'T':
+ case 'u':
+ case 'U':
+
+ DefaultLogger::get()->warn("Unsupported NFF2 texture attribute: trans");
+ };
+ if (!sz[1] || '_' != sz[2])
+ {
+ DefaultLogger::get()->warn("NFF2: Expected underscore after texture attributes");
+ continue;
+ }
+ const char* sz2 = sz+3;
+ while (!IsSpaceOrNewLine( *sz ))++sz;
+ const unsigned int diff = (unsigned int)(sz-sz2);
+ if (diff)shader.texFile = std::string(sz2,diff);
+ }
+
+ // Two-sided material?
+ else if (TokenMatch(sz,"both",4))
+ {
+ shader.twoSided = true;
+ }
+
+ // Material ID?
+ else if (!materialTable.empty() && TokenMatch(sz,"matid",5))
+ {
+ SkipSpaces(&sz);
+ matIdx = ::strtol10(sz,&sz);
+ if (matIdx >= materialTable.size())
+ {
+ DefaultLogger::get()->error("NFF2: Material index overflow.");
+ matIdx = 0;
+ }
+
+ // now combine our current shader with the shader we
+ // read from the material table.
+ ShadingInfo& mat = materialTable[matIdx];
+ shader.ambient = mat.ambient;
+ shader.diffuse = mat.diffuse;
+ shader.emissive = mat.emissive;
+ shader.opacity = mat.opacity;
+ shader.specular = mat.specular;
+ shader.shininess = mat.shininess;
+ }
+ else SkipToken(sz);
+ }
+
+ // search the list of all shaders we have for this object whether
+ // there is an identical one. In this case, we append our mesh
+ // data to it.
+ MeshInfo* mesh = NULL;
+ for (std::vector<MeshInfo>::iterator it = meshes.begin() + objStart, end = meshes.end();
+ it != end; ++it)
+ {
+ if ((*it).shader == shader && (*it).matIndex == matIdx)
+ {
+ // we have one, we can append our data to it
+ mesh = &(*it);
+ }
+ }
+ if (!mesh)
+ {
+ meshes.push_back(MeshInfo(PatchType_Simple,false));
+ mesh = &meshes.back();
+ mesh->matIndex = matIdx;
+
+ // We need to add a new mesh to the list. We assign
+ // an unique name to it to make sure the scene will
+ // pass the validation step for the moment.
+ // TODO: fix naming of objects in the scenegraph later
+ if (objectName.length())
+ {
+ ::strcpy(mesh->name,objectName.c_str());
+ ASSIMP_itoa10(&mesh->name[objectName.length()],30,subMeshIdx++);
+ }
+
+ // copy the shader to the mesh.
+ mesh->shader = shader;
+ }
+
+ // fill the mesh with data
+ if (!tempIdx.empty())
+ {
+ mesh->faces.push_back((unsigned int)tempIdx.size());
+ for (std::vector<unsigned int>::const_iterator it = tempIdx.begin(), end = tempIdx.end();
+ it != end;++it)
+ {
+ m = *it;
+
+ // copy colors -vertex color specifications override polygon color specifications
+ if (hasColor)
+ {
+ const aiColor4D& clr = tempColors[m];
+ mesh->colors.push_back((is_qnan( clr.r ) ? c : clr));
+ }
+
+ // positions should always be there
+ mesh->vertices.push_back (tempPositions[m]);
+
+ // copy normal vectors
+ if (hasNormals)
+ mesh->normals.push_back (tempNormals[m]);
+
+ // copy texture coordinates
+ if (hasUVs)
+ mesh->uvs.push_back (tempTextureCoords[m]);
+ }
+ }
+ }
+ if (!num)throw DeadlyImportError("NFF2: There are zero faces");
+ }
+ }
+ camLookAt = camLookAt + camPos;
+ }
+ else // "Normal" Neutral file format that is quite more common
+ {
+ while (GetNextLine(buffer,line))
+ {
+ sz = line;
+ if ('p' == line[0] || TokenMatch(sz,"tpp",3))
+ {
+ MeshInfo* out = NULL;
+
+ // 'tpp' - texture polygon patch primitive
+ if ('t' == line[0])
+ {
+ currentMeshWithUVCoords = NULL;
+ for (std::vector<MeshInfo>::iterator it = meshesWithUVCoords.begin(), end = meshesWithUVCoords.end();
+ it != end;++it)
+ {
+ if ((*it).shader == s)
+ {
+ currentMeshWithUVCoords = &(*it);
+ break;
+ }
+ }
+
+ if (!currentMeshWithUVCoords)
+ {
+ meshesWithUVCoords.push_back(MeshInfo(PatchType_UVAndNormals));
+ currentMeshWithUVCoords = &meshesWithUVCoords.back();
+ currentMeshWithUVCoords->shader = s;
+ }
+ out = currentMeshWithUVCoords;
+ }
+ // 'pp' - polygon patch primitive
+ else if ('p' == line[1])
+ {
+ currentMeshWithNormals = NULL;
+ for (std::vector<MeshInfo>::iterator it = meshesWithNormals.begin(), end = meshesWithNormals.end();
+ it != end;++it)
+ {
+ if ((*it).shader == s)
+ {
+ currentMeshWithNormals = &(*it);
+ break;
+ }
+ }
+
+ if (!currentMeshWithNormals)
+ {
+ meshesWithNormals.push_back(MeshInfo(PatchType_Normals));
+ currentMeshWithNormals = &meshesWithNormals.back();
+ currentMeshWithNormals->shader = s;
+ }
+ sz = &line[2];out = currentMeshWithNormals;
+ }
+ // 'p' - polygon primitive
+ else
+ {
+ currentMesh = NULL;
+ for (std::vector<MeshInfo>::iterator it = meshes.begin(), end = meshes.end();
+ it != end;++it)
+ {
+ if ((*it).shader == s)
+ {
+ currentMesh = &(*it);
+ break;
+ }
+ }
+
+ if (!currentMesh)
+ {
+ meshes.push_back(MeshInfo(PatchType_Simple));
+ currentMesh = &meshes.back();
+ currentMesh->shader = s;
+ }
+ sz = &line[1];out = currentMesh;
+ }
+ SkipSpaces(sz,&sz);
+ m = strtol10(sz);
+
+ // ---- flip the face order
+ out->vertices.resize(out->vertices.size()+m);
+ if (out != currentMesh)
+ {
+ out->normals.resize(out->vertices.size());
+ }
+ if (out == currentMeshWithUVCoords)
+ {
+ out->uvs.resize(out->vertices.size());
+ }
+ for (unsigned int n = 0; n < m;++n)
+ {
+ if (!GetNextLine(buffer,line))
+ {
+ DefaultLogger::get()->error("NFF: Unexpected EOF was encountered. Patch definition incomplete");
+ continue;
+ }
+
+ aiVector3D v; sz = &line[0];
+ AI_NFF_PARSE_TRIPLE(v);
+ out->vertices[out->vertices.size()-n-1] = v;
+
+ if (out != currentMesh)
+ {
+ AI_NFF_PARSE_TRIPLE(v);
+ out->normals[out->vertices.size()-n-1] = v;
+ }
+ if (out == currentMeshWithUVCoords)
+ {
+ // FIX: in one test file this wraps over multiple lines
+ SkipSpaces(&sz);
+ if (IsLineEnd(*sz))
+ {
+ GetNextLine(buffer,line);
+ sz = line;
+ }
+ AI_NFF_PARSE_FLOAT(v.x);
+ SkipSpaces(&sz);
+ if (IsLineEnd(*sz))
+ {
+ GetNextLine(buffer,line);
+ sz = line;
+ }
+ AI_NFF_PARSE_FLOAT(v.y);
+ v.y = 1.f - v.y;
+ out->uvs[out->vertices.size()-n-1] = v;
+ }
+ }
+ out->faces.push_back(m);
+ }
+ // 'f' - shading information block
+ else if (TokenMatch(sz,"f",1))
+ {
+ float d;
+
+ // read the RGB colors
+ AI_NFF_PARSE_TRIPLE(s.color);
+
+ // read the other properties
+ AI_NFF_PARSE_FLOAT(s.diffuse.r);
+ AI_NFF_PARSE_FLOAT(s.specular.r);
+ AI_NFF_PARSE_FLOAT(d); // skip shininess and transmittance
+ AI_NFF_PARSE_FLOAT(d);
+ AI_NFF_PARSE_FLOAT(s.refracti);
+
+ // NFF2 uses full colors here so we need to use them too
+ // although NFF uses simple scaling factors
+ s.diffuse.g = s.diffuse.b = s.diffuse.r;
+ s.specular.g = s.specular.b = s.specular.r;
+
+ // if the next one is NOT a number we assume it is a texture file name
+ // this feature is used by some NFF files on the internet and it has
+ // been implemented as it can be really useful
+ SkipSpaces(&sz);
+ if (!IsNumeric(*sz))
+ {
+ // TODO: Support full file names with spaces and quotation marks ...
+ const char* p = sz;
+ while (!IsSpaceOrNewLine( *sz ))++sz;
+
+ unsigned int diff = (unsigned int)(sz-p);
+ if (diff)
+ {
+ s.texFile = std::string(p,diff);
+ }
+ }
+ else
+ {
+ AI_NFF_PARSE_FLOAT(s.ambient); // optional
+ }
+ }
+ // 'shader' - other way to specify a texture
+ else if (TokenMatch(sz,"shader",6))
+ {
+ SkipSpaces(&sz);
+ const char* old = sz;
+ while (!IsSpaceOrNewLine(*sz))++sz;
+ s.texFile = std::string(old, (uintptr_t)sz - (uintptr_t)old);
+ }
+ // 'l' - light source
+ else if (TokenMatch(sz,"l",1))
+ {
+ lights.push_back(Light());
+ Light& light = lights.back();
+
+ AI_NFF_PARSE_TRIPLE(light.position);
+ AI_NFF_PARSE_FLOAT (light.intensity);
+ AI_NFF_PARSE_TRIPLE(light.color);
+ }
+ // 's' - sphere
+ else if (TokenMatch(sz,"s",1))
+ {
+ meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
+ MeshInfo& currentMesh = meshesLocked.back();
+ currentMesh.shader = s;
+ currentMesh.shader.mapping = aiTextureMapping_SPHERE;
+
+ AI_NFF_PARSE_SHAPE_INFORMATION();
+
+ // we don't need scaling or translation here - we do it in the node's transform
+ StandardShapes::MakeSphere(iTesselation, currentMesh.vertices);
+ currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
+
+ // generate a name for the mesh
+ ::sprintf(currentMesh.name,"sphere_%i",sphere++);
+ }
+ // 'dod' - dodecahedron
+ else if (TokenMatch(sz,"dod",3))
+ {
+ meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
+ MeshInfo& currentMesh = meshesLocked.back();
+ currentMesh.shader = s;
+ currentMesh.shader.mapping = aiTextureMapping_SPHERE;
+
+ AI_NFF_PARSE_SHAPE_INFORMATION();
+
+ // we don't need scaling or translation here - we do it in the node's transform
+ StandardShapes::MakeDodecahedron(currentMesh.vertices);
+ currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
+
+ // generate a name for the mesh
+ ::sprintf(currentMesh.name,"dodecahedron_%i",dodecahedron++);
+ }
+
+ // 'oct' - octahedron
+ else if (TokenMatch(sz,"oct",3))
+ {
+ meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
+ MeshInfo& currentMesh = meshesLocked.back();
+ currentMesh.shader = s;
+ currentMesh.shader.mapping = aiTextureMapping_SPHERE;
+
+ AI_NFF_PARSE_SHAPE_INFORMATION();
+
+ // we don't need scaling or translation here - we do it in the node's transform
+ StandardShapes::MakeOctahedron(currentMesh.vertices);
+ currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
+
+ // generate a name for the mesh
+ ::sprintf(currentMesh.name,"octahedron_%i",octahedron++);
+ }
+
+ // 'tet' - tetrahedron
+ else if (TokenMatch(sz,"tet",3))
+ {
+ meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
+ MeshInfo& currentMesh = meshesLocked.back();
+ currentMesh.shader = s;
+ currentMesh.shader.mapping = aiTextureMapping_SPHERE;
+
+ AI_NFF_PARSE_SHAPE_INFORMATION();
+
+ // we don't need scaling or translation here - we do it in the node's transform
+ StandardShapes::MakeTetrahedron(currentMesh.vertices);
+ currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
+
+ // generate a name for the mesh
+ ::sprintf(currentMesh.name,"tetrahedron_%i",tetrahedron++);
+ }
+
+ // 'hex' - hexahedron
+ else if (TokenMatch(sz,"hex",3))
+ {
+ meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
+ MeshInfo& currentMesh = meshesLocked.back();
+ currentMesh.shader = s;
+ currentMesh.shader.mapping = aiTextureMapping_BOX;
+
+ AI_NFF_PARSE_SHAPE_INFORMATION();
+
+ // we don't need scaling or translation here - we do it in the node's transform
+ StandardShapes::MakeHexahedron(currentMesh.vertices);
+ currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
+
+ // generate a name for the mesh
+ ::sprintf(currentMesh.name,"hexahedron_%i",hexahedron++);
+ }
+ // 'c' - cone
+ else if (TokenMatch(sz,"c",1))
+ {
+ meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
+ MeshInfo& currentMesh = meshesLocked.back();
+ currentMesh.shader = s;
+ currentMesh.shader.mapping = aiTextureMapping_CYLINDER;
+
+ if (!GetNextLine(buffer,line))
+ {
+ DefaultLogger::get()->error("NFF: Unexpected end of file (cone definition not complete)");
+ break;
+ }
+ sz = line;
+
+ // read the two center points and the respective radii
+ aiVector3D center1, center2; float radius1, radius2;
+ AI_NFF_PARSE_TRIPLE(center1);
+ AI_NFF_PARSE_FLOAT(radius1);
+
+ if (!GetNextLine(buffer,line))
+ {
+ DefaultLogger::get()->error("NFF: Unexpected end of file (cone definition not complete)");
+ break;
+ }
+ sz = line;
+
+ AI_NFF_PARSE_TRIPLE(center2);
+ AI_NFF_PARSE_FLOAT(radius2);
+
+ // compute the center point of the cone/cylinder -
+ // it is its local transformation origin
+ currentMesh.dir = center2-center1;
+ currentMesh.center = center1+currentMesh.dir/2.f;
+
+ float f;
+ if (( f = currentMesh.dir.Length()) < 10e-3f )
+ {
+ DefaultLogger::get()->error("NFF: Cone height is close to zero");
+ continue;
+ }
+ currentMesh.dir /= f; // normalize
+
+ // generate the cone - it consists of simple triangles
+ StandardShapes::MakeCone(f, radius1, radius2,
+ integer_pow(4, iTesselation), currentMesh.vertices);
+
+ // MakeCone() returns tris
+ currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
+
+ // generate a name for the mesh. 'cone' if it a cone,
+ // 'cylinder' if it is a cylinder. Funny, isn't it?
+ if (radius1 != radius2)
+ ::sprintf(currentMesh.name,"cone_%i",cone++);
+ else ::sprintf(currentMesh.name,"cylinder_%i",cylinder++);
+ }
+ // 'tess' - tesselation
+ else if (TokenMatch(sz,"tess",4))
+ {
+ SkipSpaces(&sz);
+ iTesselation = strtol10(sz);
+ }
+ // 'from' - camera position
+ else if (TokenMatch(sz,"from",4))
+ {
+ AI_NFF_PARSE_TRIPLE(camPos);
+ hasCam = true;
+ }
+ // 'at' - camera look-at vector
+ else if (TokenMatch(sz,"at",2))
+ {
+ AI_NFF_PARSE_TRIPLE(camLookAt);
+ hasCam = true;
+ }
+ // 'up' - camera up vector
+ else if (TokenMatch(sz,"up",2))
+ {
+ AI_NFF_PARSE_TRIPLE(camUp);
+ hasCam = true;
+ }
+ // 'angle' - (half?) camera field of view
+ else if (TokenMatch(sz,"angle",5))
+ {
+ AI_NFF_PARSE_FLOAT(angle);
+ hasCam = true;
+ }
+ // 'resolution' - used to compute the screen aspect
+ else if (TokenMatch(sz,"resolution",10))
+ {
+ AI_NFF_PARSE_FLOAT(resolution.x);
+ AI_NFF_PARSE_FLOAT(resolution.y);
+ hasCam = true;
+ }
+ // 'pb' - bezier patch. Not supported yet
+ else if (TokenMatch(sz,"pb",2))
+ {
+ DefaultLogger::get()->error("NFF: Encountered unsupported ID: bezier patch");
+ }
+ // 'pn' - NURBS. Not supported yet
+ else if (TokenMatch(sz,"pn",2) || TokenMatch(sz,"pnn",3))
+ {
+ DefaultLogger::get()->error("NFF: Encountered unsupported ID: NURBS");
+ }
+ // '' - comment
+ else if ('#' == line[0])
+ {
+ const char* sz;SkipSpaces(&line[1],&sz);
+ if (!IsLineEnd(*sz))DefaultLogger::get()->info(sz);
+ }
+ }
+ }
+
+ // copy all arrays into one large
+ meshes.reserve (meshes.size()+meshesLocked.size()+meshesWithNormals.size()+meshesWithUVCoords.size());
+ meshes.insert (meshes.end(),meshesLocked.begin(),meshesLocked.end());
+ meshes.insert (meshes.end(),meshesWithNormals.begin(),meshesWithNormals.end());
+ meshes.insert (meshes.end(),meshesWithUVCoords.begin(),meshesWithUVCoords.end());
+
+ // now generate output meshes. first find out how many meshes we'll need
+ std::vector<MeshInfo>::const_iterator it = meshes.begin(), end = meshes.end();
+ for (;it != end;++it)
+ {
+ if (!(*it).faces.empty())
+ {
+ ++pScene->mNumMeshes;
+ if ((*it).name[0])++numNamed;
+ }
+ }
+
+ // generate a dummy root node - assign all unnamed elements such
+ // as polygons and polygon patches to the root node and generate
+ // sub nodes for named objects such as spheres and cones.
+ aiNode* const root = new aiNode();
+ root->mName.Set("<NFF_Root>");
+ root->mNumChildren = numNamed + (hasCam ? 1 : 0) + (unsigned int) lights.size();
+ root->mNumMeshes = pScene->mNumMeshes-numNamed;
+
+ aiNode** ppcChildren = NULL;
+ unsigned int iChild = 0;
+ unsigned int nChildren = 0;
+ unsigned int* pMeshes = NULL;
+ if (root->mNumMeshes)
+ pMeshes = root->mMeshes = new unsigned int[root->mNumMeshes];
+ if (root->mNumChildren) {
+ ppcChildren = root->mChildren = new aiNode*[root->mNumChildren];
+ nChildren = root->mNumChildren;
+ }
+
+ // generate the camera
+ if (hasCam)
+ {
+ ai_assert(ppcChildren && iChild<nChildren);
+ if (ppcChildren && iChild<nChildren) {
+ aiNode* nd = *ppcChildren = new aiNode();
+ ++iChild;
+ nd->mName.Set("<NFF_Camera>");
+ nd->mParent = root;
+
+ // allocate the camera in the scene
+ pScene->mNumCameras = 1;
+ pScene->mCameras = new aiCamera*[1];
+ aiCamera* c = pScene->mCameras[0] = new aiCamera;
+
+ c->mName = nd->mName; // make sure the names are identical
+ c->mHorizontalFOV = AI_DEG_TO_RAD( angle );
+ c->mLookAt = camLookAt - camPos;
+ c->mPosition = camPos;
+ c->mUp = camUp;
+
+ // If the resolution is not specified in the file, we
+ // need to set 1.0 as aspect.
+ c->mAspect = (!resolution.y ? 0.f : resolution.x / resolution.y);
+ ++ppcChildren;
+ }
+ }
+
+ // generate light sources
+ if (!lights.empty())
+ {
+ pScene->mNumLights = (unsigned int)lights.size();
+ pScene->mLights = new aiLight*[pScene->mNumLights];
+ for (unsigned int i = 0; i < pScene->mNumLights;++i,++ppcChildren)
+ {
+ const Light& l = lights[i];
+
+ ai_assert(ppcChildren && iChild<nChildren);
+ if (ppcChildren && iChild<nChildren) {
+ aiNode* nd = *ppcChildren = new aiNode();
+ ++iChild;
+ nd->mParent = root;
+
+ nd->mName.length = ::sprintf(nd->mName.data,"<NFF_Light%i>",i);
+
+ // allocate the light in the scene data structure
+ aiLight* out = pScene->mLights[i] = new aiLight();
+ out->mName = nd->mName; // make sure the names are identical
+ out->mType = aiLightSource_POINT;
+ out->mColorDiffuse = out->mColorSpecular = l.color * l.intensity;
+ out->mPosition = l.position;
+ }
+ }
+ }
+
+ if (!pScene->mNumMeshes)throw DeadlyImportError("NFF: No meshes loaded");
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = pScene->mNumMeshes];
+ for (it = meshes.begin(), m = 0; it != end;++it)
+ {
+ if ((*it).faces.empty())continue;
+
+ const MeshInfo& src = *it;
+ aiMesh* const mesh = pScene->mMeshes[m] = new aiMesh();
+ mesh->mNumVertices = (unsigned int)src.vertices.size();
+ mesh->mNumFaces = (unsigned int)src.faces.size();
+
+ // Generate sub nodes for named meshes
+ if (src.name[0])
+ {
+ ai_assert(ppcChildren && iChild<nChildren);
+ if (ppcChildren && iChild<nChildren) {
+ aiNode* const node = *ppcChildren = new aiNode();
+ ++iChild;
+ node->mParent = root;
+ node->mNumMeshes = 1;
+ node->mMeshes = new unsigned int[1];
+ node->mMeshes[0] = m;
+ node->mName.Set(src.name);
+
+ // setup the transformation matrix of the node
+ aiMatrix4x4::FromToMatrix(aiVector3D(0.f,1.f,0.f),
+ src.dir,node->mTransformation);
+
+ aiMatrix4x4& mat = node->mTransformation;
+ mat.a1 *= src.radius.x; mat.b1 *= src.radius.x; mat.c1 *= src.radius.x;
+ mat.a2 *= src.radius.y; mat.b2 *= src.radius.y; mat.c2 *= src.radius.y;
+ mat.a3 *= src.radius.z; mat.b3 *= src.radius.z; mat.c3 *= src.radius.z;
+ mat.a4 = src.center.x;
+ mat.b4 = src.center.y;
+ mat.c4 = src.center.z;
+ }
+
+ ++ppcChildren;
+ }
+ else *pMeshes++ = m;
+
+ // copy vertex positions
+ mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+ ::memcpy(mesh->mVertices,&src.vertices[0],
+ sizeof(aiVector3D)*mesh->mNumVertices);
+
+ // NFF2: there could be vertex colors
+ if (!src.colors.empty())
+ {
+ ai_assert(src.colors.size() == src.vertices.size());
+
+ // copy vertex colors
+ mesh->mColors[0] = new aiColor4D[mesh->mNumVertices];
+ ::memcpy(mesh->mColors[0],&src.colors[0],
+ sizeof(aiColor4D)*mesh->mNumVertices);
+ }
+
+ if (!src.normals.empty())
+ {
+ ai_assert(src.normals.size() == src.vertices.size());
+
+ // copy normal vectors
+ mesh->mNormals = new aiVector3D[mesh->mNumVertices];
+ ::memcpy(mesh->mNormals,&src.normals[0],
+ sizeof(aiVector3D)*mesh->mNumVertices);
+ }
+
+ if (!src.uvs.empty())
+ {
+ ai_assert(src.uvs.size() == src.vertices.size());
+
+ // copy texture coordinates
+ mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
+ ::memcpy(mesh->mTextureCoords[0],&src.uvs[0],
+ sizeof(aiVector3D)*mesh->mNumVertices);
+ }
+
+ // generate faces
+ unsigned int p = 0;
+ aiFace* pFace = mesh->mFaces = new aiFace[mesh->mNumFaces];
+ for (std::vector<unsigned int>::const_iterator it2 = src.faces.begin(),
+ end2 = src.faces.end();
+ it2 != end2;++it2,++pFace)
+ {
+ pFace->mIndices = new unsigned int [ pFace->mNumIndices = *it2 ];
+ for (unsigned int o = 0; o < pFace->mNumIndices;++o)
+ pFace->mIndices[o] = p++;
+ }
+
+ // generate a material for the mesh
+ MaterialHelper* pcMat = (MaterialHelper*)(pScene->mMaterials[m] = new MaterialHelper());
+
+ mesh->mMaterialIndex = m++;
+
+ aiString s;
+ s.Set(AI_DEFAULT_MATERIAL_NAME);
+ pcMat->AddProperty(&s, AI_MATKEY_NAME);
+
+ // FIX: Ignore diffuse == 0
+ aiColor3D c = src.shader.color * (src.shader.diffuse.r ? src.shader.diffuse : aiColor3D(1.f,1.f,1.f));
+ pcMat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE);
+ c = src.shader.color * src.shader.specular;
+ pcMat->AddProperty(&c,1,AI_MATKEY_COLOR_SPECULAR);
+
+ // NFF2 - default values for NFF
+ pcMat->AddProperty(&src.shader.ambient, 1,AI_MATKEY_COLOR_AMBIENT);
+ pcMat->AddProperty(&src.shader.emissive,1,AI_MATKEY_COLOR_EMISSIVE);
+ pcMat->AddProperty(&src.shader.opacity, 1,AI_MATKEY_OPACITY);
+
+ // setup the first texture layer, if existing
+ if (src.shader.texFile.length())
+ {
+ s.Set(src.shader.texFile);
+ pcMat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ if (aiTextureMapping_UV != src.shader.mapping) {
+
+ aiVector3D v(0.f,-1.f,0.f);
+ pcMat->AddProperty(&v, 1,AI_MATKEY_TEXMAP_AXIS_DIFFUSE(0));
+ pcMat->AddProperty((int*)&src.shader.mapping, 1,AI_MATKEY_MAPPING_DIFFUSE(0));
+ }
+ }
+
+ // setup the name of the material
+ if (src.shader.name.length())
+ {
+ s.Set(src.shader.texFile);
+ pcMat->AddProperty(&s,AI_MATKEY_NAME);
+ }
+
+ // setup some more material properties that are specific to NFF2
+ int i;
+ if (src.shader.twoSided)
+ {
+ i = 1;
+ pcMat->AddProperty(&i,1,AI_MATKEY_TWOSIDED);
+ }
+ i = (src.shader.shaded ? aiShadingMode_Gouraud : aiShadingMode_NoShading);
+ if (src.shader.shininess)
+ {
+ i = aiShadingMode_Phong;
+ pcMat->AddProperty(&src.shader.shininess,1,AI_MATKEY_SHININESS);
+ }
+ pcMat->AddProperty(&i,1,AI_MATKEY_SHADING_MODEL);
+ }
+ pScene->mRootNode = root;
+}
+
+#endif // !! ASSIMP_BUILD_NO_NFF_IMPORTER
diff --git a/src/3rdparty/assimp/code/NFFLoader.h b/src/3rdparty/assimp/code/NFFLoader.h
new file mode 100644
index 000000000..2e21bdc8b
--- /dev/null
+++ b/src/3rdparty/assimp/code/NFFLoader.h
@@ -0,0 +1,216 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file NFFLoader.h
+ * @brief Declaration of the NFF importer class.
+ */
+#ifndef AI_NFFLOADER_H_INCLUDED
+#define AI_NFFLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include <vector>
+
+#include "../include/aiTypes.h"
+
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------
+/** NFF (Neutral File Format) Importer class.
+ *
+ * The class implements both Eric Haynes NFF format and Sense8's NFF (NFF2) format.
+ * Both are quite different and the loading code is somewhat dirty at
+ * the moment. Sense8 should be moved to a separate loader.
+*/
+class NFFImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ NFFImporter();
+
+ /** Destructor, private as well */
+ ~NFFImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+
+ // describes face material properties
+ struct ShadingInfo
+ {
+ ShadingInfo()
+ : color (0.6f,0.6f,0.6f)
+ , diffuse (1.f,1.f,1.f)
+ , specular (1.f,1.f,1.f)
+ , ambient (0.f,0.f,0.f)
+ , emissive (0.f,0.f,0.f)
+ , refracti (1.f)
+ , twoSided (false) // for NFF2
+ , shaded (true) // for NFF2
+ , opacity (1.f)
+ , shininess (0.f)
+ , mapping (aiTextureMapping_UV)
+ {}
+
+ aiColor3D color,diffuse,specular,ambient,emissive;
+ float refracti;
+
+ std::string texFile;
+
+ // For NFF2
+ bool twoSided;
+ bool shaded;
+ float opacity, shininess;
+
+ std::string name;
+
+ // texture mapping to be generated for the mesh - uv is the default
+ // it means: use UV if there, nothing otherwise. This property is
+ // used for locked meshes.
+ aiTextureMapping mapping;
+
+ // shininess is ignored for the moment
+ bool operator == (const ShadingInfo& other) const
+ {
+ return color == other.color &&
+ diffuse == other.diffuse &&
+ specular == other.specular &&
+ ambient == other.ambient &&
+ refracti == other.refracti &&
+ texFile == other.texFile &&
+ twoSided == other.twoSided &&
+ shaded == other.shaded;
+
+ // Some properties from NFF2 aren't compared by this operator.
+ // Comparing MeshInfo::matIndex should do that.
+ }
+ };
+
+ // describes a NFF light source
+ struct Light
+ {
+ Light()
+ : intensity (1.f)
+ , color (1.f,1.f,1.f)
+ {}
+
+ aiVector3D position;
+ float intensity;
+ aiColor3D color;
+ };
+
+ enum PatchType
+ {
+ PatchType_Simple = 0x0,
+ PatchType_Normals = 0x1,
+ PatchType_UVAndNormals = 0x2
+ };
+
+ // describes a NFF mesh
+ struct MeshInfo
+ {
+ MeshInfo(PatchType _pType, bool bL = false)
+ : pType (_pType)
+ , bLocked (bL)
+ , radius (1.f,1.f,1.f)
+ , dir (0.f,1.f,0.f)
+ , matIndex (0)
+ {
+ name[0] = '\0'; // by default meshes are unnamed
+ }
+
+ ShadingInfo shader;
+ PatchType pType;
+ bool bLocked;
+
+ // for spheres, cones and cylinders: center point of the object
+ aiVector3D center, radius, dir;
+
+ char name[128];
+
+ std::vector<aiVector3D> vertices, normals, uvs;
+ std::vector<unsigned int> faces;
+
+ // for NFF2
+ std::vector<aiColor4D> colors;
+ unsigned int matIndex;
+ };
+
+
+ // -------------------------------------------------------------------
+ /** Loads the material table for the NFF2 file format from an
+ * external file.
+ *
+ * @param output Receives the list of output meshes
+ * @param path Path to the file (abs. or rel.)
+ * @param pIOHandler IOSystem to be used to open the file
+ */
+ void LoadNFF2MaterialTable(std::vector<ShadingInfo>& output,
+ const std::string& path, IOSystem* pIOHandler);
+
+};
+
+} // end of namespace Assimp
+
+#endif // AI_NFFIMPORTER_H_IN
diff --git a/src/3rdparty/assimp/code/OFFLoader.cpp b/src/3rdparty/assimp/code/OFFLoader.cpp
new file mode 100644
index 000000000..38e21b49d
--- /dev/null
+++ b/src/3rdparty/assimp/code/OFFLoader.cpp
@@ -0,0 +1,211 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file OFFLoader.cpp
+ * @brief Implementation of the OFF importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
+
+// internal headers
+#include "OFFLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+OFFImporter::OFFImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+OFFImporter::~OFFImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool OFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "off")
+ return true;
+ else if (!extension.length() || checkSig)
+ {
+ if (!pIOHandler)return true;
+ const char* tokens[] = {"off"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void OFFImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("off");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void OFFImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open OFF file " + pFile + ".");
+ }
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+ const char* buffer = &mBuffer2[0];
+
+ char line[4096];
+ GetNextLine(buffer,line);
+ if ('O' == line[0]) {
+ GetNextLine(buffer,line); // skip the 'OFF' line
+ }
+
+ const char* sz = line; SkipSpaces(&sz);
+ const unsigned int numVertices = strtol10(sz,&sz);SkipSpaces(&sz);
+ const unsigned int numFaces = strtol10(sz,&sz);
+
+ pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = 1 ];
+ aiMesh* mesh = pScene->mMeshes[0] = new aiMesh();
+ aiFace* faces = mesh->mFaces = new aiFace [mesh->mNumFaces = numFaces];
+
+ std::vector<aiVector3D> tempPositions(numVertices);
+
+ // now read all vertex lines
+ for (unsigned int i = 0; i< numVertices;++i)
+ {
+ if (!GetNextLine(buffer,line))
+ {
+ DefaultLogger::get()->error("OFF: The number of verts in the header is incorrect");
+ break;
+ }
+ aiVector3D& v = tempPositions[i];
+
+ sz = line; SkipSpaces(&sz);
+ sz = fast_atof_move(sz,(float&)v.x); SkipSpaces(&sz);
+ sz = fast_atof_move(sz,(float&)v.y); SkipSpaces(&sz);
+ fast_atof_move(sz,(float&)v.z);
+ }
+
+
+ // First find out how many vertices we'll need
+ const char* old = buffer;
+ for (unsigned int i = 0; i< mesh->mNumFaces;++i)
+ {
+ if (!GetNextLine(buffer,line))
+ {
+ DefaultLogger::get()->error("OFF: The number of faces in the header is incorrect");
+ break;
+ }
+ sz = line;SkipSpaces(&sz);
+ if (!(faces->mNumIndices = strtol10(sz,&sz)) || faces->mNumIndices > 9)
+ {
+ DefaultLogger::get()->error("OFF: Faces with zero indices aren't allowed");
+ --mesh->mNumFaces;
+ continue;
+ }
+ mesh->mNumVertices += faces->mNumIndices;
+ ++faces;
+ }
+
+ if (!mesh->mNumVertices)
+ throw DeadlyImportError("OFF: There are no valid faces");
+
+ // allocate storage for the output vertices
+ aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+
+ // second: now parse all face indices
+ buffer = old;faces = mesh->mFaces;
+ for (unsigned int i = 0, p = 0; i< mesh->mNumFaces;)
+ {
+ if (!GetNextLine(buffer,line))break;
+
+ unsigned int idx;
+ sz = line;SkipSpaces(&sz);
+ if (!(idx = strtol10(sz,&sz)) || idx > 9)
+ continue;
+
+ faces->mIndices = new unsigned int [faces->mNumIndices];
+ for (unsigned int m = 0; m < faces->mNumIndices;++m)
+ {
+ SkipSpaces(&sz);
+ if ((idx = strtol10(sz,&sz)) >= numVertices)
+ {
+ DefaultLogger::get()->error("OFF: Vertex index is out of range");
+ idx = numVertices-1;
+ }
+ faces->mIndices[m] = p++;
+ *verts++ = tempPositions[idx];
+ }
+ ++i;
+ ++faces;
+ }
+
+ // generate the output node graph
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("<OFFRoot>");
+ pScene->mRootNode->mMeshes = new unsigned int [pScene->mRootNode->mNumMeshes = 1];
+ pScene->mRootNode->mMeshes[0] = 0;
+
+ // generate a default material
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = 1];
+ MaterialHelper* pcMat = new MaterialHelper();
+
+ aiColor4D clr(0.6f,0.6f,0.6f,1.0f);
+ pcMat->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+ pScene->mMaterials[0] = pcMat;
+
+ const int twosided =1;
+ pcMat->AddProperty(&twosided,1,AI_MATKEY_TWOSIDED);
+}
+
+#endif // !! ASSIMP_BUILD_NO_OFF_IMPORTER
diff --git a/src/3rdparty/assimp/code/OFFLoader.h b/src/3rdparty/assimp/code/OFFLoader.h
new file mode 100644
index 000000000..e4247b6a2
--- /dev/null
+++ b/src/3rdparty/assimp/code/OFFLoader.h
@@ -0,0 +1,96 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file OFFLoader.h
+ * @brief Declaration of the OFF importer class.
+ */
+#ifndef AI_OFFLOADER_H_INCLUDED
+#define AI_OFFLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+#include <vector>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Importer class for the Object File Format (.off)
+*/
+class OFFImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ OFFImporter();
+
+ /** Destructor, private as well */
+ ~OFFImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details. */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_IN
diff --git a/src/3rdparty/assimp/code/ObjFileData.h b/src/3rdparty/assimp/code/ObjFileData.h
new file mode 100644
index 000000000..674d9265b
--- /dev/null
+++ b/src/3rdparty/assimp/code/ObjFileData.h
@@ -0,0 +1,325 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef OBJ_FILEDATA_H_INC
+#define OBJ_FILEDATA_H_INC
+
+#include <vector>
+#include <map>
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+
+namespace Assimp
+{
+
+namespace ObjFile
+{
+// ------------------------------------------------------------------------------------------------
+struct Object;
+struct Face;
+struct Material;
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Face
+//! \brief Datastructure for a simple obj-face, descripes discredisation and materials
+struct Face
+{
+ typedef std::vector<unsigned int> IndexArray;
+
+ //! Primitive type
+ int m_PrimitiveType;
+ //! Vertex indices
+ IndexArray *m_pVertices;
+ //! Normal indices
+ IndexArray *m_pNormals;
+ //! Texture coordinates indices
+ IndexArray *m_pTexturCoords;
+ //! Pointer to assigned material
+ Material *m_pMaterial;
+
+ //! \brief Default constructor
+ //! \param pVertices Pointer to assigned vertex indexbuffer
+ //! \param pNormals Pointer to assigned normals indexbuffer
+ //! \param pTexCoords Pointer to assigned texture indexbuffer
+ Face( std::vector<unsigned int> *pVertices,
+ std::vector<unsigned int> *pNormals,
+ std::vector<unsigned int> *pTexCoords) :
+ m_PrimitiveType( 2 ),
+ m_pVertices( pVertices ),
+ m_pNormals( pNormals ),
+ m_pTexturCoords( pTexCoords ),
+ m_pMaterial( 0L )
+ {
+ // empty
+ }
+
+ //! \brief Destructor
+ ~Face()
+ {
+ delete m_pVertices;
+ m_pVertices = NULL;
+ delete m_pNormals;
+ m_pNormals = NULL;
+ delete m_pTexturCoords;
+ m_pTexturCoords = NULL;
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Object
+//! \brief Stores all objects of an objfile object definition
+struct Object
+{
+ enum ObjectType
+ {
+ ObjType,
+ GroupType
+ };
+
+ //! Object name
+ std::string m_strObjName;
+ //! Transformation matrix, stored in OpenGL format
+ aiMatrix4x4 m_Transformation;
+ //! All sub-objects referenced by this object
+ std::vector<Object*> m_SubObjects;
+ /// Assigned meshes
+ std::vector<unsigned int> m_Meshes;
+
+ //! \brief Default constructor
+ Object() :
+ m_strObjName("")
+ {
+ // empty
+ }
+
+ //! \brief Destructor
+ ~Object()
+ {
+ for (std::vector<Object*>::iterator it = m_SubObjects.begin();
+ it != m_SubObjects.end(); ++it)
+ {
+ delete *it;
+ }
+ m_SubObjects.clear();
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Material
+//! \brief Data structure to store all material specific data
+struct Material
+{
+ //! Name of material description
+ aiString MaterialName;
+
+ //! Texture names
+ aiString texture;
+ aiString textureSpecular;
+ aiString textureAmbient;
+ aiString textureBump;
+ aiString textureSpecularity;
+ aiString textureOpacity;
+
+ //! Ambient color
+ aiColor3D ambient;
+ //! Diffuse color
+ aiColor3D diffuse;
+ //! Speculao color
+ aiColor3D specular;
+ //! Alpha value
+ float alpha;
+ //! Shineness factor
+ float shineness;
+ //! Illumination model
+ int illumination_model;
+ //! Index of refraction
+ float ior;
+
+ //! Constructor
+ Material()
+ : diffuse (0.6f,0.6f,0.6f)
+ , alpha (1.f)
+ , shineness (0.0f)
+ , illumination_model (1)
+ , ior (1.f)
+ {
+ // empty
+ }
+
+ // Destructor
+ ~Material()
+ {
+ // empty
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Mesh
+//! \brief Data structure to store a mesh
+struct Mesh
+{
+ static const unsigned int NoMaterial = 999999999;
+
+ /// Array with pointer to all stored faces
+ std::vector<Face*> m_Faces;
+ /// Assigned material
+ Material *m_pMaterial;
+ /// Number of stored indices.
+ unsigned int m_uiNumIndices;
+ /// Number of UV
+ unsigned int m_uiUVCoordinates[ AI_MAX_NUMBER_OF_TEXTURECOORDS ];
+ /// Material index.
+ unsigned int m_uiMaterialIndex;
+ /// True, if normals are stored.
+ bool m_hasNormals;
+ /// Constructor
+ Mesh() :
+ m_pMaterial(NULL),
+ m_uiNumIndices(0),
+ m_uiMaterialIndex( NoMaterial ),
+ m_hasNormals(false)
+ {
+ memset(m_uiUVCoordinates, 0, sizeof( unsigned int ) * AI_MAX_NUMBER_OF_TEXTURECOORDS);
+ }
+
+ /// Destructor
+ ~Mesh()
+ {
+ for (std::vector<Face*>::iterator it = m_Faces.begin();
+ it != m_Faces.end(); ++it)
+ {
+ delete *it;
+ }
+
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Model
+//! \brief Data structure to store all obj-specific model datas
+struct Model
+{
+ typedef std::map<std::string*, std::vector<unsigned int>* > GroupMap;
+ typedef std::map<std::string*, std::vector<unsigned int>* >::iterator GroupMapIt;
+ typedef std::map<std::string*, std::vector<unsigned int>* >::const_iterator ConstGroupMapIt;
+
+ //! Model name
+ std::string m_ModelName;
+ //! List ob assigned objects
+ std::vector<Object*> m_Objects;
+ //! Pointer to current object
+ ObjFile::Object *m_pCurrent;
+ //! Pointer to current material
+ ObjFile::Material *m_pCurrentMaterial;
+ //! Pointer to default material
+ ObjFile::Material *m_pDefaultMaterial;
+ //! Vector with all generated materials
+ std::vector<std::string> m_MaterialLib;
+ //! Vector with all generated group
+ std::vector<std::string> m_GroupLib;
+ //! Vector with all generated vertices
+ std::vector<aiVector3D> m_Vertices;
+ //! vector with all generated normals
+ std::vector<aiVector3D> m_Normals;
+ //! Groupmap
+ GroupMap m_Groups;
+ //! Group to face id assignment
+ std::vector<unsigned int> *m_pGroupFaceIDs;
+ //! Active group
+ std::string m_strActiveGroup;
+ //! Vector with generated texture coordinates
+ std::vector<aiVector2D> m_TextureCoord;
+ //! Current mesh instance
+ Mesh *m_pCurrentMesh;
+ //! Vector with stored meshes
+ std::vector<Mesh*> m_Meshes;
+ //! Material map
+ std::map<std::string, Material*> m_MaterialMap;
+
+
+ //! \brief Default constructor
+ Model() :
+ m_ModelName(""),
+ m_pCurrent(NULL),
+ m_pCurrentMaterial(NULL),
+ m_pDefaultMaterial(NULL),
+ m_pGroupFaceIDs(NULL),
+ m_strActiveGroup(""),
+ m_pCurrentMesh(NULL)
+ {
+ // empty
+ }
+
+ //! \brief Destructor
+ ~Model()
+ {
+ // Clear all stored object instances
+ for (std::vector<Object*>::iterator it = m_Objects.begin();
+ it != m_Objects.end(); ++it)
+ {
+ delete *it;
+ }
+ m_Objects.clear();
+
+ // Clear all stored mesh instances
+ for (std::vector<Mesh*>::iterator it = m_Meshes.begin();
+ it != m_Meshes.end(); ++it)
+ {
+ delete *it;
+ }
+
+ m_Meshes.clear();
+
+ for (GroupMapIt it = m_Groups.begin();
+ it != m_Groups.end(); ++it)
+ {
+ delete it->second;
+ }
+
+ m_Groups.clear();
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace ObjFile
+} // Namespace Assimp
+
+#endif
diff --git a/src/3rdparty/assimp/code/ObjFileImporter.cpp b/src/3rdparty/assimp/code/ObjFileImporter.cpp
new file mode 100755
index 000000000..9556bf413
--- /dev/null
+++ b/src/3rdparty/assimp/code/ObjFileImporter.cpp
@@ -0,0 +1,529 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
+
+#include "DefaultIOSystem.h"
+#include "ObjFileImporter.h"
+#include "ObjFileParser.h"
+#include "ObjFileData.h"
+
+namespace Assimp {
+
+using namespace std;
+
+// ------------------------------------------------------------------------------------------------
+// Default constructor
+ObjFileImporter::ObjFileImporter() :
+ m_Buffer(),
+ m_pRootObject( NULL ),
+ m_strAbsPath( "" )
+{
+ DefaultIOSystem io;
+ m_strAbsPath = io.getOsSeparator();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor.
+ObjFileImporter::~ObjFileImporter()
+{
+ // Release root object instance
+ if (NULL != m_pRootObject)
+ {
+ delete m_pRootObject;
+ m_pRootObject = NULL;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns true, if file is an obj file.
+bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler , bool checkSig ) const
+{
+ if (!checkSig) //Check File Extension
+ {
+ return SimpleExtensionCheck(pFile,"obj");
+ }
+ else //Check file Header
+ {
+ const char* tokens[] = {"mtllib","usemtl","vt ","vn ","o "};
+ return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, 5);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Obj-file import implementation
+void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+ DefaultIOSystem io;
+
+ // Read file into memory
+ const std::string mode = "rb";
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, mode));
+ if (NULL == file.get())
+ throw DeadlyImportError( "Failed to open file " + pFile + ".");
+
+ // Get the filesize and vaslidate it, throwing an exception when failes
+ size_t fileSize = file->FileSize();
+ if ( fileSize < 16)
+ throw DeadlyImportError( "OBJ-file is too small.");
+
+ // Allocate buffer and read file into it
+ TextFileToBuffer(file.get(),m_Buffer);
+
+ // Get the model name
+ std::string strModelName;
+ std::string::size_type pos = pFile.find_last_of( "\\/" );
+ if ( pos != std::string::npos )
+ {
+ strModelName = pFile.substr(pos+1, pFile.size() - pos - 1);
+ }
+ else
+ {
+ strModelName = pFile;
+ }
+
+ // parse the file into a temporary representation
+ ObjFileParser parser(m_Buffer, strModelName, pIOHandler);
+
+ // And create the proper return structures out of it
+ CreateDataFromImport(parser.GetModel(), pScene);
+
+ // Clean up allocated storage for the next import
+ m_Buffer.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Create the data from parsed obj-file
+void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene)
+{
+ if (0L == pModel)
+ return;
+
+ // Create the root node of the scene
+ pScene->mRootNode = new aiNode;
+ if ( !pModel->m_ModelName.empty() )
+ {
+ // Set the name of the scene
+ pScene->mRootNode->mName.Set(pModel->m_ModelName);
+ }
+ else
+ {
+ // This is an error, so break down the application
+ ai_assert(false);
+ }
+
+ // Create nodes for the whole scene
+ std::vector<aiMesh*> MeshArray;
+ for (size_t index = 0; index < pModel->m_Objects.size(); index++)
+ {
+ createNodes(pModel, pModel->m_Objects[ index ], index, pScene->mRootNode, pScene, MeshArray);
+ }
+
+ // Create mesh pointer buffer for this scene
+ if (pScene->mNumMeshes > 0)
+ {
+ pScene->mMeshes = new aiMesh*[ MeshArray.size() ];
+ for (size_t index =0; index < MeshArray.size(); index++)
+ {
+ pScene->mMeshes [ index ] = MeshArray[ index ];
+ }
+ }
+
+ // Create all materials
+ createMaterials( pModel, pScene );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates all nodes of the model
+aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pObject,
+ unsigned int /* uiMeshIndex */,
+ aiNode *pParent, aiScene* pScene,
+ std::vector<aiMesh*> &MeshArray )
+{
+ ai_assert( NULL != pModel );
+ if ( NULL == pObject )
+ return NULL;
+
+ // Store older mesh size to be able to computate mesh offsets for new mesh instances
+ const size_t oldMeshSize = MeshArray.size();
+ aiNode *pNode = new aiNode;
+ pNode->mName = aiString(pObject->m_strObjName);
+
+ pNode->mName = pObject->m_strObjName;
+
+ if (pParent != NULL)
+ appendChildToParentNode(pParent, pNode);
+
+
+ for ( unsigned int i=0; i< pObject->m_Meshes.size(); i++ )
+ {
+ unsigned int meshId = pObject->m_Meshes[ i ];
+ aiMesh *pMesh = new aiMesh;
+ createTopology( pModel, pObject, meshId, pMesh );
+ if ( pMesh->mNumVertices > 0 )
+ {
+ MeshArray.push_back( pMesh );
+ }
+ else
+ {
+ delete pMesh;
+ }
+ }
+
+ // Create all nodes from the sub-objects stored in the current object
+ if ( !pObject->m_SubObjects.empty() )
+ {
+ size_t numChilds = pObject->m_SubObjects.size();
+ pNode->mNumChildren = static_cast<unsigned int>( numChilds );
+ pNode->mChildren = new aiNode*[ numChilds ];
+ pNode->mNumMeshes = 1;
+ pNode->mMeshes = new unsigned int[ 1 ];
+ }
+
+ // Set mesh instances into scene- and node-instances
+ const size_t meshSizeDiff = MeshArray.size()- oldMeshSize;
+ if ( meshSizeDiff > 0 )
+ {
+ pNode->mMeshes = new unsigned int[ meshSizeDiff ];
+ pNode->mNumMeshes = static_cast<unsigned int>( meshSizeDiff );
+ size_t index = 0;
+ for (size_t i = oldMeshSize; i < MeshArray.size(); i++)
+ {
+ pNode->mMeshes[ index ] = pScene->mNumMeshes;
+ pScene->mNumMeshes++;
+ index++;
+ }
+ }
+
+ return pNode;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Create topology data
+void ObjFileImporter::createTopology(const ObjFile::Model* pModel,
+ const ObjFile::Object* pData,
+ unsigned int uiMeshIndex,
+ aiMesh* pMesh )
+{
+ // Checking preconditions
+ ai_assert( NULL != pModel );
+ if (NULL == pData)
+ return;
+
+ // Create faces
+ ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ];
+ ai_assert( NULL != pObjMesh );
+ pMesh->mNumFaces = static_cast<unsigned int>( pObjMesh->m_Faces.size() );
+ if ( pMesh->mNumFaces > 0 )
+ {
+ pMesh->mFaces = new aiFace[ pMesh->mNumFaces ];
+ if ( pObjMesh->m_uiMaterialIndex != ObjFile::Mesh::NoMaterial )
+ {
+ pMesh->mMaterialIndex = pObjMesh->m_uiMaterialIndex;
+ }
+
+ // Copy all data from all stored meshes
+ for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++)
+ {
+ aiFace *pFace = &pMesh->mFaces[ index ];
+ const unsigned int uiNumIndices = (unsigned int) pObjMesh->m_Faces[ index ]->m_pVertices->size();
+ pFace->mNumIndices = (unsigned int) uiNumIndices;
+ if (pFace->mNumIndices > 0)
+ {
+ pFace->mIndices = new unsigned int[ uiNumIndices ];
+ ObjFile::Face::IndexArray *pIndexArray = pObjMesh->m_Faces[ index ]->m_pVertices;
+ ai_assert ( NULL != pIndexArray );
+ for ( size_t a=0; a<pFace->mNumIndices; a++ )
+ {
+ pFace->mIndices[ a ] = pIndexArray->at( a );
+ }
+ }
+ else
+ {
+ pFace->mIndices = NULL;
+ }
+ }
+ }
+
+ // Create mesh vertices
+ createVertexArray(pModel, pData, uiMeshIndex, pMesh);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates a vertex array
+void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
+ const ObjFile::Object* pCurrentObject,
+ unsigned int uiMeshIndex,
+ aiMesh* pMesh)
+{
+ // Checking preconditions
+ ai_assert( NULL != pCurrentObject );
+
+ // Break, if no faces are stored in object
+ if ( pCurrentObject->m_Meshes.empty() )
+ return;
+
+ // Get current mesh
+ ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ];
+ if ( NULL == pObjMesh || pObjMesh->m_uiNumIndices < 1)
+ return;
+
+ // Copy vertices of this mesh instance
+ pMesh->mNumVertices = (unsigned int) pObjMesh->m_uiNumIndices;
+ pMesh->mVertices = new aiVector3D[ pMesh->mNumVertices ];
+
+ // Allocate buffer for normal vectors
+ if ( !pModel->m_Normals.empty() && pObjMesh->m_hasNormals )
+ pMesh->mNormals = new aiVector3D[ pMesh->mNumVertices ];
+
+ // Allocate buffer for texture coordinates
+ if ( !pModel->m_TextureCoord.empty() && pObjMesh->m_uiUVCoordinates[0] )
+ {
+ pMesh->mNumUVComponents[ 0 ] = 2;
+ pMesh->mTextureCoords[ 0 ] = new aiVector3D[ pMesh->mNumVertices ];
+ }
+
+ // Copy vertices, normals and textures into aiMesh instance
+ unsigned int newIndex = 0;
+ for ( size_t index=0; index < pObjMesh->m_Faces.size(); index++ )
+ {
+ // Get destination face
+ aiFace *pDestFace = &pMesh->mFaces[ index ];
+
+ // Get source face
+ ObjFile::Face *pSourceFace = pObjMesh->m_Faces[ index ];
+
+ // Copy all index arrays
+ for ( size_t vertexIndex = 0; vertexIndex < pSourceFace->m_pVertices->size(); vertexIndex++ )
+ {
+ const unsigned int vertex = pSourceFace->m_pVertices->at( vertexIndex );
+ if ( vertex >= pModel->m_Vertices.size() )
+ throw DeadlyImportError( "OBJ: vertex index out of range" );
+
+ pMesh->mVertices[ newIndex ] = pModel->m_Vertices[ vertex ];
+
+ // Copy all normals
+ if ( !pSourceFace->m_pNormals->empty() && !pModel->m_Normals.empty() )
+ {
+ const unsigned int normal = pSourceFace->m_pNormals->at( vertexIndex );
+ if ( normal >= pModel->m_Normals.size() )
+ throw DeadlyImportError("OBJ: vertex normal index out of range");
+
+ pMesh->mNormals[ newIndex ] = pModel->m_Normals[ normal ];
+ }
+
+ // Copy all texture coordinates
+ if ( !pModel->m_TextureCoord.empty() )
+ {
+ if ( !pSourceFace->m_pTexturCoords->empty() )
+ {
+ const unsigned int tex = pSourceFace->m_pTexturCoords->at( vertexIndex );
+ ai_assert( tex < pModel->m_TextureCoord.size() );
+ for ( size_t i=0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; i++ )
+ {
+ if ( pMesh->mNumUVComponents[ i ] > 0 )
+ {
+ aiVector2D coord2d = pModel->m_TextureCoord[ tex ];
+ pMesh->mTextureCoords[ i ][ newIndex ] = aiVector3D( coord2d.x, coord2d.y, 0.0 );
+ }
+ }
+ }
+ }
+
+ ai_assert( pMesh->mNumVertices > newIndex );
+ pDestFace->mIndices[ vertexIndex ] = newIndex;
+ ++newIndex;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Counts all stored meshes
+void ObjFileImporter::countObjects(const std::vector<ObjFile::Object*> &rObjects, int &iNumMeshes)
+{
+ iNumMeshes = 0;
+ if ( rObjects.empty() )
+ return;
+
+ iNumMeshes += static_cast<unsigned int>( rObjects.size() );
+ for (std::vector<ObjFile::Object*>::const_iterator it = rObjects.begin();
+ it != rObjects.end();
+ ++it)
+ {
+ if (!(*it)->m_SubObjects.empty())
+ {
+ countObjects((*it)->m_SubObjects, iNumMeshes);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the material
+void ObjFileImporter::createMaterials(const ObjFile::Model* pModel, aiScene* pScene )
+{
+ ai_assert( NULL != pScene );
+ if ( NULL == pScene )
+ return;
+
+ const unsigned int numMaterials = (unsigned int) pModel->m_MaterialLib.size();
+ pScene->mNumMaterials = 0;
+ if ( pModel->m_MaterialLib.empty() )
+ {
+ DefaultLogger::get()->debug("OBJ: no materials specified");
+ return;
+ }
+ pScene->mMaterials = new aiMaterial*[ numMaterials ];
+ for ( unsigned int matIndex = 0; matIndex < numMaterials; matIndex++ )
+ {
+ Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
+
+ // Store material name
+ std::map<std::string, ObjFile::Material*>::const_iterator it;
+ it = pModel->m_MaterialMap.find( pModel->m_MaterialLib[ matIndex ] );
+
+ // No material found, use the default material
+ if ( pModel->m_MaterialMap.end() == it )
+ continue;
+
+ ObjFile::Material *pCurrentMaterial = (*it).second;
+ mat->AddProperty( &pCurrentMaterial->MaterialName, AI_MATKEY_NAME );
+
+ // convert illumination model
+ int sm = 0;
+ switch (pCurrentMaterial->illumination_model)
+ {
+ case 0:
+ sm = aiShadingMode_NoShading;
+ break;
+ case 1:
+ sm = aiShadingMode_Gouraud;
+ break;
+ case 2:
+ sm = aiShadingMode_Phong;
+ break;
+ default:
+ sm = aiShadingMode_Gouraud;
+ DefaultLogger::get()->error("OBJ/MTL: Unexpected illumination model (0-2 recognized)");
+ }
+ mat->AddProperty<int>( &sm, 1, AI_MATKEY_SHADING_MODEL);
+
+ // multiplying the specular exponent with 2 seems to yield better results
+ pCurrentMaterial->shineness *= 4.f;
+
+ // Adding material colors
+ mat->AddProperty( &pCurrentMaterial->ambient, 1, AI_MATKEY_COLOR_AMBIENT );
+ mat->AddProperty( &pCurrentMaterial->diffuse, 1, AI_MATKEY_COLOR_DIFFUSE );
+ mat->AddProperty( &pCurrentMaterial->specular, 1, AI_MATKEY_COLOR_SPECULAR );
+ mat->AddProperty( &pCurrentMaterial->shineness, 1, AI_MATKEY_SHININESS );
+ mat->AddProperty( &pCurrentMaterial->alpha, 1, AI_MATKEY_OPACITY );
+
+ // Adding refraction index
+ mat->AddProperty( &pCurrentMaterial->ior, 1, AI_MATKEY_REFRACTI );
+
+ // Adding textures
+ if ( 0 != pCurrentMaterial->texture.length )
+ mat->AddProperty( &pCurrentMaterial->texture, AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ if ( 0 != pCurrentMaterial->textureAmbient.length )
+ mat->AddProperty( &pCurrentMaterial->textureAmbient, AI_MATKEY_TEXTURE_AMBIENT(0));
+
+ if ( 0 != pCurrentMaterial->textureSpecular.length )
+ mat->AddProperty( &pCurrentMaterial->textureSpecular, AI_MATKEY_TEXTURE_SPECULAR(0));
+
+ if ( 0 != pCurrentMaterial->textureBump.length )
+ mat->AddProperty( &pCurrentMaterial->textureBump, AI_MATKEY_TEXTURE_HEIGHT(0));
+
+ if ( 0 != pCurrentMaterial->textureOpacity.length )
+ mat->AddProperty( &pCurrentMaterial->textureOpacity, AI_MATKEY_TEXTURE_OPACITY(0));
+
+ if ( 0 != pCurrentMaterial->textureSpecularity.length )
+ mat->AddProperty( &pCurrentMaterial->textureSpecularity, AI_MATKEY_TEXTURE_SHININESS(0));
+
+ // Store material property info in material array in scene
+ pScene->mMaterials[ pScene->mNumMaterials ] = mat;
+ pScene->mNumMaterials++;
+ }
+
+ // Test number of created materials.
+ ai_assert( pScene->mNumMaterials == numMaterials );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Appends this node to the parent node
+void ObjFileImporter::appendChildToParentNode(aiNode *pParent, aiNode *pChild)
+{
+ // Checking preconditions
+ ai_assert( NULL != pParent );
+ ai_assert( NULL != pChild );
+
+ // Assign parent to child
+ pChild->mParent = pParent;
+ size_t sNumChildren = 0;
+
+ // If already children was assigned to the parent node, store them in a
+ std::vector<aiNode*> temp;
+ if (pParent->mChildren != NULL)
+ {
+ sNumChildren = pParent->mNumChildren;
+ ai_assert( 0 != sNumChildren );
+ for (size_t index = 0; index < pParent->mNumChildren; index++)
+ {
+ temp.push_back(pParent->mChildren [ index ] );
+ }
+ delete [] pParent->mChildren;
+ }
+
+ // Copy node instances into parent node
+ pParent->mNumChildren++;
+ pParent->mChildren = new aiNode*[ pParent->mNumChildren ];
+ for (size_t index = 0; index < pParent->mNumChildren-1; index++)
+ {
+ pParent->mChildren[ index ] = temp [ index ];
+ }
+ pParent->mChildren[ pParent->mNumChildren-1 ] = pChild;
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER
diff --git a/src/3rdparty/assimp/code/ObjFileImporter.h b/src/3rdparty/assimp/code/ObjFileImporter.h
new file mode 100644
index 000000000..7edecac8e
--- /dev/null
+++ b/src/3rdparty/assimp/code/ObjFileImporter.h
@@ -0,0 +1,136 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+#ifndef OBJ_FILE_IMPORTER_H_INC
+#define OBJ_FILE_IMPORTER_H_INC
+
+#include "BaseImporter.h"
+#include <vector>
+
+struct aiMesh;
+struct aiNode;
+
+namespace Assimp
+{
+
+namespace ObjFile
+{
+struct Object;
+struct Model;
+}
+
+// ------------------------------------------------------------------------------------------------
+/// \class ObjFileImporter
+/// \brief Imports a waveform obj file
+// ------------------------------------------------------------------------------------------------
+class ObjFileImporter :
+ BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /// \brief Default constructor
+ ObjFileImporter();
+
+ /// \brief Destructor
+ ~ObjFileImporter();
+
+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 extention.
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ //! \brief File import implementation.
+ void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+
+ //! \brief Create the data from imported content.
+ void CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene);
+
+ //! \brief Creates all nodes stored in imported content.
+ aiNode *createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pData, unsigned int uiMeshIndex,
+ aiNode *pParent, aiScene* pScene, std::vector<aiMesh*> &MeshArray);
+
+ //! \brief Creates topology data like faces and meshes for the geometry.
+ void createTopology(const ObjFile::Model* pModel, const ObjFile::Object* pData,
+ unsigned int uiMeshIndex, aiMesh* pMesh);
+
+ //! \brief Creates vertices from model.
+ void createVertexArray(const ObjFile::Model* pModel, const ObjFile::Object* pCurrentObject,
+ unsigned int uiMeshIndex, aiMesh* pMesh);
+
+ //! \brief Object counter helper method.
+ void countObjects(const std::vector<ObjFile::Object*> &rObjects, int &iNumMeshes);
+
+ //! \brief Material creation.
+ void createMaterials(const ObjFile::Model* pModel, aiScene* pScene);
+
+ //! \brief Appends a child node to a parentnode and updates the datastructures.
+ void appendChildToParentNode(aiNode *pParent, aiNode *pChild);
+
+ //! \brief TODO!
+ void createAnimations();
+
+private:
+ //! Data buffer
+ std::vector<char> m_Buffer;
+ //! Pointer to root object instance
+ ObjFile::Object *m_pRootObject;
+ //! Absolute pathname of model in filesystem
+ std::string m_strAbsPath;
+};
+
+// ------------------------------------------------------------------------------------------------
+//
+inline void ObjFileImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("obj");
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif
diff --git a/src/3rdparty/assimp/code/ObjFileMtlImporter.cpp b/src/3rdparty/assimp/code/ObjFileMtlImporter.cpp
new file mode 100644
index 000000000..2dd89ac83
--- /dev/null
+++ b/src/3rdparty/assimp/code/ObjFileMtlImporter.cpp
@@ -0,0 +1,308 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
+
+#include "ObjFileMtlImporter.h"
+#include "ObjTools.h"
+#include "ObjFileData.h"
+#include "fast_atof.h"
+
+#if defined(__QNXNTO__)
+#include <stdlib.h>
+#endif
+
+namespace Assimp {
+
+// -------------------------------------------------------------------
+// Constructor
+ObjFileMtlImporter::ObjFileMtlImporter( std::vector<char> &buffer,
+ const std::string & /*strAbsPath*/,
+ ObjFile::Model *pModel ) :
+ m_DataIt( buffer.begin() ),
+ m_DataItEnd( buffer.end() ),
+ m_pModel( pModel ),
+ m_uiLine( 0 )
+{
+ ai_assert( NULL != m_pModel );
+ if ( NULL == m_pModel->m_pDefaultMaterial )
+ {
+ m_pModel->m_pDefaultMaterial = new ObjFile::Material;
+ m_pModel->m_pDefaultMaterial->MaterialName.Set( "default" );
+ }
+ load();
+}
+
+// -------------------------------------------------------------------
+// Destructor
+ObjFileMtlImporter::~ObjFileMtlImporter()
+{
+ // empty
+}
+
+// -------------------------------------------------------------------
+// Private copy constructor
+ObjFileMtlImporter::ObjFileMtlImporter(const ObjFileMtlImporter & /* rOther */ )
+{
+ // empty
+}
+
+// -------------------------------------------------------------------
+// Private copy constructor
+ObjFileMtlImporter &ObjFileMtlImporter::operator = ( const ObjFileMtlImporter & /*rOther */ )
+{
+ return *this;
+}
+
+// -------------------------------------------------------------------
+// Loads the material description
+void ObjFileMtlImporter::load()
+{
+ if ( m_DataIt == m_DataItEnd )
+ return;
+
+ while ( m_DataIt != m_DataItEnd )
+ {
+ switch (*m_DataIt)
+ {
+ case 'K':
+ {
+ ++m_DataIt;
+ if (*m_DataIt == 'a') // Ambient color
+ {
+ ++m_DataIt;
+ getColorRGBA( &m_pModel->m_pCurrentMaterial->ambient );
+ }
+ else if (*m_DataIt == 'd') // Diffuse color
+ {
+ ++m_DataIt;
+ getColorRGBA( &m_pModel->m_pCurrentMaterial->diffuse );
+ }
+ else if (*m_DataIt == 's')
+ {
+ ++m_DataIt;
+ getColorRGBA( &m_pModel->m_pCurrentMaterial->specular );
+ }
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+
+ case 'd': // Alpha value
+ {
+ ++m_DataIt;
+ getFloatValue( m_pModel->m_pCurrentMaterial->alpha );
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+
+ case 'N': // Shineness
+ {
+ ++m_DataIt;
+ switch(*m_DataIt)
+ {
+ case 's':
+ ++m_DataIt;
+ getFloatValue(m_pModel->m_pCurrentMaterial->shineness);
+ break;
+ case 'i': //Index Of refraction
+ ++m_DataIt;
+ getFloatValue(m_pModel->m_pCurrentMaterial->ior);
+ break;
+ }
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ break;
+ }
+ break;
+
+
+ case 'm': // Texture
+ case 'b': // quick'n'dirty - for 'bump' sections
+ {
+ getTexture();
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+
+ case 'n': // New material name
+ {
+ createMaterial();
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+
+ case 'i': // Illumination model
+ {
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ getIlluminationModel( m_pModel->m_pCurrentMaterial->illumination_model );
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+
+ default:
+ {
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+ }
+ }
+}
+
+// -------------------------------------------------------------------
+// Loads a color definition
+void ObjFileMtlImporter::getColorRGBA( aiColor3D *pColor )
+{
+ ai_assert( NULL != pColor );
+
+ float r, g, b;
+ m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, r );
+ pColor->r = r;
+
+ m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, g );
+ pColor->g = g;
+
+ m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, b );
+ pColor->b = b;
+}
+
+// -------------------------------------------------------------------
+// Loads the kind of illumination model.
+void ObjFileMtlImporter::getIlluminationModel( int &illum_model )
+{
+ m_DataIt = CopyNextWord<DataArrayIt>( m_DataIt, m_DataItEnd, m_buffer, BUFFERSIZE );
+ illum_model = atoi(m_buffer);
+}
+
+// -------------------------------------------------------------------
+// Loads a single float value.
+void ObjFileMtlImporter::getFloatValue( float &value )
+{
+ m_DataIt = CopyNextWord<DataArrayIt>( m_DataIt, m_DataItEnd, m_buffer, BUFFERSIZE );
+ value = (float) fast_atof(m_buffer);
+}
+
+// -------------------------------------------------------------------
+// Creates a material from loaded data.
+void ObjFileMtlImporter::createMaterial()
+{
+ std::string line( "" );
+ while ( !isNewLine( *m_DataIt ) ) {
+ line += *m_DataIt;
+ ++m_DataIt;
+ }
+
+ std::vector<std::string> token;
+ const unsigned int numToken = tokenize<std::string>( line, token, " " );
+ std::string name( "" );
+ if ( numToken == 1 ) {
+ name = AI_DEFAULT_MATERIAL_NAME;
+ } else {
+ name = token[ 1 ];
+ }
+
+ std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( name );
+ if ( m_pModel->m_MaterialMap.end() == it)
+ {
+ // New Material created
+ m_pModel->m_pCurrentMaterial = new ObjFile::Material();
+ m_pModel->m_pCurrentMaterial->MaterialName.Set( name );
+ m_pModel->m_MaterialLib.push_back( name );
+ m_pModel->m_MaterialMap[ name ] = m_pModel->m_pCurrentMaterial;
+ }
+ else
+ {
+ // Use older material
+ m_pModel->m_pCurrentMaterial = (*it).second;
+ }
+}
+
+// -------------------------------------------------------------------
+// Gets a texture name from data.
+void ObjFileMtlImporter::getTexture()
+{
+ aiString *out = NULL;
+
+ // FIXME: just a quick'n'dirty hack, consider cleanup later
+
+ // Diffuse texture
+ if (!ASSIMP_strincmp(&(*m_DataIt),"map_kd",6))
+ out = & m_pModel->m_pCurrentMaterial->texture;
+
+ // Ambient texture
+ else if (!ASSIMP_strincmp(&(*m_DataIt),"map_ka",6))
+ out = & m_pModel->m_pCurrentMaterial->textureAmbient;
+
+ // Specular texture
+ else if (!ASSIMP_strincmp(&(*m_DataIt),"map_ks",6))
+ out = & m_pModel->m_pCurrentMaterial->textureSpecular;
+
+ // Opacity texture
+ else if (!ASSIMP_strincmp(&(*m_DataIt),"map_d",5))
+ out = & m_pModel->m_pCurrentMaterial->textureOpacity;
+
+ // Ambient texture
+ else if (!ASSIMP_strincmp(&(*m_DataIt),"map_ka",6))
+ out = & m_pModel->m_pCurrentMaterial->textureAmbient;
+
+ // Bump texture
+ else if (!ASSIMP_strincmp(&(*m_DataIt),"map_bump",8) || !ASSIMP_strincmp(&(*m_DataIt),"bump",4))
+ out = & m_pModel->m_pCurrentMaterial->textureBump;
+
+ // Specularity scaling (glossiness)
+ else if (!ASSIMP_strincmp(&(*m_DataIt),"map_ns",6))
+ out = & m_pModel->m_pCurrentMaterial->textureSpecularity;
+
+ else
+ {
+ DefaultLogger::get()->error("OBJ/MTL: Encountered unknown texture type");
+ return;
+ }
+
+ std::string strTexture;
+ m_DataIt = getName<DataArrayIt>( m_DataIt, m_DataItEnd, strTexture );
+ out->Set( strTexture );
+}
+
+// -------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER
diff --git a/src/3rdparty/assimp/code/ObjFileMtlImporter.h b/src/3rdparty/assimp/code/ObjFileMtlImporter.h
new file mode 100644
index 000000000..f98cbbcaf
--- /dev/null
+++ b/src/3rdparty/assimp/code/ObjFileMtlImporter.h
@@ -0,0 +1,115 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------*/
+#ifndef OBJFILEMTLIMPORTER_H_INC
+#define OBJFILEMTLIMPORTER_H_INC
+
+#include <vector>
+#include <string>
+
+struct aiColor3D;
+
+namespace Assimp
+{
+
+namespace ObjFile
+{
+struct Model;
+struct Material;
+
+}
+
+
+/**
+ * @class ObjFileMtlImporter
+ * @brief Loads the material description from a mtl file.
+ */
+class ObjFileMtlImporter
+{
+public:
+ static const size_t BUFFERSIZE = 2048;
+ typedef std::vector<char> DataArray;
+ typedef std::vector<char>::iterator DataArrayIt;
+ typedef std::vector<char>::const_iterator ConstDataArrayIt;
+
+public:
+ //! \brief Default constructor
+ ObjFileMtlImporter( std::vector<char> &buffer, const std::string &strAbsPath,
+ ObjFile::Model *pModel );
+
+ //! \brief DEstructor
+ ~ObjFileMtlImporter();
+
+private:
+ /// Copy constructor, empty.
+ ObjFileMtlImporter(const ObjFileMtlImporter &rOther);
+ /// \brief Assignment operator, returns only a reference of this instance.
+ ObjFileMtlImporter &operator = (const ObjFileMtlImporter &rOther);
+ /// Load the whole material description
+ void load();
+ /// Get color data.
+ void getColorRGBA( aiColor3D *pColor);
+ /// Get illumination model from loaded data
+ void getIlluminationModel( int &illum_model );
+ /// Gets a float value from data.
+ void getFloatValue( float &value );
+ /// Creates a new material from loaded data.
+ void createMaterial();
+ /// Get texture name from loaded data.
+ void getTexture();
+
+private:
+ //! Absolute pathname
+ std::string m_strAbsPath;
+ //! Data iterator showing to the current position in data buffer
+ DataArrayIt m_DataIt;
+ //! Data iterator to end of buffer
+ DataArrayIt m_DataItEnd;
+ //! USed model instance
+ ObjFile::Model *m_pModel;
+ //! Current line in file
+ unsigned int m_uiLine;
+ //! Helper buffer
+ char m_buffer[BUFFERSIZE];
+};
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif
diff --git a/src/3rdparty/assimp/code/ObjFileParser.cpp b/src/3rdparty/assimp/code/ObjFileParser.cpp
new file mode 100755
index 000000000..03a1caa27
--- /dev/null
+++ b/src/3rdparty/assimp/code/ObjFileParser.cpp
@@ -0,0 +1,681 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
+
+#include "ObjFileParser.h"
+#include "ObjFileMtlImporter.h"
+#include "ObjTools.h"
+#include "ObjFileData.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+#include "../include/aiTypes.h"
+#include "DefaultIOSystem.h"
+
+#if defined(__QNXNTO__)
+#include <stdlib.h>
+#endif
+
+namespace Assimp
+{
+
+// -------------------------------------------------------------------
+const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME;
+// fix: changed that to our standard default name
+
+// -------------------------------------------------------------------
+// Constructor with loaded data and directories.
+ObjFileParser::ObjFileParser(std::vector<char> &Data,const std::string &strModelName, IOSystem *io ) :
+ m_DataIt(Data.begin()),
+ m_DataItEnd(Data.end()),
+ m_pModel(NULL),
+ m_uiLine(0),
+ m_pIO( io )
+{
+ std::fill_n(m_buffer,BUFFERSIZE,0);
+
+ // Create the model instance to store all the data
+ m_pModel = new ObjFile::Model();
+ m_pModel->m_ModelName = strModelName;
+
+ m_pModel->m_pDefaultMaterial = new ObjFile::Material();
+ m_pModel->m_pDefaultMaterial->MaterialName.Set( DEFAULT_MATERIAL );
+ m_pModel->m_MaterialLib.push_back( DEFAULT_MATERIAL );
+ m_pModel->m_MaterialMap[ DEFAULT_MATERIAL ] = m_pModel->m_pDefaultMaterial;
+
+ // Start parsing the file
+ parseFile();
+}
+
+// -------------------------------------------------------------------
+// Destrcutor.
+ObjFileParser::~ObjFileParser()
+{
+ delete m_pModel->m_pDefaultMaterial;
+ m_pModel->m_pDefaultMaterial = NULL;
+
+ delete m_pModel;
+ m_pModel = NULL;
+}
+
+// -------------------------------------------------------------------
+// Returns a pointer to the model instance.
+ObjFile::Model *ObjFileParser::GetModel() const
+{
+ return m_pModel;
+}
+
+// -------------------------------------------------------------------
+// File parsing method.
+void ObjFileParser::parseFile()
+{
+ if (m_DataIt == m_DataItEnd)
+ return;
+
+ while (m_DataIt != m_DataItEnd)
+ {
+ switch (*m_DataIt)
+ {
+ case 'v': // Parse a vertex texture coordinate
+ {
+ ++m_DataIt;
+ if (*m_DataIt == ' ')
+ {
+ // Read in vertex definition
+ getVector3(m_pModel->m_Vertices);
+ }
+ else if (*m_DataIt == 't')
+ {
+ // Read in texture coordinate (2D)
+ ++m_DataIt;
+ getVector2(m_pModel->m_TextureCoord);
+ }
+ else if (*m_DataIt == 'n')
+ {
+ // Read in normal vector definition
+ ++m_DataIt;
+ getVector3( m_pModel->m_Normals );
+ }
+ }
+ break;
+
+ case 'f': // Parse a face
+ {
+ getFace();
+ }
+ break;
+
+ case '#': // Parse a comment
+ {
+ getComment();
+ }
+ break;
+
+ case 'u': // Parse a material desc. setter
+ {
+ getMaterialDesc();
+ }
+ break;
+
+ case 'm': // Parse a material library
+ {
+ getMaterialLib();
+ }
+ break;
+
+ case 'g': // Parse group name
+ {
+ getGroupName();
+ }
+ break;
+
+ case 's': // Parse group number
+ {
+ getGroupNumber();
+ }
+ break;
+
+ case 'o': // Parse object name
+ {
+ getObjectName();
+ }
+ break;
+
+ default:
+ {
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+ }
+ }
+}
+
+// -------------------------------------------------------------------
+// Copy the next word in a temporary buffer
+void ObjFileParser::copyNextWord(char *pBuffer, size_t length)
+{
+ size_t index = 0;
+ m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
+ while ( m_DataIt != m_DataItEnd && !isSeparator(*m_DataIt) )
+ {
+ pBuffer[index] = *m_DataIt;
+ index++;
+ if (index == length-1)
+ break;
+ ++m_DataIt;
+ }
+ pBuffer[index] = '\0';
+}
+
+// -------------------------------------------------------------------
+// Copy the next line into a temporary buffer
+void ObjFileParser::copyNextLine(char *pBuffer, size_t length)
+{
+ size_t index = 0;
+ while (m_DataIt != m_DataItEnd)
+ {
+ if (*m_DataIt == '\n' || *m_DataIt == '\r' || index == length-1)
+ break;
+
+ pBuffer[ index ] = *m_DataIt;
+ ++index;
+ ++m_DataIt;
+ }
+ pBuffer[ index ] = '\0';
+}
+
+// -------------------------------------------------------------------
+// Get values for a new 3D vector instance
+void ObjFileParser::getVector3(std::vector<aiVector3D> &point3d_array)
+{
+ float x, y, z;
+ copyNextWord(m_buffer, BUFFERSIZE);
+ x = (float) fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, BUFFERSIZE);
+ y = (float) fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, BUFFERSIZE);
+ z = (float) fast_atof(m_buffer);
+
+ point3d_array.push_back( aiVector3D( x, y, z ) );
+ //skipLine();
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+// Get values for a new 2D vector instance
+void ObjFileParser::getVector2( std::vector<aiVector2D> &point2d_array )
+{
+ float x, y;
+ copyNextWord(m_buffer, BUFFERSIZE);
+ x = (float) fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, BUFFERSIZE);
+ y = (float) fast_atof(m_buffer);
+
+ point2d_array.push_back(aiVector2D(x, y));
+
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+// Get values for a new face instance
+void ObjFileParser::getFace()
+{
+ copyNextLine(m_buffer, BUFFERSIZE);
+ if (m_DataIt == m_DataItEnd)
+ return;
+
+ char *pPtr = m_buffer;
+ char *pEnd = &pPtr[BUFFERSIZE];
+ pPtr = getNextToken<char*>(pPtr, pEnd);
+ if (pPtr == pEnd || *pPtr == '\0')
+ return;
+
+ std::vector<unsigned int> *pIndices = new std::vector<unsigned int>;
+ std::vector<unsigned int> *pTexID = new std::vector<unsigned int>;
+ std::vector<unsigned int> *pNormalID = new std::vector<unsigned int>;
+ bool hasNormal = false;
+
+ const bool vt = (!m_pModel->m_TextureCoord.empty());
+ const bool vn = (!m_pModel->m_Normals.empty());
+ int iStep = 0, iPos = 0;
+ while (pPtr != pEnd)
+ {
+ iStep = 1;
+ if (*pPtr == '\0')
+ break;
+
+ if (IsLineEnd(*pPtr))
+ break;
+
+ if (*pPtr=='/' )
+ {
+ if (iPos == 0)
+ {
+ //if there are no texturecoordinates in the obj file but normals
+ if (!vt && vn) {
+ iPos = 1;
+ iStep++;
+ }
+ }
+ iPos++;
+ }
+ else if ( isSeparator(*pPtr) )
+ {
+ iPos = 0;
+ }
+ else
+ {
+ //OBJ USES 1 Base ARRAYS!!!!
+ const int iVal = atoi( pPtr );
+ int tmp = iVal;
+ while ( ( tmp = tmp / 10 )!=0 )
+ ++iStep;
+
+ if ( iVal > 0 )
+ {
+ // Store parsed index
+ if ( 0 == iPos )
+ {
+ pIndices->push_back( iVal-1 );
+ }
+ else if ( 1 == iPos )
+ {
+ pTexID->push_back( iVal-1 );
+ }
+ else if ( 2 == iPos )
+ {
+ pNormalID->push_back( iVal-1 );
+ hasNormal = true;
+ }
+ else
+ {
+ reportErrorTokenInFace();
+ }
+ }
+ }
+ pPtr += iStep;
+ }
+
+ if ( pIndices->empty() )
+ {
+ DefaultLogger::get()->error("Obj: Ignoring empty face");
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ return;
+ }
+
+ ObjFile::Face *face = new ObjFile::Face( pIndices, pNormalID, pTexID );
+
+ // Set active material, if one set
+ if (NULL != m_pModel->m_pCurrentMaterial)
+ face->m_pMaterial = m_pModel->m_pCurrentMaterial;
+ else
+ face->m_pMaterial = m_pModel->m_pDefaultMaterial;
+
+ // Create a default object, if nothing there
+ if ( NULL == m_pModel->m_pCurrent )
+ createObject( "defaultobject" );
+
+ // Assign face to mesh
+ if ( NULL == m_pModel->m_pCurrentMesh )
+ {
+ createMesh();
+ }
+
+ // Store the face
+ m_pModel->m_pCurrentMesh->m_Faces.push_back( face );
+ m_pModel->m_pCurrentMesh->m_uiNumIndices += (unsigned int)face->m_pVertices->size();
+ m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += (unsigned int)face->m_pTexturCoords[0].size();
+ if ( !m_pModel->m_pCurrentMesh->m_hasNormals && hasNormal )
+ {
+ m_pModel->m_pCurrentMesh->m_hasNormals = true;
+ }
+ // Skip the rest of the line
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+// Get values for a new material description
+void ObjFileParser::getMaterialDesc()
+{
+ // Each material request a new object.
+ // Sometimes the object is already created (see 'o' tag by example), but it is not initialized !
+ // So, we create a new object only if the current on is already initialized !
+ if (m_pModel->m_pCurrent != NULL &&
+ (m_pModel->m_pCurrent->m_Meshes.size() > 1 ||
+ (m_pModel->m_pCurrent->m_Meshes.size() == 1 && m_pModel->m_Meshes[m_pModel->m_pCurrent->m_Meshes[0]]->m_Faces.size() != 0))
+ )
+ m_pModel->m_pCurrent = NULL;
+
+ // Get next data for material data
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if (m_DataIt == m_DataItEnd)
+ return;
+
+ char *pStart = &(*m_DataIt);
+ while ( m_DataIt != m_DataItEnd && !isSeparator(*m_DataIt) )
+ ++m_DataIt;
+
+ // Get name
+ std::string strName(pStart, &(*m_DataIt));
+ if ( strName.empty())
+ return;
+
+ // Search for material
+ std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strName );
+ if ( it == m_pModel->m_MaterialMap.end() )
+ {
+ // Not found, use default material
+ m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
+ DefaultLogger::get()->error("OBJ: failed to locate material " + strName + ", skipping");
+ }
+ else
+ {
+ // Found, using detected material
+ m_pModel->m_pCurrentMaterial = (*it).second;
+ if ( needsNewMesh( strName ))
+ {
+ createMesh();
+ }
+ m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strName );
+ }
+
+ // Skip rest of line
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+// Get a comment, values will be skipped
+void ObjFileParser::getComment()
+{
+ while (m_DataIt != m_DataItEnd)
+ {
+ if ( '\n' == (*m_DataIt) )
+ {
+ ++m_DataIt;
+ break;
+ }
+ else
+ {
+ ++m_DataIt;
+ }
+ }
+}
+
+// -------------------------------------------------------------------
+// Get material library from file.
+void ObjFileParser::getMaterialLib()
+{
+ // Translate tuple
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if (m_DataIt == m_DataItEnd)
+ return;
+
+ char *pStart = &(*m_DataIt);
+ while (m_DataIt != m_DataItEnd && !isNewLine(*m_DataIt))
+ m_DataIt++;
+
+ // Check for existence
+ const std::string strMatName(pStart, &(*m_DataIt));
+ IOStream *pFile = m_pIO->Open(strMatName);
+
+ if (!pFile )
+ {
+ DefaultLogger::get()->error("OBJ: Unable to locate material file " + strMatName);
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ return;
+ }
+
+ // Import material library data from file
+ std::vector<char> buffer;
+ BaseImporter::TextFileToBuffer(pFile,buffer);
+ m_pIO->Close( pFile );
+
+ // Importing the material library
+ ObjFileMtlImporter mtlImporter( buffer, strMatName, m_pModel );
+}
+
+// -------------------------------------------------------------------
+// Set a new material definition as the current material.
+void ObjFileParser::getNewMaterial()
+{
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if ( m_DataIt == m_DataItEnd )
+ return;
+
+ char *pStart = &(*m_DataIt);
+ std::string strMat( pStart, *m_DataIt );
+ while ( m_DataIt != m_DataItEnd && isSeparator( *m_DataIt ) )
+ m_DataIt++;
+ std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strMat );
+ if ( it == m_pModel->m_MaterialMap.end() )
+ {
+ // Show a warning, if material was not found
+ DefaultLogger::get()->warn("OBJ: Unsupported material requested: " + strMat);
+ m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
+ }
+ else
+ {
+ // Set new material
+ if ( needsNewMesh( strMat ) )
+ {
+ createMesh();
+ }
+ m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strMat );
+ }
+
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+int ObjFileParser::getMaterialIndex( const std::string &strMaterialName )
+{
+ int mat_index = -1;
+ if ( strMaterialName.empty() )
+ return mat_index;
+ for (size_t index = 0; index < m_pModel->m_MaterialLib.size(); ++index)
+ {
+ if ( strMaterialName == m_pModel->m_MaterialLib[ index ])
+ {
+ mat_index = (int)index;
+ break;
+ }
+ }
+ return mat_index;
+}
+
+// -------------------------------------------------------------------
+// Getter for a group name.
+void ObjFileParser::getGroupName()
+{
+ std::string strGroupName;
+
+ m_DataIt = getName<DataArrayIt>(m_DataIt, m_DataItEnd, strGroupName);
+ if ( isEndOfBuffer( m_DataIt, m_DataItEnd ) )
+ return;
+
+ // Change active group, if necessary
+ if ( m_pModel->m_strActiveGroup != strGroupName )
+ {
+ // Search for already existing entry
+ ObjFile::Model::ConstGroupMapIt it = m_pModel->m_Groups.find(&strGroupName);
+
+ // We are mapping groups into the object structure
+ createObject( strGroupName );
+
+ // New group name, creating a new entry
+ if (it == m_pModel->m_Groups.end())
+ {
+ std::vector<unsigned int> *pFaceIDArray = new std::vector<unsigned int>;
+ m_pModel->m_Groups[ &strGroupName ] = pFaceIDArray;
+ m_pModel->m_pGroupFaceIDs = (pFaceIDArray);
+ }
+ else
+ {
+ m_pModel->m_pGroupFaceIDs = (*it).second;
+ }
+ m_pModel->m_strActiveGroup = strGroupName;
+ }
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+// Not supported
+void ObjFileParser::getGroupNumber()
+{
+ // Not used
+
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+// Stores values for a new object instance, name will be used to
+// identify it.
+void ObjFileParser::getObjectName()
+{
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if (m_DataIt == m_DataItEnd)
+ return;
+ char *pStart = &(*m_DataIt);
+ while ( m_DataIt != m_DataItEnd && !isSeparator( *m_DataIt ) )
+ ++m_DataIt;
+
+ std::string strObjectName(pStart, &(*m_DataIt));
+ if (!strObjectName.empty())
+ {
+ // Reset current object
+ m_pModel->m_pCurrent = NULL;
+
+ // Search for actual object
+ for (std::vector<ObjFile::Object*>::const_iterator it = m_pModel->m_Objects.begin();
+ it != m_pModel->m_Objects.end();
+ ++it)
+ {
+ if ((*it)->m_strObjName == strObjectName)
+ {
+ m_pModel->m_pCurrent = *it;
+ break;
+ }
+ }
+
+ // Allocate a new object, if current one was not found before
+ if ( NULL == m_pModel->m_pCurrent )
+ createObject(strObjectName);
+ }
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+// -------------------------------------------------------------------
+// Creates a new object instance
+void ObjFileParser::createObject(const std::string &strObjectName)
+{
+ ai_assert( NULL != m_pModel );
+ //ai_assert( !strObjectName.empty() );
+
+ m_pModel->m_pCurrent = new ObjFile::Object;
+ m_pModel->m_pCurrent->m_strObjName = strObjectName;
+ m_pModel->m_Objects.push_back( m_pModel->m_pCurrent );
+
+
+ createMesh();
+
+ if ( m_pModel->m_pCurrentMaterial )
+ {
+ m_pModel->m_pCurrentMesh->m_uiMaterialIndex =
+ getMaterialIndex( m_pModel->m_pCurrentMaterial->MaterialName.data );
+ m_pModel->m_pCurrentMesh->m_pMaterial = m_pModel->m_pCurrentMaterial;
+ }
+}
+// -------------------------------------------------------------------
+// Creates a new mesh
+void ObjFileParser::createMesh()
+{
+ ai_assert( NULL != m_pModel );
+ m_pModel->m_pCurrentMesh = new ObjFile::Mesh;
+ m_pModel->m_Meshes.push_back( m_pModel->m_pCurrentMesh );
+ unsigned int meshId = m_pModel->m_Meshes.size()-1;
+ if ( NULL != m_pModel->m_pCurrent )
+ {
+ m_pModel->m_pCurrent->m_Meshes.push_back( meshId );
+ }
+ else
+ {
+ DefaultLogger::get()->error("OBJ: No object detected to attach a new mesh instance.");
+ }
+}
+
+// -------------------------------------------------------------------
+// Returns true, if a new mesh must be created.
+bool ObjFileParser::needsNewMesh( const std::string &rMaterialName )
+{
+ if (m_pModel->m_pCurrentMesh == 0)
+ {
+ // No mesh data yet
+ return true;
+ }
+ bool newMat = false;
+ int matIdx = getMaterialIndex( rMaterialName );
+ unsigned int curMatIdx = m_pModel->m_pCurrentMesh->m_uiMaterialIndex;
+ if ( curMatIdx != ObjFile::Mesh::NoMaterial || curMatIdx != (unsigned int)matIdx )
+ {
+ // New material -> only one material per mesh, so we need to create a new
+ // material
+ newMat = true;
+ }
+ return newMat;
+}
+
+// -------------------------------------------------------------------
+// Shows an error in parsing process.
+void ObjFileParser::reportErrorTokenInFace()
+{
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ DefaultLogger::get()->error("OBJ: Not supported token in face description detected");
+}
+
+// -------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER
diff --git a/src/3rdparty/assimp/code/ObjFileParser.h b/src/3rdparty/assimp/code/ObjFileParser.h
new file mode 100644
index 000000000..e972a9add
--- /dev/null
+++ b/src/3rdparty/assimp/code/ObjFileParser.h
@@ -0,0 +1,137 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+#ifndef OBJ_FILEPARSER_H_INC
+#define OBJ_FILEPARSER_H_INC
+
+#include <vector>
+#include <string>
+#include <map>
+
+namespace Assimp
+{
+
+namespace ObjFile
+{
+struct Model;
+struct Object;
+struct Material;
+struct Point3;
+struct Point2;
+}
+class ObjFileImporter;
+class IOSystem;
+
+/// \class ObjFileParser
+/// \brief Parser for a obj waveform file
+class ObjFileParser
+{
+public:
+ static const size_t BUFFERSIZE = 4096;
+ typedef std::vector<char> DataArray;
+ typedef std::vector<char>::iterator DataArrayIt;
+ typedef std::vector<char>::const_iterator ConstDataArrayIt;
+
+public:
+ /// \brief Constructor with data array.
+ ObjFileParser(std::vector<char> &Data,const std::string &strModelName, IOSystem* io);
+ /// \brief Destructor
+ ~ObjFileParser();
+ /// \brief Model getter.
+ ObjFile::Model *GetModel() const;
+
+private:
+ /// Parse the loadedfile
+ void parseFile();
+ /// Method to copy the new delimited word in the current line.
+ void copyNextWord(char *pBuffer, size_t length);
+ /// Method to copy the new line.
+ void copyNextLine(char *pBuffer, size_t length);
+ /// Stores the following 3d vector.
+ void getVector3( std::vector<aiVector3D> &point3d_array );
+ /// Stores the following 3d vector.
+ void getVector2(std::vector<aiVector2D> &point2d_array);
+ /// Stores the following face.
+ void getFace();
+ void getMaterialDesc();
+ /// Gets a comment.
+ void getComment();
+ /// Gets a a material library.
+ void getMaterialLib();
+ /// Creates a new material.
+ void getNewMaterial();
+ /// Gets the groupname from file.
+ void getGroupName();
+ /// Gets the group number from file.
+ void getGroupNumber();
+ /// Returns the index of the material. Is -1 if not material was found.
+ int getMaterialIndex( const std::string &strMaterialName );
+ /// Parse object name
+ void getObjectName();
+ /// Creates a new object.
+ void createObject(const std::string &strObjectName);
+ /// Creates a new mesh.
+ void createMesh();
+ /// Returns true, if a new mesh instance must be created.
+ bool needsNewMesh( const std::string &rMaterialName );
+ /// Error report in token
+ void reportErrorTokenInFace();
+
+private:
+ /// Default material name
+ static const std::string DEFAULT_MATERIAL;
+ //! Iterator to current position in buffer
+ DataArrayIt m_DataIt;
+ //! Iterator to end position of buffer
+ DataArrayIt m_DataItEnd;
+ //! Pointer to model instance
+ ObjFile::Model *m_pModel;
+ //! Current line (for debugging)
+ unsigned int m_uiLine;
+ //! Helper buffer
+ char m_buffer[BUFFERSIZE];
+ /// Pointer to IO system instance.
+ IOSystem *m_pIO;
+};
+
+} // Namespace Assimp
+
+#endif
diff --git a/src/3rdparty/assimp/code/ObjTools.h b/src/3rdparty/assimp/code/ObjTools.h
new file mode 100644
index 000000000..6f0016f29
--- /dev/null
+++ b/src/3rdparty/assimp/code/ObjTools.h
@@ -0,0 +1,263 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file ObjTools.h
+ * @brief Some helpful templates for text parsing
+ */
+#ifndef OBJ_TOOLS_H_INC
+#define OBJ_TOOLS_H_INC
+
+#include "fast_atof.h"
+
+namespace Assimp
+{
+
+/** @brief Returns true, if the last entry of the buffer is reached.
+ * @param it Iterator of current position.
+ * @param end Iterator with end of buffer.
+ * @return true, if the end of the buffer is reached.
+ */
+template<class char_t>
+inline bool isEndOfBuffer( char_t it, char_t end )
+{
+ if ( it == end )
+ {
+ return true;
+ }
+ else
+ {
+ end--;
+ }
+ return ( it == end );
+}
+
+/** @brief Returns true, if token is a space on any supported platform
+* @param token Token to search in
+* @return true, if token is a space
+*/
+inline bool isSeparator( char token )
+{
+ return ( token == ' ' ||
+ token == '\n' ||
+ token == '\f' ||
+ token == '\r' ||
+ token == '\t' );
+}
+
+/** @brief Returns true, fi token id a new line marking token.
+ * @param token Token to search in
+ * @return true, if token is a newline token.
+ */
+inline bool isNewLine( char token )
+{
+ return ( token == '\n' || token == '\f' || token == '\r' );
+}
+
+/** @brief Returns next word separated by a space
+ * @param pBuffer Pointer to data buffer
+ * @param pEnd Pointer to end of buffer
+ * @return Pointer to next space
+ */
+template<class Char_T>
+inline Char_T getNextWord( Char_T pBuffer, Char_T pEnd )
+{
+ while ( !isEndOfBuffer( pBuffer, pEnd ) )
+ {
+ if ( !isSeparator( *pBuffer ) || isNewLine( *pBuffer ) )
+ break;
+ pBuffer++;
+ }
+ return pBuffer;
+}
+
+/** @brief Returns ponter a next token
+ * @param pBuffer Pointer to data buffer
+ * @param pEnd Pointer to end of buffer
+ * @return Pointer to next token
+ */
+template<class Char_T>
+inline Char_T getNextToken( Char_T pBuffer, Char_T pEnd )
+{
+ while ( !isEndOfBuffer( pBuffer, pEnd ) )
+ {
+ if ( isSeparator( *pBuffer ) )
+ break;
+ pBuffer++;
+ }
+ return getNextWord( pBuffer, pEnd );
+}
+
+/** @brief Skips a line
+ * @param it Iterator set to current position
+ * @param end Iterator set to end of scratch buffer for readout
+ * @param uiLine Current linenumber in format
+ * @return Current-iterator with new position
+ */
+template<class char_t>
+inline char_t skipLine( char_t it, char_t end, unsigned int &uiLine )
+{
+ while ( !isEndOfBuffer( it, end ) && !isNewLine( *it ) )
+ ++it;
+ if ( it != end )
+ {
+ ++it;
+ ++uiLine;
+ }
+ // fix .. from time to time there are spaces at the beginning of a material line
+ while ( it != end && (*it == '\t' || *it == ' ') )
+ ++it;
+ return it;
+}
+
+/** @brief Get a name from the current line. Preserve space in the middle,
+ * but trim it at the end.
+ * @param it set to current position
+ * @param end set to end of scratch buffer for readout
+ * @param name Separated name
+ * @return Current-iterator with new position
+ */
+template<class char_t>
+inline char_t getName( char_t it, char_t end, std::string &name )
+{
+ name = "";
+ it = getNextToken<char_t>( it, end );
+ if ( isEndOfBuffer( it, end ) )
+ return end;
+
+ char *pStart = &( *it );
+ while ( !isEndOfBuffer( it, end ) && !isNewLine( *it ) ) {
+ ++it;
+ }
+
+ while (isEndOfBuffer( it, end ) || isNewLine( *it ) || isSeparator(*it)) {
+ --it;
+ }
+ ++it;
+
+ // Get name
+ // if there is no name, and the previous char is a separator, come back to start
+ while (&(*it) < pStart) {
+ ++it;
+ }
+ std::string strName( pStart, &(*it) );
+ if ( strName.empty() )
+ return it;
+ else
+ name = strName;
+
+ return it;
+}
+
+/** @brief Get next word from given line
+ * @param it set to current position
+ * @param end set to end of scratch buffer for readout
+ * @param pBuffer Buffer for next word
+ * @param length Buffer length
+ * @return Current-iterator with new position
+ */
+template<class char_t>
+inline char_t CopyNextWord( char_t it, char_t end, char *pBuffer, size_t length )
+{
+ size_t index = 0;
+ it = getNextWord<char_t>( it, end );
+ while ( !isSeparator( *it ) && !isEndOfBuffer( it, end ) )
+ {
+ pBuffer[index] = *it ;
+ index++;
+ if (index == length-1)
+ break;
+ ++it;
+ }
+ pBuffer[ index ] = '\0';
+ return it;
+}
+
+/** @brief Get next float from given line
+ * @param it set to current position
+ * @param end set to end of scratch buffer for readout
+ * @param value Separated float value.
+ * @return Current-iterator with new position
+ */
+template<class char_t>
+inline char_t getFloat( char_t it, char_t end, float &value )
+{
+ static const size_t BUFFERSIZE = 1024;
+ char buffer[ BUFFERSIZE ];
+ it = CopyNextWord<char_t>( it, end, buffer, BUFFERSIZE );
+ value = (float) fast_atof( buffer );
+
+ return it;
+}
+
+/** @brief Will perform a simple tokenize.
+ * @param str String to tokenize.
+ * @param tokens Array with tokens, will be empty if no token was found.
+ * @param delimiters Delimiter for tokenize.
+ * @return Number of found token.
+ */
+template<class string_type>
+unsigned int tokenize( const string_type& str, std::vector<string_type>& tokens,
+ const string_type& delimiters )
+{
+ // Skip delimiters at beginning.
+ typename string_type::size_type lastPos = str.find_first_not_of( delimiters, 0 );
+
+ // Find first "non-delimiter".
+ typename string_type::size_type pos = str.find_first_of( delimiters, lastPos );
+ while ( string_type::npos != pos || string_type::npos != lastPos )
+ {
+ // Found a token, add it to the vector.
+ string_type tmp = str.substr(lastPos, pos - lastPos);
+ if ( !tmp.empty() && ' ' != tmp[ 0 ] )
+ tokens.push_back( tmp );
+
+ // Skip delimiters. Note the "not_of"
+ lastPos = str.find_first_not_of( delimiters, pos );
+
+ // Find next "non-delimiter"
+ pos = str.find_first_of( delimiters, lastPos );
+ }
+
+ return static_cast<unsigned int>( tokens.size() );
+}
+
+} // Namespace Assimp
+
+#endif
diff --git a/src/3rdparty/assimp/code/OgreImporter.cpp b/src/3rdparty/assimp/code/OgreImporter.cpp
new file mode 100644
index 000000000..63cc94787
--- /dev/null
+++ b/src/3rdparty/assimp/code/OgreImporter.cpp
@@ -0,0 +1,867 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file OgreImporter.cpp
+ * @brief Implementation of the Ogre XML (.mesh.xml) loader.
+ */
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
+
+#include <vector>
+#include <sstream>
+using namespace std;
+
+//#include "boost/format.hpp"
+//#include "boost/foreach.hpp"
+//using namespace boost;
+
+#include "TinyFormatter.h"
+
+#include "OgreImporter.h"
+#include "irrXMLWrapper.h"
+
+
+namespace Assimp
+{
+namespace Ogre
+{
+
+
+bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool checkSig) const
+{
+ if (!checkSig)//Check File Extension
+ {
+ std::string extension("mesh.xml");
+ int l=extension.length();
+ return pFile.substr(pFile.length()-l, l)==extension;
+ }
+ else//Check file Header
+ {
+ const char* tokens[] = {"<mesh>"};
+ return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
+ }
+}
+
+
+
+void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler)
+{
+ m_CurrentFilename=pFile;
+ m_CurrentIOHandler=pIOHandler;
+ m_CurrentScene=pScene;
+
+ //Open the File:
+ boost::scoped_ptr<IOStream> file(pIOHandler->Open(pFile));
+ if ( file.get() == NULL)
+ throw DeadlyImportError("Failed to open file "+pFile+".");
+
+ //Read the Mesh File:
+ boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper( new CIrrXML_IOStreamReader( file.get()));
+ XmlReader* MeshFile = irr::io::createIrrXMLReader(mIOWrapper.get());
+ if (!MeshFile)//parse the xml file
+ throw DeadlyImportError("Failed to create XML Reader for "+pFile);
+
+
+ DefaultLogger::get()->debug("Mesh File opened");
+
+ //Read root Node:
+ if (!(XmlRead(MeshFile) && string(MeshFile->getNodeName())=="mesh"))
+ {
+ throw DeadlyImportError("Root Node is not <mesh>! "+pFile+" "+MeshFile->getNodeName());
+ }
+
+ //Go to the submeshs:
+ if (!(XmlRead(MeshFile) && string(MeshFile->getNodeName())=="submeshes"))
+ {
+ throw DeadlyImportError("No <submeshes> node in <mesh> node! "+pFile);
+ }
+
+
+ //-------------------Read the submeshs and materials:-----------------------
+ std::list<boost::shared_ptr<SubMesh> > SubMeshes;
+ vector<aiMaterial*> Materials;
+ XmlRead(MeshFile);
+ while (MeshFile->getNodeName()==string("submesh"))
+ {
+ SubMesh* theSubMesh=new SubMesh();
+ theSubMesh->MaterialName=GetAttribute<string>(MeshFile, "material");
+ DefaultLogger::get()->debug("Loading Submehs with Material: "+theSubMesh->MaterialName);
+ ReadSubMesh(*theSubMesh, MeshFile);
+
+ //just a index in a array, we add a mesh in each loop cycle, so we get indicies like 0, 1, 2 ... n;
+ //so it is important to do this before pushing the mesh in the vector!
+ theSubMesh->MaterialIndex=SubMeshes.size();
+
+ SubMeshes.push_back(boost::shared_ptr<SubMesh>(theSubMesh));
+
+ //Load the Material:
+ aiMaterial* MeshMat=LoadMaterial(theSubMesh->MaterialName);
+
+ //Set the Material:
+ Materials.push_back(MeshMat);
+ }
+
+ if (SubMeshes.empty())
+ throw DeadlyImportError("no submesh loaded!");
+ if (SubMeshes.size()!=Materials.size())
+ throw DeadlyImportError("materialcount doesn't match mesh count!");
+
+ //____________________________________________________________
+
+
+ //----------------Load the skeleton: -------------------------------
+ vector<Bone> Bones;
+ vector<Animation> Animations;
+ if (MeshFile->getNodeName()==string("skeletonlink"))
+ {
+ string SkeletonFile=GetAttribute<string>(MeshFile, "name");
+ LoadSkeleton(SkeletonFile, Bones, Animations);
+ }
+ else
+ {
+ DefaultLogger::get()->warn("No skeleton file will be loaded");
+ DefaultLogger::get()->warn(MeshFile->getNodeName());
+ }
+ //__________________________________________________________________
+
+
+ //----------------- Now fill the Assimp scene ---------------------------
+
+ //put the aiMaterials in the scene:
+ m_CurrentScene->mMaterials=new aiMaterial*[Materials.size()];
+ m_CurrentScene->mNumMaterials=Materials.size();
+ for (unsigned int i=0; i<Materials.size(); ++i)
+ m_CurrentScene->mMaterials[i]=Materials[i];
+
+ //create the aiMehs...
+ vector<aiMesh*> aiMeshes;
+ BOOST_FOREACH(boost::shared_ptr<SubMesh> theSubMesh, SubMeshes)
+ {
+ aiMeshes.push_back(CreateAssimpSubMesh(*theSubMesh, Bones));
+ }
+ //... and put them in the scene:
+ m_CurrentScene->mNumMeshes=aiMeshes.size();
+ m_CurrentScene->mMeshes=new aiMesh*[aiMeshes.size()];
+ memcpy(m_CurrentScene->mMeshes, &(aiMeshes[0]), sizeof(aiMeshes[0])*aiMeshes.size());
+
+ //Create the root node
+ m_CurrentScene->mRootNode=new aiNode("root");
+
+ //link the meshs with the root node:
+ m_CurrentScene->mRootNode->mMeshes=new unsigned int[SubMeshes.size()];
+ m_CurrentScene->mRootNode->mNumMeshes=SubMeshes.size();
+ for (unsigned int i=0; i<SubMeshes.size(); ++i)
+ m_CurrentScene->mRootNode->mMeshes[i]=i;
+
+
+
+ CreateAssimpSkeleton(Bones, Animations);
+ PutAnimationsInScene(Bones, Animations);
+ //___________________________________________________________
+}
+
+
+
+void OgreImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("mesh.xml");
+}
+
+
+void OgreImporter::SetupProperties(const Importer* pImp)
+{
+ m_MaterialLibFilename=pImp->GetPropertyString(AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE, "Scene.material");
+}
+
+void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader)
+{
+ XmlRead(Reader);
+ //TODO: maybe we have alsways just 1 faces and 1 geometry and always in this order. this loop will only work correct, when the order
+ //of faces and geometry changed, and not if we have more than one of one
+ while (Reader->getNodeName()==string("faces") || string(Reader->getNodeName())=="geometry" || Reader->getNodeName()==string("boneassignments"))
+ {
+ if (string(Reader->getNodeName())=="faces")//Read the face list
+ {
+ //some info logging:
+ unsigned int NumFaces=GetAttribute<int>(Reader, "count");
+ stringstream ss; ss <<"Submesh has " << NumFaces << " Faces.";
+ DefaultLogger::get()->debug(ss.str());
+
+ while (XmlRead(Reader) && Reader->getNodeName()==string("face"))
+ {
+ Face NewFace;
+ NewFace.VertexIndices[0]=GetAttribute<int>(Reader, "v1");
+ NewFace.VertexIndices[1]=GetAttribute<int>(Reader, "v2");
+ NewFace.VertexIndices[2]=GetAttribute<int>(Reader, "v3");
+ if (Reader->getAttributeValue("v4"))//this should be supported in the future
+ {
+ throw DeadlyImportError("Submesh has quads, only traingles are supported!");
+ }
+ theSubMesh.FaceList.push_back(NewFace);
+ }
+
+ }//end of faces
+ else if (string(Reader->getNodeName())=="geometry")//Read the vertexdata
+ {
+ //some info logging:
+ unsigned int NumVertices=GetAttribute<int>(Reader, "vertexcount");
+ stringstream ss; ss<<"VertexCount: "<<NumVertices;
+ DefaultLogger::get()->debug(ss.str());
+
+ //General Informations about vertices
+ XmlRead(Reader);
+ if (!(Reader->getNodeName()==string("vertexbuffer")))
+ {
+ throw DeadlyImportError("vertexbuffer node is not first in geometry node!");
+ }
+ theSubMesh.HasPositions=GetAttribute<bool>(Reader, "positions");
+ theSubMesh.HasNormals=GetAttribute<bool>(Reader, "normals");
+ if (!Reader->getAttributeValue("texture_coords"))//we can have 1 or 0 uv channels, and if the mesh has no uvs, it also doesn't have the attribute
+ theSubMesh.NumUvs=0;
+ else
+ theSubMesh.NumUvs=GetAttribute<int>(Reader, "texture_coords");
+ if (theSubMesh.NumUvs>1)
+ throw DeadlyImportError("too many texcoords (just 1 supported!)");
+
+ //read all the vertices:
+ XmlRead(Reader);
+ while (Reader->getNodeName()==string("vertex"))
+ {
+ //read all vertex attributes:
+
+ //Position
+ if (theSubMesh.HasPositions)
+ {
+ XmlRead(Reader);
+ aiVector3D NewPos;
+ NewPos.x=GetAttribute<float>(Reader, "x");
+ NewPos.y=GetAttribute<float>(Reader, "y");
+ NewPos.z=GetAttribute<float>(Reader, "z");
+ theSubMesh.Positions.push_back(NewPos);
+ }
+
+ //Normal
+ if (theSubMesh.HasNormals)
+ {
+ XmlRead(Reader);
+ aiVector3D NewNormal;
+ NewNormal.x=GetAttribute<float>(Reader, "x");
+ NewNormal.y=GetAttribute<float>(Reader, "y");
+ NewNormal.z=GetAttribute<float>(Reader, "z");
+ theSubMesh.Normals.push_back(NewNormal);
+ }
+
+ //Uv:
+ if (1==theSubMesh.NumUvs)
+ {
+ XmlRead(Reader);
+ aiVector3D NewUv;
+ NewUv.x=GetAttribute<float>(Reader, "u");
+ NewUv.y=GetAttribute<float>(Reader, "v")*(-1)+1;//flip the uv vertikal, blender exports them so!
+ theSubMesh.Uvs.push_back(NewUv);
+ }
+ XmlRead(Reader);
+ }
+
+ }//end of "geometry
+
+
+ else if (string(Reader->getNodeName())=="boneassignments")
+ {
+ theSubMesh.Weights.resize(theSubMesh.Positions.size());
+ while (XmlRead(Reader) && Reader->getNodeName()==string("vertexboneassignment"))
+ {
+ Weight NewWeight;
+ unsigned int VertexId=GetAttribute<int>(Reader, "vertexindex");
+ NewWeight.BoneId=GetAttribute<int>(Reader, "boneindex");
+ NewWeight.Value=GetAttribute<float>(Reader, "weight");
+ theSubMesh.BonesUsed=max(theSubMesh.BonesUsed, NewWeight.BoneId+1);//calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0)
+
+ theSubMesh.Weights[VertexId].push_back(NewWeight);
+
+ //XmlRead(Reader);//Once i had this line, and than i got only every second boneassignment, but my first test models had even boneassignment counts, so i thougt, everything would work. And yes, i HATE irrXML!!!
+ }
+
+ }//end of boneassignments
+ }
+ DefaultLogger::get()->debug((Formatter::format(),
+ "Positionen: ",theSubMesh.Positions.size(),
+ " Normale: ",theSubMesh.Normals.size(),
+ " TexCoords: ",theSubMesh.Uvs.size()
+ ));
+ DefaultLogger::get()->warn(Reader->getNodeName());
+
+
+
+ //---------------Make all Vertexes unique: (this is required by assimp)-----------------------
+ vector<Face> UniqueFaceList(theSubMesh.FaceList.size());
+ unsigned int UniqueVertexCount=theSubMesh.FaceList.size()*3;//*3 because each face consists of 3 vertexes, because we only support triangles^^
+ vector<aiVector3D> UniquePositions(UniqueVertexCount);
+ vector<aiVector3D> UniqueNormals(UniqueVertexCount);
+ vector<aiVector3D> UniqueUvs(UniqueVertexCount);
+ vector< vector<Weight> > UniqueWeights((theSubMesh.Weights.size() ? UniqueVertexCount : 0));
+
+ for (unsigned int i=0; i<theSubMesh.FaceList.size(); ++i)
+ {
+ //We precalculate the index vlaues her, because we need them in all vertex attributes
+ unsigned int Vertex1=theSubMesh.FaceList[i].VertexIndices[0];
+ unsigned int Vertex2=theSubMesh.FaceList[i].VertexIndices[1];
+ unsigned int Vertex3=theSubMesh.FaceList[i].VertexIndices[2];
+
+ UniquePositions[3*i+0]=theSubMesh.Positions[Vertex1];
+ UniquePositions[3*i+1]=theSubMesh.Positions[Vertex2];
+ UniquePositions[3*i+2]=theSubMesh.Positions[Vertex3];
+
+ UniqueNormals[3*i+0]=theSubMesh.Normals[Vertex1];
+ UniqueNormals[3*i+1]=theSubMesh.Normals[Vertex2];
+ UniqueNormals[3*i+2]=theSubMesh.Normals[Vertex3];
+
+ if (1==theSubMesh.NumUvs)
+ {
+ UniqueUvs[3*i+0]=theSubMesh.Uvs[Vertex1];
+ UniqueUvs[3*i+1]=theSubMesh.Uvs[Vertex2];
+ UniqueUvs[3*i+2]=theSubMesh.Uvs[Vertex3];
+ }
+
+ if (theSubMesh.Weights.size()) {
+ UniqueWeights[3*i+0]=theSubMesh.Weights[Vertex1];
+ UniqueWeights[3*i+1]=theSubMesh.Weights[Vertex2];
+ UniqueWeights[3*i+2]=theSubMesh.Weights[Vertex3];
+ }
+
+ //The indexvalues a just continuous numbers (0, 1, 2, 3, 4, 5, 6...)
+ UniqueFaceList[i].VertexIndices[0]=3*i+0;
+ UniqueFaceList[i].VertexIndices[1]=3*i+1;
+ UniqueFaceList[i].VertexIndices[2]=3*i+2;
+ }
+ //_________________________________________________________________________________________
+
+ //now we have the unique datas, but want them in the SubMesh, so we swap all the containers:
+ theSubMesh.FaceList.swap(UniqueFaceList);
+ theSubMesh.Positions.swap(UniquePositions);
+ theSubMesh.Normals.swap(UniqueNormals);
+ theSubMesh.Uvs.swap(UniqueUvs);
+ theSubMesh.Weights.swap(UniqueWeights);
+
+ //------------- normalize weights -----------------------------
+ //The Blender exporter doesn't care about whether the sum of all boneweights for a single vertex equals 1 or not,
+ //so we have to make this sure:
+ for (unsigned int VertexId=0; VertexId<theSubMesh.Weights.size(); ++VertexId)//iterate over all vertices
+ {
+ float WeightSum=0.0f;
+ for (unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
+ {
+ WeightSum+=theSubMesh.Weights[VertexId][BoneId].Value;
+ }
+
+ //check if the sum is too far away from 1
+ if (WeightSum<1.0f-0.05f || WeightSum>1.0f+0.05f)
+ {
+ //normalize all weights:
+ for (unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
+ {
+ theSubMesh.Weights[VertexId][BoneId].Value/=WeightSum;
+ }
+ }
+ }
+ //_________________________________________________________
+}
+
+
+aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vector<Bone>& Bones) const
+{
+ aiMesh* NewAiMesh=new aiMesh();
+
+ //Positions
+ NewAiMesh->mVertices=new aiVector3D[theSubMesh.Positions.size()];
+ memcpy(NewAiMesh->mVertices, &theSubMesh.Positions[0], theSubMesh.Positions.size()*sizeof(aiVector3D));
+ NewAiMesh->mNumVertices=theSubMesh.Positions.size();
+
+ //Normals
+ NewAiMesh->mNormals=new aiVector3D[theSubMesh.Normals.size()];
+ memcpy(NewAiMesh->mNormals, &theSubMesh.Normals[0], theSubMesh.Normals.size()*sizeof(aiVector3D));
+
+ //Uvs
+ if (0!=theSubMesh.NumUvs)
+ {
+ NewAiMesh->mNumUVComponents[0]=2;
+ NewAiMesh->mTextureCoords[0]= new aiVector3D[theSubMesh.Uvs.size()];
+ memcpy(NewAiMesh->mTextureCoords[0], &theSubMesh.Uvs[0], theSubMesh.Uvs.size()*sizeof(aiVector3D));
+ }
+
+
+ //---------------------------------------- Bones --------------------------------------------
+
+ //Copy the weights in in Bone-Vertices Struktur
+ //(we have them in a Vertex-Bones Structur, this is much easier for making them unique, which is required by assimp
+ vector< vector<aiVertexWeight> > aiWeights(theSubMesh.BonesUsed);//now the outer list are the bones, and the inner vector the vertices
+ for (unsigned int VertexId=0; VertexId<theSubMesh.Weights.size(); ++VertexId)//iterate over all vertices
+ {
+ for (unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
+ {
+ aiVertexWeight NewWeight;
+ NewWeight.mVertexId=VertexId;//the current Vertex, we can't use the Id form the submehs weights, because they are bone id's
+ NewWeight.mWeight=theSubMesh.Weights[VertexId][BoneId].Value;
+ aiWeights[theSubMesh.Weights[VertexId][BoneId].BoneId].push_back(NewWeight);
+ }
+ }
+
+
+
+ vector<aiBone*> aiBones;
+ aiBones.reserve(theSubMesh.BonesUsed);//the vector might be smaller, because there might be empty bones (bones that are not attached to any vertex)
+
+ //create all the bones and fill them with informations
+ for (unsigned int i=0; i<theSubMesh.BonesUsed; ++i)
+ {
+ if (aiWeights[i].size()>0)
+ {
+ aiBone* NewBone=new aiBone();
+ NewBone->mNumWeights=aiWeights[i].size();
+ NewBone->mWeights=new aiVertexWeight[aiWeights[i].size()];
+ memcpy(NewBone->mWeights, &(aiWeights[i][0]), sizeof(aiVertexWeight)*aiWeights[i].size());
+ NewBone->mName=Bones[i].Name;//The bone list should be sorted after its id's, this was done in LoadSkeleton
+ NewBone->mOffsetMatrix=Bones[i].BoneToWorldSpace;
+
+ aiBones.push_back(NewBone);
+ }
+ }
+ NewAiMesh->mNumBones=aiBones.size();
+
+ // mBones must be NULL if mNumBones is non 0 or the validation fails.
+ if (aiBones.size()) {
+ NewAiMesh->mBones=new aiBone* [aiBones.size()];
+ memcpy(NewAiMesh->mBones, &(aiBones[0]), aiBones.size()*sizeof(aiBone*));
+ }
+
+ //______________________________________________________________________________________________________
+
+
+
+ //Faces
+ NewAiMesh->mFaces=new aiFace[theSubMesh.FaceList.size()];
+ for (unsigned int i=0; i<theSubMesh.FaceList.size(); ++i)
+ {
+ NewAiMesh->mFaces[i].mNumIndices=3;
+ NewAiMesh->mFaces[i].mIndices=new unsigned int[3];
+
+ NewAiMesh->mFaces[i].mIndices[0]=theSubMesh.FaceList[i].VertexIndices[0];
+ NewAiMesh->mFaces[i].mIndices[1]=theSubMesh.FaceList[i].VertexIndices[1];
+ NewAiMesh->mFaces[i].mIndices[2]=theSubMesh.FaceList[i].VertexIndices[2];
+ }
+ NewAiMesh->mNumFaces=theSubMesh.FaceList.size();
+
+ //Link the material:
+ NewAiMesh->mMaterialIndex=theSubMesh.MaterialIndex;//the index is set by the function who called ReadSubMesh
+
+ return NewAiMesh;
+}
+
+
+void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vector<Animation> &Animations) const
+{
+ //most likely the skeleton file will only end with .skeleton
+ //But this is a xml reader, so we need: .skeleton.xml
+ FileName+=".xml";
+
+ DefaultLogger::get()->debug(string("Loading Skeleton: ")+FileName);
+
+ //Open the File:
+ boost::scoped_ptr<IOStream> File(m_CurrentIOHandler->Open(FileName));
+ if (NULL==File.get())
+ throw DeadlyImportError("Failed to open skeleton file "+FileName+".");
+
+ //Read the Mesh File:
+ boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(File.get()));
+ XmlReader* SkeletonFile = irr::io::createIrrXMLReader(mIOWrapper.get());
+ if (!SkeletonFile)
+ throw DeadlyImportError(string("Failed to create XML Reader for ")+FileName);
+
+ XmlRead(SkeletonFile);
+ if (string("skeleton")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("No <skeleton> node in SkeletonFile: "+FileName);
+
+
+
+ //------------------------------------load bones-----------------------------------------
+ XmlRead(SkeletonFile);
+ if (string("bones")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("No bones node in skeleton "+FileName);
+
+ XmlRead(SkeletonFile);
+
+ while (string("bone")==SkeletonFile->getNodeName())
+ {
+ //TODO: Maybe we can have bone ids for the errrors, but normaly, they should never appear, so what....
+
+ //read a new bone:
+ Bone NewBone;
+ NewBone.Id=GetAttribute<int>(SkeletonFile, "id");
+ NewBone.Name=GetAttribute<string>(SkeletonFile, "name");
+
+ //load the position:
+ XmlRead(SkeletonFile);
+ if (string("position")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("Position is not first node in Bone!");
+ NewBone.Position.x=GetAttribute<float>(SkeletonFile, "x");
+ NewBone.Position.y=GetAttribute<float>(SkeletonFile, "y");
+ NewBone.Position.z=GetAttribute<float>(SkeletonFile, "z");
+
+ //Rotation:
+ XmlRead(SkeletonFile);
+ if (string("rotation")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("Rotation is not the second node in Bone!");
+ NewBone.RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
+ XmlRead(SkeletonFile);
+ if (string("axis")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("No axis specified for bone rotation!");
+ NewBone.RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
+ NewBone.RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
+ NewBone.RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
+
+ //append the newly loaded bone to the bone list
+ Bones.push_back(NewBone);
+
+ //Proceed to the next bone:
+ XmlRead(SkeletonFile);
+ }
+ //The bones in the file a not neccesarly ordered by there id's so we do it now:
+ std::sort(Bones.begin(), Bones.end());
+
+ //now the id of each bone should be equal to its position in the vector:
+ //so we do a simple check:
+ {
+ bool IdsOk=true;
+ for (int i=0; i<static_cast<signed int>(Bones.size()); ++i)//i is signed, because all Id's are also signed!
+ {
+ if (Bones[i].Id!=i)
+ IdsOk=false;
+ }
+ if (!IdsOk)
+ throw DeadlyImportError("Bone Ids are not valid!"+FileName);
+ }
+ DefaultLogger::get()->debug((Formatter::format(),"Number of bones: ",Bones.size()));
+ //________________________________________________________________________________
+
+
+
+
+
+
+ //----------------------------load bonehierarchy--------------------------------
+ if (string("bonehierarchy")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("no bonehierarchy node in "+FileName);
+
+ DefaultLogger::get()->debug("loading bonehierarchy...");
+ XmlRead(SkeletonFile);
+ while (string("boneparent")==SkeletonFile->getNodeName())
+ {
+ string Child, Parent;
+ Child=GetAttribute<string>(SkeletonFile, "bone");
+ Parent=GetAttribute<string>(SkeletonFile, "parent");
+
+ unsigned int ChildId, ParentId;
+ ChildId=find(Bones.begin(), Bones.end(), Child)->Id;
+ ParentId=find(Bones.begin(), Bones.end(), Parent)->Id;
+
+ Bones[ChildId].ParentId=ParentId;
+ Bones[ParentId].Children.push_back(ChildId);
+
+ XmlRead(SkeletonFile);//i once forget this line, which led to an endless loop, did i mentioned, that irrxml sucks??
+ }
+ //_____________________________________________________________________________
+
+
+ //--------- Calculate the WorldToBoneSpace Matrix recursivly for all bones: ------------------
+ BOOST_FOREACH(Bone theBone, Bones)
+ {
+ if (-1==theBone.ParentId) //the bone is a root bone
+ {
+ theBone.CalculateBoneToWorldSpaceMatrix(Bones);
+ }
+ }
+ //_______________________________________________________________________
+
+
+ //---------------------------load animations-----------------------------
+ if (string("animations")==SkeletonFile->getNodeName())//animations are optional values
+ {
+ DefaultLogger::get()->debug("Loading Animations");
+ XmlRead(SkeletonFile);
+ while (string("animation")==SkeletonFile->getNodeName())
+ {
+ Animation NewAnimation;
+ NewAnimation.Name=GetAttribute<string>(SkeletonFile, "name");
+ NewAnimation.Length=GetAttribute<float>(SkeletonFile, "length");
+
+ //Load all Tracks
+ XmlRead(SkeletonFile);
+ if (string("tracks")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("no tracks node in animation");
+ XmlRead(SkeletonFile);
+ while (string("track")==SkeletonFile->getNodeName())
+ {
+ Track NewTrack;
+ NewTrack.BoneName=GetAttribute<string>(SkeletonFile, "bone");
+
+ //Load all keyframes;
+ XmlRead(SkeletonFile);
+ if (string("keyframes")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("no keyframes node!");
+ XmlRead(SkeletonFile);
+ while (string("keyframe")==SkeletonFile->getNodeName())
+ {
+ Keyframe NewKeyframe;
+ NewKeyframe.Time=GetAttribute<float>(SkeletonFile, "time");
+
+ //Position:
+ XmlRead(SkeletonFile);
+ if (string("translate")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("translate node not first in keyframe");
+ NewKeyframe.Position.x=GetAttribute<float>(SkeletonFile, "x");
+ NewKeyframe.Position.y=GetAttribute<float>(SkeletonFile, "y");
+ NewKeyframe.Position.z=GetAttribute<float>(SkeletonFile, "z");
+
+ //Rotation:
+ XmlRead(SkeletonFile);
+ if (string("rotate")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("rotate is not second node in keyframe");
+ float RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
+ aiVector3D RotationAxis;
+ XmlRead(SkeletonFile);
+ if (string("axis")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("No axis for keyframe rotation!");
+ RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
+ RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
+ RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
+ NewKeyframe.Rotation=aiQuaternion(RotationAxis, RotationAngle);
+
+ //Scaling:
+ XmlRead(SkeletonFile);
+ if (string("scale")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("no scalling key in keyframe!");
+ NewKeyframe.Scaling.x=GetAttribute<float>(SkeletonFile, "x");
+ NewKeyframe.Scaling.y=GetAttribute<float>(SkeletonFile, "y");
+ NewKeyframe.Scaling.z=GetAttribute<float>(SkeletonFile, "z");
+
+
+ NewTrack.Keyframes.push_back(NewKeyframe);
+ XmlRead(SkeletonFile);
+ }
+
+
+ NewAnimation.Tracks.push_back(NewTrack);
+ }
+
+ Animations.push_back(NewAnimation);
+ }
+ }
+ //_____________________________________________________________________________
+
+}
+
+
+void OgreImporter::CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &)
+{
+ if (!m_CurrentScene->mRootNode)
+ throw DeadlyImportError("No root node exists!!");
+ if (0!=m_CurrentScene->mRootNode->mNumChildren)
+ throw DeadlyImportError("Root Node already has childnodes!");
+
+
+ //Createt the assimp bone hierarchy
+ DefaultLogger::get()->debug("Root Bones");
+ vector<aiNode*> RootBoneNodes;
+ BOOST_FOREACH(Bone theBone, Bones)
+ {
+ if (-1==theBone.ParentId) //the bone is a root bone
+ {
+ DefaultLogger::get()->debug(theBone.Name);
+ RootBoneNodes.push_back(CreateAiNodeFromBone(theBone.Id, Bones, m_CurrentScene->mRootNode));//which will recursily add all other nodes
+ }
+ }
+
+ if (RootBoneNodes.size()) {
+ m_CurrentScene->mRootNode->mNumChildren=RootBoneNodes.size();
+ m_CurrentScene->mRootNode->mChildren=new aiNode*[RootBoneNodes.size()];
+ memcpy(m_CurrentScene->mRootNode->mChildren, &RootBoneNodes[0], sizeof(aiNode*)*RootBoneNodes.size());
+ }
+}
+
+
+void OgreImporter::PutAnimationsInScene(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations)
+{
+ //-----------------Create the Assimp Animations --------------------
+ if (Animations.size()>0)//Maybe the model had only a skeleton and no animations. (If it also has no skeleton, this function would'nt have been called
+ {
+ m_CurrentScene->mNumAnimations=Animations.size();
+ m_CurrentScene->mAnimations=new aiAnimation*[Animations.size()];
+ for (unsigned int i=0; i<Animations.size(); ++i)//create all animations
+ {
+ aiAnimation* NewAnimation=new aiAnimation();
+ NewAnimation->mName=Animations[i].Name;
+ NewAnimation->mDuration=Animations[i].Length;
+ NewAnimation->mTicksPerSecond=1.0f;
+
+ //Create all tracks in this animation
+ NewAnimation->mNumChannels=Animations[i].Tracks.size();
+ NewAnimation->mChannels=new aiNodeAnim*[Animations[i].Tracks.size()];
+ for (unsigned int j=0; j<Animations[i].Tracks.size(); ++j)
+ {
+ aiNodeAnim* NewNodeAnim=new aiNodeAnim();
+ NewNodeAnim->mNodeName=Animations[i].Tracks[j].BoneName;
+
+ //we need this, to acces the bones default pose, which we need to make keys absolute
+ vector<Bone>::const_iterator CurBone=find(Bones.begin(), Bones.end(), NewNodeAnim->mNodeName);
+ aiMatrix4x4 t0, t1;
+ aiMatrix4x4 DefBonePose=//The default bone pose doesnt have a scaling value
+ aiMatrix4x4::Rotation(CurBone->RotationAngle, CurBone->RotationAxis, t0)
+ * aiMatrix4x4::Translation(CurBone->Position, t1);
+
+ //Create the keyframe arrays...
+ unsigned int KeyframeCount=Animations[i].Tracks[j].Keyframes.size();
+ NewNodeAnim->mNumPositionKeys=KeyframeCount;
+ NewNodeAnim->mPositionKeys=new aiVectorKey[KeyframeCount];
+ NewNodeAnim->mNumRotationKeys=KeyframeCount;
+ NewNodeAnim->mRotationKeys=new aiQuatKey[KeyframeCount];
+ NewNodeAnim->mNumScalingKeys=KeyframeCount;
+ NewNodeAnim->mScalingKeys=new aiVectorKey[KeyframeCount];
+
+ //...and fill them
+ for (unsigned int k=0; k<KeyframeCount; ++k)
+ {
+ aiMatrix4x4 t2, t3;
+
+ //Create a matrix to transfrom a vector from the bones default pose to the bone bones in this animation key
+ aiMatrix4x4 PoseToKey=aiMatrix4x4::Scaling(Animations[i].Tracks[j].Keyframes[k].Scaling, t2) //scale
+ * aiMatrix4x4(Animations[i].Tracks[j].Keyframes[k].Rotation.GetMatrix()) //rot
+ * aiMatrix4x4::Translation(Animations[i].Tracks[j].Keyframes[k].Position, t3); //pos
+
+
+ //calculate the complete transformation from world space to bone space
+ aiMatrix4x4 CompleteTransform=DefBonePose * PoseToKey;
+
+ aiVector3D Pos;
+ aiQuaternion Rot;
+ aiVector3D Scale;
+
+ CompleteTransform.Decompose(Scale, Rot, Pos);
+
+
+ NewNodeAnim->mPositionKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time;
+ NewNodeAnim->mPositionKeys[k].mValue=Pos;
+
+ NewNodeAnim->mRotationKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time;
+ NewNodeAnim->mRotationKeys[k].mValue=Rot;
+
+ NewNodeAnim->mScalingKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time;
+ NewNodeAnim->mScalingKeys[k].mValue=Scale;
+ }
+
+ NewAnimation->mChannels[j]=NewNodeAnim;
+ }
+
+ m_CurrentScene->mAnimations[i]=NewAnimation;
+ }
+ }
+//TODO: Auf nicht vorhandene Animationskeys achten!
+//#pragma warning (s.o.)
+ //__________________________________________________________________
+}
+
+
+
+aiNode* OgreImporter::CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &Bones, aiNode* ParentNode) const
+{
+ //----Create the node for this bone and set its values-----
+ aiNode* NewNode=new aiNode(Bones[BoneId].Name);
+ NewNode->mParent=ParentNode;
+
+ aiMatrix4x4 t0,t1;
+ //create a matrix from the transformation values of the ogre bone
+ NewNode->mTransformation=aiMatrix4x4::Rotation(Bones[BoneId].RotationAngle, Bones[BoneId].RotationAxis, t1)
+ * aiMatrix4x4::Translation(Bones[BoneId].Position, t0)
+
+ ;
+ //__________________________________________________________
+
+
+ //---------- recursivly create all children Nodes: ----------
+ NewNode->mNumChildren=Bones[BoneId].Children.size();
+ NewNode->mChildren=new aiNode*[Bones[BoneId].Children.size()];
+ for (unsigned int i=0; i<Bones[BoneId].Children.size(); ++i)
+ {
+ NewNode->mChildren[i]=CreateAiNodeFromBone(Bones[BoneId].Children[i], Bones, NewNode);
+ }
+ //____________________________________________________
+
+
+ return NewNode;
+}
+
+
+void Bone::CalculateBoneToWorldSpaceMatrix(vector<Bone> &Bones)
+{
+ //Calculate the matrix for this bone:
+
+ aiMatrix4x4 t0,t1;
+ aiMatrix4x4 Transf=aiMatrix4x4::Translation(-Position, t0)
+ * aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1)
+ ;
+ if (-1==ParentId)
+ {
+ BoneToWorldSpace=Transf;
+ }
+ else
+ {
+ BoneToWorldSpace=Transf*Bones[ParentId].BoneToWorldSpace;
+ }
+
+ //and recursivly for all children:
+ BOOST_FOREACH(int theChildren, Children)
+ {
+ Bones[theChildren].CalculateBoneToWorldSpaceMatrix(Bones);
+ }
+}
+
+}//namespace Ogre
+}//namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
diff --git a/src/3rdparty/assimp/code/OgreImporter.h b/src/3rdparty/assimp/code/OgreImporter.h
new file mode 100644
index 000000000..ee5747260
--- /dev/null
+++ b/src/3rdparty/assimp/code/OgreImporter.h
@@ -0,0 +1,142 @@
+#include "BaseImporter.h"
+
+#include <vector>
+
+#include "OgreXmlHelper.h"
+#include "irrXMLWrapper.h"
+
+namespace Assimp
+{
+namespace Ogre
+{
+
+
+//Forward declarations:
+struct SubMesh;
+
+///For the moment just triangles, no other polygon types!
+struct Face
+{
+ unsigned int VertexIndices[3];
+};
+
+///for a vertex->bone structur
+struct Weight
+{
+ unsigned int BoneId;
+ float Value;
+};
+
+/// Helper Class to describe an ogre-bone for the skeleton:
+/** All Id's are signed ints, because than we have -1 as a simple INVALID_ID Value (we start from 0 so 0 is a valid bone ID!*/
+struct Bone
+{
+ int Id;
+ int ParentId;
+ std::string Name;
+ aiVector3D Position;
+ float RotationAngle;
+ aiVector3D RotationAxis;
+ std::vector<int> Children;
+ aiMatrix4x4 BoneToWorldSpace;
+
+ ///ctor
+ Bone(): Id(-1), ParentId(-1), RotationAngle(0.0f) {}
+ ///this operator is needed to sort the bones after Id's
+ bool operator<(const Bone& rval) const
+ {return Id<rval.Id; }
+ ///this operator is needed to find a bone by its name in a vector<Bone>
+ bool operator==(const std::string& rval) const
+ {return Name==rval; }
+ bool operator==(const aiString& rval) const
+ {return Name==std::string(rval.data); }
+
+ void CalculateBoneToWorldSpaceMatrix(std::vector<Bone>& Bones);
+
+};
+
+/// keyframe (bone transformation) from a track from a animation
+struct Keyframe
+{
+ float Time;
+ aiVector3D Position;
+ aiQuaternion Rotation;
+ aiVector3D Scaling;
+};
+
+///a track (keyframes for one bone) from an animation
+struct Track
+{
+ std::string BoneName;
+ std::vector<Keyframe> Keyframes;
+};
+
+///Describes an Ogre Animation
+struct Animation
+{
+ std::string Name;
+ float Length;
+ std::vector<Track> Tracks;
+};
+
+///The Main Ogre Importer Class
+class OgreImporter : public BaseImporter
+{
+public:
+ virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+ virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+ virtual void GetExtensionList(std::set<std::string>& extensions);
+ virtual void SetupProperties(const Importer* pImp);
+private:
+
+ /// Helper Functions to read parts of the XML File
+ void ReadSubMesh(SubMesh& theSubMesh, XmlReader* Reader);//the submesh reference is the result value
+
+ /// writes the results in Bones and Animations, Filename is not const, because its call-by-value and the function will change it!
+ void LoadSkeleton(std::string FileName, std::vector<Bone> &Bones, std::vector<Animation> &Animations) const;
+
+ /// converts the animations in aiAnimations and puts them into the scene
+ void PutAnimationsInScene(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations);
+
+ /// uses the bone data to convert a SubMesh into a aiMesh which will be created and returned
+ aiMesh* CreateAssimpSubMesh(const SubMesh &theSubMesh, const std::vector<Bone>& Bones) const;
+
+ //creates the aiskeleton in current scene
+ void CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations);
+
+ aiMaterial* LoadMaterial(const std::string MaterialName) const;
+
+ ///Recursivly creates a filled aiNode from a given root bone
+ aiNode* CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &Bones, aiNode* ParentNode) const;
+
+ //Now we don't have to give theses parameters to all functions
+ std::string m_CurrentFilename;
+ std::string m_MaterialLibFilename;
+ IOSystem* m_CurrentIOHandler;
+ aiScene *m_CurrentScene;
+};
+
+///A submesh from Ogre
+struct SubMesh
+{
+ std::string Name;
+ std::string MaterialName;
+ std::vector<Face> FaceList;
+ std::vector<aiVector3D> Positions; bool HasPositions;
+ std::vector<aiVector3D> Normals; bool HasNormals;
+ std::vector<aiVector3D> Uvs; unsigned int NumUvs;//nearly always 2d, but assimp has always 3d texcoords
+ std::vector< std::vector<Weight> > Weights;//a list of bones for each vertex
+ int MaterialIndex;///< The Index in the Assimp Materialarray from the material witch is attached to this submesh
+ unsigned int BonesUsed;//the highest index of a bone from a bone weight, this is needed to create the assimp bone structur (converting from Vertex-Bones to Bone-Vertices)
+
+ SubMesh(): HasPositions(false), HasNormals(false), NumUvs(0), MaterialIndex(-1), BonesUsed(0) {}//initialize everything
+};
+
+struct BoneAssignment
+{
+ unsigned int BoneId;//this is, what we get from ogre
+ std::string BoneName;//this is, what we need for assimp
+};
+
+}//namespace Ogre
+}//namespace Assimp
diff --git a/src/3rdparty/assimp/code/OgreImporterMaterial.cpp b/src/3rdparty/assimp/code/OgreImporterMaterial.cpp
new file mode 100644
index 000000000..06de8645b
--- /dev/null
+++ b/src/3rdparty/assimp/code/OgreImporterMaterial.cpp
@@ -0,0 +1,253 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/**
+This file contains material related code. This is
+spilitted up from the main file OgreImporter.cpp
+to make it shorter easier to maintain.
+*/
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
+
+#include <vector>
+#include <sstream>
+using namespace std;
+
+//#include "boost/format.hpp"
+//#include "boost/foreach.hpp"
+//using namespace boost;
+
+#include "OgreImporter.h"
+#include "irrXMLWrapper.h"
+#include "TinyFormatter.h"
+
+namespace Assimp
+{
+namespace Ogre
+{
+
+
+
+aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const
+{
+ MaterialHelper *NewMaterial=new MaterialHelper();
+
+ aiString ts(MaterialName.c_str());
+ NewMaterial->AddProperty(&ts, AI_MATKEY_NAME);
+ /*For bettetr understanding of the material parser, here is a material example file:
+
+ material Sarg
+ {
+ receive_shadows on
+ technique
+ {
+ pass
+ {
+ ambient 0.500000 0.500000 0.500000 1.000000
+ diffuse 0.640000 0.640000 0.640000 1.000000
+ specular 0.500000 0.500000 0.500000 1.000000 12.500000
+ emissive 0.000000 0.000000 0.000000 1.000000
+ texture_unit
+ {
+ texture SargTextur.tga
+ tex_address_mode wrap
+ filtering linear linear none
+ }
+ }
+ }
+ }
+
+ */
+
+
+ const string MaterialFileName=m_CurrentFilename.substr(0, m_CurrentFilename.find('.'))+".material";
+ DefaultLogger::get()->info("Trying to load " +MaterialFileName);
+
+ //Read the file into memory and put it in a stringstream
+ stringstream ss;
+ {// after this block, the temporarly loaded data will be released
+ IOStream* MatFilePtr=m_CurrentIOHandler->Open(MaterialFileName);
+ if (NULL==MatFilePtr)
+ {
+ MatFilePtr=m_CurrentIOHandler->Open(m_MaterialLibFilename);
+ if (NULL==MatFilePtr)
+ {
+ DefaultLogger::get()->error(m_MaterialLibFilename+" and "+MaterialFileName + " could not be opened, Material will not be loaded!");
+ return NewMaterial;
+ }
+ }
+ boost::scoped_ptr<IOStream> MaterialFile(MatFilePtr);
+ vector<char> FileData(MaterialFile->FileSize());
+ MaterialFile->Read(&FileData[0], MaterialFile->FileSize(), 1);
+ BaseImporter::ConvertToUTF8(FileData);
+
+ ss << &FileData[0];
+ }
+
+ string Line;
+ ss >> Line;
+// unsigned int Level=0;//Hierarchielevels in the material file, like { } blocks into another
+ while (!ss.eof())
+ {
+ if (Line=="material")
+ {
+ ss >> Line;
+ if (Line==MaterialName)//Load the next material
+ {
+ ss >> Line;
+ if (Line!="{")
+ throw DeadlyImportError("empty material!");
+
+ while (Line!="}")//read until the end of the material
+ {
+ //Proceed to the first technique
+ ss >> Line;
+ if (Line=="technique")
+ {
+ ss >> Line;
+ if (Line!="{")
+ throw DeadlyImportError("empty technique!");
+ while (Line!="}")//read until the end of the technique
+ {
+ ss >> Line;
+ if (Line=="pass")
+ {
+ ss >> Line;
+ if (Line!="{")
+ throw DeadlyImportError("empty pass!");
+ while (Line!="}")//read until the end of the pass
+ {
+ ss >> Line;
+ if (Line=="ambient")
+ {
+ float r,g,b;
+ ss >> r >> g >> b;
+ const aiColor3D Color(r,g,b);
+ NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_AMBIENT);
+ }
+ else if (Line=="diffuse")
+ {
+ float r,g,b;
+ ss >> r >> g >> b;
+ const aiColor3D Color(r,g,b);
+ NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_DIFFUSE);
+ }
+ else if (Line=="specular")
+ {
+ float r,g,b;
+ ss >> r >> g >> b;
+ const aiColor3D Color(r,g,b);
+ NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_SPECULAR);
+ }
+ else if (Line=="emmisive")
+ {
+ float r,g,b;
+ ss >> r >> g >> b;
+ const aiColor3D Color(r,g,b);
+ NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_EMISSIVE);
+ }
+ else if (Line=="texture_unit")
+ {
+ ss >> Line;
+ if (Line!="{")
+ throw DeadlyImportError("empty texture unit!");
+ while (Line!="}")//read until the end of the texture_unit
+ {
+ ss >> Line;
+ if (Line=="texture")
+ {
+ ss >> Line;
+ aiString ts(Line.c_str());
+ NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0));
+ }
+ }//end of texture unit
+ }
+ }
+ }
+ }//end of technique
+
+
+ }
+
+
+ DefaultLogger::get()->info(Line);
+ //read informations from a custom material:
+ if (Line=="set")
+ {
+ ss >> Line;
+ if (Line=="$specular")//todo load this values:
+ {
+ }
+ if (Line=="$diffuse")
+ {
+ }
+ if (Line=="$ambient")
+ {
+ }
+ if (Line=="$colormap")
+ {
+ ss >> Line;
+ aiString ts(Line.c_str());
+ NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0));
+ }
+ if (Line=="$normalmap")
+ {
+ ss >> Line;
+ aiString ts(Line.c_str());
+ NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0));
+ }
+ }
+ }//end of material
+ }
+ else {} //this is the wrong material, proceed the file until we reach the next material
+ }
+ ss >> Line;
+ }
+
+ return NewMaterial;
+}
+
+
+
+}//namespace Ogre
+}//namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
diff --git a/src/3rdparty/assimp/code/OgreXmlHelper.h b/src/3rdparty/assimp/code/OgreXmlHelper.h
new file mode 100644
index 000000000..1b3112c62
--- /dev/null
+++ b/src/3rdparty/assimp/code/OgreXmlHelper.h
@@ -0,0 +1,83 @@
+
+#include "irrXMLWrapper.h"
+#include "fast_atof.h"
+
+#if defined(__QNXNTO__)
+#include <stdlib.h>
+#endif
+
+namespace Assimp
+{
+namespace Ogre
+{
+
+typedef irr::io::IrrXMLReader XmlReader;
+
+
+//------------Helper Funktion to Get a Attribute Save---------------
+template<typename t> inline t GetAttribute(XmlReader* Reader, std::string Name);
+
+/*
+{
+ BOOST_STATIC_ASSERT(false);
+ return t();
+}
+*/
+
+template<> inline int GetAttribute<int>(XmlReader* Reader, std::string Name)
+{
+ const char* Value=Reader->getAttributeValue(Name.c_str());
+ if (Value)
+ return atoi(Value);
+ else
+ throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
+}
+
+template<> inline float GetAttribute<float>(XmlReader* Reader, std::string Name)
+{
+ const char* Value=Reader->getAttributeValue(Name.c_str());
+ if (Value)
+ return fast_atof(Value);
+ else
+ throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
+}
+
+template<> inline std::string GetAttribute<std::string>(XmlReader* Reader, std::string Name)
+{
+ const char* Value=Reader->getAttributeValue(Name.c_str());
+ if (Value)
+ return std::string(Value);
+ else
+ throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
+}
+
+template<> inline bool GetAttribute<bool>(XmlReader* Reader, std::string Name)
+{
+ const char* Value=Reader->getAttributeValue(Name.c_str());
+ if (Value)
+ {
+ if (Value==std::string("true"))
+ return true;
+ else if (Value==std::string("false"))
+ return false;
+ else
+ throw DeadlyImportError(std::string("Bool value has invalid value: "+Name+" / "+Value+" / "+Reader->getNodeName()));
+ }
+ else
+ throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
+}
+//__________________________________________________________________
+
+inline bool XmlRead(XmlReader* Reader)
+{
+ do
+ {
+ if (!Reader->read())
+ return false;
+ }
+ while (Reader->getNodeType()!=irr::io::EXN_ELEMENT);
+ return true;
+}
+
+}//namespace Ogre
+}//namespace Assimp
diff --git a/src/3rdparty/assimp/code/OptimizeGraph.cpp b/src/3rdparty/assimp/code/OptimizeGraph.cpp
new file mode 100644
index 000000000..f7a1f7c63
--- /dev/null
+++ b/src/3rdparty/assimp/code/OptimizeGraph.cpp
@@ -0,0 +1,352 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file OptimizeGraph.cpp
+ * @brief Implementation of the aiProcess_OptimizGraph step
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+#include "OptimizeGraph.h"
+#include "ProcessHelper.h"
+#include "SceneCombiner.h"
+
+#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(0), nodes_in(0), nodes_out(0), count_merged(0)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+OptimizeGraphProcess::~OptimizeGraphProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// 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 postprocessing 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] = NULL;
+ }
+
+ // 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.size()) {
+ 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.size()) {
+ join_master->mName.length = sprintf(join_master->mName.data,"$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.size())
+ nd->mChildren = new aiNode*[child_nodes.size()];
+ else nd->mChildren = NULL;
+ }
+
+ nd->mNumChildren = child_nodes.size();
+
+ 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 += child_nodes.size();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Execute the postprocessing step on the given scene
+void OptimizeGraphProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->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 > 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.
+ ai_assert(dummy_root->mChildren);
+ 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) {
+
+ char buf[512];
+ sprintf(buf,"OptimizeGraphProcess finished; Input nodes: %i, Output nodes: %i",nodes_in,nodes_out);
+ DefaultLogger::get()->info(buf);
+ }
+ else DefaultLogger::get()->debug("OptimizeGraphProcess finished");
+ }
+ meshes.clear();
+ locked.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Buidl 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/src/3rdparty/assimp/code/OptimizeGraph.h b/src/3rdparty/assimp/code/OptimizeGraph.h
new file mode 100644
index 000000000..c6d51dfec
--- /dev/null
+++ b/src/3rdparty/assimp/code/OptimizeGraph.h
@@ -0,0 +1,147 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiTypes.h"
+
+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 ASSIMP_API OptimizeGraphProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::OptimizeGraphProcessTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ OptimizeGraphProcess();
+
+ /** Destructor, private as well */
+ ~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 Rmeove 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/src/3rdparty/assimp/code/OptimizeMeshes.cpp b/src/3rdparty/assimp/code/OptimizeMeshes.cpp
new file mode 100644
index 000000000..2861af819
--- /dev/null
+++ b/src/3rdparty/assimp/code/OptimizeMeshes.cpp
@@ -0,0 +1,249 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file OptimizeMeshes.cpp
+ * @brief Implementation of the aiProcess_OptimizeMeshes step
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+#include "OptimizeMeshes.h"
+#include "ProcessHelper.h"
+#include "SceneCombiner.h"
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+OptimizeMeshesProcess::OptimizeMeshesProcess()
+ : mScene(0)
+ , mesh(0)
+ , pts (false)
+ , max_verts (0xffffffff)
+ , max_faces (0xffffffff)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+OptimizeMeshesProcess::~OptimizeMeshesProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// 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)) ? 0xdeadbeef : 0;
+ return true;
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties for the postprocessing step
+void OptimizeMeshesProcess::SetupProperties(const Importer* pImp)
+{
+ if (max_verts == 0xdeadbeef /* 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) {
+ DefaultLogger::get()->debug("Skipping OptimizeMeshesProcess");
+ return;
+ }
+
+ DefaultLogger::get()->debug("OptimizeMeshesProcess begin");
+ mScene = pScene;
+
+ // need to clear persistent members from previous runs
+ merge_list.clear();
+ output.clear();
+
+ merge_list.reserve(pScene->mNumMeshes);
+ output.reserve(pScene->mNumMeshes);
+
+ // Prepare lookup tables
+ meshes.resize(pScene->mNumMeshes);
+ FindInstancedMeshes(pScene->mRootNode);
+ if (max_verts == 0xdeadbeef) /* undo the magic hack */
+ max_verts = 0xffffffff;
+
+ // ... 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 == 0xffffffff) {
+ meshes[i].output_id = n++;
+ output.push_back(mScene->mMeshes[i]);
+ }
+ }
+
+ // and process all nodes in the scenegraoh recursively
+ ProcessNode(pScene->mRootNode);
+ if (!output.size()) {
+ throw DeadlyImportError("OptimizeMeshes: No meshes remaining; there's definitely something wrong");
+ }
+
+ meshes.clear();
+ ai_assert(output.size() <= num_old);
+
+ mScene->mNumMeshes = output.size();
+ std::copy(output.begin(),output.end(),mScene->mMeshes);
+
+ if (output.size() != num_old) {
+ char tmp[512];
+ ::sprintf(tmp,"OptimizeMeshesProcess finished. Input meshes: %i, Output meshes: %i",num_old,pScene->mNumMeshes);
+ DefaultLogger::get()->info(tmp);
+ }
+ else DefaultLogger::get()->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.clear();
+ unsigned int verts = 0, faces = 0;
+
+ // Find meshes to merge with us
+ for (unsigned int a = i+1; a < pNode->mNumMeshes;++a) {
+ register 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->mNumMeshes;
+ for (unsigned int n = a; n < pNode->mNumMeshes; ++n)
+ pNode->mMeshes[n] = pNode->mMeshes[n+1];
+
+ --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 = 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 ((0xffffffff != max_verts && verts+mb->mNumVertices > max_verts) ||
+ (0xffffffff != 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 savely join them.
+ if (ma->HasBones()) {
+ // TODO
+ return false;
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Buidl 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/src/3rdparty/assimp/code/OptimizeMeshes.h b/src/3rdparty/assimp/code/OptimizeMeshes.h
new file mode 100644
index 000000000..406e9542a
--- /dev/null
+++ b/src/3rdparty/assimp/code/OptimizeMeshes.h
@@ -0,0 +1,187 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiTypes.h"
+
+struct aiMesh;
+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 ASSIMP_API OptimizeMeshesProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::OptimizeMeshesProcessTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ OptimizeMeshesProcess();
+
+ /** Destructor, private as well */
+ ~OptimizeMeshesProcess();
+
+
+ /** @brief Internal utility to store additional mesh info
+ */
+ struct MeshInfo
+ {
+ MeshInfo()
+ : instance_cnt (0)
+ , vertex_format (0)
+ , output_id (0xffffffff)
+ {}
+
+ //! 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 splitted.
+ * @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;
+
+ //! Next output mesh
+ aiMesh* mesh;
+
+ //! 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/src/3rdparty/assimp/code/ParsingUtils.h b/src/3rdparty/assimp/code/ParsingUtils.h
new file mode 100644
index 000000000..4cae6519b
--- /dev/null
+++ b/src/3rdparty/assimp/code/ParsingUtils.h
@@ -0,0 +1,181 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file ParsingUtils.h
+ * @brief Defines helper functions for text parsing
+ */
+#ifndef AI_PARSING_UTILS_H_INC
+#define AI_PARSING_UTILS_H_INC
+
+#include "StringComparison.h"
+namespace Assimp {
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE bool IsSpace( const char_t in)
+{
+ return (in == (char_t)' ' || in == (char_t)'\t');
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE bool IsLineEnd( const char_t in)
+{
+ return (in == (char_t)'\r' || in == (char_t)'\n' || in == (char_t)'\0');
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE bool IsSpaceOrNewLine( const 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>
+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>
+inline bool SkipLine( const char_t** inout)
+{
+ return SkipLine<char_t>(*inout,inout);
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+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>
+inline bool SkipSpacesAndLineEnd( const char_t** inout)
+{
+ return SkipSpacesAndLineEnd<char_t>(*inout,inout);
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool GetNextLine(const char_t*& buffer, char_t out[4096])
+{
+ if ((char_t)'\0' == *buffer)return false;
+
+ char* _out = out;
+ char* const end = _out+4096;
+ 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;
+}
+// ---------------------------------------------------------------------------------
+AI_FORCE_INLINE bool TokenMatch(char*& in, const char* token, unsigned int len)
+{
+ if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len]))
+ {
+ in += len+1;
+ 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 bool TokenMatch(const char*& in, const char* token, unsigned int len)
+{
+ return TokenMatch(const_cast<char*&>(in), token, len);
+}
+// ---------------------------------------------------------------------------------
+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/src/3rdparty/assimp/code/PlyLoader.cpp b/src/3rdparty/assimp/code/PlyLoader.cpp
new file mode 100644
index 000000000..68640291c
--- /dev/null
+++ b/src/3rdparty/assimp/code/PlyLoader.cpp
@@ -0,0 +1,1050 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file PlyLoader.cpp
+ * @brief Implementation of the PLY importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
+
+// internal headers
+#include "PlyLoader.h"
+#include "MaterialSystem.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+PLYImporter::PLYImporter() : mBuffer(0), pcDOM(0)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+PLYImporter::~PLYImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool PLYImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "ply")
+ return true;
+ else if (!extension.length() || checkSig)
+ {
+ if (!pIOHandler)return true;
+ const char* tokens[] = {"ply"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void PLYImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("ply");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void PLYImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open PLY file " + pFile + ".");
+ }
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+ mBuffer = (unsigned char*)&mBuffer2[0];
+
+ // the beginning of the file must be PLY - magic, magic
+ if ((mBuffer[0] != 'P' && mBuffer[0] != 'p') ||
+ (mBuffer[1] != 'L' && mBuffer[1] != 'l') ||
+ (mBuffer[2] != 'Y' && mBuffer[2] != 'y')) {
+ throw DeadlyImportError( "Invalid .ply file: Magic number \'ply\' is no there");
+ }
+
+ char* szMe = (char*)&this->mBuffer[3];
+ SkipSpacesAndLineEnd(szMe,(const char**)&szMe);
+
+ // determine the format of the file data
+ PLY::DOM sPlyDom;
+ if (TokenMatch(szMe,"format",6))
+ {
+ if (TokenMatch(szMe,"ascii",5))
+ {
+ SkipLine(szMe,(const char**)&szMe);
+ if (!PLY::DOM::ParseInstance(szMe,&sPlyDom))
+ throw DeadlyImportError( "Invalid .ply file: Unable to build DOM (#1)");
+ }
+ else if (!::strncmp(szMe,"binary_",7))
+ {
+ bool bIsBE = false;
+ szMe+=7;
+
+ // binary_little_endian
+ // binary_big_endian
+#if (defined AI_BUILD_BIG_ENDIAN)
+ if ('l' == *szMe || 'L' == *szMe)bIsBE = true;
+#else
+ if ('b' == *szMe || 'B' == *szMe)bIsBE = true;
+#endif // ! AI_BUILD_BIG_ENDIAN
+
+ // skip the line, parse the rest of the header and build the DOM
+ SkipLine(szMe,(const char**)&szMe);
+ if (!PLY::DOM::ParseInstanceBinary(szMe,&sPlyDom,bIsBE))
+ throw DeadlyImportError( "Invalid .ply file: Unable to build DOM (#2)");
+ }
+ else throw DeadlyImportError( "Invalid .ply file: Unknown file format");
+ }
+ else
+ {
+ delete[] this->mBuffer;
+ AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
+ throw DeadlyImportError( "Invalid .ply file: Missing format specification");
+ }
+ this->pcDOM = &sPlyDom;
+
+ // now load a list of vertices. This must be sucessfull in order to procede
+ std::vector<aiVector3D> avPositions;
+ this->LoadVertices(&avPositions,false);
+
+ if (avPositions.empty())
+ throw DeadlyImportError( "Invalid .ply file: No vertices found. "
+ "Unable to parse the data format of the PLY file.");
+
+ // now load a list of normals.
+ std::vector<aiVector3D> avNormals;
+ LoadVertices(&avNormals,true);
+
+ // load the face list
+ std::vector<PLY::Face> avFaces;
+ LoadFaces(&avFaces);
+
+ // if no face list is existing we assume that the vertex
+ // list is containing a list of triangles
+ if (avFaces.empty())
+ {
+ if (avPositions.size() < 3)
+ {
+ throw DeadlyImportError( "Invalid .ply file: Not enough "
+ "vertices to build a proper face list. ");
+ }
+
+ const unsigned int iNum = (unsigned int)avPositions.size() / 3;
+ for (unsigned int i = 0; i< iNum;++i)
+ {
+ PLY::Face sFace;
+ sFace.mIndices.push_back((iNum*3));
+ sFace.mIndices.push_back((iNum*3)+1);
+ sFace.mIndices.push_back((iNum*3)+2);
+ avFaces.push_back(sFace);
+ }
+ }
+
+ // now load a list of all materials
+ std::vector<MaterialHelper*> avMaterials;
+ LoadMaterial(&avMaterials);
+
+ // now load a list of all vertex color channels
+ std::vector<aiColor4D> avColors;
+ avColors.reserve(avPositions.size());
+ LoadVertexColor(&avColors);
+
+ // now try to load texture coordinates
+ std::vector<aiVector2D> avTexCoords;
+ avTexCoords.reserve(avPositions.size());
+ LoadTextureCoordinates(&avTexCoords);
+
+ // now replace the default material in all faces and validate all material indices
+ ReplaceDefaultMaterial(&avFaces,&avMaterials);
+
+ // now convert this to a list of aiMesh instances
+ std::vector<aiMesh*> avMeshes;
+ avMeshes.reserve(avMaterials.size()+1);
+ ConvertMeshes(&avFaces,&avPositions,&avNormals,
+ &avColors,&avTexCoords,&avMaterials,&avMeshes);
+
+ if (avMeshes.empty())
+ throw DeadlyImportError( "Invalid .ply file: Unable to extract mesh data ");
+
+ // now generate the output scene object. Fill the material list
+ pScene->mNumMaterials = (unsigned int)avMaterials.size();
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+ pScene->mMaterials[i] = avMaterials[i];
+
+ // fill the mesh list
+ pScene->mNumMeshes = (unsigned int)avMeshes.size();
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mMeshes[i] = avMeshes[i];
+
+ // generate a simple node structure
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+ pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
+
+ for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes;++i)
+ pScene->mRootNode->mMeshes[i] = i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Split meshes by material IDs
+void PLYImporter::ConvertMeshes(std::vector<PLY::Face>* avFaces,
+ const std::vector<aiVector3D>* avPositions,
+ const std::vector<aiVector3D>* avNormals,
+ const std::vector<aiColor4D>* avColors,
+ const std::vector<aiVector2D>* avTexCoords,
+ const std::vector<MaterialHelper*>* avMaterials,
+ std::vector<aiMesh*>* avOut)
+{
+ ai_assert(NULL != avFaces);
+ ai_assert(NULL != avPositions);
+ ai_assert(NULL != avMaterials);
+
+ // split by materials
+ std::vector<unsigned int>* aiSplit = new std::vector<unsigned int>[avMaterials->size()];
+
+ unsigned int iNum = 0;
+ for (std::vector<PLY::Face>::const_iterator i = avFaces->begin();i != avFaces->end();++i,++iNum)
+ aiSplit[(*i).iMaterialIndex].push_back(iNum);
+
+ // now generate submeshes
+ for (unsigned int p = 0; p < avMaterials->size();++p)
+ {
+ if (aiSplit[p].size() != 0)
+ {
+ // allocate the mesh object
+ aiMesh* p_pcOut = new aiMesh();
+ p_pcOut->mMaterialIndex = p;
+
+ p_pcOut->mNumFaces = (unsigned int)aiSplit[p].size();
+ p_pcOut->mFaces = new aiFace[aiSplit[p].size()];
+
+ // at first we need to determine the size of the output vector array
+ unsigned int iNum = 0;
+ for (unsigned int i = 0; i < aiSplit[p].size();++i)
+ {
+ iNum += (unsigned int)(*avFaces)[aiSplit[p][i]].mIndices.size();
+ }
+ p_pcOut->mNumVertices = iNum;
+ p_pcOut->mVertices = new aiVector3D[iNum];
+
+ if (!avColors->empty())
+ p_pcOut->mColors[0] = new aiColor4D[iNum];
+ if (!avTexCoords->empty())
+ {
+ p_pcOut->mNumUVComponents[0] = 2;
+ p_pcOut->mTextureCoords[0] = new aiVector3D[iNum];
+ }
+ if (!avNormals->empty())
+ p_pcOut->mNormals = new aiVector3D[iNum];
+
+ // add all faces
+ iNum = 0;
+ unsigned int iVertex = 0;
+ for (std::vector<unsigned int>::const_iterator i = aiSplit[p].begin();
+ i != aiSplit[p].end();++i,++iNum)
+ {
+ p_pcOut->mFaces[iNum].mNumIndices = (unsigned int)(*avFaces)[*i].mIndices.size();
+ p_pcOut->mFaces[iNum].mIndices = new unsigned int[p_pcOut->mFaces[iNum].mNumIndices];
+
+ // build an unique set of vertices/colors for this face
+ for (unsigned int q = 0; q < p_pcOut->mFaces[iNum].mNumIndices;++q)
+ {
+ p_pcOut->mFaces[iNum].mIndices[q] = iVertex;
+ p_pcOut->mVertices[iVertex] = (*avPositions)[(*avFaces)[*i].mIndices[q]];
+
+ if (!avColors->empty())
+ p_pcOut->mColors[0][iVertex] = (*avColors)[(*avFaces)[*i].mIndices[q]];
+
+ if (!avTexCoords->empty())
+ {
+ const aiVector2D& vec = (*avTexCoords)[(*avFaces)[*i].mIndices[q]];
+ p_pcOut->mTextureCoords[0][iVertex].x = vec.x;
+ p_pcOut->mTextureCoords[0][iVertex].y = vec.y;
+ }
+
+ if (!avNormals->empty())
+ p_pcOut->mNormals[iVertex] = (*avNormals)[(*avFaces)[*i].mIndices[q]];
+ iVertex++;
+ }
+
+ }
+ // add the mesh to the output list
+ avOut->push_back(p_pcOut);
+ }
+ }
+ delete[] aiSplit; // cleanup
+}
+
+// ------------------------------------------------------------------------------------------------
+// Generate a default material if none was specified and apply it to all vanilla faces
+void PLYImporter::ReplaceDefaultMaterial(std::vector<PLY::Face>* avFaces,
+ std::vector<MaterialHelper*>* avMaterials)
+{
+ bool bNeedDefaultMat = false;
+
+ for (std::vector<PLY::Face>::iterator i = avFaces->begin();i != avFaces->end();++i) {
+ if (0xFFFFFFFF == (*i).iMaterialIndex) {
+ bNeedDefaultMat = true;
+ (*i).iMaterialIndex = (unsigned int)avMaterials->size();
+ }
+ else if ((*i).iMaterialIndex >= avMaterials->size() ) {
+ // clamp the index
+ (*i).iMaterialIndex = (unsigned int)avMaterials->size()-1;
+ }
+ }
+
+ if (bNeedDefaultMat) {
+ // generate a default material
+ MaterialHelper* pcHelper = new MaterialHelper();
+
+ // fill in a default material
+ int iMode = (int)aiShadingMode_Gouraud;
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.6f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ // The face order is absolutely undefined for PLY, so we have to
+ // use two-sided rendering to be sure it's ok.
+ const int two_sided = 1;
+ pcHelper->AddProperty(&two_sided,1,AI_MATKEY_TWOSIDED);
+
+ avMaterials->push_back(pcHelper);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void PLYImporter::LoadTextureCoordinates(std::vector<aiVector2D>* pvOut)
+{
+ ai_assert(NULL != pvOut);
+
+ unsigned int aiPositions[2] = {0xFFFFFFFF,0xFFFFFFFF};
+ PLY::EDataType aiTypes[2] = {EDT_Char,EDT_Char};
+ PLY::ElementInstanceList* pcList = NULL;
+ unsigned int cnt = 0;
+
+ // serach in the DOM for a vertex entry
+ unsigned int _i = 0;
+ for (std::vector<PLY::Element>::const_iterator i = pcDOM->alElements.begin();
+ i != pcDOM->alElements.end();++i,++_i)
+ {
+ if (PLY::EEST_Vertex == (*i).eSemantic)
+ {
+ pcList = &this->pcDOM->alElementData[_i];
+
+ // now check whether which normal components are available
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ if ((*a).bIsList)continue;
+ if (PLY::EST_UTextureCoord == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[0] = _a;
+ aiTypes[0] = (*a).eType;
+ }
+ else if (PLY::EST_VTextureCoord == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[1] = _a;
+ aiTypes[1] = (*a).eType;
+ }
+ }
+ }
+ }
+ // check whether we have a valid source for the texture coordinates data
+ if (NULL != pcList && 0 != cnt)
+ {
+ pvOut->reserve(pcList->alInstances.size());
+ for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();
+ i != pcList->alInstances.end();++i)
+ {
+ // convert the vertices to sp floats
+ aiVector2D vOut;
+
+ if (0xFFFFFFFF != aiPositions[0])
+ {
+ vOut.x = PLY::PropertyInstance::ConvertTo<float>(
+ (*i).alProperties[aiPositions[0]].avList.front(),aiTypes[0]);
+ }
+
+ if (0xFFFFFFFF != aiPositions[1])
+ {
+ vOut.y = PLY::PropertyInstance::ConvertTo<float>(
+ (*i).alProperties[aiPositions[1]].avList.front(),aiTypes[1]);
+ }
+ // and add them to our nice list
+ pvOut->push_back(vOut);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Try to extract vertices from the PLY DOM
+void PLYImporter::LoadVertices(std::vector<aiVector3D>* pvOut, bool p_bNormals)
+{
+ ai_assert(NULL != pvOut);
+
+ unsigned int aiPositions[3] = {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF};
+ PLY::EDataType aiTypes[3] = {EDT_Char,EDT_Char,EDT_Char};
+ PLY::ElementInstanceList* pcList = NULL;
+ unsigned int cnt = 0;
+
+ // serach in the DOM for a vertex entry
+ unsigned int _i = 0;
+ for (std::vector<PLY::Element>::const_iterator i = pcDOM->alElements.begin();
+ i != pcDOM->alElements.end();++i,++_i)
+ {
+ if (PLY::EEST_Vertex == (*i).eSemantic)
+ {
+ pcList = &pcDOM->alElementData[_i];
+
+ // load normal vectors?
+ if (p_bNormals)
+ {
+ // now check whether which normal components are available
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ if ((*a).bIsList)continue;
+ if (PLY::EST_XNormal == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[0] = _a;
+ aiTypes[0] = (*a).eType;
+ }
+ else if (PLY::EST_YNormal == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[1] = _a;
+ aiTypes[1] = (*a).eType;
+ }
+ else if (PLY::EST_ZNormal == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[2] = _a;
+ aiTypes[2] = (*a).eType;
+ }
+ }
+ }
+ // load vertex coordinates
+ else
+ {
+ // now check whether which coordinate sets are available
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ if ((*a).bIsList)continue;
+ if (PLY::EST_XCoord == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[0] = _a;
+ aiTypes[0] = (*a).eType;
+ }
+ else if (PLY::EST_YCoord == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[1] = _a;
+ aiTypes[1] = (*a).eType;
+ }
+ else if (PLY::EST_ZCoord == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[2] = _a;
+ aiTypes[2] = (*a).eType;
+ }
+ if (3 == cnt)break;
+ }
+ }
+ break;
+ }
+ }
+ // check whether we have a valid source for the vertex data
+ if (NULL != pcList && 0 != cnt)
+ {
+ pvOut->reserve(pcList->alInstances.size());
+ for (std::vector<ElementInstance>::const_iterator
+ i = pcList->alInstances.begin();
+ i != pcList->alInstances.end();++i)
+ {
+ // convert the vertices to sp floats
+ aiVector3D vOut;
+
+ if (0xFFFFFFFF != aiPositions[0])
+ {
+ vOut.x = PLY::PropertyInstance::ConvertTo<float>(
+ (*i).alProperties[aiPositions[0]].avList.front(),aiTypes[0]);
+ }
+
+ if (0xFFFFFFFF != aiPositions[1])
+ {
+ vOut.y = PLY::PropertyInstance::ConvertTo<float>(
+ (*i).alProperties[aiPositions[1]].avList.front(),aiTypes[1]);
+ }
+
+ if (0xFFFFFFFF != aiPositions[2])
+ {
+ vOut.z = PLY::PropertyInstance::ConvertTo<float>(
+ (*i).alProperties[aiPositions[2]].avList.front(),aiTypes[2]);
+ }
+
+ // and add them to our nice list
+ pvOut->push_back(vOut);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a color component to [0...1]
+float PLYImporter::NormalizeColorValue (PLY::PropertyInstance::ValueUnion val,
+ PLY::EDataType eType)
+{
+ switch (eType)
+ {
+ case EDT_Float:
+ return val.fFloat;
+ case EDT_Double:
+ return (float)val.fDouble;
+
+ case EDT_UChar:
+ return (float)val.iUInt / (float)0xFF;
+ case EDT_Char:
+ return (float)(val.iInt+(0xFF/2)) / (float)0xFF;
+ case EDT_UShort:
+ return (float)val.iUInt / (float)0xFFFF;
+ case EDT_Short:
+ return (float)(val.iInt+(0xFFFF/2)) / (float)0xFFFF;
+ case EDT_UInt:
+ return (float)val.iUInt / (float)0xFFFF;
+ case EDT_Int:
+ return ((float)val.iInt / (float)0xFF) + 0.5f;
+ default: ;
+ };
+ return 0.0f;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Try to extract proper vertex colors from the PLY DOM
+void PLYImporter::LoadVertexColor(std::vector<aiColor4D>* pvOut)
+{
+ ai_assert(NULL != pvOut);
+
+ unsigned int aiPositions[4] = {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF};
+ PLY::EDataType aiTypes[4] = {EDT_Char, EDT_Char, EDT_Char, EDT_Char}; // silencing gcc
+ unsigned int cnt = 0;
+ PLY::ElementInstanceList* pcList = NULL;
+
+ // serach in the DOM for a vertex entry
+ unsigned int _i = 0;
+ for (std::vector<PLY::Element>::const_iterator i = pcDOM->alElements.begin();
+ i != pcDOM->alElements.end();++i,++_i)
+ {
+ if (PLY::EEST_Vertex == (*i).eSemantic)
+ {
+ pcList = &this->pcDOM->alElementData[_i];
+
+ // now check whether which coordinate sets are available
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator
+ a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ if ((*a).bIsList)continue;
+ if (PLY::EST_Red == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[0] = _a;
+ aiTypes[0] = (*a).eType;
+ }
+ else if (PLY::EST_Green == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[1] = _a;
+ aiTypes[1] = (*a).eType;
+ }
+ else if (PLY::EST_Blue == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[2] = _a;
+ aiTypes[2] = (*a).eType;
+ }
+ else if (PLY::EST_Alpha == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[3] = _a;
+ aiTypes[3] = (*a).eType;
+ }
+ if (4 == cnt)break;
+ }
+ break;
+ }
+ }
+ // check whether we have a valid source for the vertex data
+ if (NULL != pcList && 0 != cnt)
+ {
+ pvOut->reserve(pcList->alInstances.size());
+ for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();
+ i != pcList->alInstances.end();++i)
+ {
+ // convert the vertices to sp floats
+ aiColor4D vOut;
+
+ if (0xFFFFFFFF != aiPositions[0])
+ {
+ vOut.r = NormalizeColorValue((*i).alProperties[
+ aiPositions[0]].avList.front(),aiTypes[0]);
+ }
+
+ if (0xFFFFFFFF != aiPositions[1])
+ {
+ vOut.g = NormalizeColorValue((*i).alProperties[
+ aiPositions[1]].avList.front(),aiTypes[1]);
+ }
+
+ if (0xFFFFFFFF != aiPositions[2])
+ {
+ vOut.b = NormalizeColorValue((*i).alProperties[
+ aiPositions[2]].avList.front(),aiTypes[2]);
+ }
+
+ // assume 1.0 for the alpha channel ifit is not set
+ if (0xFFFFFFFF == aiPositions[3])vOut.a = 1.0f;
+ else
+ {
+ vOut.a = NormalizeColorValue((*i).alProperties[
+ aiPositions[3]].avList.front(),aiTypes[3]);
+ }
+
+ // and add them to our nice list
+ pvOut->push_back(vOut);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Try to extract proper faces from the PLY DOM
+void PLYImporter::LoadFaces(std::vector<PLY::Face>* pvOut)
+{
+ ai_assert(NULL != pvOut);
+
+ PLY::ElementInstanceList* pcList = NULL;
+ bool bOne = false;
+
+ // index of the vertex index list
+ unsigned int iProperty = 0xFFFFFFFF;
+ PLY::EDataType eType = EDT_Char;
+ bool bIsTristrip = false;
+
+ // index of the material index property
+ unsigned int iMaterialIndex = 0xFFFFFFFF;
+ PLY::EDataType eType2 = EDT_Char;
+
+ // serach in the DOM for a face entry
+ unsigned int _i = 0;
+ for (std::vector<PLY::Element>::const_iterator i = pcDOM->alElements.begin();
+ i != pcDOM->alElements.end();++i,++_i)
+ {
+ // face = unique number of vertex indices
+ if (PLY::EEST_Face == (*i).eSemantic)
+ {
+ pcList = &pcDOM->alElementData[_i];
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ if (PLY::EST_VertexIndex == (*a).Semantic)
+ {
+ // must be a dynamic list!
+ if (!(*a).bIsList)continue;
+ iProperty = _a;
+ bOne = true;
+ eType = (*a).eType;
+ }
+ else if (PLY::EST_MaterialIndex == (*a).Semantic)
+ {
+ if ((*a).bIsList)continue;
+ iMaterialIndex = _a;
+ bOne = true;
+ eType2 = (*a).eType;
+ }
+ }
+ break;
+ }
+ // triangle strip
+ // TODO: triangle strip and material index support???
+ else if (PLY::EEST_TriStrip == (*i).eSemantic)
+ {
+ // find a list property in this ...
+ pcList = &this->pcDOM->alElementData[_i];
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ // must be a dynamic list!
+ if (!(*a).bIsList)continue;
+ iProperty = _a;
+ bOne = true;
+ bIsTristrip = true;
+ eType = (*a).eType;
+ break;
+ }
+ break;
+ }
+ }
+ // check whether we have at least one per-face information set
+ if (pcList && bOne)
+ {
+ if (!bIsTristrip)
+ {
+ pvOut->reserve(pcList->alInstances.size());
+ for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();
+ i != pcList->alInstances.end();++i)
+ {
+ PLY::Face sFace;
+
+ // parse the list of vertex indices
+ if (0xFFFFFFFF != iProperty)
+ {
+ const unsigned int iNum = (unsigned int)(*i).alProperties[iProperty].avList.size();
+ sFace.mIndices.resize(iNum);
+
+ std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator p =
+ (*i).alProperties[iProperty].avList.begin();
+
+ for (unsigned int a = 0; a < iNum;++a,++p)
+ {
+ sFace.mIndices[a] = PLY::PropertyInstance::ConvertTo<unsigned int>(*p,eType);
+ }
+ }
+
+ // parse the material index
+ if (0xFFFFFFFF != iMaterialIndex)
+ {
+ sFace.iMaterialIndex = PLY::PropertyInstance::ConvertTo<unsigned int>(
+ (*i).alProperties[iMaterialIndex].avList.front(),eType2);
+ }
+ pvOut->push_back(sFace);
+ }
+ }
+ else // triangle strips
+ {
+ // normally we have only one triangle strip instance where
+ // a value of -1 indicates a restart of the strip
+ bool flip = false;
+ for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();i != pcList->alInstances.end();++i) {
+ const std::vector<PLY::PropertyInstance::ValueUnion>& quak = (*i).alProperties[iProperty].avList;
+ pvOut->reserve(pvOut->size() + quak.size() + (quak.size()>>2u));
+
+ int aiTable[2] = {-1,-1};
+ for (std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator a = quak.begin();a != quak.end();++a) {
+ const int p = PLY::PropertyInstance::ConvertTo<int>(*a,eType);
+
+ if (-1 == p) {
+ // restart the strip ...
+ aiTable[0] = aiTable[1] = -1;
+ flip = false;
+ continue;
+ }
+ if (-1 == aiTable[0]) {
+ aiTable[0] = p;
+ continue;
+ }
+ if (-1 == aiTable[1]) {
+ aiTable[1] = p;
+ continue;
+ }
+
+ pvOut->push_back(PLY::Face());
+ PLY::Face& sFace = pvOut->back();
+ sFace.mIndices[0] = aiTable[0];
+ sFace.mIndices[1] = aiTable[1];
+ sFace.mIndices[2] = p;
+ if ((flip = !flip)) {
+ std::swap(sFace.mIndices[0],sFace.mIndices[1]);
+ }
+
+ aiTable[0] = aiTable[1];
+ aiTable[1] = p;
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a RGBA color in [0...1] range
+void PLYImporter::GetMaterialColor(const std::vector<PLY::PropertyInstance>& avList,
+ unsigned int aiPositions[4],
+ PLY::EDataType aiTypes[4],
+ aiColor4D* clrOut)
+{
+ ai_assert(NULL != clrOut);
+
+ if (0xFFFFFFFF == aiPositions[0])clrOut->r = 0.0f;
+ else
+ {
+ clrOut->r = NormalizeColorValue(avList[
+ aiPositions[0]].avList.front(),aiTypes[0]);
+ }
+
+ if (0xFFFFFFFF == aiPositions[1])clrOut->g = 0.0f;
+ else
+ {
+ clrOut->g = NormalizeColorValue(avList[
+ aiPositions[1]].avList.front(),aiTypes[1]);
+ }
+
+ if (0xFFFFFFFF == aiPositions[2])clrOut->b = 0.0f;
+ else
+ {
+ clrOut->b = NormalizeColorValue(avList[
+ aiPositions[2]].avList.front(),aiTypes[2]);
+ }
+
+ // assume 1.0 for the alpha channel ifit is not set
+ if (0xFFFFFFFF == aiPositions[3])clrOut->a = 1.0f;
+ else
+ {
+ clrOut->a = NormalizeColorValue(avList[
+ aiPositions[3]].avList.front(),aiTypes[3]);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Extract a material from the PLY DOM
+void PLYImporter::LoadMaterial(std::vector<MaterialHelper*>* pvOut)
+{
+ ai_assert(NULL != pvOut);
+
+ // diffuse[4], specular[4], ambient[4]
+ // rgba order
+ unsigned int aaiPositions[3][4] = {
+
+ {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF},
+ {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF},
+ {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF},
+ };
+
+ PLY::EDataType aaiTypes[3][4] = {
+ {EDT_Char,EDT_Char,EDT_Char,EDT_Char},
+ {EDT_Char,EDT_Char,EDT_Char,EDT_Char},
+ {EDT_Char,EDT_Char,EDT_Char,EDT_Char}
+ };
+ PLY::ElementInstanceList* pcList = NULL;
+
+ unsigned int iPhong = 0xFFFFFFFF;
+ PLY::EDataType ePhong = EDT_Char;
+
+ unsigned int iOpacity = 0xFFFFFFFF;
+ PLY::EDataType eOpacity = EDT_Char;
+
+ // serach in the DOM for a vertex entry
+ unsigned int _i = 0;
+ for (std::vector<PLY::Element>::const_iterator i = this->pcDOM->alElements.begin();
+ i != this->pcDOM->alElements.end();++i,++_i)
+ {
+ if (PLY::EEST_Material == (*i).eSemantic)
+ {
+ pcList = &this->pcDOM->alElementData[_i];
+
+ // now check whether which coordinate sets are available
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator
+ a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ if ((*a).bIsList)continue;
+
+ // pohng specularity -----------------------------------
+ if (PLY::EST_PhongPower == (*a).Semantic)
+ {
+ iPhong = _a;
+ ePhong = (*a).eType;
+ }
+
+ // general opacity -----------------------------------
+ if (PLY::EST_Opacity == (*a).Semantic)
+ {
+ iOpacity = _a;
+ eOpacity = (*a).eType;
+ }
+
+ // diffuse color channels -----------------------------------
+ if (PLY::EST_DiffuseRed == (*a).Semantic)
+ {
+ aaiPositions[0][0] = _a;
+ aaiTypes[0][0] = (*a).eType;
+ }
+ else if (PLY::EST_DiffuseGreen == (*a).Semantic)
+ {
+ aaiPositions[0][1] = _a;
+ aaiTypes[0][1] = (*a).eType;
+ }
+ else if (PLY::EST_DiffuseBlue == (*a).Semantic)
+ {
+ aaiPositions[0][2] = _a;
+ aaiTypes[0][2] = (*a).eType;
+ }
+ else if (PLY::EST_DiffuseAlpha == (*a).Semantic)
+ {
+ aaiPositions[0][3] = _a;
+ aaiTypes[0][3] = (*a).eType;
+ }
+ // specular color channels -----------------------------------
+ else if (PLY::EST_SpecularRed == (*a).Semantic)
+ {
+ aaiPositions[1][0] = _a;
+ aaiTypes[1][0] = (*a).eType;
+ }
+ else if (PLY::EST_SpecularGreen == (*a).Semantic)
+ {
+ aaiPositions[1][1] = _a;
+ aaiTypes[1][1] = (*a).eType;
+ }
+ else if (PLY::EST_SpecularBlue == (*a).Semantic)
+ {
+ aaiPositions[1][2] = _a;
+ aaiTypes[1][2] = (*a).eType;
+ }
+ else if (PLY::EST_SpecularAlpha == (*a).Semantic)
+ {
+ aaiPositions[1][3] = _a;
+ aaiTypes[1][3] = (*a).eType;
+ }
+ // ambient color channels -----------------------------------
+ else if (PLY::EST_AmbientRed == (*a).Semantic)
+ {
+ aaiPositions[2][0] = _a;
+ aaiTypes[2][0] = (*a).eType;
+ }
+ else if (PLY::EST_AmbientGreen == (*a).Semantic)
+ {
+ aaiPositions[2][1] = _a;
+ aaiTypes[2][1] = (*a).eType;
+ }
+ else if (PLY::EST_AmbientBlue == (*a).Semantic)
+ {
+ aaiPositions[2][2] = _a;
+ aaiTypes[2][2] = (*a).eType;
+ }
+ else if (PLY::EST_AmbientAlpha == (*a).Semantic)
+ {
+ aaiPositions[2][3] = _a;
+ aaiTypes[2][3] = (*a).eType;
+ }
+ }
+ break;
+ }
+ }
+ // check whether we have a valid source for the material data
+ if (NULL != pcList) {
+ for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();i != pcList->alInstances.end();++i) {
+ aiColor4D clrOut;
+ MaterialHelper* pcHelper = new MaterialHelper();
+
+ // build the diffuse material color
+ GetMaterialColor((*i).alProperties,aaiPositions[0],aaiTypes[0],&clrOut);
+ pcHelper->AddProperty<aiColor4D>(&clrOut,1,AI_MATKEY_COLOR_DIFFUSE);
+
+ // build the specular material color
+ GetMaterialColor((*i).alProperties,aaiPositions[1],aaiTypes[1],&clrOut);
+ pcHelper->AddProperty<aiColor4D>(&clrOut,1,AI_MATKEY_COLOR_SPECULAR);
+
+ // build the ambient material color
+ GetMaterialColor((*i).alProperties,aaiPositions[2],aaiTypes[2],&clrOut);
+ pcHelper->AddProperty<aiColor4D>(&clrOut,1,AI_MATKEY_COLOR_AMBIENT);
+
+ // handle phong power and shading mode
+ int iMode;
+ if (0xFFFFFFFF != iPhong) {
+ float fSpec = PLY::PropertyInstance::ConvertTo<float>((*i).alProperties[iPhong].avList.front(),ePhong);
+
+ // if shininess is 0 (and the pow() calculation would therefore always
+ // become 1, not depending on the angle), use gouraud lighting
+ if (fSpec) {
+ // scale this with 15 ... hopefully this is correct
+ fSpec *= 15;
+ pcHelper->AddProperty<float>(&fSpec, 1, AI_MATKEY_SHININESS);
+
+ iMode = (int)aiShadingMode_Phong;
+ }
+ else iMode = (int)aiShadingMode_Gouraud;
+ }
+ else iMode = (int)aiShadingMode_Gouraud;
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ // handle opacity
+ if (0xFFFFFFFF != iOpacity) {
+ float fOpacity = PLY::PropertyInstance::ConvertTo<float>((*i).alProperties[iPhong].avList.front(),eOpacity);
+ pcHelper->AddProperty<float>(&fOpacity, 1, AI_MATKEY_OPACITY);
+ }
+
+ // The face order is absolutely undefined for PLY, so we have to
+ // use two-sided rendering to be sure it's ok.
+ const int two_sided = 1;
+ pcHelper->AddProperty(&two_sided,1,AI_MATKEY_TWOSIDED);
+
+ // add the newly created material instance to the list
+ pvOut->push_back(pcHelper);
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER
diff --git a/src/3rdparty/assimp/code/PlyLoader.h b/src/3rdparty/assimp/code/PlyLoader.h
new file mode 100644
index 000000000..3cdfb5e18
--- /dev/null
+++ b/src/3rdparty/assimp/code/PlyLoader.h
@@ -0,0 +1,174 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file PLYLoader.h
+ * @brief Declaration of the .ply importer class.
+ */
+#ifndef AI_PLYLOADER_H_INCLUDED
+#define AI_PLYLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+struct aiNode;
+
+#include "PlyParser.h"
+
+namespace Assimp {
+class MaterialHelper;
+
+using namespace PLY;
+
+// ---------------------------------------------------------------------------
+/** Importer class to load the stanford PLY file format
+*/
+class PLYImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ PLYImporter();
+
+ /** Destructor, private as well */
+ ~PLYImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Extract vertices from the DOM
+ */
+ void LoadVertices(std::vector<aiVector3D>* pvOut,
+ bool p_bNormals = false);
+
+ // -------------------------------------------------------------------
+ /** Extract vertex color channels from the DOM
+ */
+ void LoadVertexColor(std::vector<aiColor4D>* pvOut);
+
+ // -------------------------------------------------------------------
+ /** Extract texture coordinate channels from the DOM
+ */
+ void LoadTextureCoordinates(std::vector<aiVector2D>* pvOut);
+
+ // -------------------------------------------------------------------
+ /** Extract a face list from the DOM
+ */
+ void LoadFaces(std::vector<PLY::Face>* pvOut);
+
+ // -------------------------------------------------------------------
+ /** Extract a material list from the DOM
+ */
+ void LoadMaterial(std::vector<MaterialHelper*>* pvOut);
+
+
+ // -------------------------------------------------------------------
+ /** Validate material indices, replace default material identifiers
+ */
+ void ReplaceDefaultMaterial(std::vector<PLY::Face>* avFaces,
+ std::vector<MaterialHelper*>* avMaterials);
+
+
+ // -------------------------------------------------------------------
+ /** Convert all meshes into our ourer representation
+ */
+ void ConvertMeshes(std::vector<PLY::Face>* avFaces,
+ const std::vector<aiVector3D>* avPositions,
+ const std::vector<aiVector3D>* avNormals,
+ const std::vector<aiColor4D>* avColors,
+ const std::vector<aiVector2D>* avTexCoords,
+ const std::vector<MaterialHelper*>* avMaterials,
+ std::vector<aiMesh*>* avOut);
+
+
+ // -------------------------------------------------------------------
+ /** Static helper to parse a color from four single channels in
+ */
+ static void GetMaterialColor(
+ const std::vector<PLY::PropertyInstance>& avList,
+ unsigned int aiPositions[4],
+ PLY::EDataType aiTypes[4],
+ aiColor4D* clrOut);
+
+
+ // -------------------------------------------------------------------
+ /** Static helper to parse a color channel value. The input value
+ * is normalized to 0-1.
+ */
+ static float NormalizeColorValue (
+ PLY::PropertyInstance::ValueUnion val,
+ PLY::EDataType eType);
+
+
+ /** Buffer to hold the loaded file */
+ unsigned char* mBuffer;
+
+ /** Document object model representation extracted from the file */
+ PLY::DOM* pcDOM;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/PlyParser.cpp b/src/3rdparty/assimp/code/PlyParser.cpp
new file mode 100644
index 000000000..d0e3fa0f5
--- /dev/null
+++ b/src/3rdparty/assimp/code/PlyParser.cpp
@@ -0,0 +1,919 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the PLY parser class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
+
+#include "PlyLoader.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+PLY::EDataType PLY::Property::ParseDataType(const char* pCur,const char** pCurOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+ PLY::EDataType eOut = PLY::EDT_INVALID;
+
+ if (TokenMatch(pCur,"char",4) ||
+ TokenMatch(pCur,"int8",4))
+ {
+ eOut = PLY::EDT_Char;
+ }
+ else if (TokenMatch(pCur,"uchar",5) ||
+ TokenMatch(pCur,"uint8",5))
+ {
+ eOut = PLY::EDT_UChar;
+ }
+ else if (TokenMatch(pCur,"short",5) ||
+ TokenMatch(pCur,"int16",5))
+ {
+ eOut = PLY::EDT_Short;
+ }
+ else if (TokenMatch(pCur,"ushort",6) ||
+ TokenMatch(pCur,"uint16",6))
+ {
+ eOut = PLY::EDT_UShort;
+ }
+ else if (TokenMatch(pCur,"int32",5) || TokenMatch(pCur,"int",3))
+ {
+ eOut = PLY::EDT_Int;
+ }
+ else if (TokenMatch(pCur,"uint32",6) || TokenMatch(pCur,"uint",4))
+ {
+ eOut = PLY::EDT_UInt;
+ }
+ else if (TokenMatch(pCur,"float",5) || TokenMatch(pCur,"float32",7))
+ {
+ eOut = PLY::EDT_Float;
+ }
+ else if (TokenMatch(pCur,"double64",8) || TokenMatch(pCur,"double",6) ||
+ TokenMatch(pCur,"float64",7))
+ {
+ eOut = PLY::EDT_Double;
+ }
+ if (PLY::EDT_INVALID == eOut)
+ {
+ DefaultLogger::get()->info("Found unknown data type in PLY file. This is OK");
+ }
+ *pCurOut = pCur;
+ return eOut;
+}
+
+// ------------------------------------------------------------------------------------------------
+PLY::ESemantic PLY::Property::ParseSemantic(const char* pCur,const char** pCurOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+
+ PLY::ESemantic eOut = PLY::EST_INVALID;
+ if (TokenMatch(pCur,"red",3))
+ {
+ eOut = PLY::EST_Red;
+ }
+ else if (TokenMatch(pCur,"green",5))
+ {
+ eOut = PLY::EST_Green;
+ }
+ else if (TokenMatch(pCur,"blue",4))
+ {
+ eOut = PLY::EST_Blue;
+ }
+ else if (TokenMatch(pCur,"alpha",5))
+ {
+ eOut = PLY::EST_Alpha;
+ }
+ else if (TokenMatch(pCur,"vertex_index",12) || TokenMatch(pCur,"vertex_indices",14))
+ {
+ eOut = PLY::EST_VertexIndex;
+ }
+ else if (TokenMatch(pCur,"material_index",14))
+ {
+ eOut = PLY::EST_MaterialIndex;
+ }
+ else if (TokenMatch(pCur,"ambient_red",11))
+ {
+ eOut = PLY::EST_AmbientRed;
+ }
+ else if (TokenMatch(pCur,"ambient_green",13))
+ {
+ eOut = PLY::EST_AmbientGreen;
+ }
+ else if (TokenMatch(pCur,"ambient_blue",12))
+ {
+ eOut = PLY::EST_AmbientBlue;
+ }
+ else if (TokenMatch(pCur,"ambient_alpha",13))
+ {
+ eOut = PLY::EST_AmbientAlpha;
+ }
+ else if (TokenMatch(pCur,"diffuse_red",11))
+ {
+ eOut = PLY::EST_DiffuseRed;
+ }
+ else if (TokenMatch(pCur,"diffuse_green",13))
+ {
+ eOut = PLY::EST_DiffuseGreen;
+ }
+ else if (TokenMatch(pCur,"diffuse_blue",12))
+ {
+ eOut = PLY::EST_DiffuseBlue;
+ }
+ else if (TokenMatch(pCur,"diffuse_alpha",13))
+ {
+ eOut = PLY::EST_DiffuseAlpha;
+ }
+ else if (TokenMatch(pCur,"specular_red",12))
+ {
+ eOut = PLY::EST_SpecularRed;
+ }
+ else if (TokenMatch(pCur,"specular_green",14))
+ {
+ eOut = PLY::EST_SpecularGreen;
+ }
+ else if (TokenMatch(pCur,"specular_blue",13))
+ {
+ eOut = PLY::EST_SpecularBlue;
+ }
+ else if (TokenMatch(pCur,"specular_alpha",14))
+ {
+ eOut = PLY::EST_SpecularAlpha;
+ }
+ else if (TokenMatch(pCur,"opacity",7))
+ {
+ eOut = PLY::EST_Opacity;
+ }
+ else if (TokenMatch(pCur,"specular_power",6))
+ {
+ eOut = PLY::EST_PhongPower;
+ }
+ else if (TokenMatch(pCur,"r",1))
+ {
+ eOut = PLY::EST_Red;
+ }
+ else if (TokenMatch(pCur,"g",1))
+ {
+ eOut = PLY::EST_Green;
+ }
+ else if (TokenMatch(pCur,"b",1))
+ {
+ eOut = PLY::EST_Blue;
+ }
+ // NOTE: Blender3D exports texture coordinates as s,t tuples
+ else if (TokenMatch(pCur,"u",1) || TokenMatch(pCur,"s",1) || TokenMatch(pCur,"tx",2))
+ {
+ eOut = PLY::EST_UTextureCoord;
+ }
+ else if (TokenMatch(pCur,"v",1) || TokenMatch(pCur,"t",1) || TokenMatch(pCur,"ty",2))
+ {
+ eOut = PLY::EST_VTextureCoord;
+ }
+ else if (TokenMatch(pCur,"x",1))
+ {
+ eOut = PLY::EST_XCoord;
+ }
+ else if (TokenMatch(pCur,"y",1))
+ {
+ eOut = PLY::EST_YCoord;
+ }
+ else if (TokenMatch(pCur,"z",1))
+ {
+ eOut = PLY::EST_ZCoord;
+ }
+ else if (TokenMatch(pCur,"nx",2))
+ {
+ eOut = PLY::EST_XNormal;
+ }
+ else if (TokenMatch(pCur,"ny",2))
+ {
+ eOut = PLY::EST_YNormal;
+ }
+ else if (TokenMatch(pCur,"nz",2))
+ {
+ eOut = PLY::EST_ZNormal;
+ }
+ else
+ {
+ DefaultLogger::get()->info("Found unknown property semantic in file. This is ok");
+ SkipLine(&pCur);
+ }
+ *pCurOut = pCur;
+ return eOut;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::Property::ParseProperty (const char* pCur,
+ const char** pCurOut,
+ PLY::Property* pOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+
+ // Forms supported:
+ // "property float x"
+ // "property list uchar int vertex_index"
+ *pCurOut = pCur;
+
+ // skip leading spaces
+ if (!SkipSpaces(pCur,&pCur))return false;
+
+ // skip the "property" string at the beginning
+ if (!TokenMatch(pCur,"property",8))
+ {
+ // seems not to be a valid property entry
+ return false;
+ }
+ // get next word
+ if (!SkipSpaces(pCur,&pCur))return false;
+ if (TokenMatch(pCur,"list",4))
+ {
+ pOut->bIsList = true;
+
+ // seems to be a list.
+ if (EDT_INVALID == (pOut->eFirstType = PLY::Property::ParseDataType(pCur, &pCur)))
+ {
+ // unable to parse list size data type
+ SkipLine(pCur,&pCur);
+ *pCurOut = pCur;
+ return false;
+ }
+ if (!SkipSpaces(pCur,&pCur))return false;
+ if (EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(pCur, &pCur)))
+ {
+ // unable to parse list data type
+ SkipLine(pCur,&pCur);
+ *pCurOut = pCur;
+ return false;
+ }
+ }
+ else
+ {
+ if (EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(pCur, &pCur)))
+ {
+ // unable to parse data type. Skip the property
+ SkipLine(pCur,&pCur);
+ *pCurOut = pCur;
+ return false;
+ }
+ }
+
+ if (!SkipSpaces(pCur,&pCur))return false;
+ const char* szCur = pCur;
+ pOut->Semantic = PLY::Property::ParseSemantic(pCur, &pCur);
+
+ if (PLY::EST_INVALID == pOut->Semantic)
+ {
+ // store the name of the semantic
+ uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
+
+ DefaultLogger::get()->info("Found unknown semantic in PLY file. This is OK");
+ pOut->szName = std::string(szCur,iDiff);
+ }
+
+ SkipSpacesAndLineEnd(pCur,&pCur);
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+PLY::EElementSemantic PLY::Element::ParseSemantic(const char* pCur,
+ const char** pCurOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+ PLY::EElementSemantic eOut = PLY::EEST_INVALID;
+ if (TokenMatch(pCur,"vertex",6))
+ {
+ eOut = PLY::EEST_Vertex;
+ }
+ else if (TokenMatch(pCur,"face",4))
+ {
+ eOut = PLY::EEST_Face;
+ }
+#if 0
+ // TODO: maybe implement this?
+ else if (TokenMatch(pCur,"range_grid",10))
+ {
+ eOut = PLY::EEST_Face;
+ }
+#endif
+ else if (TokenMatch(pCur,"tristrips",9))
+ {
+ eOut = PLY::EEST_TriStrip;
+ }
+ else if (TokenMatch(pCur,"edge",4))
+ {
+ eOut = PLY::EEST_Edge;
+ }
+ else if (TokenMatch(pCur,"material",8))
+ {
+ eOut = PLY::EEST_Material;
+ }
+ *pCurOut = pCur;
+ return eOut;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::Element::ParseElement (const char* pCur,
+ const char** pCurOut,
+ PLY::Element* pOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != pOut);
+
+ // Example format: "element vertex 8"
+ *pCurOut = pCur;
+
+ // skip leading spaces
+ if (!SkipSpaces(&pCur))return false;
+
+ // skip the "element" string at the beginning
+ if (!TokenMatch(pCur,"element",7))
+ {
+ // seems not to be a valid property entry
+ return false;
+ }
+ // get next word
+ if (!SkipSpaces(&pCur))return false;
+
+ // parse the semantic of the element
+ const char* szCur = pCur;
+ pOut->eSemantic = PLY::Element::ParseSemantic(pCur,&pCur);
+ if (PLY::EEST_INVALID == pOut->eSemantic)
+ {
+ // if the exact semantic can't be determined, just store
+ // the original string identifier
+ uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
+ pOut->szName = std::string(szCur,iDiff);
+ }
+
+ if (!SkipSpaces(&pCur))return false;
+
+ //parse the number of occurences of this element
+ pOut->NumOccur = strtol10(pCur,&pCur);
+
+ // go to the next line
+ SkipSpacesAndLineEnd(pCur,&pCur);
+
+ // now parse all properties of the element
+ while (true)
+ {
+ // skip all comments
+ PLY::DOM::SkipComments(pCur,&pCur);
+
+ PLY::Property prop;
+ if (!PLY::Property::ParseProperty(pCur,&pCur,&prop))break;
+ pOut->alProperties.push_back(prop);
+ }
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::SkipComments (const char* pCur,
+ const char** pCurOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+ *pCurOut = pCur;
+
+ // skip spaces
+ if (!SkipSpaces(pCur,&pCur))return false;
+
+ if (TokenMatch(pCur,"comment",7))
+ {
+ SkipLine(pCur,&pCur);
+ SkipComments(pCur,&pCur);
+ *pCurOut = pCur;
+ return true;
+ }
+ *pCurOut = pCur;
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseHeader (const char* pCur,const char** pCurOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+ DefaultLogger::get()->debug("PLY::DOM::ParseHeader() begin");
+
+ // after ply and format line
+ *pCurOut = pCur;
+
+ // parse all elements
+ while (true)
+ {
+ // skip all comments
+ PLY::DOM::SkipComments(pCur,&pCur);
+
+ PLY::Element out;
+ if (PLY::Element::ParseElement(pCur,&pCur,&out))
+ {
+ // add the element to the list of elements
+ alElements.push_back(out);
+ }
+ else if (TokenMatch(pCur,"end_header",10))
+ {
+ // we have reached the end of the header
+ break;
+ }
+ else
+ {
+ // ignore unknown header elements
+ SkipLine(&pCur);
+ }
+ }
+ SkipSpacesAndLineEnd(pCur,&pCur);
+ *pCurOut = pCur;
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseHeader() succeeded");
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseElementInstanceLists (
+ const char* pCur,
+ const char** pCurOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseElementInstanceLists() begin");
+ *pCurOut = pCur;
+
+ alElementData.resize(alElements.size());
+
+ std::vector<PLY::Element>::const_iterator i = alElements.begin();
+ std::vector<PLY::ElementInstanceList>::iterator a = alElementData.begin();
+
+ // parse all element instances
+ for (;i != alElements.end();++i,++a)
+ {
+ (*a).alInstances.resize((*i).NumOccur);
+ PLY::ElementInstanceList::ParseInstanceList(pCur,&pCur,&(*i),&(*a));
+ }
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseElementInstanceLists() succeeded");
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseElementInstanceListsBinary (
+ const char* pCur,
+ const char** pCurOut,
+ bool p_bBE)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseElementInstanceListsBinary() begin");
+ *pCurOut = pCur;
+
+ alElementData.resize(alElements.size());
+
+ std::vector<PLY::Element>::const_iterator i = alElements.begin();
+ std::vector<PLY::ElementInstanceList>::iterator a = alElementData.begin();
+
+ // parse all element instances
+ for (;i != alElements.end();++i,++a)
+ {
+ (*a).alInstances.resize((*i).NumOccur);
+ PLY::ElementInstanceList::ParseInstanceListBinary(pCur,&pCur,&(*i),&(*a),p_bBE);
+ }
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseElementInstanceListsBinary() succeeded");
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseInstanceBinary (const char* pCur,DOM* p_pcOut,bool p_bBE)
+{
+ ai_assert(NULL != pCur && NULL != p_pcOut);
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstanceBinary() begin");
+
+ if (!p_pcOut->ParseHeader(pCur,&pCur))
+ {
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstanceBinary() failure");
+ return false;
+ }
+ if (!p_pcOut->ParseElementInstanceListsBinary(pCur,&pCur,p_bBE))
+ {
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstanceBinary() failure");
+ return false;
+ }
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstanceBinary() succeeded");
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseInstance (const char* pCur,DOM* p_pcOut)
+{
+ ai_assert(NULL != pCur);
+ ai_assert(NULL != p_pcOut);
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstance() begin");
+
+
+ if (!p_pcOut->ParseHeader(pCur,&pCur))
+ {
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstance() failure");
+ return false;
+ }
+ if (!p_pcOut->ParseElementInstanceLists(pCur,&pCur))
+ {
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstance() failure");
+ return false;
+ }
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstance() succeeded");
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::ElementInstanceList::ParseInstanceList (
+ const char* pCur,
+ const char** pCurOut,
+ const PLY::Element* pcElement,
+ PLY::ElementInstanceList* p_pcOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != pcElement && NULL != p_pcOut);
+
+ if (EEST_INVALID == pcElement->eSemantic || pcElement->alProperties.empty())
+ {
+ // if the element has an unknown semantic we can skip all lines
+ // However, there could be comments
+ for (unsigned int i = 0; i < pcElement->NumOccur;++i)
+ {
+ PLY::DOM::SkipComments(pCur,&pCur);
+ SkipLine(pCur,&pCur);
+ }
+ }
+ else
+ {
+ // be sure to have enough storage
+ for (unsigned int i = 0; i < pcElement->NumOccur;++i)
+ {
+ PLY::DOM::SkipComments(pCur,&pCur);
+ PLY::ElementInstance::ParseInstance(pCur, &pCur,pcElement,
+ &p_pcOut->alInstances[i]);
+ }
+ }
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::ElementInstanceList::ParseInstanceListBinary (
+ const char* pCur,
+ const char** pCurOut,
+ const PLY::Element* pcElement,
+ PLY::ElementInstanceList* p_pcOut,
+ bool p_bBE /* = false */)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != pcElement && NULL != p_pcOut);
+
+ // we can add special handling code for unknown element semantics since
+ // we can't skip it as a whole block (we don't know its exact size
+ // due to the fact that lists could be contained in the property list
+ // of the unknown element)
+ for (unsigned int i = 0; i < pcElement->NumOccur;++i)
+ {
+ PLY::ElementInstance::ParseInstanceBinary(pCur, &pCur,pcElement,
+ &p_pcOut->alInstances[i], p_bBE);
+ }
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::ElementInstance::ParseInstance (
+ const char* pCur,
+ const char** pCurOut,
+ const PLY::Element* pcElement,
+ PLY::ElementInstance* p_pcOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != pcElement && NULL != p_pcOut);
+
+ if (!SkipSpaces(pCur, &pCur))return false;
+
+ // allocate enough storage
+ p_pcOut->alProperties.resize(pcElement->alProperties.size());
+
+ std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin();
+ std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
+ for (;i != p_pcOut->alProperties.end();++i,++a)
+ {
+ if (!(PLY::PropertyInstance::ParseInstance(pCur, &pCur,&(*a),&(*i))))
+ {
+ DefaultLogger::get()->warn("Unable to parse property instance. "
+ "Skipping this element instance");
+
+ // skip the rest of the instance
+ SkipLine(pCur, &pCur);
+
+ PLY::PropertyInstance::ValueUnion v = PLY::PropertyInstance::DefaultValue((*a).eType);
+ (*i).avList.push_back(v);
+ }
+ }
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::ElementInstance::ParseInstanceBinary (
+ const char* pCur,
+ const char** pCurOut,
+ const PLY::Element* pcElement,
+ PLY::ElementInstance* p_pcOut,
+ bool p_bBE /* = false */)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != pcElement && NULL != p_pcOut);
+
+ // allocate enough storage
+ p_pcOut->alProperties.resize(pcElement->alProperties.size());
+
+ std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin();
+ std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
+ for (;i != p_pcOut->alProperties.end();++i,++a)
+ {
+ if (!(PLY::PropertyInstance::ParseInstanceBinary(pCur, &pCur,&(*a),&(*i),p_bBE)))
+ {
+ DefaultLogger::get()->warn("Unable to parse binary property instance. "
+ "Skipping this element instance");
+
+ (*i).avList.push_back(PLY::PropertyInstance::DefaultValue((*a).eType));
+ }
+ }
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::PropertyInstance::ParseInstance (const char* pCur,const char** pCurOut,
+ const PLY::Property* prop, PLY::PropertyInstance* p_pcOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != prop && NULL != p_pcOut);
+
+ *pCurOut = pCur;
+
+ // skip spaces at the beginning
+ if (!SkipSpaces(pCur, &pCur))return false;
+
+ if (prop->bIsList)
+ {
+ // parse the number of elements in the list
+ PLY::PropertyInstance::ValueUnion v;
+ PLY::PropertyInstance::ParseValue(pCur, &pCur,prop->eFirstType,&v);
+
+ // convert to unsigned int
+ unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v,prop->eFirstType);
+
+ // parse all list elements
+ p_pcOut->avList.resize(iNum);
+ for (unsigned int i = 0; i < iNum;++i)
+ {
+ if (!SkipSpaces(pCur, &pCur))return false;
+ PLY::PropertyInstance::ParseValue(pCur, &pCur,prop->eType,&p_pcOut->avList[i]);
+ }
+ }
+ else
+ {
+ // parse the property
+ PLY::PropertyInstance::ValueUnion v;
+
+ PLY::PropertyInstance::ParseValue(pCur, &pCur,prop->eType,&v);
+ p_pcOut->avList.push_back(v);
+ }
+ SkipSpacesAndLineEnd(pCur, &pCur);
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::PropertyInstance::ParseInstanceBinary (
+ const char* pCur,
+ const char** pCurOut,
+ const PLY::Property* prop,
+ PLY::PropertyInstance* p_pcOut,
+ bool p_bBE)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != prop && NULL != p_pcOut);
+
+ if (prop->bIsList)
+ {
+ // parse the number of elements in the list
+ PLY::PropertyInstance::ValueUnion v;
+ PLY::PropertyInstance::ParseValueBinary(pCur, &pCur,prop->eFirstType,&v,p_bBE);
+
+ // convert to unsigned int
+ unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v,prop->eFirstType);
+
+ // parse all list elements
+ p_pcOut->avList.resize(iNum);
+ for (unsigned int i = 0; i < iNum;++i){
+ PLY::PropertyInstance::ParseValueBinary(pCur, &pCur,prop->eType,&p_pcOut->avList[i],p_bBE);
+ }
+ }
+ else
+ {
+ // parse the property
+ PLY::PropertyInstance::ValueUnion v;
+ PLY::PropertyInstance::ParseValueBinary(pCur, &pCur,prop->eType,&v,p_bBE);
+ p_pcOut->avList.push_back(v);
+ }
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+PLY::PropertyInstance::ValueUnion PLY::PropertyInstance::DefaultValue(
+ PLY::EDataType eType)
+{
+ PLY::PropertyInstance::ValueUnion out;
+
+ switch (eType)
+ {
+ case EDT_Float:
+ out.fFloat = 0.f;
+ return out;
+
+ case EDT_Double:
+ out.fDouble = 0.;
+ return out;
+
+ default: ;
+ };
+ out.iUInt = 0;
+ return out;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::PropertyInstance::ParseValue(
+ const char* pCur,
+ const char** pCurOut,
+ PLY::EDataType eType,
+ PLY::PropertyInstance::ValueUnion* out)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != out);
+
+ register bool ret = true;
+ *pCurOut = pCur;
+ switch (eType)
+ {
+ case EDT_UInt:
+ case EDT_UShort:
+ case EDT_UChar:
+
+ out->iUInt = (uint32_t)strtol10(pCur, &pCur);
+ break;
+
+ case EDT_Int:
+ case EDT_Short:
+ case EDT_Char:
+
+ out->iInt = (int32_t)strtol10s(pCur, &pCur);
+ break;
+
+ case EDT_Float:
+
+ pCur = fast_atof_move(pCur,out->fFloat);
+ break;
+
+ case EDT_Double:
+
+ float f;
+ pCur = fast_atof_move(pCur,f);
+ out->fDouble = (double)f;
+ break;
+
+ default:
+ ret = false;
+ }
+ *pCurOut = pCur;
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::PropertyInstance::ParseValueBinary(
+ const char* pCur,
+ const char** pCurOut,
+ PLY::EDataType eType,
+ PLY::PropertyInstance::ValueUnion* out,
+ bool p_bBE)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != out);
+
+ register bool ret = true;
+ switch (eType)
+ {
+ case EDT_UInt:
+ out->iUInt = (uint32_t)*((uint32_t*)pCur);
+ pCur += 4;
+
+ // Swap endianess
+ if (p_bBE)ByteSwap::Swap((int32_t*)&out->iUInt);
+ break;
+
+ case EDT_UShort:
+ {
+ int16_t i = *((uint16_t*)pCur);
+
+ // Swap endianess
+ if (p_bBE)ByteSwap::Swap(&i);
+ out->iUInt = (uint32_t)i;
+ pCur += 2;
+ break;
+ }
+
+ case EDT_UChar:
+ {
+ out->iUInt = (uint32_t)(*((uint8_t*)pCur));
+ pCur ++;
+ break;
+ }
+
+ case EDT_Int:
+ out->iInt = *((int32_t*)pCur);
+ pCur += 4;
+
+ // Swap endianess
+ if (p_bBE)ByteSwap::Swap(&out->iInt);
+ break;
+
+ case EDT_Short:
+ {
+ int16_t i = *((int16_t*)pCur);
+
+ // Swap endianess
+ if (p_bBE)ByteSwap::Swap(&i);
+ out->iInt = (int32_t)i;
+ pCur += 2;
+ break;
+ }
+
+ case EDT_Char:
+ out->iInt = (int32_t)*((int8_t*)pCur);
+ pCur ++;
+ break;
+
+ case EDT_Float:
+ {
+ out->fFloat = *((float*)pCur);
+
+ // Swap endianess
+ if (p_bBE)ByteSwap::Swap((int32_t*)&out->fFloat);
+ pCur += 4;
+ break;
+ }
+ case EDT_Double:
+ {
+ out->fDouble = *((double*)pCur);
+
+ // Swap endianess
+ if (p_bBE)ByteSwap::Swap((int64_t*)&out->fDouble);
+ pCur += 8;
+ break;
+ }
+ default:
+ ret = false;
+ }
+ *pCurOut = pCur;
+ return ret;
+}
+
+#endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER
diff --git a/src/3rdparty/assimp/code/PlyParser.h b/src/3rdparty/assimp/code/PlyParser.h
new file mode 100644
index 000000000..9423c52fb
--- /dev/null
+++ b/src/3rdparty/assimp/code/PlyParser.h
@@ -0,0 +1,501 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file Defines the helper data structures for importing PLY files */
+#ifndef AI_PLYFILEHELPER_H_INC
+#define AI_PLYFILEHELPER_H_INC
+
+
+#include "ParsingUtils.h"
+
+
+namespace Assimp
+{
+
+// http://local.wasp.uwa.edu.au/~pbourke/dataformats/ply/
+// http://w3.impa.br/~lvelho/outgoing/sossai/old/ViHAP_D4.4.2_PLY_format_v1.1.pdf
+// http://www.okino.com/conv/exp_ply.htm
+namespace PLY
+{
+
+
+// ---------------------------------------------------------------------------------
+/*
+name type number of bytes
+---------------------------------------
+char character 1
+uchar unsigned character 1
+short short integer 2
+ushort unsigned short integer 2
+int integer 4
+uint unsigned integer 4
+float single-precision float 4
+double double-precision float 8
+
+int8
+int16
+uint8 ... forms are also used
+*/
+enum EDataType
+{
+ EDT_Char = 0x0u,
+ EDT_UChar,
+ EDT_Short,
+ EDT_UShort,
+ EDT_Int,
+ EDT_UInt,
+ EDT_Float,
+ EDT_Double,
+
+ // Marks invalid entries
+ EDT_INVALID
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Specifies semantics for PLY element properties
+ *
+ * Semantics define the usage of a property, e.g. x coordinate
+*/
+enum ESemantic
+{
+ //! vertex position x coordinate
+ EST_XCoord = 0x0u,
+ //! vertex position x coordinate
+ EST_YCoord,
+ //! vertex position x coordinate
+ EST_ZCoord,
+
+ //! vertex normal x coordinate
+ EST_XNormal,
+ //! vertex normal y coordinate
+ EST_YNormal,
+ //! vertex normal z coordinate
+ EST_ZNormal,
+
+ //! u texture coordinate
+ EST_UTextureCoord,
+ //! v texture coordinate
+ EST_VTextureCoord,
+
+ //! vertex colors, red channel
+ EST_Red,
+ //! vertex colors, green channel
+ EST_Green,
+ //! vertex colors, blue channel
+ EST_Blue,
+ //! vertex colors, alpha channel
+ EST_Alpha,
+
+ //! vertex index list
+ EST_VertexIndex,
+
+ //! texture index
+ EST_TextureIndex,
+
+ //! texture coordinates (stored as element of a face)
+ EST_TextureCoordinates,
+
+ //! material index
+ EST_MaterialIndex,
+
+ //! ambient color, red channel
+ EST_AmbientRed,
+ //! ambient color, green channel
+ EST_AmbientGreen,
+ //! ambient color, blue channel
+ EST_AmbientBlue,
+ //! ambient color, alpha channel
+ EST_AmbientAlpha,
+
+ //! diffuse color, red channel
+ EST_DiffuseRed,
+ //! diffuse color, green channel
+ EST_DiffuseGreen,
+ //! diffuse color, blue channel
+ EST_DiffuseBlue,
+ //! diffuse color, alpha channel
+ EST_DiffuseAlpha,
+
+ //! specular color, red channel
+ EST_SpecularRed,
+ //! specular color, green channel
+ EST_SpecularGreen,
+ //! specular color, blue channel
+ EST_SpecularBlue,
+ //! specular color, alpha channel
+ EST_SpecularAlpha,
+
+ //! specular power for phong shading
+ EST_PhongPower,
+
+ //! opacity between 0 and 1
+ EST_Opacity,
+
+ //! Marks invalid entries
+ EST_INVALID
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Specifies semantics for PLY elements
+ *
+ * Semantics define the usage of an element, e.g. vertex or material
+*/
+enum EElementSemantic
+{
+ //! The element is a vertex
+ EEST_Vertex = 0x0u,
+
+ //! The element is a face description (index table)
+ EEST_Face,
+
+ //! The element is a tristrip description (index table)
+ EEST_TriStrip,
+
+ //! The element is an edge description (ignored)
+ EEST_Edge,
+
+ //! The element is a material description
+ EEST_Material,
+
+ //! Marks invalid entries
+ EEST_INVALID
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Helper class for a property in a PLY file.
+ *
+ * This can e.g. be a part of the vertex declaration
+ */
+class Property
+{
+public:
+
+ //! Default constructor
+ Property()
+ : eType (EDT_Int), Semantic(EST_INVALID), bIsList(false), eFirstType(EDT_UChar)
+ {}
+
+ //! Data type of the property
+ EDataType eType;
+
+ //! Semantical meaning of the property
+ ESemantic Semantic;
+
+ //! Of the semantic of the property could not be parsed:
+ //! Contains the semantic specified in the file
+ std::string szName;
+
+ //! Specifies whether the data type is a list where
+ //! the first element specifies the size of the list
+ bool bIsList;
+ EDataType eFirstType;
+
+ // -------------------------------------------------------------------
+ //! Parse a property from a string. The end of the
+ //! string is either '\n', '\r' or '\0'. Return valie is false
+ //! if the input string is NOT a valid property (E.g. does
+ //! not start with the "property" keyword)
+ static bool ParseProperty (const char* pCur, const char** pCurOut,
+ Property* pOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a data type from a string
+ static EDataType ParseDataType(const char* pCur,const char** pCurOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a semantic from a string
+ static ESemantic ParseSemantic(const char* pCur,const char** pCurOut);
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Helper class for an element in a PLY file.
+ *
+ * This can e.g. be the vertex declaration. Elements contain a
+ * well-defined number of properties.
+ */
+class Element
+{
+public:
+
+ //! Default constructor
+ Element()
+ : eSemantic (EEST_INVALID)
+ , NumOccur(0)
+ {}
+
+ //! List of properties assigned to the element
+ //! std::vector to support operator[]
+ std::vector<Property> alProperties;
+
+ //! Semantic of the element
+ EElementSemantic eSemantic;
+
+ //! Of the semantic of the element could not be parsed:
+ //! Contains the semantic specified in the file
+ std::string szName;
+
+ //! How many times will the element occur?
+ unsigned int NumOccur;
+
+
+ // -------------------------------------------------------------------
+ //! Parse an element from a string.
+ //! The function will parse all properties contained in the
+ //! element, too.
+ static bool ParseElement (const char* pCur, const char** pCurOut,
+ Element* pOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a semantic from a string
+ static EElementSemantic ParseSemantic(const char* pCur,
+ const char** pCurOut);
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Instance of a property in a PLY file
+ */
+class PropertyInstance
+{
+public:
+
+ //! Default constructor
+ PropertyInstance ()
+ {}
+
+ union ValueUnion
+ {
+
+ //! uInt32 representation of the property. All
+ // uint types are automatically converted to uint32
+ uint32_t iUInt;
+
+ //! Int32 representation of the property. All
+ // int types are automatically converted to int32
+ int32_t iInt;
+
+ //! Float32 representation of the property
+ float fFloat;
+
+ //! Float64 representation of the property
+ double fDouble;
+
+ };
+
+ // -------------------------------------------------------------------
+ //! List of all values parsed. Contains only one value
+ // for non-list properties
+ std::vector<ValueUnion> avList;
+
+ // -------------------------------------------------------------------
+ //! Parse a property instance
+ static bool ParseInstance (const char* pCur,const char** pCurOut,
+ const Property* prop, PropertyInstance* p_pcOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a property instance in binary format
+ static bool ParseInstanceBinary (const char* pCur,const char** pCurOut,
+ const Property* prop, PropertyInstance* p_pcOut,bool p_bBE);
+
+ // -------------------------------------------------------------------
+ //! Get the default value for a given data type
+ static ValueUnion DefaultValue(EDataType eType);
+
+ // -------------------------------------------------------------------
+ //! Parse a value
+ static bool ParseValue(const char* pCur,const char** pCurOut,
+ EDataType eType,ValueUnion* out);
+
+ // -------------------------------------------------------------------
+ //! Parse a binary value
+ static bool ParseValueBinary(const char* pCur,const char** pCurOut,
+ EDataType eType,ValueUnion* out,bool p_bBE);
+
+ // -------------------------------------------------------------------
+ //! Convert a property value to a given type TYPE
+ template <typename TYPE>
+ static TYPE ConvertTo(ValueUnion v, EDataType eType);
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Class for an element instance in a PLY file
+ */
+class ElementInstance
+{
+public:
+
+ //! Default constructor
+ ElementInstance ()
+ {}
+
+ //! List of all parsed properties
+ std::vector< PropertyInstance > alProperties;
+
+ // -------------------------------------------------------------------
+ //! Parse an element instance
+ static bool ParseInstance (const char* pCur,const char** pCurOut,
+ const Element* pcElement, ElementInstance* p_pcOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a binary element instance
+ static bool ParseInstanceBinary (const char* pCur,const char** pCurOut,
+ const Element* pcElement, ElementInstance* p_pcOut,bool p_bBE);
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Class for an element instance list in a PLY file
+ */
+class ElementInstanceList
+{
+public:
+
+ //! Default constructor
+ ElementInstanceList ()
+ {}
+
+ //! List of all element instances
+ std::vector< ElementInstance > alInstances;
+
+ // -------------------------------------------------------------------
+ //! Parse an element instance list
+ static bool ParseInstanceList (const char* pCur,const char** pCurOut,
+ const Element* pcElement, ElementInstanceList* p_pcOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a binary element instance list
+ static bool ParseInstanceListBinary (const char* pCur,const char** pCurOut,
+ const Element* pcElement, ElementInstanceList* p_pcOut,bool p_bBE);
+};
+// ---------------------------------------------------------------------------------
+/** \brief Class to represent the document object model of an ASCII or binary
+ * (both little and big-endian) PLY file
+ */
+class DOM
+{
+public:
+
+ //! Default constructor
+ DOM()
+ {}
+
+
+ //! Contains all elements of the file format
+ std::vector<Element> alElements;
+ //! Contains the real data of each element's instance list
+ std::vector<ElementInstanceList> alElementData;
+
+ //! Parse the DOM for a PLY file. The input string is assumed
+ //! to be terminated with zero
+ static bool ParseInstance (const char* pCur,DOM* p_pcOut);
+ static bool ParseInstanceBinary (const char* pCur,
+ DOM* p_pcOut,bool p_bBE);
+
+ //! Skip all comment lines after this
+ static bool SkipComments (const char* pCur,const char** pCurOut);
+
+private:
+
+ // -------------------------------------------------------------------
+ //! Handle the file header and read all element descriptions
+ bool ParseHeader (const char* pCur,const char** pCurOut);
+
+ // -------------------------------------------------------------------
+ //! Read in all element instance lists
+ bool ParseElementInstanceLists (const char* pCur,const char** pCurOut);
+
+ // -------------------------------------------------------------------
+ //! Read in all element instance lists for a binary file format
+ bool ParseElementInstanceListsBinary (const char* pCur,
+ const char** pCurOut,bool p_bBE);
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Helper class to represent a loaded PLY face
+ */
+class Face
+{
+public:
+
+ Face()
+ : iMaterialIndex(0xFFFFFFFF)
+ {
+ // set all indices to zero by default
+ mIndices.resize(3,0);
+ }
+
+public:
+
+ //! List of vertex indices
+ std::vector<unsigned int> mIndices;
+
+ //! Material index
+ unsigned int iMaterialIndex;
+};
+
+// ---------------------------------------------------------------------------------
+template <typename TYPE>
+inline TYPE PLY::PropertyInstance::ConvertTo(
+ PLY::PropertyInstance::ValueUnion v, PLY::EDataType eType)
+{
+ switch (eType)
+ {
+ case EDT_Float:
+ return (TYPE)v.fFloat;
+ case EDT_Double:
+ return (TYPE)v.fDouble;
+
+ case EDT_UInt:
+ case EDT_UShort:
+ case EDT_UChar:
+ return (TYPE)v.iUInt;
+
+ case EDT_Int:
+ case EDT_Short:
+ case EDT_Char:
+ return (TYPE)v.iInt;
+ default: ;
+ };
+ return (TYPE)0;
+}
+
+} // Namespace PLY
+} // Namespace AssImp
+
+#endif // !! include guard
diff --git a/src/3rdparty/assimp/code/PretransformVertices.cpp b/src/3rdparty/assimp/code/PretransformVertices.cpp
new file mode 100644
index 000000000..4b1c8cab6
--- /dev/null
+++ b/src/3rdparty/assimp/code/PretransformVertices.cpp
@@ -0,0 +1,715 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file PretransformVertices.cpp
+ * @brief Implementation of the "PretransformVertices" post processing step
+*/
+
+#include "AssimpPCH.h"
+#include "PretransformVertices.h"
+#include "ProcessHelper.h"
+#include "SceneCombiner.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+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)
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 and AI_CONFIG_PP_PTV_NORMALIZE
+ configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY,0));
+ configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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;
+
+ 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];
+ }
+ }
+ 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];
+ pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mBitangents[n];
+ }
+ }
+ }
+ 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];
+ }
+ }
+ if (mesh->HasTangentsAndBitangents()) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ mesh->mTangents[i] = m * mesh->mTangents[i];
+ mesh->mBitangents[i] = m * mesh->mBitangents[i];
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 0xffffffff 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 = 0xffffffff;
+ }
+ 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
+ DefaultLogger::get()->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);
+ }
+ }
+ }
+
+ // 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)
+{
+ DefaultLogger::get()->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);
+
+ // 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 += 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]);
+ }
+ }
+ }
+
+ // 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;
+ }
+
+ // 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 nodes");
+ }
+ else
+ {
+ // 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;
+ }
+
+ 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
+ delete pScene->mRootNode;
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("<dummy_root>");
+
+ 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 = *nodes = new aiNode();
+ pcNode->mParent = pScene->mRootNode;
+ pcNode->mName.length = ::sprintf(pcNode->mName.data,"mesh_%i",i);
+
+ // 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 = *nodes = new aiNode();
+ pcNode->mParent = pScene->mRootNode;
+ pcNode->mName.length = ::sprintf(pcNode->mName.data,"light_%i",i);
+ pScene->mLights[i]->mName = pcNode->mName;
+ }
+ // generate camera nodes
+ for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes)
+ {
+ aiNode* pcNode = *nodes = new aiNode();
+ pcNode->mParent = pScene->mRootNode;
+ pcNode->mName.length = ::sprintf(pcNode->mName.data,"cam_%i",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 float div = std::max(d.x,std::max(d.y,d.z))*0.5f;
+
+ d = min+d*0.5f;
+ 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())
+ {
+ char buffer[4096];
+
+ DefaultLogger::get()->debug("PretransformVerticesProcess finished");
+
+ sprintf(buffer,"Removed %i nodes and %i animation channels (%i output nodes)",
+ iOldNodes,iOldAnimationChannels,CountNodes(pScene->mRootNode));
+ DefaultLogger::get()->info(buffer);
+
+ sprintf(buffer,"Kept %i lights and %i cameras",
+ pScene->mNumLights,pScene->mNumCameras);
+ DefaultLogger::get()->info(buffer);
+
+ sprintf(buffer,"Moved %i meshes to WCS (number of output meshes: %i)",
+ iOldMeshes,pScene->mNumMeshes);
+ DefaultLogger::get()->info(buffer);
+ }
+}
+
diff --git a/src/3rdparty/assimp/code/PretransformVertices.h b/src/3rdparty/assimp/code/PretransformVertices.h
new file mode 100644
index 000000000..df2d84509
--- /dev/null
+++ b/src/3rdparty/assimp/code/PretransformVertices.h
@@ -0,0 +1,166 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiMesh.h"
+
+class PretransformVerticesTest;
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** The PretransformVertices pretransforms all vertices in the nodegraph
+ * and removes the whole graph. The output is a list of meshes, one for
+ * each material.
+*/
+class ASSIMP_API PretransformVertices : public BaseProcess
+{
+ friend class Importer;
+ friend class ::PretransformVerticesTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ PretransformVertices ();
+
+ /** Destructor, private as well */
+ ~PretransformVertices ();
+
+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 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, configNormalize;
+
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_GENFACENORMALPROCESS_H_INC
diff --git a/src/3rdparty/assimp/code/ProcessHelper.h b/src/3rdparty/assimp/code/ProcessHelper.h
new file mode 100644
index 000000000..5f39eedfb
--- /dev/null
+++ b/src/3rdparty/assimp/code/ProcessHelper.h
@@ -0,0 +1,559 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef AI_PROCESS_HELPER_H_INCLUDED
+#define AI_PROCESS_HELPER_H_INCLUDED
+
+#include "../include/aiPostProcess.h"
+
+#include "SpatialSort.h"
+#include "BaseProcess.h"
+#include "ParsingUtils.h"
+
+// -------------------------------------------------------------------------------
+// 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
+ inline ::aiVector3D min (const ::aiVector3D& a, const ::aiVector3D& b) {
+ return ::aiVector3D (min(a.x,b.x),min(a.y,b.y),min(a.z,b.z));
+ }
+
+ // std::max for aiVector3D
+ inline ::aiVector3D max (const ::aiVector3D& a, const ::aiVector3D& b) {
+ return ::aiVector3D (max(a.x,b.x),max(a.y,b.y),max(a.z,b.z));
+ }
+
+ // std::min for aiColor4D
+ inline ::aiColor4D min (const ::aiColor4D& a, const ::aiColor4D& b) {
+ return ::aiColor4D (min(a.r,b.r),min(a.g,b.g),min(a.b,b.b),min(a.a,b.a));
+ }
+
+ // std::max for aiColor4D
+ inline ::aiColor4D max (const ::aiColor4D& a, const ::aiColor4D& b) {
+ return ::aiColor4D (max(a.r,b.r),max(a.g,b.g),max(a.b,b.b),max(a.a,b.a));
+ }
+
+ // std::min for aiQuaternion
+ inline ::aiQuaternion min (const ::aiQuaternion& a, const ::aiQuaternion& b) {
+ return ::aiQuaternion (min(a.w,b.w),min(a.x,b.x),min(a.y,b.y),min(a.z,b.z));
+ }
+
+ // std::max for aiQuaternion
+ inline ::aiQuaternion max (const ::aiQuaternion& a, const ::aiQuaternion& b) {
+ return ::aiQuaternion (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 = -10e10f;
+ min = 10e10f;
+}};
+template <> struct MinMaxChooser<double> {
+ void operator ()(double& min,double& max) {
+ max = -10e10;
+ min = 10e10;
+}};
+template <> struct MinMaxChooser<unsigned int> {
+ void operator ()(unsigned int& min,unsigned int& max) {
+ max = 0;
+ min = (1u<<(sizeof(unsigned int)*8-1));
+}};
+
+template <> struct MinMaxChooser<aiVector3D> {
+ void operator ()(aiVector3D& min,aiVector3D& max) {
+ max = aiVector3D(-10e10f,-10e10f,-10e10f);
+ min = aiVector3D( 10e10f, 10e10f, 10e10f);
+}};
+template <> struct MinMaxChooser<aiColor4D> {
+ void operator ()(aiColor4D& min,aiColor4D& max) {
+ max = aiColor4D(-10e10f,-10e10f,-10e10f,-10e10f);
+ min = aiColor4D( 10e10f, 10e10f, 10e10f, 10e10f);
+}};
+
+template <> struct MinMaxChooser<aiQuaternion> {
+ void operator ()(aiQuaternion& min,aiQuaternion& max) {
+ max = aiQuaternion(-10e10f,-10e10f,-10e10f,-10e10f);
+ min = aiQuaternion( 10e10f, 10e10f, 10e10f, 10e10f);
+}};
+
+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 Numebr 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);
+ }
+}
+
+// -------------------------------------------------------------------------------
+/** @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
+ */
+inline 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') {
+ DefaultLogger::get()->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));
+ }
+ }
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Compute the newell normal of a polygon regardless of its shape
+ *
+ * @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>
+inline void NewellNormal (aiVector3D& out, int num, float* x, float* y, float* 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];
+
+ float sum_xy = 0.0, sum_yz = 0.0, sum_zx = 0.0;
+
+ float *xptr = x +ofs_x, *xlow = x, *xhigh = x + ofs_x*2;
+ float *yptr = y +ofs_y, *ylow = y, *yhigh = y + ofs_y*2;
+ float *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 = aiVector3D(sum_yz,sum_zx,sum_xy);
+}
+
+#if 0
+// -------------------------------------------------------------------------------
+/** @brief Compute newell normal of a polgon regardless of its shape
+ *
+ * @param out Receives the output normal
+ * @param data Input vertices
+ * @param idx Index buffer
+ * @param num Number of indices
+ */
+inline void NewellNormal (aiVector3D& out, const aiVector3D* data, unsigned int* idx, unsigned int num )
+{
+ // TODO: intended to be used in GenNormals.
+}
+#endif
+
+// -------------------------------------------------------------------------------
+/** Little helper function to calculate the quadratic difference
+ * of two colours.
+ * @param pColor1 First color
+ * @param pColor2 second color
+ * @return Quadratic color difference
+ */
+inline float 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 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
+ */
+inline void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max,
+ const aiMatrix4x4& m)
+{
+ min = aiVector3D (10e10f, 10e10f, 10e10f);
+ max = aiVector3D (-10e10f,-10e10f,-10e10f);
+ 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);
+ }
+}
+
+// -------------------------------------------------------------------------------
+/** @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
+ */
+inline void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max)
+{
+ ArrayBounds(mesh->mVertices,mesh->mNumVertices, min,max);
+ out = min + (max-min)*0.5f;
+}
+
+// -------------------------------------------------------------------------------
+// Helper function to determine the 'real' center of a mesh after applying a given transform
+inline void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,
+ aiVector3D& max, const aiMatrix4x4& m)
+{
+ FindAABBTransformed(mesh,min,max,m);
+ out = min + (max-min)*0.5f;
+}
+
+// -------------------------------------------------------------------------------
+// Helper function to determine the 'real' center of a mesh
+inline void FindMeshCenter (aiMesh* mesh, aiVector3D& out)
+{
+ aiVector3D min,max;
+ FindMeshCenter(mesh,out,min,max);
+}
+
+// -------------------------------------------------------------------------------
+// Helper function to determine the 'real' center of a mesh after applying a given transform
+inline void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,
+ const aiMatrix4x4& m)
+{
+ aiVector3D min,max;
+ FindMeshCenterTransformed(mesh,out,min,max,m);
+}
+
+// -------------------------------------------------------------------------------
+// Compute a good epsilon value for position comparisons on a mesh
+inline float ComputePositionEpsilon(const aiMesh* pMesh)
+{
+ const float epsilon = 1e-4f;
+
+ // 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;
+}
+
+// -------------------------------------------------------------------------------
+// Compute a good epsilon value for position comparisons on a array of meshes
+inline float ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num)
+{
+ const float epsilon = 1e-4f;
+
+ // 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;
+}
+
+// -------------------------------------------------------------------------------
+// Compute an unique value for the vertex format of a mesh
+inline unsigned int GetMeshVFormatUnique(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;
+
+ // 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;
+}
+
+typedef std::pair <unsigned int,float> PerVertexWeight;
+typedef std::vector <PerVertexWeight> VertexWeightTable;
+
+// -------------------------------------------------------------------------------
+// Compute a per-vertex bone weight table
+// please .... delete result with operator delete[] ...
+inline VertexWeightTable* ComputeVertexBoneWeightTable(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;
+}
+
+// -------------------------------------------------------------------------------
+// Get a string for a given aiTextureType
+inline 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:
+ return "HUGE ERROR. Expect BSOD (linux guys: kernel panic ...).";
+ }
+}
+
+// -------------------------------------------------------------------------------
+// Get a string for a given aiTextureMapping
+inline 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:
+ return "HUGE ERROR. Expect BSOD (linux guys: kernel panic ...).";
+ }
+}
+
+// -------------------------------------------------------------------------------
+// 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, float> _Type;
+ DefaultLogger::get()->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*)
+ {
+ shared->RemoveProperty(AI_SPP_SPATIAL_SORT);
+ }
+};
+
+} // ! namespace Assimp
+#endif // !! AI_PROCESS_HELPER_H_INCLUDED
diff --git a/src/3rdparty/assimp/code/Profiler.h b/src/3rdparty/assimp/code/Profiler.h
new file mode 100644
index 000000000..74fed429a
--- /dev/null
+++ b/src/3rdparty/assimp/code/Profiler.h
@@ -0,0 +1,97 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Profiler.h
+ * @brief Utility to measure the respective runtime of each import step
+ */
+#ifndef INCLUDED_PROFILER_H
+#define INCLUDED_PROFILER_H
+
+#include "boost/timer.hpp"
+
+#include "../include/DefaultLogger.h"
+#include "TinyFormatter.h"
+
+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() {}
+
+public:
+
+ /** Start a named timer */
+ void BeginRegion(const std::string& region) {
+ regions[region] = boost::timer();
+ DefaultLogger::get()->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;
+ }
+
+ DefaultLogger::get()->debug((format("END `"),region,"`, dt= ",(*it).second.elapsed()," s"));
+ }
+
+private:
+
+ typedef std::map<std::string,boost::timer> RegionMap;
+ RegionMap regions;
+};
+
+ }
+}
+
+#endif
diff --git a/src/3rdparty/assimp/code/Q3BSPFileData.h b/src/3rdparty/assimp/code/Q3BSPFileData.h
new file mode 100644
index 000000000..eb3ae8289
--- /dev/null
+++ b/src/3rdparty/assimp/code/Q3BSPFileData.h
@@ -0,0 +1,228 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#ifndef ASSIMP_Q3BSPFILEDATA_H_INC
+#define ASSIMP_Q3BSPFILEDATA_H_INC
+
+#include <vector>
+
+namespace Assimp
+{
+namespace Q3BSP
+{
+
+static const unsigned int CE_BSP_LIGHTMAPWIDTH = 128;
+static const unsigned int CE_BSP_LIGHTMAPHEIGHT = 128;
+
+static const unsigned int CE_BSP_LIGHTMAPSIZE = 128*128*3; ///< = 128( width ) * 128 ( height ) * 3 ( channels / RGB ).
+static const int VERION_Q3LEVEL = 46; ///< Supported version.
+
+/// Geometric type enumeration
+enum Q3BSPGeoType
+{
+ Polygon = 1,
+ Patch,
+ TriangleMesh,
+ Billboard
+};
+
+/// Integer vector.
+struct ceVec3i
+{
+ int x, y, z;
+ ceVec3i(): x( 0 ), y( 0 ), z( 0 ) { /* empty */ }
+ ceVec3i( int iX, int iY=0, int iZ=0) : x( iX ), y( iY ), z( iZ ) { /* empty */ }
+};
+
+/// Fileheader
+struct sQ3BSPHeader
+{
+ char strID[ 4 ]; //!< Should be "IBSP"
+ int iVersion; //!< 46 for standard levels
+};
+
+/// Descripes an entry.
+struct sQ3BSPLump
+{
+ int iOffset; ///< Offset from startpointer of file
+ int iSize; ///< Size fo part
+};
+
+struct vec2f
+{
+ float x,y;
+};
+
+struct vec3f
+{
+ float x, y, z;
+};
+
+/// Vertex of a Q3 level
+struct sQ3BSPVertex
+{
+ vec3f vPosition; ///< Position of vertex
+ vec2f vTexCoord; ///< (u,v) Texturecoordinate of detailtexture
+ vec2f vLightmap; ///< (u,v) Texturecoordinate of lightmap
+ vec3f vNormal; ///< vertex normale
+ unsigned char bColor[ 4 ]; ///< Color in RGBA
+};
+
+/// A face in bsp format info
+struct sQ3BSPFace
+{
+ int iTextureID; ///< Index in texture array
+ int iEffect; ///< Index in effectarray (-1 = no effect)
+ int iType; ///< 1=Polygon, 2=Patch, 3=Mesh, 4=Billboard
+ int iVertexIndex; ///< Start index of polygon
+ int iNumOfVerts; ///< Number of vertices
+ int iFaceVertexIndex; ///< Index of first mesh vertex
+ int iNumOfFaceVerts; ///< Anzahl der Meshvertices
+ int iLightmapID; ///< Index to the lightmap array
+ int iLMapCorner[ 2 ]; ///< Die Ecke der Lightmap in der Textur
+ int iLMapSize[ 2 ]; ///< Size of the lightmap stored on the texture
+ vec3f vLMapPos; ///< 3D-Ursprung der Lightmap
+ vec3f vLMapVecs[ 2 ]; ///< 3D-s-t-Vektoren
+ vec3f vNormal; ///< Polygonnormale
+ int patchWidth, patchHeight; ///< bezier patch
+};
+
+/// A quake3 texture name.
+struct sQ3BSPTexture
+{
+ char strName[ 64 ]; ///< Name of the texture without extention
+ int iFlags; ///< Not used
+ int iContents; ///< Not used
+};
+
+/// A lightmap of the level, size 128 x 128, RGB components.
+struct sQ3BSPLightmap
+{
+ unsigned char bLMapData[ CE_BSP_LIGHTMAPSIZE ];
+ sQ3BSPLightmap()
+ {
+ memset(bLMapData, 0, CE_BSP_LIGHTMAPSIZE );
+ }
+};
+
+struct SubPatch
+{
+ std::vector<size_t> indices;
+ int lightmapID;
+};
+
+enum eLumps
+{
+ kEntities = 0,
+ kTextures,
+ kPlanes,
+ kNodes,
+ kLeafs,
+ kLeafFaces,
+ kLeafBrushes,
+ kModels,
+ kBrushes,
+ kBrushSides,
+ kVertices,
+ kMeshVerts,
+ kShaders,
+ kFaces,
+ kLightmaps,
+ kLightVolumes,
+ kVisData,
+ kMaxLumps
+};
+
+struct Q3BSPModel
+{
+ std::vector<unsigned char> m_Data;
+ std::vector<sQ3BSPLump*> m_Lumps;
+ std::vector<sQ3BSPVertex*> m_Vertices;
+ std::vector<sQ3BSPFace*> m_Faces;
+ std::vector<int> m_Indices;
+ std::vector<sQ3BSPTexture*> m_Textures;
+ std::vector<sQ3BSPLightmap*> m_Lightmaps;
+ std::vector<char> m_EntityData;
+ std::string m_ModelName;
+
+ Q3BSPModel() :
+ m_Data(),
+ m_Lumps(),
+ m_Vertices(),
+ m_Faces(),
+ m_Indices(),
+ m_Textures(),
+ m_Lightmaps(),
+ m_EntityData(),
+ m_ModelName( "" )
+ {
+ // empty
+ }
+
+ ~Q3BSPModel()
+ {
+ for ( unsigned int i=0; i<m_Lumps.size(); i++ )
+ if ( NULL != m_Lumps[i] )
+ delete m_Lumps[i];
+
+ for ( unsigned int i=0; i<m_Vertices.size(); i++ )
+ if ( NULL != m_Vertices[ i ] )
+ delete m_Vertices[ i ];
+ for ( unsigned int i=0; i<m_Faces.size(); i++ )
+ if ( NULL != m_Faces[ i ] )
+ delete m_Faces[ i ];
+ for ( unsigned int i=0; i<m_Textures.size(); i++ )
+ if ( NULL != m_Textures[ i ] )
+ delete m_Textures[ i ];
+ for ( unsigned int i=0; i<m_Lightmaps.size(); i++ )
+ if ( NULL != m_Lightmaps[ i ] )
+ delete m_Lightmaps[ i ];
+
+ m_Lumps.clear();
+ m_Vertices.clear();
+ m_Faces.clear();
+ m_Textures.clear();
+ m_Lightmaps.clear();
+ }
+};
+
+} // Namespace Q3BSP
+} // Namespace Assimp
+
+#endif // ASSIMP_Q3BSPFILEDATA_H_INC
diff --git a/src/3rdparty/assimp/code/Q3BSPFileImporter.cpp b/src/3rdparty/assimp/code/Q3BSPFileImporter.cpp
new file mode 100644
index 000000000..ad5a86406
--- /dev/null
+++ b/src/3rdparty/assimp/code/Q3BSPFileImporter.cpp
@@ -0,0 +1,738 @@
+/*
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---------------------------------------------------------------------------------------------------
+*/
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
+
+//#include <windows.h>
+#include "DefaultIOSystem.h"
+#include "Q3BSPFileImporter.h"
+#include "Q3BSPZipArchive.h"
+#include "Q3BSPFileParser.h"
+#include "Q3BSPFileData.h"
+
+#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+# include <zlib.h>
+#else
+# include "../contrib/zlib/zlib.h"
+#endif
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include <vector>
+
+#include <QtGlobal>
+
+#if defined(__QNXNTO__)
+#include <stdlib.h>
+#endif
+
+namespace Assimp
+{
+
+using namespace Q3BSP;
+
+static const std::string Q3BSPExtention = "pk3";
+
+// ------------------------------------------------------------------------------------------------
+// Local fnction to create a material keyname.
+static void createKey( int id1, int id2, std::string &rKey )
+{
+ std::stringstream str;
+ str << id1 << "." << id2;
+ rKey = str.str();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Local function to extract the texture ids from a material keyname.
+static void extractIds( const std::string &rKey, int &rId1, int &rId2 )
+{
+ rId1 = -1;
+ rId2 = -1;
+ if ( rKey.empty() )
+ return;
+
+ std::string::size_type pos = rKey.find( "." );
+ if ( std::string::npos == pos )
+ return;
+
+ std::string tmp1 = rKey.substr( 0, pos );
+ std::string tmp2 = rKey.substr( pos + 1, rKey.size() - pos - 1 );
+ rId1 = atoi( tmp1.c_str() );
+ rId2 = atoi( tmp2.c_str() );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Local helper fuction to normalize filenames.
+static void normalizePathName( const std::string &rPath, std::string &rNormalizedPath )
+{
+ rNormalizedPath = "";
+ if ( rPath.empty() )
+ return;
+
+#ifdef _WIN32
+ std::string sep = "\\";
+#else
+ std::string sep = "/";
+#endif
+
+ static const unsigned int numDelimiters = 2;
+ const char delimiters[ numDelimiters ] = { '/', '\\' };
+ rNormalizedPath = rPath;
+ for ( unsigned int i=0; i<numDelimiters; i++ )
+ {
+ for ( size_t j=0; j<rNormalizedPath.size(); j++ )
+ {
+ if ( rNormalizedPath[j] == delimiters[ i ] )
+ {
+ rNormalizedPath[ j ] = sep[ 0 ];
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor.
+Q3BSPFileImporter::Q3BSPFileImporter() :
+ m_pCurrentMesh( NULL ),
+ m_pCurrentFace( NULL ),
+ m_MaterialLookupMap(),
+ mTextures()
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor.
+Q3BSPFileImporter::~Q3BSPFileImporter()
+{
+ // For lint
+ m_pCurrentMesh = NULL;
+ m_pCurrentFace = NULL;
+
+ // Clear face-to-material map
+ for ( FaceMap::iterator it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end();
+ ++it )
+ {
+ const std::string matName = (*it).first;
+ if ( matName.empty() )
+ {
+ continue;
+ }
+
+ std::vector<Q3BSP::sQ3BSPFace*> *pCurFaceArray = (*it).second;
+ if ( NULL != pCurFaceArray )
+ {
+ delete pCurFaceArray;
+ }
+ }
+ m_MaterialLookupMap.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns true, if the loader can read this.
+bool Q3BSPFileImporter::CanRead( const std::string& rFile, IOSystem*, bool checkSig ) const
+{
+ bool isBSPData = false;
+ if ( checkSig )
+ isBSPData = SimpleExtensionCheck( rFile, Q3BSPExtention.c_str() );
+
+ return isBSPData;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Adds extensions.
+void Q3BSPFileImporter::GetExtensionList( std::set<std::string>& extensions )
+{
+ extensions.insert( Q3BSPExtention );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Import method.
+void Q3BSPFileImporter::InternReadFile(const std::string &rFile, aiScene* pScene, IOSystem*)
+{
+ Q3BSPZipArchive Archive( rFile );
+ if ( !Archive.isOpen() )
+ {
+ throw new DeadlyImportError( "Failed to open file " + rFile + "." );
+ }
+
+ std::string archiveName( "" ), mapName( "" );
+ separateMapName( rFile, archiveName, mapName );
+
+ if ( mapName.empty() )
+ {
+ if ( !findFirstMapInArchive( Archive, mapName ) )
+ {
+ return;
+ }
+ }
+
+ Q3BSPFileParser fileParser( mapName, &Archive );
+ Q3BSPModel *pBSPModel = fileParser.getModel();
+ if ( NULL != pBSPModel )
+ {
+ CreateDataFromImport( pBSPModel, pScene, &Archive );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Separates the map name from the import name.
+void Q3BSPFileImporter::separateMapName( const std::string &rImportName, std::string &rArchiveName,
+ std::string &rMapName )
+{
+ rArchiveName = "";
+ rMapName = "";
+ if ( rImportName.empty() )
+ return;
+
+ std::string::size_type pos = rImportName.rfind( "," );
+ if ( std::string::npos == pos )
+ {
+ rArchiveName = rImportName;
+ return;
+ }
+
+ rArchiveName = rImportName.substr( 0, pos );
+ rMapName = rImportName.substr( pos, rImportName.size() - pos - 1 );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the first map in the map archive.
+bool Q3BSPFileImporter::findFirstMapInArchive( Q3BSPZipArchive &rArchive, std::string &rMapName )
+{
+ rMapName = "";
+ std::vector<std::string> fileList;
+ rArchive.getFileList( fileList );
+ if ( fileList.empty() )
+ return false;
+
+ for ( std::vector<std::string>::iterator it = fileList.begin(); it != fileList.end();
+ ++it )
+ {
+ std::string::size_type pos = (*it).find( "maps/" );
+ if ( std::string::npos != pos )
+ {
+ std::string::size_type extPos = (*it).find( ".bsp" );
+ if ( std::string::npos != extPos )
+ {
+ rMapName = *it;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the assimp specific data.
+void Q3BSPFileImporter::CreateDataFromImport( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
+ Q3BSPZipArchive *pArchive )
+{
+ if ( NULL == pModel || NULL == pScene )
+ return;
+
+ pScene->mRootNode = new aiNode;
+ if ( !pModel->m_ModelName.empty() )
+ {
+ pScene->mRootNode->mName.Set( pModel->m_ModelName );
+ }
+
+ // Create the face to material relation map
+ createMaterialMap( pModel );
+
+ // Create all nodes
+ CreateNodes( pModel, pScene, pScene->mRootNode );
+
+ // Create the assigned materials
+ createMaterials( pModel, pScene, pArchive );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates all assimp nodes.
+void Q3BSPFileImporter::CreateNodes( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
+ aiNode *pParent )
+{
+ ai_assert( NULL != pModel );
+ if ( NULL == pModel )
+ {
+ return;
+ }
+
+ unsigned int matIdx = 0;
+ std::vector<aiMesh*> MeshArray;
+ std::vector<aiNode*> NodeArray;
+ for ( FaceMapIt it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end(); ++it )
+ {
+ std::vector<Q3BSP::sQ3BSPFace*> *pArray = (*it).second;
+ size_t numVerts = countData( *pArray );
+ if ( 0 != numVerts )
+ {
+ aiMesh* pMesh = new aiMesh;
+ aiNode *pNode = CreateTopology( pModel, matIdx, *pArray, pMesh );
+ if ( NULL != pNode )
+ {
+ NodeArray.push_back( pNode );
+ MeshArray.push_back( pMesh );
+ }
+ else
+ {
+ delete pMesh;
+ }
+ }
+ matIdx++;
+ }
+
+ pScene->mNumMeshes = MeshArray.size();
+ if ( pScene->mNumMeshes > 0 )
+ {
+ pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ];
+ for ( size_t i = 0; i < MeshArray.size(); i++ )
+ {
+ aiMesh *pMesh = MeshArray[ i ];
+ if ( NULL != pMesh )
+ {
+ pScene->mMeshes[ i ] = pMesh;
+ }
+ }
+ }
+
+ pParent->mNumChildren = MeshArray.size();
+ pParent->mChildren = new aiNode*[ pScene->mRootNode->mNumChildren ];
+ for ( size_t i=0; i<NodeArray.size(); i++ )
+ {
+ aiNode *pNode = NodeArray[ i ];
+ pNode->mParent = pParent;
+ pParent->mChildren[ i ] = pNode;
+ pParent->mChildren[ i ]->mMeshes[ 0 ] = i;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the topology.
+aiNode *Q3BSPFileImporter::CreateTopology( const Q3BSP::Q3BSPModel *pModel,
+ unsigned int materialIdx,
+ std::vector<sQ3BSPFace*> &rArray,
+ aiMesh* pMesh )
+{
+ size_t numVerts = countData( rArray );
+ if ( 0 == numVerts )
+ {
+ return NULL;
+ }
+
+ size_t numFaces = countFaces( rArray );
+ if ( 0 == numFaces )
+ {
+ return NULL;
+ }
+
+ size_t numTriangles = countTriangles( rArray );
+ pMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ pMesh->mFaces = new aiFace[ numTriangles ];
+ pMesh->mNumFaces = numTriangles;
+
+ pMesh->mNumVertices = numVerts;
+ pMesh->mVertices = new aiVector3D[ numVerts ];
+ pMesh->mNormals = new aiVector3D[ numVerts ];
+ pMesh->mTextureCoords[ 0 ] = new aiVector3D[ numVerts ];
+ pMesh->mTextureCoords[ 1 ] = new aiVector3D[ numVerts ];
+ pMesh->mMaterialIndex = materialIdx;
+
+ unsigned int faceIdx = 0;
+ unsigned int vertIdx = 0;
+ pMesh->mNumUVComponents[ 0 ] = 2;
+ pMesh->mNumUVComponents[ 1 ] = 2;
+ for ( std::vector<sQ3BSPFace*>::const_iterator it = rArray.begin(); it != rArray.end(); ++it )
+ {
+ Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
+ ai_assert( NULL != pQ3BSPFace );
+ if ( NULL == pQ3BSPFace )
+ {
+ continue;
+ }
+
+ if ( pQ3BSPFace->iNumOfFaceVerts > 0 )
+ {
+ if ( pQ3BSPFace->iType == Polygon || pQ3BSPFace->iType == TriangleMesh )
+ {
+ createTriangleTopology( pModel, pQ3BSPFace, pMesh, faceIdx, vertIdx );
+ }
+ }
+ }
+
+ aiNode *pNode = new aiNode;
+ pNode->mNumMeshes = 1;
+ pNode->mMeshes = new unsigned int[ 1 ];
+
+ return pNode;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the triangle topology from a face array.
+void Q3BSPFileImporter::createTriangleTopology( const Q3BSP::Q3BSPModel *pModel,
+ Q3BSP::sQ3BSPFace *pQ3BSPFace,
+ aiMesh* pMesh,
+ unsigned int &rFaceIdx,
+ unsigned int &rVertIdx )
+{
+ ai_assert( rFaceIdx < pMesh->mNumFaces );
+
+ m_pCurrentFace = getNextFace( pMesh, rFaceIdx );
+ ai_assert( NULL != m_pCurrentFace );
+ if ( NULL == m_pCurrentFace )
+ {
+ return;
+ }
+
+ m_pCurrentFace->mNumIndices = 3;
+ m_pCurrentFace->mIndices = new unsigned int[ m_pCurrentFace->mNumIndices ];
+
+ size_t idx = 0;
+ for ( size_t i = 0; i < (size_t) pQ3BSPFace->iNumOfFaceVerts; i++ )
+ {
+ const size_t index = pQ3BSPFace->iVertexIndex + pModel->m_Indices[ pQ3BSPFace->iFaceVertexIndex + i ];
+ ai_assert( index < pModel->m_Vertices.size() );
+ if ( index >= pModel->m_Vertices.size() )
+ {
+ continue;
+ }
+
+ sQ3BSPVertex *pVertex = pModel->m_Vertices[ index ];
+ ai_assert( NULL != pVertex );
+ if ( NULL == pVertex )
+ {
+ continue;
+ }
+
+ pMesh->mVertices[ rVertIdx ].Set( pVertex->vPosition.x, pVertex->vPosition.y, pVertex->vPosition.z );
+ pMesh->mNormals[ rVertIdx ].Set( pVertex->vNormal.x, pVertex->vNormal.y, pVertex->vNormal.z );
+
+ pMesh->mTextureCoords[ 0 ][ rVertIdx ].Set( pVertex->vTexCoord.x, pVertex->vTexCoord.y, 0.0f );
+ pMesh->mTextureCoords[ 1 ][ rVertIdx ].Set( pVertex->vLightmap.x, pVertex->vLightmap.y, 0.0f );
+
+ m_pCurrentFace->mIndices[ idx ] = rVertIdx;
+ rVertIdx++;
+
+ idx++;
+ if ( idx > 2 )
+ {
+ idx = 0;
+ m_pCurrentFace = getNextFace( pMesh, rFaceIdx );
+ if ( NULL != m_pCurrentFace )
+ {
+ m_pCurrentFace->mNumIndices = 3;
+ m_pCurrentFace->mIndices = new unsigned int[ 3 ];
+ }
+ }
+ }
+ rFaceIdx--;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates all referenced materials.
+void Q3BSPFileImporter::createMaterials( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
+ Q3BSPZipArchive *pArchive )
+{
+ if ( m_MaterialLookupMap.empty() )
+ {
+ return;
+ }
+
+ pScene->mMaterials = new aiMaterial*[ m_MaterialLookupMap.size() ];
+ aiString aiMatName;
+ int textureId( -1 ), lightmapId( -1 );
+ for ( FaceMapIt it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end();
+ ++it )
+ {
+ const std::string matName = (*it).first;
+ if ( matName.empty() )
+ {
+ continue;
+ }
+
+ aiMatName.Set( matName );
+ Assimp::MaterialHelper *pMatHelper = new Assimp::MaterialHelper;
+ pMatHelper->AddProperty( &aiMatName, AI_MATKEY_NAME );
+
+ extractIds( matName, textureId, lightmapId );
+
+ // Adding the texture
+ if ( -1 != textureId )
+ {
+ sQ3BSPTexture *pTexture = pModel->m_Textures[ textureId ];
+ if ( NULL != pTexture )
+ {
+ std::string tmp( "*" ), texName( "" );
+ tmp += pTexture->strName;
+ tmp += ".jpg";
+ normalizePathName( tmp, texName );
+
+ if ( !importTextureFromArchive( pModel, pArchive, pScene, pMatHelper, textureId ) )
+ {
+ }
+ }
+
+ }
+ if ( -1 != lightmapId )
+ {
+ importLightmap( pModel, pScene, pMatHelper, lightmapId );
+ }
+ pScene->mMaterials[ pScene->mNumMaterials ] = pMatHelper;
+ pScene->mNumMaterials++;
+ }
+ pScene->mNumTextures = mTextures.size();
+ pScene->mTextures = new aiTexture*[ pScene->mNumTextures ];
+ std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Counts the number of referenced vertices.
+size_t Q3BSPFileImporter::countData( const std::vector<sQ3BSPFace*> &rArray ) const
+{
+ size_t numVerts = 0;
+ for ( std::vector<sQ3BSPFace*>::const_iterator it = rArray.begin(); it != rArray.end();
+ ++it )
+ {
+ sQ3BSPFace *pQ3BSPFace = *it;
+ if ( pQ3BSPFace->iType == Polygon || pQ3BSPFace->iType == TriangleMesh )
+ {
+ Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
+ ai_assert( NULL != pQ3BSPFace );
+ numVerts += pQ3BSPFace->iNumOfFaceVerts;
+ }
+ }
+
+ return numVerts;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Counts the faces with vertices.
+size_t Q3BSPFileImporter::countFaces( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const
+{
+ size_t numFaces = 0;
+ for ( std::vector<sQ3BSPFace*>::const_iterator it = rArray.begin(); it != rArray.end();
+ ++it )
+ {
+ Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
+ if ( pQ3BSPFace->iNumOfFaceVerts > 0 )
+ {
+ numFaces++;
+ }
+ }
+
+ return numFaces;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Counts the number of triangles in a Q3-facearray.
+size_t Q3BSPFileImporter::countTriangles( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const
+{
+ size_t numTriangles = 0;
+ for ( std::vector<Q3BSP::sQ3BSPFace*>::const_iterator it = rArray.begin(); it != rArray.end();
+ ++it )
+ {
+ const Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
+ if ( NULL != pQ3BSPFace )
+ {
+ numTriangles += pQ3BSPFace->iNumOfFaceVerts / 3;
+ }
+ }
+
+ return numTriangles;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the faces-to-material map.
+void Q3BSPFileImporter::createMaterialMap( const Q3BSP::Q3BSPModel *pModel )
+{
+ std::string key( "" );
+ std::vector<sQ3BSPFace*> *pCurFaceArray = NULL;
+ for ( size_t idx = 0; idx < pModel->m_Faces.size(); idx++ )
+ {
+ Q3BSP::sQ3BSPFace *pQ3BSPFace = pModel->m_Faces[ idx ];
+ const int texId = pQ3BSPFace->iTextureID;
+ const int lightMapId = pQ3BSPFace->iLightmapID;
+ createKey( texId, lightMapId, key );
+ FaceMapIt it = m_MaterialLookupMap.find( key );
+ if ( m_MaterialLookupMap.end() == it )
+ {
+ pCurFaceArray = new std::vector<Q3BSP::sQ3BSPFace*>;
+ m_MaterialLookupMap[ key ] = pCurFaceArray;
+ }
+ else
+ {
+ pCurFaceArray = (*it).second;
+ }
+ ai_assert( NULL != pCurFaceArray );
+ if ( NULL != pCurFaceArray )
+ {
+ pCurFaceArray->push_back( pQ3BSPFace );
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the next face.
+aiFace *Q3BSPFileImporter::getNextFace( aiMesh *pMesh, unsigned int &rFaceIdx )
+{
+ aiFace *pFace = NULL;
+ if ( rFaceIdx < pMesh->mNumFaces )
+ {
+ pFace = &pMesh->mFaces[ rFaceIdx ];
+ rFaceIdx++;
+ }
+ else
+ {
+ pFace = NULL;
+ }
+
+ return pFace;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports a texture file.
+bool Q3BSPFileImporter::importTextureFromArchive( const Q3BSP::Q3BSPModel *pModel,
+ Q3BSP::Q3BSPZipArchive *pArchive, aiScene*,
+ Assimp::MaterialHelper *pMatHelper, int textureId )
+{
+ if ( NULL == pArchive || NULL == pArchive || NULL == pMatHelper )
+ {
+ return false;
+ }
+
+ if ( textureId < 0 || textureId >= static_cast<int>( pModel->m_Textures.size() ) )
+ {
+ return false;
+ }
+
+ bool res = true;
+ sQ3BSPTexture *pTexture = pModel->m_Textures[ textureId ];
+ if ( NULL == pTexture )
+ return false;
+
+ std::string textureName = pTexture->strName;
+ textureName += ".jpg";
+ if ( pArchive->Exists( textureName.c_str() ) )
+ {
+ IOStream *pTextureStream = pArchive->Open( textureName.c_str() );
+ if ( NULL != pTextureStream )
+ {
+ size_t texSize = pTextureStream->FileSize();
+ aiTexture *pTexture = new aiTexture;
+ pTexture->mHeight = 0;
+ pTexture->mWidth = texSize;
+ unsigned char *pData = new unsigned char[ pTexture->mWidth ];
+#ifndef QT_NO_DEBUG
+ const size_t readSize = pTextureStream->Read( pData, sizeof( unsigned char ), pTexture->mWidth );
+ Q_ASSERT( readSize == pTexture->mWidth );
+#endif
+ pTexture->pcData = reinterpret_cast<aiTexel*>( pData );
+ pTexture->achFormatHint[ 0 ] = 'j';
+ pTexture->achFormatHint[ 1 ] = 'p';
+ pTexture->achFormatHint[ 2 ] = 'g';
+ pTexture->achFormatHint[ 2 ] = '\0';
+ res = true;
+
+ aiString name;
+ name.data[ 0 ] = '*';
+ name.length = 1 + ASSIMP_itoa10( name.data + 1, MAXLEN-1, mTextures.size() );
+
+ pArchive->Close( pTextureStream );
+
+ pMatHelper->AddProperty( &name, AI_MATKEY_TEXTURE_DIFFUSE( 0 ) );
+ mTextures.push_back( pTexture );
+ }
+ }
+
+ return res;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports a lightmap file.
+bool Q3BSPFileImporter::importLightmap( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
+ Assimp::MaterialHelper *pMatHelper, int lightmapId )
+{
+ if ( NULL == pModel || NULL == pScene || NULL == pMatHelper )
+ {
+ return false;
+ }
+
+ if ( lightmapId < 0 || lightmapId >= static_cast<int>( pModel->m_Lightmaps.size() ) )
+ {
+ return false;
+ }
+
+ sQ3BSPLightmap *pLightMap = pModel->m_Lightmaps[ lightmapId ];
+ if ( NULL == pLightMap )
+ {
+ return false;
+ }
+
+ aiTexture *pTexture = new aiTexture;
+ pTexture->mHeight = 0;
+ pTexture->mWidth = CE_BSP_LIGHTMAPWIDTH * CE_BSP_LIGHTMAPHEIGHT;
+
+ unsigned char *pData = new unsigned char[ pTexture->mWidth ];
+ pTexture->pcData = reinterpret_cast<aiTexel*>( pData );
+
+ pTexture->achFormatHint[ 0 ] = 'b';
+ pTexture->achFormatHint[ 1 ] = 'm';
+ pTexture->achFormatHint[ 2 ] = 'p';
+ pTexture->achFormatHint[ 3 ] = '\0';
+
+ memcpy( pTexture->pcData, pLightMap->bLMapData, pTexture->mWidth );
+
+ aiString name;
+ name.data[ 0 ] = '*';
+ name.length = 1 + ASSIMP_itoa10( name.data + 1, MAXLEN-1, mTextures.size() );
+
+ pMatHelper->AddProperty( &name,AI_MATKEY_TEXTURE_LIGHTMAP( 1 ) );
+ mTextures.push_back( pTexture );
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_Q3BSP_IMPORTER
diff --git a/src/3rdparty/assimp/code/Q3BSPFileImporter.h b/src/3rdparty/assimp/code/Q3BSPFileImporter.h
new file mode 100644
index 000000000..fa2e215ae
--- /dev/null
+++ b/src/3rdparty/assimp/code/Q3BSPFileImporter.h
@@ -0,0 +1,110 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#ifndef ASSIMP_Q3BSPFILEIMPORTER_H_INC
+#define ASSIMP_Q3BSPFILEIMPORTER_H_INC
+
+#include "BaseImporter.h"
+
+struct aiMesh;
+
+namespace Assimp
+{
+namespace Q3BSP
+{
+
+class Q3BSPZipArchive;
+struct Q3BSPModel;
+struct sQ3BSPFace;
+
+}
+
+/** Loader to import BSP-levels from a PK3 archive or from a unpacked BSP-level.
+ */
+class Q3BSPFileImporter : BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /// @brief Default constructor.
+ Q3BSPFileImporter();
+
+ /// @brief Destructor.
+ ~Q3BSPFileImporter();
+
+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:
+ typedef std::map<std::string, std::vector<Q3BSP::sQ3BSPFace*>*> FaceMap;
+ typedef std::map<std::string, std::vector<Q3BSP::sQ3BSPFace*>* >::iterator FaceMapIt;
+ typedef std::map<std::string, std::vector<Q3BSP::sQ3BSPFace*>*>::const_iterator FaceMapConstIt;
+
+ void GetExtensionList(std::set<std::string>& extensions);
+ void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+ void separateMapName( const std::string &rImportName, std::string &rArchiveName, std::string &rMapName );
+ bool findFirstMapInArchive( Q3BSP::Q3BSPZipArchive &rArchive, std::string &rMapName );
+ void CreateDataFromImport( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, Q3BSP::Q3BSPZipArchive *pArchive );
+ void CreateNodes( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, aiNode *pParent );
+ aiNode *CreateTopology( const Q3BSP::Q3BSPModel *pModel, unsigned int materialIdx,
+ std::vector<Q3BSP::sQ3BSPFace*> &rArray, aiMesh* pMesh );
+ void createTriangleTopology( const Q3BSP::Q3BSPModel *pModel, Q3BSP::sQ3BSPFace *pQ3BSPFace, aiMesh* pMesh, unsigned int &rFaceIdx,
+ unsigned int &rVertIdx );
+ void createMaterials( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, Q3BSP::Q3BSPZipArchive *pArchive );
+ size_t countData( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const;
+ size_t countFaces( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const;
+ size_t countTriangles( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const;
+ void createMaterialMap( const Q3BSP::Q3BSPModel *pModel);
+ aiFace *getNextFace( aiMesh *pMesh, unsigned int &rFaceIdx );
+ bool importTextureFromArchive( const Q3BSP::Q3BSPModel *pModel, Q3BSP::Q3BSPZipArchive *pArchive, aiScene* pScene,
+ Assimp::MaterialHelper *pMatHelper, int textureId );
+ bool importLightmap( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, Assimp::MaterialHelper *pMatHelper, int lightmapId );
+
+private:
+ aiMesh *m_pCurrentMesh;
+ aiFace *m_pCurrentFace;
+ FaceMap m_MaterialLookupMap;
+ std::vector<aiTexture*> mTextures;
+};
+
+} // Namespace Assimp
+
+#endif // ASSIMP_Q3BSPFILEIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/Q3BSPFileParser.cpp b/src/3rdparty/assimp/code/Q3BSPFileParser.cpp
new file mode 100644
index 000000000..09411ca56
--- /dev/null
+++ b/src/3rdparty/assimp/code/Q3BSPFileParser.cpp
@@ -0,0 +1,272 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#include "AssimpPCH.h"
+#include "Q3BSPFileParser.h"
+#include "DefaultIOSystem.h"
+#include "Q3BSPFileData.h"
+#include "Q3BSPZipArchive.h"
+#include <vector>
+
+namespace Assimp
+{
+
+using namespace Q3BSP;
+
+// ------------------------------------------------------------------------------------------------
+Q3BSPFileParser::Q3BSPFileParser( const std::string &rMapName, Q3BSPZipArchive *pZipArchive ) :
+ m_sOffset( 0 ),
+ m_Data(),
+ m_pModel( NULL ),
+ m_pZipArchive( pZipArchive )
+{
+ ai_assert( NULL != m_pZipArchive );
+ ai_assert( !rMapName.empty() );
+
+ if ( !readData( rMapName ) )
+ return;
+
+ m_pModel = new Q3BSPModel;
+ m_pModel->m_ModelName = rMapName;
+ if ( !parseFile() )
+ {
+ delete m_pModel;
+ m_pModel = NULL;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Q3BSPFileParser::~Q3BSPFileParser()
+{
+ delete m_pModel;
+ m_pModel = NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+Q3BSP::Q3BSPModel *Q3BSPFileParser::getModel() const
+{
+ return m_pModel;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Q3BSPFileParser::readData( const std::string &rMapName )
+{
+ if ( !m_pZipArchive->Exists( rMapName.c_str() ) )
+ return false;
+
+ IOStream *pMapFile = m_pZipArchive->Open( rMapName.c_str() );
+ if ( NULL == pMapFile )
+ return false;
+
+ const size_t size = pMapFile->FileSize();
+ m_Data.resize( size );
+
+ const size_t readSize = pMapFile->Read( &m_Data[0], sizeof( char ), size );
+ if ( readSize != size )
+ {
+ m_Data.clear();
+ return false;
+ }
+ m_pZipArchive->Close( pMapFile );
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Q3BSPFileParser::parseFile()
+{
+ if ( m_Data.empty() )
+ {
+ return false;
+ }
+
+ if ( !validateFormat() )
+ {
+ return false;
+ }
+
+ // Imports the dictionary of the level
+ getLumps();
+
+ // Conunt data and prepare model data
+ countLumps();
+
+ // Read in Vertices
+ getVertices();
+
+ // Read in Indices
+ getIndices();
+
+ // Read Faces
+ getFaces();
+
+ // Read Textures
+ getTextures();
+
+ // Read Lightmaps
+ getLightMaps();
+
+ // Load the entities
+ getEntities();
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Q3BSPFileParser::validateFormat()
+{
+ sQ3BSPHeader *pHeader = (sQ3BSPHeader*) &m_Data[ 0 ];
+ m_sOffset += sizeof( sQ3BSPHeader );
+
+ // Version and identify string validation
+ if (pHeader->strID[ 0 ] != 'I' || pHeader->strID[ 1 ] != 'B' || pHeader->strID[ 2 ] != 'S'
+ || pHeader->strID[ 3 ] != 'P')
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getLumps()
+{
+ size_t Offset = m_sOffset;
+ m_pModel->m_Lumps.resize( kMaxLumps );
+ for ( size_t idx=0; idx < kMaxLumps; idx++ )
+ {
+ sQ3BSPLump *pLump = new sQ3BSPLump;
+ memcpy( pLump, &m_Data[ Offset ], sizeof( sQ3BSPLump ) );
+ Offset += sizeof( sQ3BSPLump );
+ m_pModel->m_Lumps[ idx ] = pLump;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::countLumps()
+{
+ m_pModel->m_Vertices.resize( m_pModel->m_Lumps[ kVertices ]->iSize / sizeof( sQ3BSPVertex ) );
+ m_pModel->m_Indices.resize( m_pModel->m_Lumps[ kMeshVerts ]->iSize / sizeof( int ) );
+ m_pModel->m_Faces.resize( m_pModel->m_Lumps[ kFaces ]->iSize / sizeof( sQ3BSPFace ) );
+ m_pModel->m_Textures.resize( m_pModel->m_Lumps[ kTextures ]->iSize / sizeof( sQ3BSPTexture ) );
+ m_pModel->m_Lightmaps.resize( m_pModel->m_Lumps[ kLightmaps ]->iSize / sizeof( sQ3BSPLightmap ) );
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getVertices()
+{
+ size_t Offset = m_pModel->m_Lumps[ kVertices ]->iOffset;
+ for ( size_t idx = 0; idx < m_pModel->m_Vertices.size(); idx++ )
+ {
+ sQ3BSPVertex *pVertex = new sQ3BSPVertex;
+ memcpy( pVertex, &m_Data[ Offset ], sizeof( sQ3BSPVertex ) );
+ Offset += sizeof( sQ3BSPVertex );
+ m_pModel->m_Vertices[ idx ] = pVertex;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getIndices()
+{
+ ai_assert( NULL != m_pModel );
+
+ sQ3BSPLump *lump = m_pModel->m_Lumps[ kMeshVerts ];
+ size_t Offset = (size_t) lump->iOffset;
+ const size_t nIndices = lump->iSize / sizeof( int );
+ m_pModel->m_Indices.resize( nIndices );
+ memcpy( &m_pModel->m_Indices[ 0 ], &m_Data[ Offset ], lump->iSize );
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getFaces()
+{
+ ai_assert( NULL != m_pModel );
+
+ size_t Offset = m_pModel->m_Lumps[ kFaces ]->iOffset;
+ for ( size_t idx = 0; idx < m_pModel->m_Faces.size(); idx++ )
+ {
+ sQ3BSPFace *pFace = new sQ3BSPFace;
+ memcpy( pFace, &m_Data[ Offset ], sizeof( sQ3BSPFace ) );
+ m_pModel->m_Faces[ idx ] = pFace;
+ Offset += sizeof( sQ3BSPFace );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getTextures()
+{
+ ai_assert( NULL != m_pModel );
+
+ size_t Offset = m_pModel->m_Lumps[ kTextures ]->iOffset;
+ for ( size_t idx=0; idx < m_pModel->m_Textures.size(); idx++ )
+ {
+ sQ3BSPTexture *pTexture = new sQ3BSPTexture;
+ memcpy( pTexture, &m_Data[ Offset ], sizeof(sQ3BSPTexture) );
+ m_pModel->m_Textures[ idx ] = pTexture;
+ Offset += sizeof(sQ3BSPTexture);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getLightMaps()
+{
+ ai_assert( NULL != m_pModel );
+
+ size_t Offset = m_pModel->m_Lumps[kLightmaps]->iOffset;
+ for ( size_t idx=0; idx < m_pModel->m_Lightmaps.size(); idx++ )
+ {
+ sQ3BSPLightmap *pLightmap = new sQ3BSPLightmap;
+ memcpy( pLightmap, &m_Data[ Offset ], sizeof( sQ3BSPLightmap ) );
+ Offset += sizeof( sQ3BSPLightmap );
+ m_pModel->m_Lightmaps[ idx ] = pLightmap;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getEntities()
+{
+ int size = m_pModel->m_Lumps[ kEntities ]->iSize;
+ m_pModel->m_EntityData.resize( size );
+ size_t Offset = m_pModel->m_Lumps[ kEntities ]->iOffset;
+ memcpy( &m_pModel->m_EntityData[ 0 ], &m_Data[ Offset ], sizeof( char ) * size );
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
diff --git a/src/3rdparty/assimp/code/Q3BSPFileParser.h b/src/3rdparty/assimp/code/Q3BSPFileParser.h
new file mode 100644
index 000000000..a049dc297
--- /dev/null
+++ b/src/3rdparty/assimp/code/Q3BSPFileParser.h
@@ -0,0 +1,89 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef ASSIMP_Q3BSPFILEPARSER_H_INC
+#define ASSIMP_Q3BSPFILEPARSER_H_INC
+
+#include "BaseImporter.h"
+#include <string>
+
+namespace Assimp
+{
+namespace Q3BSP
+{
+
+class Q3BSPZipArchive;
+struct Q3BSPModel;
+class ZipFile;
+
+}
+
+// -------------------------------------------------------------------
+// -------------------------------------------------------------------
+class Q3BSPFileParser
+{
+public:
+ Q3BSPFileParser( const std::string &rMapName, Q3BSP::Q3BSPZipArchive *pZipArchive );
+ ~Q3BSPFileParser();
+ Q3BSP::Q3BSPModel *getModel() const;
+
+protected:
+ bool readData(const std::string &rMapName);
+ bool parseFile();
+ bool validateFormat();
+ void getLumps();
+ void countLumps();
+ void getVertices();
+ void getIndices();
+ void getFaces();
+ void getTextures();
+ void getLightMaps();
+ void getEntities();
+
+private:
+ size_t m_sOffset;
+ std::vector<char> m_Data;
+ Q3BSP::Q3BSPModel *m_pModel;
+ Q3BSP::Q3BSPZipArchive *m_pZipArchive;
+};
+
+} // Namespace Assimp
+
+#endif // ASSIMP_Q3BSPFILEPARSER_H_INC
diff --git a/src/3rdparty/assimp/code/Q3BSPZipArchive.cpp b/src/3rdparty/assimp/code/Q3BSPZipArchive.cpp
new file mode 100644
index 000000000..77e782107
--- /dev/null
+++ b/src/3rdparty/assimp/code/Q3BSPZipArchive.cpp
@@ -0,0 +1,196 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#include "Q3BSPZipArchive.h"
+#include <algorithm>
+#include <cassert>
+
+namespace Assimp
+{
+namespace Q3BSP
+{
+
+// ------------------------------------------------------------------------------------------------
+// Constructor.
+Q3BSPZipArchive::Q3BSPZipArchive( const std::string& rFile ) :
+ m_ZipFileHandle( NULL ),
+ m_FileList(),
+ m_bDirty( true )
+{
+ if ( !rFile.empty() )
+ {
+ m_ZipFileHandle = unzOpen( rFile.c_str() );
+ if ( NULL != m_ZipFileHandle )
+ {
+ mapArchive();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor.
+Q3BSPZipArchive::~Q3BSPZipArchive()
+{
+ if ( NULL != m_ZipFileHandle )
+ {
+ unzClose( m_ZipFileHandle );
+ }
+ m_ZipFileHandle = NULL;
+ m_FileList.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns true, if the archive is already open.
+bool Q3BSPZipArchive::isOpen() const
+{
+ return ( NULL != m_ZipFileHandle );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns true, if the filename is part of the archive.
+bool Q3BSPZipArchive::Exists( const char* pFile ) const
+{
+ ai_assert( NULL != pFile );
+ if ( NULL == pFile )
+ {
+ return false;
+ }
+
+ std::string rFile( pFile );
+ std::vector<std::string>::const_iterator it = std::find( m_FileList.begin(), m_FileList.end(), rFile );
+ if ( m_FileList.end() == it )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the separator delimiter.
+char Q3BSPZipArchive::getOsSeparator() const
+{
+ return '/';
+}
+
+// ------------------------------------------------------------------------------------------------
+// Opens a file, which is part of the archive.
+IOStream *Q3BSPZipArchive::Open( const char* pFile, const char* /* pMode */)
+{
+ ai_assert( NULL != pFile );
+
+ std::string rItem( pFile );
+ std::vector<std::string>::iterator it = std::find( m_FileList.begin(), m_FileList.end(), rItem );
+ if ( m_FileList.end() == it )
+ return NULL;
+
+ ZipFile *pZipFile = new ZipFile( *it, m_ZipFileHandle );
+ m_ArchiveMap[ rItem ] = pZipFile;
+
+ return pZipFile;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Close a filestream.
+void Q3BSPZipArchive::Close( IOStream *pFile )
+{
+ ai_assert( NULL != pFile );
+
+ std::map<std::string, IOStream*>::iterator it;
+ for ( it = m_ArchiveMap.begin(); it != m_ArchiveMap.end(); ++it )
+ {
+ if ( (*it).second == pFile )
+ {
+ ZipFile *pZipFile = reinterpret_cast<ZipFile*>( (*it).second );
+ delete pZipFile;
+ m_ArchiveMap.erase( it );
+ break;
+ }
+ }
+}
+// ------------------------------------------------------------------------------------------------
+// Returns the file-list of the archive.
+void Q3BSPZipArchive::getFileList( std::vector<std::string> &rFileList )
+{
+ rFileList = m_FileList;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Maps the archive content.
+bool Q3BSPZipArchive::mapArchive()
+{
+ if ( NULL == m_ZipFileHandle )
+ return false;
+
+ if ( !m_bDirty )
+ return true;
+
+ if ( !m_FileList.empty() )
+ m_FileList.resize( 0 );
+
+ // At first ensure file is already open
+ if ( UNZ_OK == unzGoToFirstFile( m_ZipFileHandle ) )
+ {
+ char filename[ FileNameSize ];
+ unzGetCurrentFileInfo( m_ZipFileHandle, NULL, filename, FileNameSize, NULL, 0, NULL, 0 );
+ m_FileList.push_back( filename );
+ unzCloseCurrentFile( m_ZipFileHandle );
+
+ // Loop over all files
+ while ( unzGoToNextFile( m_ZipFileHandle ) != UNZ_END_OF_LIST_OF_FILE )
+ {
+ char filename[ FileNameSize ];
+ unzGetCurrentFileInfo( m_ZipFileHandle, NULL, filename, FileNameSize, NULL, 0, NULL, 0 );
+ m_FileList.push_back( filename );
+ unzCloseCurrentFile( m_ZipFileHandle );
+ }
+ }
+
+ std::sort( m_FileList.begin(), m_FileList.end() );
+ m_bDirty = false;
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Q3BSP
+} // Namespace Assimp
diff --git a/src/3rdparty/assimp/code/Q3BSPZipArchive.h b/src/3rdparty/assimp/code/Q3BSPZipArchive.h
new file mode 100644
index 000000000..4d795f83c
--- /dev/null
+++ b/src/3rdparty/assimp/code/Q3BSPZipArchive.h
@@ -0,0 +1,181 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#ifndef AI_Q3BSP_ZIPARCHIVE_H_INC
+#define AI_Q3BSP_ZIPARCHIVE_H_INC
+
+#include "../contrib/unzip/unzip.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
+#include <string>
+#include <vector>
+#include <map>
+#include <cassert>
+
+namespace Assimp
+{
+namespace Q3BSP
+{
+
+// ------------------------------------------------------------------------------------------------
+/// \class ZipFile
+/// \ingroup Assimp::Q3BSP
+///
+/// \brief
+// ------------------------------------------------------------------------------------------------
+class ZipFile : public IOStream
+{
+public:
+ ZipFile( const std::string &rFileName, unzFile zipFile ) :
+ m_Name( rFileName ),
+ m_zipFile( zipFile )
+ {
+ ai_assert( NULL != m_zipFile );
+ }
+
+ ~ZipFile()
+ {
+ m_zipFile = NULL;
+ }
+
+ size_t Read(void* pvBuffer, size_t pSize, size_t pCount )
+ {
+ size_t bytes_read = 0;
+ if ( NULL == m_zipFile )
+ return bytes_read;
+
+ // search file and place file pointer there
+ if ( unzLocateFile( m_zipFile, m_Name.c_str(), 0 ) == UNZ_OK )
+ {
+ // get file size, etc.
+ unz_file_info fileInfo;
+ unzGetCurrentFileInfo( m_zipFile, &fileInfo, 0, 0, 0, 0, 0, 0 );
+ const size_t size = pSize * pCount;
+ assert( size <= fileInfo.uncompressed_size );
+
+ // The file has EXACTLY the size of uncompressed_size. In C
+ // you need to mark the last character with '\0', so add
+ // another character
+ unzOpenCurrentFile( m_zipFile );
+ bytes_read = unzReadCurrentFile( m_zipFile, pvBuffer, fileInfo.uncompressed_size);
+ if ( bytes_read != static_cast<size_t>( fileInfo.uncompressed_size ) )
+ {
+ return 0;
+ }
+ unzCloseCurrentFile( m_zipFile );
+ }
+ return bytes_read;
+ }
+
+ size_t Write(const void* /* pvBuffer */, size_t /* pSize */, size_t /* pCount */)
+ {
+ return 0;
+ }
+
+ size_t FileSize() const
+ {
+ if ( NULL == m_zipFile )
+ return 0;
+ if ( unzLocateFile( m_zipFile, m_Name.c_str(), 0 ) == UNZ_OK )
+ {
+ unz_file_info fileInfo;
+ unzGetCurrentFileInfo( m_zipFile, &fileInfo, 0, 0, 0, 0, 0, 0 );
+ return fileInfo.uncompressed_size;
+ }
+ return 0;
+ }
+
+ aiReturn Seek(size_t /* pOffset */, aiOrigin /* pOrigin */)
+ {
+ return aiReturn_FAILURE;
+ }
+
+ size_t Tell() const
+ {
+ return 0;
+ }
+
+ void Flush()
+ {
+ // empty
+ }
+
+private:
+ std::string m_Name;
+ unzFile m_zipFile;
+};
+
+// ------------------------------------------------------------------------------------------------
+/// \class Q3BSPZipArchive
+/// \ingroup Assimp::Q3BSP
+///
+/// \brief IMplements a zip archive like the WinZip archives. Will be also used to import data
+/// from a P3K archive ( Quake level format ).
+// ------------------------------------------------------------------------------------------------
+class Q3BSPZipArchive : public Assimp::IOSystem
+{
+public:
+ static const unsigned int FileNameSize = 256;
+
+public:
+ Q3BSPZipArchive( const std::string & rFile );
+ ~Q3BSPZipArchive();
+ bool Exists( const char* pFile) const;
+ char getOsSeparator() const;
+ IOStream* Open(const char* pFile, const char* pMode = "rb");
+ void Close( IOStream* pFile);
+ bool isOpen() const;
+ void getFileList( std::vector<std::string> &rFileList );
+
+private:
+ bool mapArchive();
+
+private:
+ unzFile m_ZipFileHandle;
+ std::map<std::string, IOStream*> m_ArchiveMap;
+ std::vector<std::string> m_FileList;
+ bool m_bDirty;
+};
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Q3BSP
+} // Namespace Assimp
+
+#endif // AI_Q3BSP_ZIPARCHIVE_H_INC
diff --git a/src/3rdparty/assimp/code/Q3DLoader.cpp b/src/3rdparty/assimp/code/Q3DLoader.cpp
new file mode 100644
index 000000000..af0ec0633
--- /dev/null
+++ b/src/3rdparty/assimp/code/Q3DLoader.cpp
@@ -0,0 +1,600 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Q3DLoader.cpp
+ * @brief Implementation of the Q3D importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
+
+// internal headers
+#include "Q3DLoader.h"
+#include "StreamReader.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+Q3DImporter::Q3DImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+Q3DImporter::~Q3DImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool Q3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "q3s" || extension == "q3o")
+ return true;
+ else if (!extension.length() || checkSig) {
+ if (!pIOHandler)
+ return true;
+ const char* tokens[] = {"quick3Do","quick3Ds"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,2);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3DImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("q3o");
+ extensions.insert("q3s");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void Q3DImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ StreamReaderLE stream(pIOHandler->Open(pFile,"rb"));
+
+ // The header is 22 bytes large
+ if (stream.GetRemainingSize() < 22)
+ throw DeadlyImportError("File is either empty or corrupt: " + pFile);
+
+ // Check the file's signature
+ if (ASSIMP_strincmp( (const char*)stream.GetPtr(), "quick3Do", 8 ) &&
+ ASSIMP_strincmp( (const char*)stream.GetPtr(), "quick3Ds", 8 ))
+ {
+ throw DeadlyImportError("Not a Quick3D file. Signature string is: " +
+ std::string((const char*)stream.GetPtr(),8));
+ }
+
+ // Print the file format version
+ DefaultLogger::get()->info("Quick3D File format version: " +
+ std::string(&((const char*)stream.GetPtr())[8],2));
+
+ // ... an store it
+ char major = ((const char*)stream.GetPtr())[8];
+ char minor = ((const char*)stream.GetPtr())[9];
+
+ stream.IncPtr(10);
+ unsigned int numMeshes = (unsigned int)stream.GetI4();
+ unsigned int numMats = (unsigned int)stream.GetI4();
+ unsigned int numTextures = (unsigned int)stream.GetI4();
+
+ std::vector<Material> materials;
+ materials.reserve(numMats);
+
+ std::vector<Mesh> meshes;
+ meshes.reserve(numMeshes);
+
+ // Allocate the scene root node
+ pScene->mRootNode = new aiNode();
+
+ aiColor3D fgColor (0.6f,0.6f,0.6f);
+
+ // Now read all file chunks
+ while (true)
+ {
+ if (stream.GetRemainingSize() < 1)break;
+ char c = stream.GetI1();
+ switch (c)
+ {
+ // Meshes chunk
+ case 'm':
+ {
+ for (unsigned int quak = 0; quak < numMeshes; ++quak)
+ {
+ meshes.push_back(Mesh());
+ Mesh& mesh = meshes.back();
+
+ // read all vertices
+ unsigned int numVerts = (unsigned int)stream.GetI4();
+ if (!numVerts)
+ throw DeadlyImportError("Quick3D: Found mesh with zero vertices");
+
+ std::vector<aiVector3D>& verts = mesh.verts;
+ verts.resize(numVerts);
+
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ verts[i].x = stream.GetF4();
+ verts[i].y = stream.GetF4();
+ verts[i].z = stream.GetF4();
+ }
+
+ // read all faces
+ numVerts = (unsigned int)stream.GetI4();
+ if (!numVerts)
+ throw DeadlyImportError("Quick3D: Found mesh with zero faces");
+
+ std::vector<Face >& faces = mesh.faces;
+ faces.reserve(numVerts);
+
+ // number of indices
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ faces.push_back(Face(stream.GetI2()) );
+ if (faces.back().indices.empty())
+ throw DeadlyImportError("Quick3D: Found face with zero indices");
+ }
+
+ // indices
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ Face& vec = faces[i];
+ for (unsigned int a = 0; a < (unsigned int)vec.indices.size();++a)
+ vec.indices[a] = stream.GetI4();
+ }
+
+ // material indices
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ faces[i].mat = (unsigned int)stream.GetI4();
+ }
+
+ // read all normals
+ numVerts = (unsigned int)stream.GetI4();
+ std::vector<aiVector3D>& normals = mesh.normals;
+ normals.resize(numVerts);
+
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ normals[i].x = stream.GetF4();
+ normals[i].y = stream.GetF4();
+ normals[i].z = stream.GetF4();
+ }
+
+ numVerts = (unsigned int)stream.GetI4();
+ if (numTextures && numVerts)
+ {
+ // read all texture coordinates
+ std::vector<aiVector3D>& uv = mesh.uv;
+ uv.resize(numVerts);
+
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ uv[i].x = stream.GetF4();
+ uv[i].y = stream.GetF4();
+ }
+
+ // UV indices
+ for (unsigned int i = 0; i < (unsigned int)faces.size();++i)
+ {
+ Face& vec = faces[i];
+ for (unsigned int a = 0; a < (unsigned int)vec.indices.size();++a)
+ {
+ vec.uvindices[a] = stream.GetI4();
+ if (!i && !a)
+ mesh.prevUVIdx = vec.uvindices[a];
+ else if (vec.uvindices[a] != mesh.prevUVIdx)
+ mesh.prevUVIdx = 0xffffffff;
+ }
+ }
+ }
+
+ // we don't need the rest, but we need to get to the next chunk
+ stream.IncPtr(36);
+ if (minor > '0' && major == '3')
+ stream.IncPtr(mesh.faces.size());
+ }
+ // stream.IncPtr(4); // unknown value here
+ }
+ break;
+
+ // materials chunk
+ case 'c':
+
+ for (unsigned int i = 0; i < numMats; ++i)
+ {
+ materials.push_back(Material());
+ Material& mat = materials.back();
+
+ // read the material name
+ while (( c = stream.GetI1()))
+ mat.name.data[mat.name.length++] = c;
+
+ // add the terminal character
+ mat.name.data[mat.name.length] = '\0';
+
+ // read the ambient color
+ mat.ambient.r = stream.GetF4();
+ mat.ambient.g = stream.GetF4();
+ mat.ambient.b = stream.GetF4();
+
+ // read the diffuse color
+ mat.diffuse.r = stream.GetF4();
+ mat.diffuse.g = stream.GetF4();
+ mat.diffuse.b = stream.GetF4();
+
+ // read the ambient color
+ mat.specular.r = stream.GetF4();
+ mat.specular.g = stream.GetF4();
+ mat.specular.b = stream.GetF4();
+
+ // read the transparency
+ mat.transparency = stream.GetF4();
+
+ // unknown value here
+ // stream.IncPtr(4);
+ // FIX: it could be the texture index ...
+ mat.texIdx = (unsigned int)stream.GetI4();
+ }
+
+ break;
+
+ // texture chunk
+ case 't':
+
+ pScene->mNumTextures = numTextures;
+ if (!numTextures)break;
+ pScene->mTextures = new aiTexture*[pScene->mNumTextures];
+ // to make sure we won't crash if we leave through an exception
+ ::memset(pScene->mTextures,0,sizeof(void*)*pScene->mNumTextures);
+ for (unsigned int i = 0; i < pScene->mNumTextures; ++i)
+ {
+ aiTexture* tex = pScene->mTextures[i] = new aiTexture();
+
+ // skip the texture name
+ while (stream.GetI1()) {}
+
+ // read texture width and height
+ tex->mWidth = (unsigned int)stream.GetI4();
+ tex->mHeight = (unsigned int)stream.GetI4();
+
+ if (!tex->mWidth || !tex->mHeight)
+ throw DeadlyImportError("Quick3D: Invalid texture. Width or height is zero");
+
+ register unsigned int mul = tex->mWidth * tex->mHeight;
+ aiTexel* begin = tex->pcData = new aiTexel[mul];
+ aiTexel* const end = & begin [mul];
+
+ for (;begin != end; ++begin)
+ {
+ begin->r = stream.GetI1();
+ begin->g = stream.GetI1();
+ begin->b = stream.GetI1();
+ begin->a = 0xff;
+ }
+ }
+
+ break;
+
+ // scene chunk
+ case 's':
+ {
+ // skip position and rotation
+ stream.IncPtr(12);
+
+ for (unsigned int i = 0; i < 4;++i)
+ for (unsigned int a = 0; a < 4;++a)
+ pScene->mRootNode->mTransformation[i][a] = stream.GetF4();
+
+ stream.IncPtr(16);
+
+ // now setup a single camera
+ pScene->mNumCameras = 1;
+ pScene->mCameras = new aiCamera*[1];
+ aiCamera* cam = pScene->mCameras[0] = new aiCamera();
+ cam->mPosition.x = stream.GetF4();
+ cam->mPosition.y = stream.GetF4();
+ cam->mPosition.z = stream.GetF4();
+ cam->mName.Set("Q3DCamera");
+
+ // skip eye rotation for the moment
+ stream.IncPtr(12);
+
+ // read the default material color
+ fgColor .r = stream.GetF4();
+ fgColor .g = stream.GetF4();
+ fgColor .b = stream.GetF4();
+
+ // skip some unimportant properties
+ stream.IncPtr(29);
+
+ // setup a single point light with no attenuation
+ pScene->mNumLights = 1;
+ pScene->mLights = new aiLight*[1];
+ aiLight* light = pScene->mLights[0] = new aiLight();
+ light->mName.Set("Q3DLight");
+ light->mType = aiLightSource_POINT;
+
+ light->mAttenuationConstant = 1;
+ light->mAttenuationLinear = 0;
+ light->mAttenuationQuadratic = 0;
+
+ light->mColorDiffuse.r = stream.GetF4();
+ light->mColorDiffuse.g = stream.GetF4();
+ light->mColorDiffuse.b = stream.GetF4();
+
+ light->mColorSpecular = light->mColorDiffuse;
+
+
+ // We don't need the rest, but we need to know where
+ // this chunk ends.
+ unsigned int temp = (unsigned int)(stream.GetI4() * stream.GetI4());
+
+ // skip the background file name
+ while (stream.GetI1()) {}
+
+ // skip background texture data + the remaining fields
+ stream.IncPtr(temp*3 + 20); // 4 bytes of unknown data here
+
+ // TODO
+ goto outer;
+ }
+ break;
+
+ default:
+ throw DeadlyImportError("Quick3D: Unknown chunk");
+ break;
+ };
+ }
+outer:
+
+ // If we have no mesh loaded - break here
+ if (meshes.empty())
+ throw DeadlyImportError("Quick3D: No meshes loaded");
+
+ // If we have no materials loaded - generate a default mat
+ if (materials.empty())
+ {
+ DefaultLogger::get()->info("Quick3D: No material found, generating one");
+ materials.push_back(Material());
+ materials.back().diffuse = fgColor ;
+ }
+
+ // find out which materials we'll need
+ typedef std::pair<unsigned int, unsigned int> FaceIdx;
+ typedef std::vector< FaceIdx > FaceIdxArray;
+ FaceIdxArray* fidx = new FaceIdxArray[materials.size()];
+
+ unsigned int p = 0;
+ for (std::vector<Mesh>::iterator it = meshes.begin(), end = meshes.end();
+ it != end; ++it,++p)
+ {
+ unsigned int q = 0;
+ for (std::vector<Face>::iterator fit = (*it).faces.begin(), fend = (*it).faces.end();
+ fit != fend; ++fit,++q)
+ {
+ if ((*fit).mat >= materials.size())
+ {
+ DefaultLogger::get()->warn("Quick3D: Material index overflow");
+ (*fit).mat = 0;
+ }
+ if (fidx[(*fit).mat].empty())++pScene->mNumMeshes;
+ fidx[(*fit).mat].push_back( FaceIdx(p,q) );
+ }
+ }
+ pScene->mNumMaterials = pScene->mNumMeshes;
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+ pScene->mMeshes = new aiMesh*[pScene->mNumMaterials];
+
+ for (unsigned int i = 0, real = 0; i < (unsigned int)materials.size(); ++i)
+ {
+ if (fidx[i].empty())continue;
+
+ // Allocate a mesh and a material
+ aiMesh* mesh = pScene->mMeshes[real] = new aiMesh();
+ MaterialHelper* mat = new MaterialHelper();
+ pScene->mMaterials[real] = mat;
+
+ mesh->mMaterialIndex = real;
+
+ // Build the output material
+ Material& srcMat = materials[i];
+ mat->AddProperty(&srcMat.diffuse, 1,AI_MATKEY_COLOR_DIFFUSE);
+ mat->AddProperty(&srcMat.specular, 1,AI_MATKEY_COLOR_SPECULAR);
+ mat->AddProperty(&srcMat.ambient, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ // NOTE: Ignore transparency for the moment - it seems
+ // unclear how to interpret the data
+#if 0
+ if (!(minor > '0' && major == '3'))
+ srcMat.transparency = 1.0f - srcMat.transparency;
+ mat->AddProperty(&srcMat.transparency, 1, AI_MATKEY_OPACITY);
+#endif
+
+ // add shininess - Quick3D seems to use it ins its viewer
+ srcMat.transparency = 16.f;
+ mat->AddProperty(&srcMat.transparency, 1, AI_MATKEY_SHININESS);
+
+ int m = (int)aiShadingMode_Phong;
+ mat->AddProperty(&m, 1, AI_MATKEY_SHADING_MODEL);
+
+ if (srcMat.name.length)
+ mat->AddProperty(&srcMat.name,AI_MATKEY_NAME);
+
+ // Add a texture
+ if (srcMat.texIdx < pScene->mNumTextures || real < pScene->mNumTextures)
+ {
+ srcMat.name.data[0] = '*';
+ srcMat.name.length = ASSIMP_itoa10(&srcMat.name.data[1],1000,
+ (srcMat.texIdx < pScene->mNumTextures ? srcMat.texIdx : real));
+ mat->AddProperty(&srcMat.name,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+
+ mesh->mNumFaces = (unsigned int)fidx[i].size();
+ aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
+
+ // Now build the output mesh. First find out how many
+ // vertices we'll need
+ for (FaceIdxArray::const_iterator it = fidx[i].begin(),end = fidx[i].end();
+ it != end; ++it)
+ {
+ mesh->mNumVertices += (unsigned int)meshes[(*it).first].faces[
+ (*it).second].indices.size();
+ }
+
+ aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+ aiVector3D* norms = mesh->mNormals = new aiVector3D[mesh->mNumVertices];
+ aiVector3D* uv;
+ if (real < pScene->mNumTextures)
+ {
+ uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
+ mesh->mNumUVComponents[0] = 2;
+ }
+ else uv = NULL;
+
+ // Build the final array
+ unsigned int cnt = 0;
+ for (FaceIdxArray::const_iterator it = fidx[i].begin(),end = fidx[i].end();
+ it != end; ++it, ++faces)
+ {
+ Mesh& m = meshes[(*it).first];
+ Face& face = m.faces[(*it).second];
+ faces->mNumIndices = (unsigned int)face.indices.size();
+ faces->mIndices = new unsigned int [faces->mNumIndices];
+
+
+ aiVector3D faceNormal;
+ bool fnOK = false;
+
+ for (unsigned int n = 0; n < faces->mNumIndices;++n, ++cnt, ++norms, ++verts)
+ {
+ if (face.indices[n] >= m.verts.size())
+ {
+ DefaultLogger::get()->warn("Quick3D: Vertex index overflow");
+ face.indices[n] = 0;
+ }
+
+ // copy vertices
+ *verts = m.verts[ face.indices[n] ];
+
+ if (face.indices[n] >= m.normals.size() && faces->mNumIndices >= 3)
+ {
+ // we have no normal here - assign the face normal
+ if (!fnOK)
+ {
+ const aiVector3D& pV1 = m.verts[ face.indices[0] ];
+ const aiVector3D& pV2 = m.verts[ face.indices[1] ];
+ const aiVector3D& pV3 = m.verts[ face.indices.size() - 1 ];
+ faceNormal = (pV2 - pV1) ^ (pV3 - pV1).Normalize();
+ fnOK = true;
+ }
+ *norms = faceNormal;
+ }
+ else *norms = m.normals[ face.indices[n] ];
+
+ // copy texture coordinates
+ if (uv && m.uv.size())
+ {
+ if (m.prevUVIdx != 0xffffffff && m.uv.size() >= m.verts.size()) // workaround
+ {
+ *uv = m.uv[face.indices[n]];
+ }
+ else
+ {
+ if (face.uvindices[n] >= m.uv.size())
+ {
+ DefaultLogger::get()->warn("Quick3D: Texture coordinate index overflow");
+ face.uvindices[n] = 0;
+ }
+ *uv = m.uv[face.uvindices[n]];
+ }
+ uv->y = 1.f - uv->y;
+ ++uv;
+ }
+
+ // setup the new vertex index
+ faces->mIndices[n] = cnt;
+ }
+
+ }
+ ++real;
+ }
+
+ // Delete our nice helper array
+ delete[] fidx;
+
+ // Now we need to attach the meshes to the root node of the scene
+ pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+ pScene->mRootNode->mMeshes = new unsigned int [pScene->mNumMeshes];
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mRootNode->mMeshes[i] = i;
+
+ /*pScene->mRootNode->mTransformation *= aiMatrix4x4(
+ 1.f, 0.f, 0.f, 0.f,
+ 0.f, -1.f,0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 0.f, 0.f, 0.f, 1.f);*/
+
+ // Add cameras and light sources to the scene root node
+ pScene->mRootNode->mNumChildren = pScene->mNumLights+pScene->mNumCameras;
+ if (pScene->mRootNode->mNumChildren)
+ {
+ pScene->mRootNode->mChildren = new aiNode* [ pScene->mRootNode->mNumChildren ];
+
+ // the light source
+ aiNode* nd = pScene->mRootNode->mChildren[0] = new aiNode();
+ nd->mParent = pScene->mRootNode;
+ nd->mName.Set("Q3DLight");
+ nd->mTransformation = pScene->mRootNode->mTransformation;
+ nd->mTransformation.Inverse();
+
+ // camera
+ nd = pScene->mRootNode->mChildren[1] = new aiNode();
+ nd->mParent = pScene->mRootNode;
+ nd->mName.Set("Q3DCamera");
+ nd->mTransformation = pScene->mRootNode->mChildren[0]->mTransformation;
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_Q3D_IMPORTER
diff --git a/src/3rdparty/assimp/code/Q3DLoader.h b/src/3rdparty/assimp/code/Q3DLoader.h
new file mode 100644
index 000000000..fef4eac3a
--- /dev/null
+++ b/src/3rdparty/assimp/code/Q3DLoader.h
@@ -0,0 +1,135 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Q3DLoader.h
+ * @brief Declaration of the Q3D importer class.
+ */
+#ifndef AI_Q3DLOADER_H_INCLUDED
+#define AI_Q3DLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+#include <vector>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Importer class for the Quick3D Object and Scene formats.
+*/
+class Q3DImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ Q3DImporter();
+
+ /** Destructor, private as well */
+ ~Q3DImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details. */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+ struct Material
+ {
+ Material()
+ : diffuse (0.6f,0.6f,0.6f)
+ , transparency (0.f)
+ , texIdx (0xffffffff)
+ {}
+
+ aiString name;
+ aiColor3D ambient, diffuse, specular;
+ float transparency;
+
+ unsigned int texIdx;
+ };
+
+ struct Face
+ {
+ Face(unsigned int s)
+ : indices (s)
+ , uvindices (s)
+ , mat (0)
+ {
+ }
+
+ std::vector<unsigned int> indices;
+ std::vector<unsigned int> uvindices;
+ unsigned int mat;
+ };
+
+ struct Mesh
+ {
+
+ std::vector<aiVector3D> verts;
+ std::vector<aiVector3D> normals;
+ std::vector<aiVector3D> uv;
+ std::vector<Face> faces;
+
+ uint32_t prevUVIdx;
+ };
+};
+
+} // end of namespace Assimp
+
+#endif // AI_Q3DIMPORTER_H_IN
diff --git a/src/3rdparty/assimp/code/RawLoader.cpp b/src/3rdparty/assimp/code/RawLoader.cpp
new file mode 100644
index 000000000..388044a02
--- /dev/null
+++ b/src/3rdparty/assimp/code/RawLoader.cpp
@@ -0,0 +1,312 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file RawLoader.cpp
+ * @brief Implementation of the RAW importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
+
+// internal headers
+#include "RawLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// 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*, bool) const
+{
+ return SimpleExtensionCheck(pFile,"raw");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the list of all supported file extensions
+void RAWImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("raw");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void RAWImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_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_atof_move(sz,data[num]);
+ }
+ if (num != 12 && num != 9)
+ {
+ DefaultLogger::get()->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 (std::vector< MeshInformation >::iterator it = (*curGroup).meshes.begin(),
+ end = (*curGroup).meshes.end(); it != end; ++it)
+ {
+ if (length == (*it).name.length() && (length ? !::strcmp(sz,(*it).name.c_str()) : true))
+ {
+ output = &(*it);
+ 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 (std::vector< GroupInformation >::iterator it = outGroups.begin(), end = outGroups.end();
+ it != end;++it)
+ {
+ if (!(*it).meshes.empty())
+ {
+ ++pScene->mRootNode->mNumChildren;
+ pScene->mNumMeshes += (unsigned int)(*it).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 = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
+
+ pScene->mNumMaterials = pScene->mNumMeshes;
+ aiMaterial** mats = pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+
+ unsigned int meshIdx = 0;
+ for (std::vector< GroupInformation >::iterator it = outGroups.begin(), end = outGroups.end();
+ it != end;++it)
+ {
+ if ((*it).meshes.empty())continue;
+
+ aiNode* node;
+ if (pScene->mRootNode->mNumChildren)
+ {
+ node = *cc = new aiNode();
+ node->mParent = pScene->mRootNode;
+ }
+ else node = *cc;++cc;
+ node->mName.Set((*it).name);
+
+ // add all meshes
+ node->mNumMeshes = (unsigned int)(*it).meshes.size();
+ unsigned int* pi = node->mMeshes = new unsigned int[ node->mNumMeshes ];
+ for (std::vector< MeshInformation >::iterator it2 = (*it).meshes.begin(),
+ end2 = (*it).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
+ MaterialHelper* mat = new MaterialHelper();
+
+ 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/src/3rdparty/assimp/code/RawLoader.h b/src/3rdparty/assimp/code/RawLoader.h
new file mode 100644
index 000000000..47a4a5aa0
--- /dev/null
+++ b/src/3rdparty/assimp/code/RawLoader.h
@@ -0,0 +1,123 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file RAWLoader.h
+ * @brief Declaration of the RAW importer class.
+ */
+#ifndef AI_RAWLOADER_H_INCLUDED
+#define AI_RAWLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+#include <vector>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Importer class for the PovRay RAW triangle format
+*/
+class RAWImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ RAWImporter();
+
+ /** Destructor, private as well */
+ ~RAWImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+ struct MeshInformation
+ {
+ MeshInformation(const std::string& _name)
+ : name(_name)
+ {
+ vertices.reserve(100);
+ colors.reserve(100);
+ }
+
+ std::string name;
+
+ std::vector<aiVector3D> vertices;
+ std::vector<aiColor4D> colors;
+ };
+
+ struct GroupInformation
+ {
+ GroupInformation(const std::string& _name)
+ : name(_name)
+ {
+ meshes.reserve(10);
+ }
+
+ std::string name;
+ std::vector<MeshInformation> meshes;
+ };
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_IN
diff --git a/src/3rdparty/assimp/code/RemoveComments.cpp b/src/3rdparty/assimp/code/RemoveComments.cpp
new file mode 100644
index 000000000..e9b6af5d0
--- /dev/null
+++ b/src/3rdparty/assimp/code/RemoveComments.cpp
@@ -0,0 +1,108 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file RemoveComments.cpp
+ * @brief Defines the CommentRemover utility class
+ */
+
+#include "AssimpPCH.h"
+#include "RemoveComments.h"
+#include "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;
+ }
+ ++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/src/3rdparty/assimp/code/RemoveComments.h b/src/3rdparty/assimp/code/RemoveComments.h
new file mode 100644
index 000000000..54899527d
--- /dev/null
+++ b/src/3rdparty/assimp/code/RemoveComments.h
@@ -0,0 +1,88 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiAssert.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/src/3rdparty/assimp/code/RemoveRedundantMaterials.cpp b/src/3rdparty/assimp/code/RemoveRedundantMaterials.cpp
new file mode 100644
index 000000000..438c384e0
--- /dev/null
+++ b/src/3rdparty/assimp/code/RemoveRedundantMaterials.cpp
@@ -0,0 +1,208 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file RemoveRedundantMaterials.cpp
+ * @brief Implementation of the "RemoveRedundantMaterials" post processing step
+*/
+
+// internal headers
+#include "AssimpPCH.h"
+#include "RemoveRedundantMaterials.h"
+#include "ParsingUtils.h"
+#include "ProcessHelper.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+RemoveRedundantMatsProcess::RemoveRedundantMatsProcess()
+{
+ // 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)
+{
+ DefaultLogger::get()->debug("RemoveRedundantMatsProcess begin");
+
+ unsigned int iCnt = 0, unreferenced = 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;
+ ((MaterialHelper*)mat)->AddProperty(&dummy,1,"~RRM.UniqueMaterial",0,0);
+
+ // Keep this material even if no mesh references it
+ abReferenced[i] = true;
+ DefaultLogger::get()->debug(std::string("Found positive match in exclusion list: \'") + name.data + "\'");
+ }
+ }
+ }
+ }
+
+
+ // TODO: reimplement this algorithm to work in-place
+
+ unsigned int* aiMappingTable = new unsigned int[pScene->mNumMaterials];
+ 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;
+ aiHashes = new uint32_t[pScene->mNumMaterials];
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+ {
+ // if the material is not referenced ... remove it
+ if (!abReferenced[i]) {
+ ++unreferenced;
+ continue;
+ }
+
+ uint32_t me = aiHashes[i] = ((MaterialHelper*)pScene->mMaterials[i])->ComputeHash();
+ for (unsigned int a = 0; a < i;++a)
+ {
+ if (me == aiHashes[a]) {
+ ++iCnt;
+ me = 0;
+ aiMappingTable[i] = aiMappingTable[a];
+ delete pScene->mMaterials[i];
+ break;
+ }
+ }
+ if (me) {
+ aiMappingTable[i] = iNewNum++;
+ }
+ }
+ if (iCnt) {
+ // build an output material list
+ 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 all modified materials
+ const unsigned int idx = aiMappingTable[p];
+ if (ppcMaterials[idx])
+ {
+ aiString sz;
+ sz.length = ::sprintf(sz.data,"JoinedMaterial_#%i",p);
+ ((MaterialHelper*)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];
+ 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 (!iCnt)DefaultLogger::get()->debug("RemoveRedundantMatsProcess finished ");
+ else
+ {
+ char szBuffer[128]; // should be sufficiently large
+ ::sprintf(szBuffer,"RemoveRedundantMatsProcess finished. %i redundant and %i unused materials",
+ iCnt,unreferenced);
+ DefaultLogger::get()->info(szBuffer);
+ }
+}
diff --git a/src/3rdparty/assimp/code/RemoveRedundantMaterials.h b/src/3rdparty/assimp/code/RemoveRedundantMaterials.h
new file mode 100644
index 000000000..65bbf2cfb
--- /dev/null
+++ b/src/3rdparty/assimp/code/RemoveRedundantMaterials.h
@@ -0,0 +1,107 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiMesh.h"
+
+class RemoveRedundantMatsTest;
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** RemoveRedundantMatsProcess: Postprocessing steo to remove redundant
+ * materials from the imported scene.
+ */
+class ASSIMP_API RemoveRedundantMatsProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::RemoveRedundantMatsTest; // grant the unit test full access to us
+
+protected:
+ /** Constructor to be privately used by Importer */
+ RemoveRedundantMatsProcess();
+
+ /** Destructor, private as well */
+ ~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/src/3rdparty/assimp/code/RemoveVCProcess.cpp b/src/3rdparty/assimp/code/RemoveVCProcess.cpp
new file mode 100644
index 000000000..c2e42fa1c
--- /dev/null
+++ b/src/3rdparty/assimp/code/RemoveVCProcess.cpp
@@ -0,0 +1,328 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file Implementation of the post processing step to remove
+ * any parts of the mesh structure from the imported data.
+*/
+
+#include "AssimpPCH.h"
+#include "RemoveVCProcess.h"
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+RemoveVCProcess::RemoveVCProcess() : configDeleteFlags(0), mScene(0)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// 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)
+{
+ register 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)
+{
+ DefaultLogger::get()->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;
+ MaterialHelper* helper = (MaterialHelper*) 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;
+ DefaultLogger::get()->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)DefaultLogger::get()->info("RemoveVCProcess finished. Data structure cleanup has been done.");
+ else DefaultLogger::get()->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)
+ {
+ DefaultLogger::get()->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
+ register 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/src/3rdparty/assimp/code/RemoveVCProcess.h b/src/3rdparty/assimp/code/RemoveVCProcess.h
new file mode 100644
index 000000000..43693f639
--- /dev/null
+++ b/src/3rdparty/assimp/code/RemoveVCProcess.h
@@ -0,0 +1,123 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiMesh.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
+{
+ friend class Importer;
+ friend class ::RemoveVCProcessTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ RemoveVCProcess();
+
+ /** Destructor, private as well */
+ ~RemoveVCProcess();
+
+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);
+
+ // -------------------------------------------------------------------
+ /** 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 combintion 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/src/3rdparty/assimp/code/SGSpatialSort.cpp b/src/3rdparty/assimp/code/SGSpatialSort.cpp
new file mode 100644
index 000000000..705dafc93
--- /dev/null
+++ b/src/3rdparty/assimp/code/SGSpatialSort.cpp
@@ -0,0 +1,169 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file 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 "AssimpPCH.h"
+#include "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 in this strange fashion because a simple clear() would also deallocate
+ // the array which we want to avoid
+ poResults.erase( poResults.begin(), poResults.end());
+
+ // 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
+
+ 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/src/3rdparty/assimp/code/SGSpatialSort.h b/src/3rdparty/assimp/code/SGSpatialSort.h
new file mode 100644
index 000000000..e657ae3a0
--- /dev/null
+++ b/src/3rdparty/assimp/code/SGSpatialSort.h
@@ -0,0 +1,139 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** Small helper classes to optimise finding vertizes close to a given location
+ */
+#ifndef AI_D3DSSPATIALSORT_H_INC
+#define AI_D3DSSPATIALSORT_H_INC
+
+#include <vector>
+#include "../include/aiTypes.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 SGSpatialSort
+{
+public:
+
+ SGSpatialSort();
+
+ // -------------------------------------------------------------------
+ /** Construction from a given face array, handling smoothing groups
+ * properly
+ */
+ 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 precalculated 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() { /** intentionally not initialized.*/ }
+ Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance,uint32_t pSG)
+ :
+ mIndex( pIndex),
+ mPosition( pPosition),
+ mSmoothGroups (pSG),
+ mDistance( pDistance)
+ { }
+
+ 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/src/3rdparty/assimp/code/SMDLoader.cpp b/src/3rdparty/assimp/code/SMDLoader.cpp
new file mode 100644
index 000000000..d5781002d
--- /dev/null
+++ b/src/3rdparty/assimp/code/SMDLoader.cpp
@@ -0,0 +1,1135 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file SMDLoader.cpp
+ * @brief Implementation of the SMD importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
+
+// internal headers
+#include "SMDLoader.h"
+#include "fast_atof.h"
+#include "SkeletonMeshBuilder.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+SMDImporter::SMDImporter() : configFrameID(0), mBuffer(0), pScene(0), iFileSize(0), iSmallestFrame(0), dLengthOfAnim(0), bHasUVs(false), iLineNumber(0)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+SMDImporter::~SMDImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool SMDImporter::CanRead( const std::string& pFile, IOSystem*, bool) const
+{
+ // fixme: auto format detection
+ return SimpleExtensionCheck(pFile,"smd","vta");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all supported file extensions
+void SMDImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("smd");
+ extensions.insert("vta");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void SMDImporter::SetupProperties(const Importer* pImp)
+{
+ // The
+ // AI_CONFIG_IMPORT_SMD_KEYFRAME option overrides the
+ // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_SMD_KEYFRAME,0xffffffff);
+ if (0xffffffff == configFrameID) {
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void SMDImporter::InternReadFile(
+ const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open SMD/VTA file " + pFile + ".");
+ }
+
+ iFileSize = (unsigned int)file->FileSize();
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ this->pScene = pScene;
+
+ std::vector<char> buff(iFileSize+1);
+ TextFileToBuffer(file.get(),buff);
+ mBuffer = &buff[0];
+
+ iSmallestFrame = (1 << 31);
+ bHasUVs = true;
+ iLineNumber = 1;
+
+ // Reserve enough space for ... hm ... 10 textures
+ aszTextures.reserve(10);
+
+ // Reserve enough space for ... hm ... 1000 triangles
+ asTriangles.reserve(1000);
+
+ // Reserve enough space for ... hm ... 20 bones
+ asBones.reserve(20);
+
+
+ // parse the file ...
+ ParseFile();
+
+ // If there are no triangles it seems to be an animation SMD,
+ // containing only the animation skeleton.
+ if (asTriangles.empty())
+ {
+ if (asBones.empty())
+ {
+ throw DeadlyImportError("SMD: No triangles and no bones have "
+ "been found in the file. This file seems to be invalid.");
+ }
+
+ // Set the flag in the scene structure which indicates
+ // that there is nothing than an animation skeleton
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ }
+
+ if (!asBones.empty())
+ {
+ // Check whether all bones have been initialized
+ for (std::vector<SMD::Bone>::const_iterator
+ i = asBones.begin();
+ i != asBones.end();++i)
+ {
+ if (!(*i).mName.length())
+ {
+ DefaultLogger::get()->warn("SMD: Not all bones have been initialized");
+ break;
+ }
+ }
+
+ // now fix invalid time values and make sure the animation starts at frame 0
+ FixTimeValues();
+
+ // compute absolute bone transformation matrices
+ // ComputeAbsoluteBoneTransformations();
+ }
+
+ if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE))
+ {
+ // create output meshes
+ CreateOutputMeshes();
+
+ // build an output material list
+ CreateOutputMaterials();
+ }
+
+ // build the output animation
+ CreateOutputAnimations();
+
+ // build output nodes (bones are added as empty dummy nodes)
+ CreateOutputNodes();
+
+ if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)
+ {
+ SkeletonMeshBuilder skeleton(pScene);
+ }
+}
+// ------------------------------------------------------------------------------------------------
+// Write an error message with line number to the log file
+void SMDImporter::LogErrorNoThrow(const char* msg)
+{
+ char szTemp[1024];
+ ai_assert(strlen(msg) < 1000);
+ sprintf(szTemp,"Line %i: %s",iLineNumber,msg);
+ DefaultLogger::get()->error(szTemp);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Write a warning with line number to the log file
+void SMDImporter::LogWarning(const char* msg)
+{
+ char szTemp[1024];
+ ai_assert(strlen(msg) < 1000);
+ sprintf(szTemp,"Line %i: %s",iLineNumber,msg);
+ DefaultLogger::get()->warn(szTemp);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Fix invalid time values in the file
+void SMDImporter::FixTimeValues()
+{
+ double dDelta = (double)iSmallestFrame;
+ double dMax = 0.0f;
+ for (std::vector<SMD::Bone>::iterator
+ iBone = asBones.begin();
+ iBone != asBones.end();++iBone)
+ {
+ for (std::vector<SMD::Bone::Animation::MatrixKey>::iterator
+ iKey = (*iBone).sAnim.asKeys.begin();
+ iKey != (*iBone).sAnim.asKeys.end();++iKey)
+ {
+ (*iKey).dTime -= dDelta;
+ dMax = std::max(dMax, (*iKey).dTime);
+ }
+ }
+ dLengthOfAnim = dMax;
+}
+
+// ------------------------------------------------------------------------------------------------
+// create output meshes
+void SMDImporter::CreateOutputMeshes()
+{
+ if (aszTextures.empty())
+ aszTextures.push_back(std::string());
+
+ // we need to sort all faces by their material index
+ // in opposition to other loaders we can be sure that each
+ // material is at least used once.
+ pScene->mNumMeshes = (unsigned int) aszTextures.size();
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+
+ typedef std::vector<unsigned int> FaceList;
+ FaceList* aaiFaces = new FaceList[pScene->mNumMeshes];
+
+ // approximate the space that will be required
+ unsigned int iNum = (unsigned int)asTriangles.size() / pScene->mNumMeshes;
+ iNum += iNum >> 1;
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ aaiFaces[i].reserve(iNum);
+
+
+ // collect all faces
+ iNum = 0;
+ for (std::vector<SMD::Face>::const_iterator
+ iFace = asTriangles.begin();
+ iFace != asTriangles.end();++iFace,++iNum)
+ {
+ if (0xffffffff == (*iFace).iTexture)aaiFaces[(*iFace).iTexture].push_back( 0 );
+ else if ((*iFace).iTexture >= aszTextures.size())
+ {
+ DefaultLogger::get()->error("[SMD/VTA] Material index overflow in face");
+ aaiFaces[(*iFace).iTexture].push_back((unsigned int)aszTextures.size()-1);
+ }
+ else aaiFaces[(*iFace).iTexture].push_back(iNum);
+ }
+
+ // now create the output meshes
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ {
+ aiMesh*& pcMesh = pScene->mMeshes[i] = new aiMesh();
+ ai_assert(!aaiFaces[i].empty()); // should not be empty ...
+
+ pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+ pcMesh->mNumVertices = (unsigned int)aaiFaces[i].size()*3;
+ pcMesh->mNumFaces = (unsigned int)aaiFaces[i].size();
+ pcMesh->mMaterialIndex = i;
+
+ // storage for bones
+ typedef std::pair<unsigned int,float> TempWeightListEntry;
+ typedef std::vector< TempWeightListEntry > TempBoneWeightList;
+
+ TempBoneWeightList* aaiBones = new TempBoneWeightList[asBones.size()]();
+
+ // try to reserve enough memory without wasting too much
+ for (unsigned int iBone = 0; iBone < asBones.size();++iBone)
+ {
+ aaiBones[iBone].reserve(pcMesh->mNumVertices/asBones.size());
+ }
+
+ // allocate storage
+ pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+ aiVector3D* pcNormals = pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+ aiVector3D* pcVerts = pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+
+ aiVector3D* pcUVs = NULL;
+ if (bHasUVs)
+ {
+ pcUVs = pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNumUVComponents[0] = 2;
+ }
+
+ iNum = 0;
+ for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace)
+ {
+ pcMesh->mFaces[iFace].mIndices = new unsigned int[3];
+ pcMesh->mFaces[iFace].mNumIndices = 3;
+
+ // fill the vertices
+ unsigned int iSrcFace = aaiFaces[i][iFace];
+ SMD::Face& face = asTriangles[iSrcFace];
+
+ *pcVerts++ = face.avVertices[0].pos;
+ *pcVerts++ = face.avVertices[1].pos;
+ *pcVerts++ = face.avVertices[2].pos;
+
+ // fill the normals
+ *pcNormals++ = face.avVertices[0].nor;
+ *pcNormals++ = face.avVertices[1].nor;
+ *pcNormals++ = face.avVertices[2].nor;
+
+ // fill the texture coordinates
+ if (pcUVs)
+ {
+ *pcUVs++ = face.avVertices[0].uv;
+ *pcUVs++ = face.avVertices[1].uv;
+ *pcUVs++ = face.avVertices[2].uv;
+ }
+
+ for (unsigned int iVert = 0; iVert < 3;++iVert)
+ {
+ float fSum = 0.0f;
+ for (unsigned int iBone = 0;iBone < face.avVertices[iVert].aiBoneLinks.size();++iBone)
+ {
+ TempWeightListEntry& pairval = face.avVertices[iVert].aiBoneLinks[iBone];
+
+ // FIX: The second check is here just to make sure we won't
+ // assign more than one weight to a single vertex index
+ if (pairval.first >= asBones.size() ||
+ pairval.first == face.avVertices[iVert].iParentNode)
+ {
+ DefaultLogger::get()->error("[SMD/VTA] Bone index overflow. "
+ "The bone index will be ignored, the weight will be assigned "
+ "to the vertex' parent node");
+ continue;
+ }
+ aaiBones[pairval.first].push_back(TempWeightListEntry(iNum,pairval.second));
+ fSum += pairval.second;
+ }
+ // ******************************************************************
+ // If the sum of all vertex weights is not 1.0 we must assign
+ // the rest to the vertex' parent node. Well, at least the doc says
+ // we should ...
+ // FIX: We use 0.975 as limit, floating-point inaccuracies seem to
+ // be very strong in some SMD exporters. Furthermore it is possible
+ // that the parent of a vertex is 0xffffffff (if the corresponding
+ // entry in the file was unreadable)
+ // ******************************************************************
+ if (fSum < 0.975f && face.avVertices[iVert].iParentNode != 0xffffffff)
+ {
+ if (face.avVertices[iVert].iParentNode >= asBones.size())
+ {
+ DefaultLogger::get()->error("[SMD/VTA] Bone index overflow. "
+ "The index of the vertex parent bone is invalid. "
+ "The remaining weights will be normalized to 1.0");
+
+ if (fSum)
+ {
+ fSum = 1 / fSum;
+ for (unsigned int iBone = 0;iBone < face.avVertices[iVert].aiBoneLinks.size();++iBone)
+ {
+ TempWeightListEntry& pairval = face.avVertices[iVert].aiBoneLinks[iBone];
+ if (pairval.first >= asBones.size())continue;
+ aaiBones[pairval.first].back().second *= fSum;
+ }
+ }
+ }
+ else
+ {
+ aaiBones[face.avVertices[iVert].iParentNode].push_back(
+ TempWeightListEntry(iNum,1.0f-fSum));
+ }
+ }
+ pcMesh->mFaces[iFace].mIndices[iVert] = iNum++;
+ }
+ }
+
+ // now build all bones of the mesh
+ iNum = 0;
+ for (unsigned int iBone = 0; iBone < asBones.size();++iBone)
+ if (!aaiBones[iBone].empty())++iNum;
+
+ if (false && iNum)
+ {
+ pcMesh->mNumBones = iNum;
+ pcMesh->mBones = new aiBone*[pcMesh->mNumBones];
+ iNum = 0;
+ for (unsigned int iBone = 0; iBone < asBones.size();++iBone)
+ {
+ if (aaiBones[iBone].empty())continue;
+ aiBone*& bone = pcMesh->mBones[iNum] = new aiBone();
+
+ bone->mNumWeights = (unsigned int)aaiBones[iBone].size();
+ bone->mWeights = new aiVertexWeight[bone->mNumWeights];
+ bone->mOffsetMatrix = asBones[iBone].mOffsetMatrix;
+ bone->mName.Set( asBones[iBone].mName );
+
+ asBones[iBone].bIsUsed = true;
+
+ for (unsigned int iWeight = 0; iWeight < bone->mNumWeights;++iWeight)
+ {
+ bone->mWeights[iWeight].mVertexId = aaiBones[iBone][iWeight].first;
+ bone->mWeights[iWeight].mWeight = aaiBones[iBone][iWeight].second;
+ }
+ ++iNum;
+ }
+ }
+ delete[] aaiBones;
+ }
+ delete[] aaiFaces;
+}
+
+// ------------------------------------------------------------------------------------------------
+// add bone child nodes
+void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent)
+{
+ ai_assert(NULL != pcNode && 0 == pcNode->mNumChildren && NULL == pcNode->mChildren);
+
+ // first count ...
+ for (unsigned int i = 0; i < asBones.size();++i)
+ {
+ SMD::Bone& bone = asBones[i];
+ if (bone.iParent == iParent)++pcNode->mNumChildren;
+ }
+
+ // now allocate the output array
+ pcNode->mChildren = new aiNode*[pcNode->mNumChildren];
+
+ // and fill all subnodes
+ unsigned int qq = 0;
+ for (unsigned int i = 0; i < asBones.size();++i)
+ {
+ SMD::Bone& bone = asBones[i];
+ if (bone.iParent != iParent)continue;
+
+ aiNode* pc = pcNode->mChildren[qq++] = new aiNode();
+ pc->mName.Set(bone.mName);
+
+ // store the local transformation matrix of the bind pose
+ pc->mTransformation = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrix;
+ pc->mParent = pcNode;
+
+ // add children to this node, too
+ AddBoneChildren(pc,i);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// create output nodes
+void SMDImporter::CreateOutputNodes()
+{
+ pScene->mRootNode = new aiNode();
+ if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE))
+ {
+ // create one root node that renders all meshes
+ pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+ pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mRootNode->mMeshes[i] = i;
+ }
+
+ // now add all bones as dummy sub nodes to the graph
+ // AddBoneChildren(pScene->mRootNode,(uint32_t)-1);
+
+ // if we have only one bone we can even remove the root node
+ if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE &&
+ 1 == pScene->mRootNode->mNumChildren)
+ {
+ aiNode* pcOldRoot = pScene->mRootNode;
+ pScene->mRootNode = pcOldRoot->mChildren[0];
+ pcOldRoot->mChildren[0] = NULL;
+ delete pcOldRoot;
+
+ pScene->mRootNode->mParent = NULL;
+ }
+ else
+ {
+ ::strcpy(pScene->mRootNode->mName.data, "<SMD_root>");
+ pScene->mRootNode->mName.length = 10;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// create output animations
+void SMDImporter::CreateOutputAnimations()
+{
+ unsigned int iNumBones = 0;
+ for (std::vector<SMD::Bone>::const_iterator
+ i = asBones.begin();
+ i != asBones.end();++i)
+ {
+ if ((*i).bIsUsed)++iNumBones;
+ }
+ if (!iNumBones)
+ {
+ // just make sure this case doesn't occur ... (it could occur
+ // if the file was invalid)
+ return;
+ }
+
+ pScene->mNumAnimations = 1;
+ pScene->mAnimations = new aiAnimation*[1];
+ aiAnimation*& anim = pScene->mAnimations[0] = new aiAnimation();
+
+ anim->mDuration = dLengthOfAnim;
+ anim->mNumChannels = iNumBones;
+ anim->mTicksPerSecond = 25.0; // FIXME: is this correct?
+
+ aiNodeAnim** pp = anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
+
+ // now build valid keys
+ unsigned int a = 0;
+ for (std::vector<SMD::Bone>::const_iterator
+ i = asBones.begin();
+ i != asBones.end();++i)
+ {
+ if (!(*i).bIsUsed)continue;
+
+ aiNodeAnim* p = pp[a] = new aiNodeAnim();
+
+ // copy the name of the bone
+ p->mNodeName.Set( i->mName);
+
+ p->mNumRotationKeys = (unsigned int) (*i).sAnim.asKeys.size();
+ if (p->mNumRotationKeys)
+ {
+ p->mNumPositionKeys = p->mNumRotationKeys;
+ aiVectorKey* pVecKeys = p->mPositionKeys = new aiVectorKey[p->mNumRotationKeys];
+ aiQuatKey* pRotKeys = p->mRotationKeys = new aiQuatKey[p->mNumRotationKeys];
+
+ for (std::vector<SMD::Bone::Animation::MatrixKey>::const_iterator
+ qq = (*i).sAnim.asKeys.begin();
+ qq != (*i).sAnim.asKeys.end(); ++qq)
+ {
+ pRotKeys->mTime = pVecKeys->mTime = (*qq).dTime;
+
+ // compute the rotation quaternion from the euler angles
+ pRotKeys->mValue = aiQuaternion( (*qq).vRot.x, (*qq).vRot.y, (*qq).vRot.z );
+ pVecKeys->mValue = (*qq).vPos;
+
+ ++pVecKeys; ++pRotKeys;
+ }
+ }
+ ++a;
+
+ // there are no scaling keys ...
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void SMDImporter::ComputeAbsoluteBoneTransformations()
+{
+ // For each bone: determine the key with the lowest time value
+ // theoretically the SMD format should have all keyframes
+ // in order. However, I've seen a file where this wasn't true.
+ for (unsigned int i = 0; i < asBones.size();++i)
+ {
+ SMD::Bone& bone = asBones[i];
+
+ uint32_t iIndex = 0;
+ double dMin = 10e10;
+ for (unsigned int i = 0; i < bone.sAnim.asKeys.size();++i)
+ {
+ double d = std::min(bone.sAnim.asKeys[i].dTime,dMin);
+ if (d < dMin)
+ {
+ dMin = d;
+ iIndex = i;
+ }
+ }
+ bone.sAnim.iFirstTimeKey = iIndex;
+ }
+
+ unsigned int iParent = 0;
+ while (iParent < asBones.size())
+ {
+ for (unsigned int iBone = 0; iBone < asBones.size();++iBone)
+ {
+ SMD::Bone& bone = asBones[iBone];
+
+ if (iParent == bone.iParent)
+ {
+ SMD::Bone& parentBone = asBones[iParent];
+
+
+ uint32_t iIndex = bone.sAnim.iFirstTimeKey;
+ const aiMatrix4x4& mat = bone.sAnim.asKeys[iIndex].matrix;
+ aiMatrix4x4& matOut = bone.sAnim.asKeys[iIndex].matrixAbsolute;
+
+ // The same for the parent bone ...
+ iIndex = parentBone.sAnim.iFirstTimeKey;
+ const aiMatrix4x4& mat2 = parentBone.sAnim.asKeys[iIndex].matrixAbsolute;
+
+ // Compute the absolute transformation matrix
+ matOut = mat * mat2;
+ }
+ }
+ ++iParent;
+ }
+
+ // Store the inverse of the absolute transformation matrix
+ // of the first key as bone offset matrix
+ for (iParent = 0; iParent < asBones.size();++iParent)
+ {
+ SMD::Bone& bone = asBones[iParent];
+ bone.mOffsetMatrix = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrixAbsolute;
+ bone.mOffsetMatrix.Inverse();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// create output materials
+void SMDImporter::CreateOutputMaterials()
+{
+ pScene->mNumMaterials = (unsigned int)aszTextures.size();
+ pScene->mMaterials = new aiMaterial*[std::max(1u, pScene->mNumMaterials)];
+
+ for (unsigned int iMat = 0; iMat < pScene->mNumMaterials;++iMat)
+ {
+ MaterialHelper* pcMat = new MaterialHelper();
+ pScene->mMaterials[iMat] = pcMat;
+
+ aiString szName;
+ szName.length = (size_t)::sprintf(szName.data,"Texture_%i",iMat);
+ pcMat->AddProperty(&szName,AI_MATKEY_NAME);
+
+ if (aszTextures[iMat].length())
+ {
+ ::strcpy(szName.data, aszTextures[iMat].c_str() );
+ szName.length = aszTextures[iMat].length();
+ pcMat->AddProperty(&szName,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ }
+
+ // create a default material if necessary
+ if (0 == pScene->mNumMaterials)
+ {
+ pScene->mNumMaterials = 1;
+
+ MaterialHelper* pcHelper = new MaterialHelper();
+ pScene->mMaterials[0] = pcHelper;
+
+ int iMode = (int)aiShadingMode_Gouraud;
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.7f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ aiString szName;
+ szName.Set(AI_DEFAULT_MATERIAL_NAME);
+ pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse the file
+void SMDImporter::ParseFile()
+{
+ const char* szCurrent = mBuffer;
+
+ // read line per line ...
+ while (true)
+ {
+ if (!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break;
+
+ // "version <n> \n", <n> should be 1 for hl and hl SMD files
+ if (TokenMatch(szCurrent,"version",7))
+ {
+ if (!SkipSpaces(szCurrent,&szCurrent)) break;
+ if (1 != strtol10(szCurrent,&szCurrent))
+ {
+ DefaultLogger::get()->warn("SMD.version is not 1. This "
+ "file format is not known. Continuing happily ...");
+ }
+ continue;
+ }
+ // "nodes\n" - Starts the node section
+ if (TokenMatch(szCurrent,"nodes",5))
+ {
+ ParseNodesSection(szCurrent,&szCurrent);
+ continue;
+ }
+ // "triangles\n" - Starts the triangle section
+ if (TokenMatch(szCurrent,"triangles",9))
+ {
+ ParseTrianglesSection(szCurrent,&szCurrent);
+ continue;
+ }
+ // "vertexanimation\n" - Starts the vertex animation section
+ if (TokenMatch(szCurrent,"vertexanimation",15))
+ {
+ bHasUVs = false;
+ ParseVASection(szCurrent,&szCurrent);
+ continue;
+ }
+ // "skeleton\n" - Starts the skeleton section
+ if (TokenMatch(szCurrent,"skeleton",8))
+ {
+ ParseSkeletonSection(szCurrent,&szCurrent);
+ continue;
+ }
+ SkipLine(szCurrent,&szCurrent);
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned int SMDImporter::GetTextureIndex(const std::string& filename)
+{
+ unsigned int iIndex = 0;
+ for (std::vector<std::string>::const_iterator
+ i = aszTextures.begin();
+ i != aszTextures.end();++i,++iIndex)
+ {
+ // case-insensitive ... it's a path
+ if (0 == ASSIMP_stricmp ( filename.c_str(),(*i).c_str()))return iIndex;
+ }
+ iIndex = (unsigned int)aszTextures.size();
+ aszTextures.push_back(filename);
+ return iIndex;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse the nodes section of the file
+void SMDImporter::ParseNodesSection(const char* szCurrent,
+ const char** szCurrentOut)
+{
+ while (true)
+ {
+ // "end\n" - Ends the nodes section
+ if (0 == ASSIMP_strincmp(szCurrent,"end",3) &&
+ IsSpaceOrNewLine(*(szCurrent+3)))
+ {
+ szCurrent += 4;
+ break;
+ }
+ ParseNodeInfo(szCurrent,&szCurrent);
+ }
+ SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+ *szCurrentOut = szCurrent;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse the triangles section of the file
+void SMDImporter::ParseTrianglesSection(const char* szCurrent,
+ const char** szCurrentOut)
+{
+ // Parse a triangle, parse another triangle, parse the next triangle ...
+ // and so on until we reach a token that looks quite similar to "end"
+ while (true)
+ {
+ if (!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break;
+
+ // "end\n" - Ends the triangles section
+ if (TokenMatch(szCurrent,"end",3))
+ break;
+ ParseTriangle(szCurrent,&szCurrent);
+ }
+ SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+ *szCurrentOut = szCurrent;
+}
+// ------------------------------------------------------------------------------------------------
+// Parse the vertex animation section of the file
+void SMDImporter::ParseVASection(const char* szCurrent,
+ const char** szCurrentOut)
+{
+ unsigned int iCurIndex = 0;
+ while (true)
+ {
+ if (!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break;
+
+ // "end\n" - Ends the "vertexanimation" section
+ if (TokenMatch(szCurrent,"end",3))
+ break;
+
+ // "time <n>\n"
+ if (TokenMatch(szCurrent,"time",4))
+ {
+ // NOTE: The doc says that time values COULD be negative ...
+ // NOTE2: this is the shape key -> valve docs
+ int iTime = 0;
+ if (!ParseSignedInt(szCurrent,&szCurrent,iTime) || configFrameID != (unsigned int)iTime)break;
+ SkipLine(szCurrent,&szCurrent);
+ }
+ else
+ {
+ if (0 == iCurIndex)
+ {
+ asTriangles.push_back(SMD::Face());
+ }
+ if (++iCurIndex == 3)iCurIndex = 0;
+ ParseVertex(szCurrent,&szCurrent,asTriangles.back().avVertices[iCurIndex],true);
+ }
+ }
+
+ if (iCurIndex != 2 && !asTriangles.empty())
+ {
+ // we want to no degenerates, so throw this triangle away
+ asTriangles.pop_back();
+ }
+
+ SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+ *szCurrentOut = szCurrent;
+}
+// ------------------------------------------------------------------------------------------------
+// Parse the skeleton section of the file
+void SMDImporter::ParseSkeletonSection(const char* szCurrent,
+ const char** szCurrentOut)
+{
+ int iTime = 0;
+ while (true)
+ {
+ if (!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break;
+
+ // "end\n" - Ends the skeleton section
+ if (TokenMatch(szCurrent,"end",3))
+ break;
+
+ // "time <n>\n" - Specifies the current animation frame
+ else if (TokenMatch(szCurrent,"time",4))
+ {
+ // NOTE: The doc says that time values COULD be negative ...
+ if (!ParseSignedInt(szCurrent,&szCurrent,iTime))break;
+
+ iSmallestFrame = std::min(iSmallestFrame,iTime);
+ SkipLine(szCurrent,&szCurrent);
+ }
+ else ParseSkeletonElement(szCurrent,&szCurrent,iTime);
+ }
+ *szCurrentOut = szCurrent;
+}
+
+// ------------------------------------------------------------------------------------------------
+#define SMDI_PARSE_RETURN { \
+ SkipLine(szCurrent,&szCurrent); \
+ *szCurrentOut = szCurrent; \
+ return; \
+}
+// ------------------------------------------------------------------------------------------------
+// Parse a node line
+void SMDImporter::ParseNodeInfo(const char* szCurrent,
+ const char** szCurrentOut)
+{
+ unsigned int iBone = 0;
+ SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+ if (!ParseUnsignedInt(szCurrent,&szCurrent,iBone) || !SkipSpaces(szCurrent,&szCurrent))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone index");
+ SMDI_PARSE_RETURN;
+ }
+ // add our bone to the list
+ if (iBone >= asBones.size())asBones.resize(iBone+1);
+ SMD::Bone& bone = asBones[iBone];
+
+ bool bQuota = true;
+ if ('\"' != *szCurrent)
+ {
+ LogWarning("Bone name is expcted to be enclosed in "
+ "double quotation marks. ");
+ bQuota = false;
+ }
+ else ++szCurrent;
+
+ const char* szEnd = szCurrent;
+ while (true)
+ {
+ if (bQuota && '\"' == *szEnd)
+ {
+ iBone = (unsigned int)(szEnd - szCurrent);
+ ++szEnd;
+ break;
+ }
+ else if (IsSpaceOrNewLine(*szEnd))
+ {
+ iBone = (unsigned int)(szEnd - szCurrent);
+ break;
+ }
+ else if (!(*szEnd))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone name");
+ SMDI_PARSE_RETURN;
+ }
+ ++szEnd;
+ }
+ bone.mName = std::string(szCurrent,iBone);
+ szCurrent = szEnd;
+
+ // the only negative bone parent index that could occur is -1 AFAIK
+ if (!ParseSignedInt(szCurrent,&szCurrent,(int&)bone.iParent))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone parent index. Assuming -1");
+ SMDI_PARSE_RETURN;
+ }
+
+ // go to the beginning of the next line
+ SMDI_PARSE_RETURN;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse a skeleton element
+void SMDImporter::ParseSkeletonElement(const char* szCurrent,
+ const char** szCurrentOut,int iTime)
+{
+ aiVector3D vPos;
+ aiVector3D vRot;
+
+ unsigned int iBone = 0;
+ if (!ParseUnsignedInt(szCurrent,&szCurrent,iBone))
+ {
+ DefaultLogger::get()->error("Unexpected EOF/EOL while parsing bone index");
+ SMDI_PARSE_RETURN;
+ }
+ if (iBone >= asBones.size())
+ {
+ LogErrorNoThrow("Bone index in skeleton section is out of range");
+ SMDI_PARSE_RETURN;
+ }
+ SMD::Bone& bone = asBones[iBone];
+
+ bone.sAnim.asKeys.push_back(SMD::Bone::Animation::MatrixKey());
+ SMD::Bone::Animation::MatrixKey& key = bone.sAnim.asKeys.back();
+
+ key.dTime = (double)iTime;
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vPos.x))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.x");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vPos.y))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.y");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vPos.z))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.z");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vRot.x))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.x");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vRot.y))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.y");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vRot.z))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.z");
+ SMDI_PARSE_RETURN;
+ }
+ // build the transformation matrix of the key
+ key.matrix.FromEulerAnglesXYZ(vRot.x,vRot.y,vRot.z);
+ {
+ aiMatrix4x4 mTemp;
+ mTemp.a4 = vPos.x;
+ mTemp.b4 = vPos.y;
+ mTemp.c4 = vPos.z;
+ key.matrix = key.matrix * mTemp;
+ }
+
+ // go to the beginning of the next line
+ SMDI_PARSE_RETURN;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse a triangle
+void SMDImporter::ParseTriangle(const char* szCurrent,
+ const char** szCurrentOut)
+{
+ asTriangles.push_back(SMD::Face());
+ SMD::Face& face = asTriangles.back();
+
+ if (!SkipSpaces(szCurrent,&szCurrent))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing a triangle");
+ return;
+ }
+
+ // read the texture file name
+ const char* szLast = szCurrent;
+ while (!IsSpaceOrNewLine(*szCurrent++)) {}
+
+ // ... and get the index that belongs to this file name
+ face.iTexture = GetTextureIndex(std::string(szLast,(uintptr_t)szCurrent-(uintptr_t)szLast));
+
+ SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+
+ // load three vertices
+ for (unsigned int iVert = 0; iVert < 3;++iVert)
+ {
+ ParseVertex(szCurrent,&szCurrent,
+ face.avVertices[iVert]);
+ }
+ *szCurrentOut = szCurrent;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse a float
+bool SMDImporter::ParseFloat(const char* szCurrent,
+ const char** szCurrentOut, float& out)
+{
+ if (!SkipSpaces(&szCurrent))
+ return false;
+
+ *szCurrentOut = fast_atof_move(szCurrent,out);
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse an unsigned int
+bool SMDImporter::ParseUnsignedInt(const char* szCurrent,
+ const char** szCurrentOut, unsigned int& out)
+{
+ if (!SkipSpaces(&szCurrent))
+ return false;
+
+ out = strtol10(szCurrent,szCurrentOut);
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse a signed int
+bool SMDImporter::ParseSignedInt(const char* szCurrent,
+ const char** szCurrentOut, int& out)
+{
+ if (!SkipSpaces(&szCurrent))
+ return false;
+
+ out = strtol10s(szCurrent,szCurrentOut);
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse a vertex
+void SMDImporter::ParseVertex(const char* szCurrent,
+ const char** szCurrentOut, SMD::Vertex& vertex,
+ bool bVASection /*= false*/)
+{
+ if (SkipSpaces(&szCurrent) && IsLineEnd(*szCurrent))
+ {
+ SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+ return ParseVertex(szCurrent,szCurrentOut,vertex,bVASection);
+ }
+ if (!ParseSignedInt(szCurrent,&szCurrent,(int&)vertex.iParentNode))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.parent");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.x))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.x");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.y))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.y");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.z))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.z");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.x))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.x");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.y))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.y");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.z))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.z");
+ SMDI_PARSE_RETURN;
+ }
+
+ if (bVASection)SMDI_PARSE_RETURN;
+
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.x))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.x");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.y))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.y");
+ SMDI_PARSE_RETURN;
+ }
+
+ // now read the number of bones affecting this vertex
+ // all elements from now are fully optional, we don't need them
+ unsigned int iSize = 0;
+ if (!ParseUnsignedInt(szCurrent,&szCurrent,iSize))SMDI_PARSE_RETURN;
+ vertex.aiBoneLinks.resize(iSize,std::pair<unsigned int, float>(0,0.0f));
+
+ for (std::vector<std::pair<unsigned int, float> >::iterator
+ i = vertex.aiBoneLinks.begin();
+ i != vertex.aiBoneLinks.end();++i)
+ {
+ if (!ParseUnsignedInt(szCurrent,&szCurrent,(*i).first))
+ SMDI_PARSE_RETURN;
+ if (!ParseFloat(szCurrent,&szCurrent,(*i).second))
+ SMDI_PARSE_RETURN;
+ }
+
+ // go to the beginning of the next line
+ SMDI_PARSE_RETURN;
+}
+
+#endif // !! ASSIMP_BUILD_NO_SMD_IMPORTER
diff --git a/src/3rdparty/assimp/code/SMDLoader.h b/src/3rdparty/assimp/code/SMDLoader.h
new file mode 100644
index 000000000..7aa714d94
--- /dev/null
+++ b/src/3rdparty/assimp/code/SMDLoader.h
@@ -0,0 +1,420 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file SMDLoader.h
+ * @brief Defintion of the Valve SMD file format
+ */
+
+#ifndef AI_SMDLOADER_H_INCLUDED
+#define AI_SMDLOADER_H_INCLUDED
+
+// internal headers
+#include "BaseImporter.h"
+#include "ParsingUtils.h"
+
+// public Assimp headers
+#include "../include/aiTypes.h"
+#include "../include/aiTexture.h"
+#include "../include/aiAnim.h"
+#include "../include/aiMaterial.h"
+struct aiNode;
+
+// STL headers
+#include <vector>
+
+namespace Assimp {
+class MaterialHelper;
+
+namespace SMD {
+
+// ---------------------------------------------------------------------------
+/** Data structure for a vertex in a SMD file
+*/
+struct Vertex
+{
+ Vertex() : iParentNode(0xffffffff)
+ {}
+
+ //! Vertex position, normal and texture coordinate
+ aiVector3D pos,nor,uv;
+
+ //! Vertex parent node
+ unsigned int iParentNode;
+
+ //! Links to bones: pair.first is the bone index,
+ //! pair.second is the vertex weight.
+ //! WARN: The remaining weight (to reach 1.0f) is assigned
+ //! to the parent node/bone
+ std::vector<std::pair<unsigned int, float> > aiBoneLinks;
+};
+
+// ---------------------------------------------------------------------------
+/** Data structure for a face in a SMD file
+*/
+struct Face
+{
+ Face() : iTexture(0x0)
+ {}
+
+ //! Texture index for the face
+ unsigned int iTexture;
+
+ //! The three vertices of the face
+ Vertex avVertices[3];
+};
+
+// ---------------------------------------------------------------------------
+/** Data structure for a bone in a SMD file
+*/
+struct Bone
+{
+ //! Default constructor
+ Bone() : iParent(0xffffffff), bIsUsed(false)
+ {
+ }
+
+ //! Destructor
+ ~Bone()
+ {
+ }
+
+ //! Name of the bone
+ std::string mName;
+
+ //! Parent of the bone
+ uint32_t iParent;
+
+ //! Animation of the bone
+ struct Animation
+ {
+ //! Public default constructor
+ Animation() : iFirstTimeKey(0)
+ {
+ asKeys.reserve(20);
+ }
+
+ //! Data structure for a matrix key
+ struct MatrixKey
+ {
+ //! Matrix at this time
+ aiMatrix4x4 matrix;
+
+ //! Absolute transformation matrix
+ aiMatrix4x4 matrixAbsolute;
+
+ //! Position
+ aiVector3D vPos;
+
+ //! Rotation (euler angles)
+ aiVector3D vRot;
+
+ //! Current time. may be negative, this
+ //! will be fixed later
+ double dTime;
+ };
+
+ //! Index of the key with the smallest time value
+ uint32_t iFirstTimeKey;
+
+ //! Array of matrix keys
+ std::vector<MatrixKey> asKeys;
+
+ } sAnim;
+
+ //! Offset matrix of the bone
+ aiMatrix4x4 mOffsetMatrix;
+
+ //! true if the bone is referenced by at least one mesh
+ bool bIsUsed;
+};
+
+} //! namespace SMD
+
+// ---------------------------------------------------------------------------
+/** Used to load Half-life 1 and 2 SMD models
+*/
+class SMDImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ SMDImporter();
+
+ /** Destructor, private as well */
+ ~SMDImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Parse the SMD file and create the output scene
+ */
+ void ParseFile();
+
+ // -------------------------------------------------------------------
+ /** Parse the triangles section of the SMD file
+ * \param szCurrent Current position in the file. Points to the first
+ * data line of the section.
+ * \param szCurrentOut Receives a pointer to the heading line of
+ * the next section (or to EOF)
+ */
+ void ParseTrianglesSection(const char* szCurrent,
+ const char** szCurrentOut);
+
+ // -------------------------------------------------------------------
+ /** Parse the vertex animation section in VTA files
+ * \param szCurrent Current position in the file. Points to the first
+ * data line of the section.
+ * \param szCurrentOut Receives a pointer to the heading line of
+ * the next section (or to EOF)
+ */
+ void ParseVASection(const char* szCurrent,
+ const char** szCurrentOut);
+
+ // -------------------------------------------------------------------
+ /** Parse the nodes section of the SMD file
+ * \param szCurrent Current position in the file. Points to the first
+ * data line of the section.
+ * \param szCurrentOut Receives a pointer to the heading line of
+ * the next section (or to EOF)
+ */
+ void ParseNodesSection(const char* szCurrent,
+ const char** szCurrentOut);
+
+ // -------------------------------------------------------------------
+ /** Parse the skeleton section of the SMD file
+ * \param szCurrent Current position in the file. Points to the first
+ * data line of the section.
+ * \param szCurrentOut Receives a pointer to the heading line of
+ * the next section (or to EOF)
+ */
+ void ParseSkeletonSection(const char* szCurrent,
+ const char** szCurrentOut);
+
+ // -------------------------------------------------------------------
+ /** Parse a single triangle in the SMD file
+ * \param szCurrent Current position in the file. Points to the first
+ * data line of the section.
+ * \param szCurrentOut Receives the output cursor position
+ */
+ void ParseTriangle(const char* szCurrent,
+ const char** szCurrentOut);
+
+
+ // -------------------------------------------------------------------
+ /** Parse a single vertex in the SMD file
+ * \param szCurrent Current position in the file. Points to the first
+ * data line of the section.
+ * \param szCurrentOut Receives the output cursor position
+ * \param vertex Vertex to be filled
+ */
+ void ParseVertex(const char* szCurrent,
+ const char** szCurrentOut, SMD::Vertex& vertex,
+ bool bVASection = false);
+
+ // -------------------------------------------------------------------
+ /** Get the index of a texture. If the texture was not yet known
+ * it will be added to the internal texture list.
+ * \param filename Name of the texture
+ * \return Value texture index
+ */
+ unsigned int GetTextureIndex(const std::string& filename);
+
+ // -------------------------------------------------------------------
+ /** Computes absolute bone transformations
+ * All output transformations are in worldspace.
+ */
+ void ComputeAbsoluteBoneTransformations();
+
+
+ // -------------------------------------------------------------------
+ /** Parse a line in the skeleton section
+ */
+ void ParseSkeletonElement(const char* szCurrent,
+ const char** szCurrentOut,int iTime);
+
+ // -------------------------------------------------------------------
+ /** Parse a line in the nodes section
+ */
+ void ParseNodeInfo(const char* szCurrent,
+ const char** szCurrentOut);
+
+
+ // -------------------------------------------------------------------
+ /** Parse a floating-point value
+ */
+ bool ParseFloat(const char* szCurrent,
+ const char** szCurrentOut, float& out);
+
+ // -------------------------------------------------------------------
+ /** Parse an unsigned integer. There may be no sign!
+ */
+ bool ParseUnsignedInt(const char* szCurrent,
+ const char** szCurrentOut, unsigned int& out);
+
+ // -------------------------------------------------------------------
+ /** Parse a signed integer. Signs (+,-) are handled.
+ */
+ bool ParseSignedInt(const char* szCurrent,
+ const char** szCurrentOut, int& out);
+
+ // -------------------------------------------------------------------
+ /** Fix invalid time values in the file
+ */
+ void FixTimeValues();
+
+ // -------------------------------------------------------------------
+ /** Add all children of a bone as subnodes to a node
+ * \param pcNode Parent node
+ * \param iParent Parent bone index
+ */
+ void AddBoneChildren(aiNode* pcNode, uint32_t iParent);
+
+ // -------------------------------------------------------------------
+ /** Build output meshes/materials/nodes/animations
+ */
+ void CreateOutputMeshes();
+ void CreateOutputNodes();
+ void CreateOutputAnimations();
+ void CreateOutputMaterials();
+
+
+ // -------------------------------------------------------------------
+ /** Print a log message together with the current line number
+ */
+ void LogErrorNoThrow(const char* msg);
+ void LogWarning(const char* msg);
+
+
+ // -------------------------------------------------------------------
+ inline bool SkipLine( const char* in, const char** out)
+ {
+ Assimp::SkipLine(in,out);
+ ++iLineNumber;
+ return true;
+ }
+ // -------------------------------------------------------------------
+ inline bool SkipSpacesAndLineEnd( const char* in, const char** out)
+ {
+ ++iLineNumber;
+ return Assimp::SkipSpacesAndLineEnd(in,out);
+ }
+
+private:
+
+ /** Configuration option: frame to be loaded */
+ unsigned int configFrameID;
+
+ /** Buffer to hold the loaded file */
+ const char* mBuffer;
+
+ /** Output scene to be filled
+ */
+ aiScene* pScene;
+
+ /** Size of the input file in bytes
+ */
+ unsigned int iFileSize;
+
+ /** Array of textures found in the file
+ */
+ std::vector<std::string> aszTextures;
+
+ /** Array of triangles found in the file
+ */
+ std::vector<SMD::Face> asTriangles;
+
+ /** Array of bones found in the file
+ */
+ std::vector<SMD::Bone> asBones;
+
+ /** Smallest frame index found in the skeleton
+ */
+ int iSmallestFrame;
+
+ /** Length of the whole animation, in frames
+ */
+ double dLengthOfAnim;
+
+ /** Do we have texture coordinates?
+ */
+ bool bHasUVs;
+
+ /** Current line numer
+ */
+ unsigned int iLineNumber;
+
+};
+
+} // end of namespace Assimp
+
+#endif // AI_SMDIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/STLLoader.cpp b/src/3rdparty/assimp/code/STLLoader.cpp
new file mode 100644
index 000000000..357f52961
--- /dev/null
+++ b/src/3rdparty/assimp/code/STLLoader.cpp
@@ -0,0 +1,398 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the STL importer class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_STL_IMPORTER
+
+// internal headers
+#include "STLLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+STLImporter::STLImporter() : mBuffer(0), fileSize(0), pScene(0)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+STLImporter::~STLImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool STLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "stl")
+ return true;
+ else if (!extension.length() || checkSig) {
+ if (!pIOHandler)
+ return true;
+ const char* tokens[] = {"STL","solid"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,2);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void STLImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("stl");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void STLImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open STL file " + pFile + ".");
+ }
+
+ fileSize = (unsigned int)file->FileSize();
+
+ // 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);
+
+ this->pScene = pScene;
+ this->mBuffer = &mBuffer2[0];
+
+ // the default vertex color is white
+ clrColorDefault.r = clrColorDefault.g = clrColorDefault.b = clrColorDefault.a = 1.0f;
+
+ // allocate one mesh
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes = new aiMesh*[1];
+ aiMesh* pMesh = pScene->mMeshes[0] = new aiMesh();
+ pMesh->mMaterialIndex = 0;
+
+ // allocate a single node
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+
+ bool bMatClr = false;
+
+ // check whether the file starts with 'solid' -
+ // in this case we can simply assume it IS a text file. finished.
+ if (!::strncmp(mBuffer,"solid",5)) {
+ LoadASCIIFile();
+ }
+ else bMatClr = LoadBinaryFile();
+
+ // now copy faces
+ pMesh->mFaces = new aiFace[pMesh->mNumFaces];
+ for (unsigned int i = 0, p = 0; i < pMesh->mNumFaces;++i) {
+
+ aiFace& face = pMesh->mFaces[i];
+ face.mIndices = new unsigned int[face.mNumIndices = 3];
+ for (unsigned int o = 0; o < 3;++o,++p) {
+ face.mIndices[o] = p;
+ }
+ }
+
+ // create a single default material - everything white, as we have vertex colors
+ MaterialHelper* pcMat = new MaterialHelper();
+ aiString s;
+ s.Set(AI_DEFAULT_MATERIAL_NAME);
+ pcMat->AddProperty(&s, AI_MATKEY_NAME);
+
+ aiColor4D clrDiffuse(1.0f,1.0f,1.0f,1.0f);
+ if (bMatClr) {
+ clrDiffuse = clrColorDefault;
+ }
+ pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_DIFFUSE);
+ pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_SPECULAR);
+ clrDiffuse = aiColor4D(0.05f,0.05f,0.05f,1.0f);
+ pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_AMBIENT);
+
+ pScene->mNumMaterials = 1;
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = pcMat;
+}
+// ------------------------------------------------------------------------------------------------
+// Read an ASCII STL file
+void STLImporter::LoadASCIIFile()
+{
+ aiMesh* pMesh = pScene->mMeshes[0];
+
+ const char* sz = mBuffer + 5; // skip the "solid"
+ SkipSpaces(&sz);
+ const char* szMe = sz;
+ while (!::IsSpaceOrNewLine(*sz)) {
+ sz++;
+ }
+
+ size_t temp;
+ // setup the name of the node
+ if ((temp = (size_t)(sz-szMe))) {
+
+ pScene->mRootNode->mName.length = temp;
+ memcpy(pScene->mRootNode->mName.data,szMe,temp);
+ pScene->mRootNode->mName.data[temp] = '\0';
+ }
+ else pScene->mRootNode->mName.Set("<STL_ASCII>");
+
+ // try to guess how many vertices we could have
+ // assume we'll need 160 bytes for each face
+ pMesh->mNumVertices = ( pMesh->mNumFaces = fileSize / 160 ) * 3;
+ pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+ pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+
+ unsigned int curFace = 0, curVertex = 3;
+ while (true)
+ {
+ // go to the next token
+ if (!SkipSpacesAndLineEnd(&sz))
+ {
+ // seems we're finished although there was no end marker
+ DefaultLogger::get()->warn("STL: unexpected EOF. \'endsolid\' keyword was expected");
+ break;
+ }
+ // facet normal -0.13 -0.13 -0.98
+ if (!strncmp(sz,"facet",5) && IsSpaceOrNewLine(*(sz+5))) {
+
+ if (3 != curVertex) {
+ DefaultLogger::get()->warn("STL: A new facet begins but the old is not yet complete");
+ }
+ if (pMesh->mNumFaces == curFace) {
+ // need to resize the arrays, our size estimate was wrong
+ unsigned int iNeededSize = (unsigned int)(sz-mBuffer) / pMesh->mNumFaces;
+ if (iNeededSize <= 160)iNeededSize >>= 1; // prevent endless looping
+ unsigned int add = (unsigned int)((mBuffer+fileSize)-sz) / iNeededSize;
+ add += add >> 3; // add 12.5% as buffer
+ iNeededSize = (pMesh->mNumFaces + add)*3;
+ aiVector3D* pv = new aiVector3D[iNeededSize];
+ memcpy(pv,pMesh->mVertices,pMesh->mNumVertices*sizeof(aiVector3D));
+ delete[] pMesh->mVertices;
+ pMesh->mVertices = pv;
+ pv = new aiVector3D[iNeededSize];
+ memcpy(pv,pMesh->mNormals,pMesh->mNumVertices*sizeof(aiVector3D));
+ delete[] pMesh->mNormals;
+ pMesh->mNormals = pv;
+
+ pMesh->mNumVertices = iNeededSize;
+ pMesh->mNumFaces += add;
+ }
+ aiVector3D* vn = &pMesh->mNormals[curFace++*3];
+
+ sz += 6;
+ curVertex = 0;
+ SkipSpaces(&sz);
+ if (strncmp(sz,"normal",6)) {
+ DefaultLogger::get()->warn("STL: a facet normal vector was expected but not found");
+ }
+ else
+ {
+ sz += 7;
+ SkipSpaces(&sz);
+ sz = fast_atof_move(sz, (float&)vn->x );
+ SkipSpaces(&sz);
+ sz = fast_atof_move(sz, (float&)vn->y );
+ SkipSpaces(&sz);
+ sz = fast_atof_move(sz, (float&)vn->z );
+ *(vn+1) = *vn;
+ *(vn+2) = *vn;
+ }
+ }
+ // vertex 1.50000 1.50000 0.00000
+ else if (!strncmp(sz,"vertex",6) && ::IsSpaceOrNewLine(*(sz+6)))
+ {
+ if (3 == curVertex) {
+ DefaultLogger::get()->error("STL: a facet with more than 3 vertices has been found");
+ }
+ else
+ {
+ sz += 7;
+ SkipSpaces(&sz);
+ aiVector3D* vn = &pMesh->mVertices[(curFace-1)*3 + curVertex++];
+ sz = fast_atof_move(sz, (float&)vn->x );
+ SkipSpaces(&sz);
+ sz = fast_atof_move(sz, (float&)vn->y );
+ SkipSpaces(&sz);
+ sz = fast_atof_move(sz, (float&)vn->z );
+ }
+ }
+ else if (!::strncmp(sz,"endsolid",8)) {
+ // finished!
+ break;
+ }
+ // else skip the whole identifier
+ else while (!::IsSpaceOrNewLine(*sz)) {
+ ++sz;
+ }
+ }
+
+ if (!curFace) {
+ pMesh->mNumFaces = 0;
+ throw DeadlyImportError("STL: ASCII file is empty or invalid; no data loaded");
+ }
+ pMesh->mNumFaces = curFace;
+ pMesh->mNumVertices = curFace*3;
+ // we are finished!
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a binary STL file
+bool STLImporter::LoadBinaryFile()
+{
+ // skip the first 80 bytes
+ if (fileSize < 84) {
+ throw DeadlyImportError("STL: file is too small for the header");
+ }
+ bool bIsMaterialise = false;
+
+ // search for an occurence of "COLOR=" in the header
+ const char* sz2 = (const char*)mBuffer;
+ const char* const szEnd = sz2+80;
+ while (sz2 < szEnd) {
+
+ if ('C' == *sz2++ && 'O' == *sz2++ && 'L' == *sz2++ &&
+ 'O' == *sz2++ && 'R' == *sz2++ && '=' == *sz2++) {
+
+ // read the default vertex color for facets
+ bIsMaterialise = true;
+ DefaultLogger::get()->info("STL: Taking code path for Materialise files");
+ clrColorDefault.r = (*sz2++) / 255.0f;
+ clrColorDefault.g = (*sz2++) / 255.0f;
+ clrColorDefault.b = (*sz2++) / 255.0f;
+ clrColorDefault.a = (*sz2++) / 255.0f;
+ break;
+ }
+ }
+ const unsigned char* sz = (const unsigned char*)mBuffer + 80;
+
+ // now read the number of facets
+ aiMesh* pMesh = pScene->mMeshes[0];
+ pScene->mRootNode->mName.Set("<STL_BINARY>");
+
+ pMesh->mNumFaces = *((uint32_t*)sz);
+ sz += 4;
+
+ if (fileSize < 84 + pMesh->mNumFaces*50) {
+ throw DeadlyImportError("STL: file is too small to hold all facets");
+ }
+
+ if (!pMesh->mNumFaces) {
+ throw DeadlyImportError("STL: file is empty. There are no facets defined");
+ }
+
+ pMesh->mNumVertices = pMesh->mNumFaces*3;
+
+ aiVector3D* vp,*vn;
+ vp = pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+ vn = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+
+ for (unsigned int i = 0; i < pMesh->mNumFaces;++i) {
+
+ // NOTE: Blender sometimes writes empty normals ... this is not
+ // our fault ... the RemoveInvalidData helper step should fix that
+ *vn = *((aiVector3D*)sz);
+ sz += sizeof(aiVector3D);
+ *(vn+1) = *vn;
+ *(vn+2) = *vn;
+ vn += 3;
+
+ *vp++ = *((aiVector3D*)sz);
+ sz += sizeof(aiVector3D);
+
+ *vp++ = *((aiVector3D*)sz);
+ sz += sizeof(aiVector3D);
+
+ *vp++ = *((aiVector3D*)sz);
+ sz += sizeof(aiVector3D);
+
+ uint16_t color = *((uint16_t*)sz);
+ sz += 2;
+
+ if (color & (1 << 15))
+ {
+ // seems we need to take the color
+ if (!pMesh->mColors[0])
+ {
+ pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
+ for (unsigned int i = 0; i <pMesh->mNumVertices;++i)
+ *pMesh->mColors[0]++ = this->clrColorDefault;
+ pMesh->mColors[0] -= pMesh->mNumVertices;
+
+ DefaultLogger::get()->info("STL: Mesh has vertex colors");
+ }
+ aiColor4D* clr = &pMesh->mColors[0][pMesh->mNumFaces*3];
+ clr->a = 1.0f;
+ if (bIsMaterialise) // this is reversed
+ {
+ clr->r = (color & 0x31u) / 31.0f;
+ clr->g = ((color & (0x31u<<5))>>5u) / 31.0f;
+ clr->b = ((color & (0x31u<<10))>>10u) / 31.0f;
+ }
+ else
+ {
+ clr->b = (color & 0x31u) / 31.0f;
+ clr->g = ((color & (0x31u<<5))>>5u) / 31.0f;
+ clr->r = ((color & (0x31u<<10))>>10u) / 31.0f;
+ }
+ // assign the color to all vertices of the face
+ *(clr+1) = *clr;
+ *(clr+2) = *clr;
+ }
+ }
+ if (bIsMaterialise && !pMesh->mColors[0])
+ {
+ // use the color as diffuse material color
+ return true;
+ }
+ return false;
+}
+
+#endif // !! ASSIMP_BUILD_NO_STL_IMPORTER
diff --git a/src/3rdparty/assimp/code/STLLoader.h b/src/3rdparty/assimp/code/STLLoader.h
new file mode 100644
index 000000000..edd0b2a95
--- /dev/null
+++ b/src/3rdparty/assimp/code/STLLoader.h
@@ -0,0 +1,119 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file STLLoader.h
+ * Declaration of the STL importer class.
+ */
+#ifndef AI_STLLOADER_H_INCLUDED
+#define AI_STLLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Importer class for the sterolithography STL file format
+*/
+class STLImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ STLImporter();
+
+ /** Destructor, private as well */
+ ~STLImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+
+ // -------------------------------------------------------------------
+ /** Loads a binary .stl file
+ * @return true if the default vertex color must be used as material color
+ */
+ bool LoadBinaryFile();
+
+ // -------------------------------------------------------------------
+ /** Loads a ASCII text .stl file
+ */
+ void LoadASCIIFile();
+
+protected:
+
+ /** Buffer to hold the loaded file */
+ const char* mBuffer;
+
+ /** Size of the file, in bytes */
+ unsigned int fileSize;
+
+ /** Output scene */
+ aiScene* pScene;
+
+ /** Default vertex color */
+ aiColor4D clrColorDefault;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_IN
diff --git a/src/3rdparty/assimp/code/SceneCombiner.cpp b/src/3rdparty/assimp/code/SceneCombiner.cpp
new file mode 100644
index 000000000..d13d8ae0a
--- /dev/null
+++ b/src/3rdparty/assimp/code/SceneCombiner.cpp
@@ -0,0 +1,1140 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+// ----------------------------------------------------------------------------
+/** @file 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 "AssimpPCH.h"
+#include "SceneCombiner.h"
+#include "fast_atof.h"
+#include "Hash.h"
+#include "time.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+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) {
+ DefaultLogger::get()->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,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, 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,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)
+{
+ ai_assert(NULL != _dest);
+
+ // 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)
+{
+ ai_assert(NULL != _dest);
+
+ // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it
+ if (srcList.empty())
+ {
+ if (*_dest)
+ {
+ (*_dest)->~aiScene();
+ SceneCombiner::CopySceneFlat(_dest,master);
+ }
+ else *_dest = master;
+ return;
+ }
+ if (*_dest)(*_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(),0xffffffff);
+
+ // 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] != 0xffffffff)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 = ::sprintf(src[i].id,"$%.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,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 = strtol10(&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 = 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 scenegraph 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)
+ {
+ ai_assert(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) {
+ ai_assert(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) {
+ ai_assert(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) {
+ DefaultLogger::get()->error(std::string("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)
+{
+ ai_assert(NULL != out && !out->mNumBones);
+
+ // 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 it = asBones.begin(),end = asBones.end(); it != end;++it) {
+ // Allocate a bone and setup it's name
+ aiBone* pc = out->mBones[out->mNumBones++] = new aiBone();
+ pc->mName = aiString( *((*it).second ));
+
+ std::vector< BoneSrcIndex >::const_iterator wend = (*it).pSrcBones.end();
+
+ // Loop through all bones to be joined for this bone
+ for (std::vector< BoneSrcIndex >::const_iterator wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit) {
+ pc->mNumWeights += (*wmit).first->mNumWeights;
+
+ // NOTE: different offset matrices for bones with equal names
+ // are - at the moment - not handled correctly.
+ if (wmit != (*it).pSrcBones.begin() && pc->mOffsetMatrix != (*wmit).first->mOffsetMatrix) {
+ DefaultLogger::get()->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 coresponding mesh.
+ for (std::vector< BoneSrcIndex >::const_iterator wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit) {
+ aiBone* pip = (*wmit).first;
+ for (unsigned int mp = 0; mp < pip->mNumWeights;++mp,++avw) {
+ const aiVertexWeight& vfi = pip->mWeights[mp];
+ 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)
+{
+ ai_assert(NULL != _out);
+
+ if (begin == end) {
+ *_out = NULL; // no meshes ...
+ return;
+ }
+
+ // Allocate the output mesh
+ aiMesh* out = *_out = new aiMesh();
+ out->mMaterialIndex = (*begin)->mMaterialIndex;
+
+ // Find out how much output storage we'll need
+ for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) {
+ out->mNumVertices += (*it)->mNumVertices;
+ out->mNumFaces += (*it)->mNumFaces;
+ out->mNumBones += (*it)->mNumBones;
+
+ // combine primitive type flags
+ out->mPrimitiveTypes |= (*it)->mPrimitiveTypes;
+ }
+
+ 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 DefaultLogger::get()->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 DefaultLogger::get()->warn("JoinMeshes: Normals expected but input mesh contains no normals");
+ pv2 += (*it)->mNumVertices;
+ }
+ }
+ // copy tangents and bitangents
+ 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 DefaultLogger::get()->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 DefaultLogger::get()->warn("JoinMeshes: UVs expected but input mesh contains no UVs");
+ pv2 += (*it)->mNumVertices;
+ }
+ ++n;
+ }
+ // copy vertex colors
+ n = 0;
+ while ((**begin).HasVertexColors(n)) {
+ aiColor4D* pv2 = out->mColors[n] = new aiColor4D[out->mNumVertices];
+ for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) {
+
+ if ((*it)->mColors[n]) {
+ ::memcpy(pv2,(*it)->mColors[n],(*it)->mNumVertices*sizeof(aiColor4D));
+ }
+ else DefaultLogger::get()->warn("JoinMeshes: VCs expected but input mesh contains no VCs");
+ pv2 += (*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;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename Type>
+inline void CopyPtrArray (Type**& dest, Type** src, unsigned int num)
+{
+ if (!num)
+ {
+ dest = NULL;
+ return;
+ }
+ dest = new Type*[num];
+ for (unsigned int i = 0; i < num;++i)
+ SceneCombiner::Copy(&dest[i],src[i]);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename Type>
+inline void GetArrayCopy (Type*& dest, unsigned int num )
+{
+ if (!dest)return;
+ Type* old = dest;
+
+ dest = new Type[num];
+ ::memcpy(dest, old, sizeof(Type) * num);
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::CopySceneFlat(aiScene** _dest,aiScene* src)
+{
+ // reuse the old scene or allocate a new?
+ if (*_dest)(*_dest)->~aiScene();
+ else *_dest = new aiScene();
+
+ ::memcpy(*_dest,src,sizeof(aiScene));
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::CopyScene(aiScene** _dest,aiScene* src)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ aiScene* dest = *_dest = new aiScene();
+
+ // 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;
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy (aiMesh** _dest, const aiMesh* src)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ 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)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ MaterialHelper* dest = (MaterialHelper*) ( *_dest = new MaterialHelper() );
+ 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)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ 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)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ 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)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ 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)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ 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)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ 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)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ 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));
+
+ // and reallocate all arrays
+ GetArrayCopy( dest->mMeshes, dest->mNumMeshes );
+ CopyPtrArray( dest->mChildren, src->mChildren,dest->mNumChildren);
+}
+
+
+}
diff --git a/src/3rdparty/assimp/code/SceneCombiner.h b/src/3rdparty/assimp/code/SceneCombiner.h
new file mode 100644
index 000000000..a8fd43cba
--- /dev/null
+++ b/src/3rdparty/assimp/code/SceneCombiner.h
@@ -0,0 +1,365 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Declares a helper class, "SceneCombiner" providing various
+ * utilities to merge scenes.
+ */
+#ifndef AI_SCENE_COMBINER_H_INC
+#define AI_SCENE_COMBINER_H_INC
+
+#include "../include/aiAssert.h"
+
+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 (0xffffffff)
+ {}
+
+ 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;
+ }
+
+ 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() {}
+
+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 sceenes to a specific
+ * position in the node graph of the masteer 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);
+
+
+ // -------------------------------------------------------------------
+ /** 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 occurence.
+ */
+ 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,aiScene* source);
+
+
+ // -------------------------------------------------------------------
+ /** 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,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);
+
+ // 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/src/3rdparty/assimp/code/ScenePreprocessor.cpp b/src/3rdparty/assimp/code/ScenePreprocessor.cpp
new file mode 100644
index 000000000..e7a83c512
--- /dev/null
+++ b/src/3rdparty/assimp/code/ScenePreprocessor.cpp
@@ -0,0 +1,281 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#include "ScenePreprocessor.h"
+
+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];
+ MaterialHelper* helper;
+
+ aiString name;
+
+ // Check whether there are meshes with at least one set of uv coordinates ... add a dummy texture for them
+ // meshes without texture coordinates receive a boring gray default material.
+ unsigned int mat0 = 0xffffffff, mat1 = 0xffffffff;
+ for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
+ if (scene->mMeshes[i]->mTextureCoords[0]) {
+
+ if (mat0 == 0xffffffff) {
+
+ scene->mMaterials[scene->mNumMaterials] = helper = new MaterialHelper();
+ name.Set("$texture.png");
+ helper->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ name.Set(AI_DEFAULT_TEXTURED_MATERIAL_NAME);
+ helper->AddProperty(&name,AI_MATKEY_NAME);
+
+ mat0 = scene->mNumMaterials++;
+ DefaultLogger::get()->debug("ScenePreprocessor: Adding textured material \'" AI_DEFAULT_TEXTURED_MATERIAL_NAME "\'");
+ }
+ scene->mMeshes[i]->mMaterialIndex = mat0;
+ }
+ else
+ {
+ if (mat1 == 0xffffffff) {
+
+ scene->mMaterials[scene->mNumMaterials] = helper = new MaterialHelper();
+ aiColor3D clr(0.6f,0.6f,0.6f);
+ helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+
+ // setup the default name
+ name.Set(AI_DEFAULT_MATERIAL_NAME);
+ helper->AddProperty(&name,AI_MATKEY_NAME);
+
+ mat1 = scene->mNumMaterials++;
+ DefaultLogger::get()->debug("ScenePreprocessor: Adding grey material \'" AI_DEFAULT_MATERIAL_NAME "\'");
+ }
+ scene->mMeshes[i]->mMaterialIndex = mat1;
+ }
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------------------------
+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 unsued 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) {
+ DefaultLogger::get()->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 i = 0; i < channel->mNumPositionKeys;++i) {
+ aiVectorKey& key = channel->mPositionKeys[i];
+ first = std::min (first, key.mTime);
+ last = std::max (last, key.mTime);
+ }
+
+ // Scaling keys
+ for (unsigned int i = 0; i < channel->mNumScalingKeys;++i) {
+ aiVectorKey& key = channel->mScalingKeys[i];
+ first = std::min (first, key.mTime);
+ last = std::max (last, key.mTime);
+ }
+
+ // Rotation keys
+ for (unsigned int i = 0; i < channel->mNumRotationKeys;++i) {
+ aiQuatKey& key = channel->mRotationKeys[i];
+ 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;
+
+ DefaultLogger::get()->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;
+
+ DefaultLogger::get()->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;
+
+ DefaultLogger::get()->debug("ScenePreprocessor: Dummy position track has been generated");
+ }
+ }
+ }
+ }
+
+ if (anim->mDuration == -1.) {
+ DefaultLogger::get()->debug("ScenePreprocessor: Setting animation duration");
+ anim->mDuration = last - std::min( first, 0. );
+ }
+}
diff --git a/src/3rdparty/assimp/code/ScenePreprocessor.h b/src/3rdparty/assimp/code/ScenePreprocessor.h
new file mode 100644
index 000000000..862544699
--- /dev/null
+++ b/src/3rdparty/assimp/code/ScenePreprocessor.h
@@ -0,0 +1,116 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Defines a post processing step to search all meshes for
+ degenerated faces */
+#ifndef AI_SCENE_PREPROCESSOR_H_INC
+#define AI_SCENE_PREPROCESSOR_H_INC
+
+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/src/3rdparty/assimp/code/SkeletonMeshBuilder.cpp b/src/3rdparty/assimp/code/SkeletonMeshBuilder.cpp
new file mode 100644
index 000000000..409474523
--- /dev/null
+++ b/src/3rdparty/assimp/code/SkeletonMeshBuilder.cpp
@@ -0,0 +1,265 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file SkeletonMeshBuilder.cpp
+ * @brief Implementation of a little class to construct a dummy mesh for a skeleton
+ */
+
+#include "AssimpPCH.h"
+#include "../include/aiScene.h"
+#include "SkeletonMeshBuilder.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// The constructor processes the given scene and adds a mesh there.
+SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root, bool bKnobsOnly) : mKnobsOnly(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;
+
+ // 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
+ 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 = 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);
+ float distanceToChild = childpos.Length();
+ if ( distanceToChild < 0.0001f)
+ continue;
+ aiVector3D up = aiVector3D( childpos).Normalize();
+
+ aiVector3D orth( 1.0f, 0.0f, 0.0f);
+ if ( fabs( orth * up) > 0.99f)
+ orth.Set( 0.0f, 1.0f, 0.0f);
+
+ aiVector3D front = (up ^ orth).Normalize();
+ aiVector3D side = (front ^ up).Normalize();
+
+ unsigned int localVertexStart = mVertices.size();
+ mVertices.push_back( -front * distanceToChild * 0.1f);
+ mVertices.push_back( childpos);
+ mVertices.push_back( -side * distanceToChild * 0.1f);
+ mVertices.push_back( -side * distanceToChild * 0.1f);
+ mVertices.push_back( childpos);
+ mVertices.push_back( front * distanceToChild * 0.1f);
+ mVertices.push_back( front * distanceToChild * 0.1f);
+ mVertices.push_back( childpos);
+ mVertices.push_back( side * distanceToChild * 0.1f);
+ mVertices.push_back( side * distanceToChild * 0.1f);
+ mVertices.push_back( childpos);
+ mVertices.push_back( -front * distanceToChild * 0.1f);
+
+ 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);
+ float sizeEstimate = ownpos.Length() * 0.18f;
+
+ mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate));
+ mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate));
+ mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate));
+ mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate));
+
+ mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate));
+ mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate));
+ mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate));
+ mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate));
+ mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f));
+
+ 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 = 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.0f);
+
+ // 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 = 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 = 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-5f) /* ensure that FindInvalidData won't remove us ...*/
+ nor = aiVector3D(1.f,0.f,0.f);
+
+ for (unsigned int n = 0; n < 3; ++n)
+ mesh->mNormals[inface.mIndices[n]] = nor;
+ }
+
+ // add the bones
+ mesh->mNumBones = 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()
+{
+ Assimp::MaterialHelper* matHelper = new Assimp::MaterialHelper;
+
+ // 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/src/3rdparty/assimp/code/SkeletonMeshBuilder.h b/src/3rdparty/assimp/code/SkeletonMeshBuilder.h
new file mode 100644
index 000000000..2575d0725
--- /dev/null
+++ b/src/3rdparty/assimp/code/SkeletonMeshBuilder.h
@@ -0,0 +1,122 @@
+/** Helper class to construct a dummy mesh for file formats containing only motion data */
+
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiMesh.h"
+
+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/src/3rdparty/assimp/code/SmoothingGroups.h b/src/3rdparty/assimp/code/SmoothingGroups.h
new file mode 100644
index 000000000..50535820c
--- /dev/null
+++ b/src/3rdparty/assimp/code/SmoothingGroups.h
@@ -0,0 +1,103 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a face with smoothing groups assigned */
+struct FaceWithSmoothingGroup
+{
+ FaceWithSmoothingGroup() : iSmoothGroup(0)
+ {
+ // let the rest uninitialized for performance - in release builds.
+ // in debug builds set all indices to a common magic value
+#ifdef _DEBUG
+ this->mIndices[0] = 0xffffffff;
+ this->mIndices[1] = 0xffffffff;
+ this->mIndices[2] = 0xffffffff;
+#endif
+ }
+
+
+ //! Indices. .3ds is using uint16. However, after
+ //! an unique vrtex set has been generated it might
+ //! be an index becomes > 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/src/3rdparty/assimp/code/SmoothingGroups.inl b/src/3rdparty/assimp/code/SmoothingGroups.inl
new file mode 100644
index 000000000..10eb7d25f
--- /dev/null
+++ b/src/3rdparty/assimp/code/SmoothingGroups.inl
@@ -0,0 +1,138 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Generation of normal vectors basing on smoothing groups */
+
+#ifndef AI_SMOOTHINGGROUPS_INL_INCLUDED
+#define AI_SMOOTHINGGROUPS_INL_INCLUDED
+
+// internal headers
+#include "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)
+ {
+ register 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.Normalize();
+
+ // 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/src/3rdparty/assimp/code/SortByPTypeProcess.cpp b/src/3rdparty/assimp/code/SortByPTypeProcess.cpp
new file mode 100644
index 000000000..f1906ca6e
--- /dev/null
+++ b/src/3rdparty/assimp/code/SortByPTypeProcess.cpp
@@ -0,0 +1,409 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the DeterminePTypeHelperProcess and
+ * SortByPTypeProcess post-process steps.
+*/
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "ProcessHelper.h"
+#include "SortByPTypeProcess.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+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)
+{
+// std::vector<unsigned int>::const_iterator it;
+
+ 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 (0xffffffff != 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 (0xffffffff != 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 (!pScene->mNumMeshes)
+ {
+ DefaultLogger::get()->debug("SortByPTypeProcess skipped, there are no meshes");
+ return;
+ }
+
+ DefaultLogger::get()->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,0xffffffff);
+ std::vector<unsigned int>::iterator meshIdx = replaceMeshIndex.begin();
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ {
+ aiMesh* 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 = (unsigned int) outMeshes.size();
+ outMeshes.push_back(mesh);
+ }
+ else bAnyChanges = true;
+
+ meshIdx += 4;
+ continue;
+ }
+ bAnyChanges = true;
+
+ // reuse our current mesh arrays for the submesh
+ // with the largest numer 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(NULL), *nor(NULL), *tan(NULL), *bit(NULL);
+ 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 i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
+ {
+ if (mesh->mTextureCoords[i])
+ uv[i] = out->mTextureCoords[i] = new aiVector3D[out->mNumVertices];
+ else uv[i] = NULL;
+
+ out->mNumUVComponents[i] = mesh->mNumUVComponents[i];
+ }
+
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS;++i)
+ {
+ if (mesh->mColors[i])
+ cols[i] = out->mColors[i] = new aiColor4D[out->mNumVertices];
+ else cols[i] = NULL;
+ }
+
+ 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)
+ {
+ register 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 = NULL;
+ ++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;
+ }
+
+ 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];
+ ::sprintf(buffer,"Points: %i%s, Lines: %i%s, Triangles: %i%s, Polygons: %i%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" : ""));
+ DefaultLogger::get()->info(buffer);
+ DefaultLogger::get()->debug("SortByPTypeProcess finished");
+ }
+}
+
diff --git a/src/3rdparty/assimp/code/SortByPTypeProcess.h b/src/3rdparty/assimp/code/SortByPTypeProcess.h
new file mode 100644
index 000000000..539df2995
--- /dev/null
+++ b/src/3rdparty/assimp/code/SortByPTypeProcess.h
@@ -0,0 +1,88 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiMesh.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
+{
+ friend class Importer;
+ friend class ::SortByPTypeProcessTest; // grant the unit test full access to us
+
+protected:
+ /** Constructor to be privately used by Importer */
+ SortByPTypeProcess();
+
+ /** Destructor, private as well */
+ ~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/src/3rdparty/assimp/code/SpatialSort.cpp b/src/3rdparty/assimp/code/SpatialSort.cpp
new file mode 100644
index 000000000..290e7990a
--- /dev/null
+++ b/src/3rdparty/assimp/code/SpatialSort.cpp
@@ -0,0 +1,350 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the helper class to quickly find vertices close to a given position */
+
+#include "AssimpPCH.h"
+#include "SpatialSort.h"
+
+#include <QtGlobal>
+
+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
+ float distance = *vec * mPlaneNormal;
+
+ // UGLY HACK !!!
+ // TODO: fix it
+ volatile int KillOptimisation = *((int*)&distance);
+ Q_UNUSED(KillOptimisation);
+
+ mPositions.push_back( Entry( 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,
+ float pRadius, std::vector<unsigned int>& poResults) const
+{
+ const float dist = pPosition * mPlaneNormal;
+ const float minDist = dist - pRadius, maxDist = dist + pRadius;
+
+ // clear the array in this strange fashion because a simple clear() would also deallocate
+ // the array which we want to avoid
+ poResults.erase( poResults.begin(), poResults.end());
+
+ // 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 float 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 signed int BinFloat;
+
+ // --------------------------------------------------------------------------------------------
+ // Converts the bit pattern of a floating-point number to its signed integer representation.
+ BinFloat ToBinary( const float & 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!
+ Q_ASSERT( sizeof(BinFloat) >= sizeof(float));
+
+ #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).
+ Q_ASSERT( sizeof(BinFloat) == sizeof(float));
+
+ // 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 {
+ float 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 indentical 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.erase( poResults.begin(), poResults.end());
+
+ // 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 aray
+ 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,float pRadius) const
+{
+ fill.resize(mPositions.size(),0xffffffff);
+ float dist, maxDist;
+
+ unsigned int t=0;
+ const float 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 _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/src/3rdparty/assimp/code/SpatialSort.h b/src/3rdparty/assimp/code/SpatialSort.h
new file mode 100644
index 000000000..3205c05f1
--- /dev/null
+++ b/src/3rdparty/assimp/code/SpatialSort.h
@@ -0,0 +1,170 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** 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 "../include/aiTypes.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, float pRadius,
+ std::vector<unsigned int>& poResults) const;
+
+ // ------------------------------------------------------------------------------------
+ /** Fills an array with indices of all positions indentical 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,
+ float 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 precalculated distance from the reference plane */
+ struct Entry
+ {
+ unsigned int mIndex; ///< The vertex referred by this entry
+ aiVector3D mPosition; ///< Position
+ float mDistance; ///< Distance of this vertex to the sorting plane
+
+ Entry() { /** intentionally not initialized.*/ }
+ Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance)
+ : mIndex( pIndex), mPosition( pPosition), mDistance( pDistance)
+ { }
+
+ 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/src/3rdparty/assimp/code/SplitLargeMeshes.cpp b/src/3rdparty/assimp/code/SplitLargeMeshes.cpp
new file mode 100644
index 000000000..f4950461a
--- /dev/null
+++ b/src/3rdparty/assimp/code/SplitLargeMeshes.cpp
@@ -0,0 +1,677 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file Implementation of the SplitLargeMeshes postprocessing step
+*/
+
+#include "AssimpPCH.h"
+
+// 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)return;
+
+ DefaultLogger::get()->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 splitted. 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);
+ DefaultLogger::get()->info("SplitLargeMeshesProcess_Triangle finished. Meshes have been splitted");
+ }
+ else DefaultLogger::get()->debug("SplitLargeMeshesProcess_Triangle finished. There was nothing to do");
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 );
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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)
+ {
+ DefaultLogger::get()->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 != NULL)
+ 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 splitted 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 = NULL;
+ }
+ 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 != NULL)
+ 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));
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+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)
+{
+ std::vector<std::pair<aiMesh*, unsigned int> > avList;
+
+ if (0xffffffff == this->LIMIT)return;
+
+ DefaultLogger::get()->debug("SplitLargeMeshesProcess_Vertex begin");
+ 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 splitted. 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);
+ DefaultLogger::get()->info("SplitLargeMeshesProcess_Vertex finished. Meshes have been splitted");
+ }
+ else DefaultLogger::get()->debug("SplitLargeMeshesProcess_Vertex finished. There was nothing to do");
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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;
+ //const unsigned int iOutVertexNum2 = pMesh->mNumVertices /iSubMeshes;
+
+ // 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 (std::vector<unsigned int>::iterator
+ iter = avWasCopied.begin(),end = avWasCopied.end();
+ iter != end;++iter)
+ {
+ (*iter) = 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 (!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;
+ *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));
+ return;
+}
diff --git a/src/3rdparty/assimp/code/SplitLargeMeshes.h b/src/3rdparty/assimp/code/SplitLargeMeshes.h
new file mode 100644
index 000000000..e3c08ddba
--- /dev/null
+++ b/src/3rdparty/assimp/code/SplitLargeMeshes.h
@@ -0,0 +1,216 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiMesh.h"
+#include "../include/aiScene.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
+
+// ---------------------------------------------------------------------------
+/** Postprocessing filter to split large meshes into submeshes
+ *
+ * Applied BEFORE the JoinVertices-Step occurs.
+ * Returns NON-UNIQUE vertices, splits by triangle number.
+*/
+class ASSIMP_API SplitLargeMeshesProcess_Triangle : public BaseProcess
+{
+ friend class Importer;
+ friend class SplitLargeMeshesProcess_Vertex;
+ friend class ::SplitLargeMeshesTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ SplitLargeMeshesProcess_Triangle();
+
+ /** Destructor, private as well */
+ ~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;}
+
+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);
+
+ // -------------------------------------------------------------------
+ //! 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;
+};
+
+
+// ---------------------------------------------------------------------------
+/** Postprocessing filter to split large meshes into submeshes
+ *
+ * Applied AFTER the JoinVertices-Step occurs.
+ * Returns UNIQUE vertices, splits by vertex number.
+*/
+class ASSIMP_API SplitLargeMeshesProcess_Vertex : public BaseProcess
+{
+ friend class Importer;
+ friend class ::SplitLargeMeshesTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ SplitLargeMeshesProcess_Vertex();
+
+ /** Destructor, private as well */
+ ~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;}
+
+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);
+
+ // -------------------------------------------------------------------
+ //! 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/src/3rdparty/assimp/code/StandardShapes.cpp b/src/3rdparty/assimp/code/StandardShapes.cpp
new file mode 100644
index 000000000..e752918b0
--- /dev/null
+++ b/src/3rdparty/assimp/code/StandardShapes.cpp
@@ -0,0 +1,502 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file StandardShapes.cpp
+ * @brief Implementation of the StandardShapes class
+ *
+ * The primitive geometry data comes from
+ * http://geometrictools.com/Documentation/PlatonicSolids.pdf.
+ */
+
+#include "AssimpPCH.h"
+#include "StandardShapes.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 float 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.size() & numIndices || 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 i = 0; i < numIndices;++i,++a)
+ f.mIndices[i] = 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 float t = (1.f + 2.236067977f)/2.f;
+ const float s = sqrt(1.f + t*t);
+
+ const aiVector3D v0 = aiVector3D(t,1.f, 0.f)/s;
+ const aiVector3D v1 = aiVector3D(-t,1.f, 0.f)/s;
+ const aiVector3D v2 = aiVector3D(t,-1.f, 0.f)/s;
+ const aiVector3D v3 = aiVector3D(-t,-1.f, 0.f)/s;
+ const aiVector3D v4 = aiVector3D(1.f, 0.f, t)/s;
+ const aiVector3D v5 = aiVector3D(1.f, 0.f,-t)/s;
+ const aiVector3D v6 = aiVector3D(-1.f, 0.f,t)/s;
+ const aiVector3D v7 = aiVector3D(-1.f, 0.f,-t)/s;
+ const aiVector3D v8 = aiVector3D(0.f, t, 1.f)/s;
+ const aiVector3D v9 = aiVector3D(0.f,-t, 1.f)/s;
+ const aiVector3D v10 = aiVector3D(0.f, t,-1.f)/s;
+ const aiVector3D v11 = aiVector3D(0.f,-t,-1.f)/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 float a = 1.f / 1.7320508f;
+ const float b = sqrt((3.f-2.23606797f)/6.f);
+ const float c = sqrt((3.f+2.23606797f)/6.f);
+
+ 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.f);
+ const aiVector3D v9 = aiVector3D(-b,c,0.f);
+ const aiVector3D v10 = aiVector3D(b,-c,0.f);
+ const aiVector3D v11 = aiVector3D(-b,-c,0.f);
+ const aiVector3D v12 = aiVector3D(c, 0.f, b);
+ const aiVector3D v13 = aiVector3D(c, 0.f, -b);
+ const aiVector3D v14 = aiVector3D(-c, 0.f, b);
+ const aiVector3D v15 = aiVector3D(-c, 0.f, -b);
+ const aiVector3D v16 = aiVector3D(0.f, b, c);
+ const aiVector3D v17 = aiVector3D(0.f, -b, c);
+ const aiVector3D v18 = aiVector3D(0.f, b, -c);
+ const aiVector3D v19 = aiVector3D(0.f, -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.0f, 0.f, 0.f) ;
+ const aiVector3D v1 = aiVector3D(-1.0f, 0.f, 0.f);
+ const aiVector3D v2 = aiVector3D(0.f, 1.0f, 0.f);
+ const aiVector3D v3 = aiVector3D(0.f, -1.0f, 0.f);
+ const aiVector3D v4 = aiVector3D(0.f, 0.f, 1.0f);
+ const aiVector3D v5 = aiVector3D(0.f, 0.f, -1.0f);
+
+ 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 float a = 1.41421f/3.f;
+ const float b = 2.4494f/3.f;
+
+ const aiVector3D v0 = aiVector3D(0.f,0.f,1.f);
+ const aiVector3D v1 = aiVector3D(2*a,0,-1.f/3.f);
+ const aiVector3D v2 = aiVector3D(-a,b,-1.f/3.f);
+ const aiVector3D v3 = aiVector3D(-a,-b,-1.f/3.f);
+
+ 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 float length = 1.f/1.73205080f;
+
+ const aiVector3D v0 = aiVector3D(-1.f,-1.f,-1.f)*length;
+ const aiVector3D v1 = aiVector3D(1.f,-1.f,-1.f)*length;
+ const aiVector3D v2 = aiVector3D(1.f,1.f,-1.f)*length;
+ const aiVector3D v3 = aiVector3D(-1.f,1.f,-1.f)*length;
+ const aiVector3D v4 = aiVector3D(-1.f,-1.f,1.f)*length;
+ const aiVector3D v5 = aiVector3D(1.f,-1.f,1.f)*length;
+ const aiVector3D v6 = aiVector3D(1.f,1.f,1.f)*length;
+ const aiVector3D v7 = aiVector3D(-1.f,1.f,1.f)*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
+ // tesselation is reached
+ for (unsigned int i = 0; i<tess;++i)
+ Subdivide(positions);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a cone
+void StandardShapes::MakeCone(float height,float radius1,
+ float 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 = ::fabs(radius1);
+ radius2 = ::fabs(radius2);
+
+ float halfHeight = height / 2;
+
+ // radius1 is always the smaller one
+ if (radius2 > radius1)
+ {
+ std::swap(radius2,radius1);
+ halfHeight = -halfHeight;
+ }
+ else old = 0xffffffff;
+
+ // Use a large epsilon to check whether the cone is pointy
+ if (radius1 < (radius2-radius1)*10e-3f)radius1 = 0.f;
+
+ // 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 float angle_delta = (float)AI_MATH_TWO_PI / tess;
+ const float angle_max = (float)AI_MATH_TWO_PI;
+
+ float s = 1.f; // cos(angle == 0);
+ float t = 0.f; // sin(angle == 0);
+
+ for (float angle = 0.f; angle < angle_max; )
+ {
+ const aiVector3D v1 = aiVector3D (s * radius1, -halfHeight, t * radius1 );
+ const aiVector3D v2 = aiVector3D (s * radius2, halfHeight, t * radius2 );
+
+ const float next = angle + angle_delta;
+ float s2 = ::cos(next);
+ float t2 = ::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.f, halfHeight, 0.f));
+
+
+ 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.f, -halfHeight, 0.f));
+
+ }
+ }
+ s = s2;
+ t = t2;
+ angle = next;
+ }
+
+ // Need to flip face order?
+ if (0xffffffff != old )
+ {
+ for (size_t s = old; s < positions.size();s += 3)
+ std::swap(positions[s],positions[s+1]);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a circle
+void StandardShapes::MakeCircle(float 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 = ::fabs(radius);
+
+ // We will need 3 vertices per segment
+ positions.reserve(positions.size()+tess*3);
+
+ const float angle_delta = (float)AI_MATH_TWO_PI / tess;
+ const float angle_max = (float)AI_MATH_TWO_PI;
+
+ float s = 1.f; // cos(angle == 0);
+ float t = 0.f; // sin(angle == 0);
+
+ for (float angle = 0.f; angle < angle_max; )
+ {
+ positions.push_back(aiVector3D(s * radius,0.f,t * radius));
+ angle += angle_delta;
+ s = ::cos(angle);
+ t = ::sin(angle);
+ positions.push_back(aiVector3D(s * radius,0.f,t * radius));
+
+ positions.push_back(aiVector3D(0.f,0.f,0.f));
+ }
+}
+
+} // ! Assimp
diff --git a/src/3rdparty/assimp/code/StandardShapes.h b/src/3rdparty/assimp/code/StandardShapes.h
new file mode 100644
index 000000000..ae59eb9d0
--- /dev/null
+++ b/src/3rdparty/assimp/code/StandardShapes.h
@@ -0,0 +1,196 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 <vector>
+
+
+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(float height,float radius1,
+ float radius2,unsigned int tess,
+ std::vector<aiVector3D>& positions,bool bOpen= false);
+
+
+ // ----------------------------------------------------------------
+ /** @brief Generates a flat circle
+ *
+ * The circle is constructed in the planed 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(float radius, unsigned int tess,
+ std::vector<aiVector3D>& positions);
+
+};
+} // ! Assimp
+
+#endif // !! AI_STANDARD_SHAPES_H_INC
diff --git a/src/3rdparty/assimp/code/StdOStreamLogStream.h b/src/3rdparty/assimp/code/StdOStreamLogStream.h
new file mode 100644
index 000000000..653dbf42e
--- /dev/null
+++ b/src/3rdparty/assimp/code/StdOStreamLogStream.h
@@ -0,0 +1,52 @@
+#ifndef AI_STROSTREAMLOGSTREAM_H_INC
+#define AI_STROSTREAMLOGSTREAM_H_INC
+
+#include "../include/LogStream.h"
+#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
+ */
+ StdOStreamLogStream(std::ostream& _ostream);
+
+ /** @brief Destructor */
+ ~StdOStreamLogStream();
+
+ /** @brief Writer */
+ void write(const char* message);
+private:
+ std::ostream& ostream;
+};
+
+// ---------------------------------------------------------------------------
+// Default constructor
+inline StdOStreamLogStream::StdOStreamLogStream(std::ostream& _ostream)
+ : ostream (_ostream)
+{}
+
+// ---------------------------------------------------------------------------
+// Default constructor
+inline StdOStreamLogStream::~StdOStreamLogStream()
+{}
+
+// ---------------------------------------------------------------------------
+// Write method
+inline void StdOStreamLogStream::write(const char* message)
+{
+ ostream << message;
+ ostream.flush();
+}
+
+// ---------------------------------------------------------------------------
+} // Namespace Assimp
+
+#endif // guard
diff --git a/src/3rdparty/assimp/code/StreamReader.h b/src/3rdparty/assimp/code/StreamReader.h
new file mode 100644
index 000000000..7539d4829
--- /dev/null
+++ b/src/3rdparty/assimp/code/StreamReader.h
@@ -0,0 +1,387 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Defines the StreamReader class which reads data from
+ * a binary stream with a well-defined endianess. */
+
+#ifndef AI_STREAMREADER_H_INCLUDED
+#define AI_STREAMREADER_H_INCLUDED
+
+#include <cstdio>
+
+#include "ByteSwap.h"
+namespace Assimp {
+ 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) {
+
+ // static branch
+ ByteSwapper<T,(SwapEndianess && sizeof(T)>1)> () (inout);
+ }
+};
+} // end Intern
+
+// --------------------------------------------------------------------------------------------
+/** 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 endianess of any input data is known at
+ * compile-time, which should usually be true (#BaseImporter::ConvertToUTF8 implements
+ * runtime endianess 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 :-)
+
+ typedef int diff;
+ typedef unsigned int pos;
+
+public:
+
+
+ // ---------------------------------------------------------------------
+ /** Construction from a given stream with a well-defined endianess.
+ *
+ * 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
+ * endianess information is contained in the @c SwapEndianess
+ * template parameter and this parameter is meaningless. */
+ StreamReader(boost::shared_ptr<IOStream> stream, bool le = false)
+ : stream(stream)
+ , le(le)
+ {
+ _Begin();
+ }
+
+ // ---------------------------------------------------------------------
+ StreamReader(IOStream* stream, bool le = false)
+ : stream(boost::shared_ptr<IOStream>(stream))
+ , le(le)
+ {
+ _Begin();
+ }
+
+ // ---------------------------------------------------------------------
+ ~StreamReader() {
+ delete[] buffer;
+ }
+
+public:
+
+ // 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>();
+ }
+
+public:
+
+ // ---------------------------------------------------------------------
+ /** Get the remaining stream size (to the end of the srream) */
+ 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(int 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. Passing 0xffffffff
+ * resets the limit to the original end of the stream. */
+ void SetReadLimit(unsigned int _limit) {
+
+ if (0xffffffff == _limit) {
+ limit = end;
+ return;
+ }
+
+ limit = buffer + _limit;
+ if (limit > end) {
+ throw DeadlyImportError("StreamReader: Invalid read limit");
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ /** Get the current read limit in bytes. Reading over this limit
+ * accidentially raises an exception. */
+ int GetReadLimit() const {
+ return (unsigned int)(limit - buffer);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Skip to the read limit in bytes. Reading over this limit
+ * accidentially 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;
+ }
+
+private:
+
+ // ---------------------------------------------------------------------
+ /** 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;
+ }
+
+ // ---------------------------------------------------------------------
+ void _Begin() {
+ if (!stream) {
+ 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];
+ stream->Read(current,s,1);
+ end = limit = &buffer[s];
+ }
+
+private:
+
+
+ boost::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/src/3rdparty/assimp/code/StringComparison.h b/src/3rdparty/assimp/code/StringComparison.h
new file mode 100644
index 000000000..ed79874c0
--- /dev/null
+++ b/src/3rdparty/assimp/code/StringComparison.h
@@ -0,0 +1,217 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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
+
+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'
+ */
+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 zeroes from now
+ mustPrint = true;
+
+ *out++ = '0'+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>
+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
+ */
+inline int ASSIMP_stricmp(const char *s1, const char *s2)
+{
+ ai_assert(NULL != s1 && NULL != s2);
+
+#if (defined _MSC_VER)
+
+ return ::_stricmp(s1,s2);
+#elif defined( __GNUC__ )
+
+ return ::strcasecmp(s1,s2);
+#else
+
+ register 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
+ */
+inline int ASSIMP_stricmp(const std::string& a, const std::string& b)
+{
+ register 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
+ */
+inline int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n)
+{
+ ai_assert(NULL != s1 && NULL != s2);
+ if (!n)return 0;
+
+#if (defined _MSC_VER)
+
+ return ::_strnicmp(s1,s2,n);
+
+#elif defined( __GNUC__ )
+
+ return ::strncasecmp(s1,s2, n);
+
+#else
+ register 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
+ */
+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/src/3rdparty/assimp/code/Subdivision.cpp b/src/3rdparty/assimp/code/Subdivision.cpp
new file mode 100644
index 000000000..088f15480
--- /dev/null
+++ b/src/3rdparty/assimp/code/Subdivision.cpp
@@ -0,0 +1,591 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+
+#include "Subdivision.h"
+#include "SceneCombiner.h"
+#include "SpatialSort.h"
+#include "ProcessHelper.h"
+#include "Vertex.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+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
+ * possibel and much quicker, but it depends on lengthy matrix lookup tables. */
+// ------------------------------------------------------------------------------------------------
+class CatmullClarkSubdivider : public Subdivider
+{
+
+public:
+
+ void Subdivide (const aiMesh* mesh, aiMesh*& out, unsigned int num, bool discard_input);
+ void Subdivide (const aiMesh* const * 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 (
+ const aiMesh* mesh,
+ aiMesh*& out,
+ unsigned int num,
+ bool discard_input
+ )
+{
+ assert(mesh != out);
+ Subdivide(&mesh,1,&out,num,discard_input);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Call the Catmull Clark subdivision algorithm for multiple meshes
+void CatmullClarkSubdivider::Subdivide (
+ const aiMesh* const * smesh,
+ size_t nmesh,
+ aiMesh** out,
+ unsigned int num,
+ bool discard_input
+ )
+{
+ ai_assert(NULL != smesh && NULL != out);
+
+ // course, both regions may not overlap
+ 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] = const_cast<aiMesh*>( smesh[s] );
+ const_cast<aiMesh*&>( smesh[s] ) = NULL;
+ }
+ }
+ else {
+ for (size_t s = 0; s < nmesh; ++s) {
+ SceneCombiner::Copy(out+s,smesh[s]);
+ }
+ }
+ return;
+ }
+
+ std::vector<const 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) {
+ const aiMesh* i = smesh[s];
+ // FIX - mPrimitiveTypes might not yet be initialized
+ if (i->mPrimitiveTypes && (i->mPrimitiveTypes & (aiPrimitiveType_LINE|aiPrimitiveType_POINT))==i->mPrimitiveTypes) {
+ DefaultLogger::get()->debug("Catmull-Clark Subdivider: Skipping pure line/point mesh");
+
+ if (discard_input) {
+ out[s] = const_cast<aiMesh*>( i );
+ const_cast<aiMesh*&>( smesh[s] ) = NULL;
+ }
+ else {
+ SceneCombiner::Copy(out+s,i);
+ }
+ continue;
+ }
+
+ outmeshes.push_back(NULL);inmeshes.push_back(i);
+ maptbl.push_back(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()) {
+ DefaultLogger::get()->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(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 continously , 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;
+ }
+ }
+
+ 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.
+ char tmp[512];
+ sprintf(tmp,"Catmull-Clark Subdivider: got %u bad edges touching only one face (totally %u edges). ",
+ bad_cnt,static_cast<unsigned int>(edges.size()));
+
+ DefaultLogger::get()->debug(tmp);
+ }}
+
+ // ---------------------------------------------------------------------
+ // 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 _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);
+ 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];
+# ifdef _DEBUG
+ bool haveit = false;
+# endif
+
+ // 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;
+
+# ifdef _DEBUG
+ haveit = true;
+# endif
+ break;
+ }
+ }
+
+ // this invariant *must* hold if the vertex-to-face adjacency table is valid
+ ai_assert(haveit);
+ }
+
+ 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++);
+ }
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ // 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/src/3rdparty/assimp/code/Subdivision.h b/src/3rdparty/assimp/code/Subdivision.h
new file mode 100644
index 000000000..31a7a258b
--- /dev/null
+++ b/src/3rdparty/assimp/code/Subdivision.h
@@ -0,0 +1,124 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Defines a helper class to evaluate subdivision surfaces.*/
+#ifndef AI_SUBDISIVION_H_INC
+#define AI_SUBDISIVION_H_INC
+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
+ };
+
+public:
+
+ virtual ~Subdivider() {
+ }
+
+public:
+
+ // ---------------------------------------------------------------
+ /** 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 (const 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 (
+ const aiMesh* const * smesh,
+ size_t nmesh,
+ aiMesh** out,
+ unsigned int num,
+ bool discard_input = false) = 0;
+
+private:
+};
+
+} // end namespace Assimp
+
+
+#endif // !! AI_SUBDISIVION_H_INC
+
diff --git a/src/3rdparty/assimp/code/TargetAnimation.cpp b/src/3rdparty/assimp/code/TargetAnimation.cpp
new file mode 100644
index 000000000..76fe5e3db
--- /dev/null
+++ b/src/3rdparty/assimp/code/TargetAnimation.cpp
@@ -0,0 +1,246 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#include "TargetAnimation.h"
+#include <algorithm>
+
+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, float 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<unsigned int> ( nextObjPos, objPos->size()-1) ).mTime;
+ d1 = targetObjPos->at( std::min<unsigned int> ( nextTargetObjPos, 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, (float) (
+ (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, (float) (
+ (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;
+ float 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/src/3rdparty/assimp/code/TargetAnimation.h b/src/3rdparty/assimp/code/TargetAnimation.h
new file mode 100644
index 000000000..9c6753022
--- /dev/null
+++ b/src/3rdparty/assimp/code/TargetAnimation.h
@@ -0,0 +1,179 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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
+
+
+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 ASSIMP_API 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 ASSIMP_API 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/src/3rdparty/assimp/code/TerragenLoader.cpp b/src/3rdparty/assimp/code/TerragenLoader.cpp
new file mode 100644
index 000000000..33bbe3721
--- /dev/null
+++ b/src/3rdparty/assimp/code/TerragenLoader.cpp
@@ -0,0 +1,248 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the Terragen importer class */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
+#include "TerragenLoader.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+TerragenImporter::TerragenImporter()
+: configComputeUVs (false)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+TerragenImporter::~TerragenImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool TerragenImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ // check file extension
+ std::string extension = GetExtension(pFile);
+
+ if ( extension == "ter")
+ return true;
+
+ if ( !extension.length() || checkSig) {
+ /* If CanRead() is called in order to check whether we
+ * support a specific file extension in general pIOHandler
+ * might be NULL and it's our duty to return true here.
+ */
+ if (!pIOHandler)return true;
+ const char* tokens[] = {"terragen"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a string of all file extensions supported
+void TerragenImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("ter");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup import properties
+void TerragenImporter::SetupProperties(const Importer* pImp)
+{
+ // AI_CONFIG_IMPORT_TER_MAKE_UVS
+ configComputeUVs = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_TER_MAKE_UVS,0) );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void TerragenImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ IOStream* file = pIOHandler->Open( pFile, "rb");
+
+ // Check whether we can read from the file
+ if ( file == NULL)
+ throw DeadlyImportError( "Failed to open TERRAGEN TERRAIN file " + pFile + ".");
+
+ // Construct a stream reader to read all data in the correct endianess
+ StreamReaderLE reader(file);
+ if (reader.GetRemainingSize() < 16)
+ throw DeadlyImportError( "TER: file is too small" );
+
+ // Check for the existence of the two magic strings 'TERRAGEN' and 'TERRAIN '
+ if (::strncmp((const char*)reader.GetPtr(),AI_TERR_BASE_STRING,8))
+ throw DeadlyImportError( "TER: Magic string \'TERRAGEN\' not found" );
+
+ if (::strncmp((const char*)reader.GetPtr()+8,AI_TERR_TERRAIN_STRING,8))
+ throw DeadlyImportError( "TER: Magic string \'TERRAIN\' not found" );
+
+ unsigned int x = 0,y = 0,mode = 0;
+
+ aiNode* root = pScene->mRootNode = new aiNode();
+ root->mName.Set("<TERRAGEN.TERRAIN>");
+
+ // Default scaling is 30
+ root->mTransformation.a1 = root->mTransformation.b2 = root->mTransformation.c3 = 30.f;
+
+ // Now read all chunks until we're finished or an EOF marker is encountered
+ reader.IncPtr(16);
+ while (reader.GetRemainingSize() >= 4)
+ {
+ const char* head = (const char*)reader.GetPtr();
+ reader.IncPtr(4);
+
+ // EOF, break in every case
+ if (!::strncmp(head,AI_TERR_EOF_STRING,4))
+ break;
+
+ // Number of x-data points
+ if (!::strncmp(head,AI_TERR_CHUNK_XPTS,4))
+ {
+ x = (uint16_t)reader.GetI2();
+ }
+ // Number of y-data points
+ else if (!::strncmp(head,AI_TERR_CHUNK_YPTS,4))
+ {
+ y = (uint16_t)reader.GetI2();
+ }
+ // Squared terrains width-1.
+ else if (!::strncmp(head,AI_TERR_CHUNK_SIZE,4))
+ {
+ x = y = (uint16_t)reader.GetI2()+1;
+ }
+ // terrain scaling
+ else if (!::strncmp(head,AI_TERR_CHUNK_SCAL,4))
+ {
+ root->mTransformation.a1 = reader.GetF4();
+ root->mTransformation.b2 = reader.GetF4();
+ root->mTransformation.c3 = reader.GetF4();
+ }
+ // mapping == 1: earth radius
+ // mapping mode
+ else if (!::strncmp(head,AI_TERR_CHUNK_CRVM,4))
+ {
+ mode = reader.GetI1();
+ if (0 != mode)
+ DefaultLogger::get()->error("TER: Unsupported mapping mode, a flat terrain is returned");
+ }
+ // actual terrain data
+ else if (!::strncmp(head,AI_TERR_CHUNK_ALTW,4))
+ {
+ float hscale = (float)reader.GetI2() / 65536;
+ float bheight = (float)reader.GetI2();
+
+ if (!hscale)hscale = 1;
+
+ // Ensure we have enough data
+ if (reader.GetRemainingSize() < x*y*2)
+ throw DeadlyImportError("TER: ALTW chunk is too small");
+
+ if (x <= 1 || y <= 1)
+ throw DeadlyImportError("TER: Invalid terrain size");
+
+ // Allocate the output mesh
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes = 1];
+ aiMesh* m = pScene->mMeshes[0] = new aiMesh();
+
+ // We return quads
+ aiFace* f = m->mFaces = new aiFace[m->mNumFaces = (x-1)*(y-1)];
+ aiVector3D* pv = m->mVertices = new aiVector3D[m->mNumVertices = m->mNumFaces*4];
+
+ aiVector3D *uv( NULL );
+ float step_y( 0.0f ), step_x( 0.0f );
+ if (configComputeUVs) {
+ uv = m->mTextureCoords[0] = new aiVector3D[m->mNumVertices];
+ step_y = 1.f/y;
+ step_x = 1.f/x;
+ }
+ const int16_t* data = (const int16_t*)reader.GetPtr();
+
+ for (unsigned int yy = 0, t = 0; yy < y-1;++yy) {
+ for (unsigned int xx = 0; xx < x-1;++xx,++f) {
+
+ // make verts
+ const float fy = (float)yy, fx = (float)xx;
+ register unsigned tmp,tmp2;
+ *pv++ = aiVector3D(fx,fy, (float)data[(tmp2=x*yy) + xx] * hscale + bheight);
+ *pv++ = aiVector3D(fx,fy+1, (float)data[(tmp=x*(yy+1)) + xx] * hscale + bheight);
+ *pv++ = aiVector3D(fx+1,fy+1,(float)data[tmp + xx+1] * hscale + bheight);
+ *pv++ = aiVector3D(fx+1,fy, (float)data[tmp2 + xx+1] * hscale + bheight);
+
+ // also make texture coordinates, if necessary
+ if (configComputeUVs) {
+ *uv++ = aiVector3D( step_x*xx, step_y*yy, 0.f );
+ *uv++ = aiVector3D( step_x*xx, step_y*(yy+1), 0.f );
+ *uv++ = aiVector3D( step_x*(xx+1), step_y*(yy+1), 0.f );
+ *uv++ = aiVector3D( step_x*(xx+1), step_y*yy, 0.f );
+ }
+
+ // make indices
+ f->mIndices = new unsigned int[f->mNumIndices = 4];
+ for (unsigned int i = 0; i < 4;++i)
+ f->mIndices[i] = t++;
+ }
+ }
+
+ // Add the mesh to the root node
+ root->mMeshes = new unsigned int[root->mNumMeshes = 1];
+ root->mMeshes[0] = 0;
+ }
+
+ // Get to the next chunk (4 byte aligned)
+ unsigned dtt;
+ if ((dtt = reader.GetCurrentPos() & 0x3))
+ reader.IncPtr(4-dtt);
+ }
+
+ // Check whether we have a mesh now
+ if (pScene->mNumMeshes != 1)
+ throw DeadlyImportError("TER: Unable to load terrain");
+
+ // Set the AI_SCENE_FLAGS_TERRAIN bit
+ pScene->mFlags |= AI_SCENE_FLAGS_TERRAIN;
+}
+
+#endif // !! ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
diff --git a/src/3rdparty/assimp/code/TerragenLoader.h b/src/3rdparty/assimp/code/TerragenLoader.h
new file mode 100644
index 000000000..48e97d1a4
--- /dev/null
+++ b/src/3rdparty/assimp/code/TerragenLoader.h
@@ -0,0 +1,107 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file TerragenLoader.h
+ * @brief Declaration of the .ter importer class.
+ */
+#ifndef INCLUDED_AI_TERRAGEN_TERRAIN_LOADER_H
+#define INCLUDED_AI_TERRAGEN_TERRAIN_LOADER_H
+
+#include "BaseImporter.h"
+namespace Assimp {
+
+// Magic strings
+#define AI_TERR_BASE_STRING "TERRAGEN"
+#define AI_TERR_TERRAIN_STRING "TERRAIN "
+#define AI_TERR_EOF_STRING "EOF "
+
+// Chunka
+#define AI_TERR_CHUNK_XPTS "XPTS"
+#define AI_TERR_CHUNK_YPTS "YPTS"
+#define AI_TERR_CHUNK_SIZE "SIZE"
+#define AI_TERR_CHUNK_SCAL "SCAL"
+#define AI_TERR_CHUNK_CRAD "CRAD"
+#define AI_TERR_CHUNK_CRVM "CRVM"
+#define AI_TERR_CHUNK_ALTW "ALTW"
+
+// ---------------------------------------------------------------------------
+/** @brief Importer class to load Terragen (0.9) terrain files.
+ *
+ * The loader is basing on the information found here:
+ * http://www.planetside.co.uk/terragen/dev/tgterrain.html#chunks
+*/
+class TerragenImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ TerragenImporter();
+
+ /** Destructor, private as well */
+ ~TerragenImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+private:
+
+ bool configComputeUVs;
+
+}; //! class TerragenImporter
+
+} // end of namespace Assimp
+
+#endif // AI_AC3DIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/TextureTransform.cpp b/src/3rdparty/assimp/code/TextureTransform.cpp
new file mode 100644
index 000000000..d1b775e86
--- /dev/null
+++ b/src/3rdparty/assimp/code/TextureTransform.cpp
@@ -0,0 +1,567 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file A helper class that processes texture transformations */
+
+
+#include "AssimpPCH.h"
+#include "TextureTransform.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+TextureTransformStep::TextureTransformStep() : configFlags(0)
+{
+ // 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 oder 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 = (int)(info.mRotation / (float)AI_MATH_TWO_PI)))
+ {
+ out -= rounded*(float)AI_MATH_PI;
+
+ sprintf(szTemp,"Texture coordinate rotation %f can be simplified to %f",info.mRotation,out);
+ DefaultLogger::get()->info(szTemp);
+ }
+
+ // 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;
+ szTemp[0] = 0;
+ if (aiTextureMapMode_Wrap == info.mapU) {
+ // Wrap - simple take the fraction of the field
+ out = info.mTranslation.x-(float)rounded;
+ sprintf(szTemp,"[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;
+
+ sprintf(szTemp,"[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
+ sprintf(szTemp,"[c] UV U offset %f can be clamped to 1.0f",info.mTranslation.x);
+
+ out = 1.f;
+ }
+ if (szTemp[0]) {
+ DefaultLogger::get()->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;
+ szTemp[0] = 0;
+ if (aiTextureMapMode_Wrap == info.mapV) {
+ // Wrap - simple take the fraction of the field
+ out = info.mTranslation.y-(float)rounded;
+ sprintf(szTemp,"[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;
+
+ sprintf(szTemp,"[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
+ sprintf(szTemp,"[c] UV V offset %f canbe clamped to 1.0f",info.mTranslation.y);
+
+ out = 1.f;
+ }
+ if (szTemp[0]) {
+ DefaultLogger::get()->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)
+{
+ DefaultLogger::get()->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 = (MaterialHelper*) 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);
+ delete[] prop2->mData;
+
+ // 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];
+
+ // Warn: could be an underflow, but nevertheless it should work
+ --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 coresponding 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){
+ DefaultLogger::get()->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) {
+ DefaultLogger::get()->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()) {
+ ::sprintf(buffer,"%u UV channels required but just %u available",
+ static_cast<unsigned int>(trafo.size()),AI_MAX_NUMBER_OF_TEXTURECOORDS);
+
+ DefaultLogger::get()->error(buffer);
+ }
+ 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()) {
+ sprintf(buffer,"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));
+
+ DefaultLogger::get()->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 detailled statistics into the log
+ if (!DefaultLogger::isNullLogger()) {
+
+ if (transformedChannels) {
+ ::sprintf(buffer,"TransformUVCoordsProcess end: %u output channels (in: %u, modified: %u)",
+ outChannels,inChannels,transformedChannels);
+
+ DefaultLogger::get()->info(buffer);
+ }
+ else DefaultLogger::get()->debug("TransformUVCoordsProcess finished");
+ }
+}
+
+
diff --git a/src/3rdparty/assimp/code/TextureTransform.h b/src/3rdparty/assimp/code/TextureTransform.h
new file mode 100644
index 000000000..dd57e2a5c
--- /dev/null
+++ b/src/3rdparty/assimp/code/TextureTransform.h
@@ -0,0 +1,225 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Definition of a helper step that processes texture transformations */
+#ifndef AI_TEXTURE_TRANSFORM_H_INCLUDED
+#define AI_TEXTURE_TRANSFORM_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "BaseProcess.h"
+
+struct aiNode;
+
+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() :
+ directShortcut (NULL)
+ , mat (NULL)
+ , semantic (0)
+ , index (0)
+ {}
+
+ //! Direct shortcut, if available
+ unsigned int* directShortcut;
+
+ //! Material
+ MaterialHelper *mat;
+
+ //! Texture type and index
+ unsigned int semantic, index;
+};
+
+
+// ---------------------------------------------------------------------------
+/** Helper class representing texture coordinate transformations
+*/
+struct STransformVecInfo : public aiUVTransform
+{
+
+ STransformVecInfo()
+ : uvIndex (0)
+ , mapU (aiTextureMapMode_Wrap)
+ , mapV (aiTextureMapMode_Wrap)
+ , lockedPos (AI_TT_UV_IDX_LOCK_NONE)
+ {}
+
+ //! 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 (fabs( mTranslation.x - other.mTranslation.x ) > epsilon ||
+ fabs( mTranslation.y - other.mTranslation.y ) > epsilon)
+ {
+ return false;
+ }
+
+ if (fabs( mScaling.x - other.mScaling.x ) > epsilon ||
+ fabs( mScaling.y - other.mScaling.y ) > epsilon)
+ {
+ return false;
+ }
+
+ if (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 = cos(mRotation);
+ mRot.a2 = mRot.b1 = 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 ASSIMP_API TextureTransformStep : public BaseProcess
+{
+public:
+
+ TextureTransformStep();
+ ~TextureTransformStep();
+
+ // -------------------------------------------------------------------
+ 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/src/3rdparty/assimp/code/TinyFormatter.h b/src/3rdparty/assimp/code/TinyFormatter.h
new file mode 100644
index 000000000..c8478e124
--- /dev/null
+++ b/src/3rdparty/assimp/code/TinyFormatter.h
@@ -0,0 +1,141 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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;
+ }
+
+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;
+ }
+
+private:
+ mutable stringstream underlying;
+};
+
+
+typedef basic_formatter< char > format;
+typedef basic_formatter< wchar_t > wformat;
+
+} // ! namespace Formatter
+
+} // ! namespace Assimp
+#endif
diff --git a/src/3rdparty/assimp/code/TriangulateProcess.cpp b/src/3rdparty/assimp/code/TriangulateProcess.cpp
new file mode 100644
index 000000000..c1460f14f
--- /dev/null
+++ b/src/3rdparty/assimp/code/TriangulateProcess.cpp
@@ -0,0 +1,401 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2009, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file 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.
+ *
+ * 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.
+ */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
+#include "TriangulateProcess.h"
+#include "ProcessHelper.h"
+
+//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
+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)
+{
+ DefaultLogger::get()->debug("TriangulateProcess begin");
+
+ bool bHas = false;
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ {
+ if ( TriangulateMesh( pScene->mMeshes[a]))
+ bHas = true;
+ }
+ if (bHas)DefaultLogger::get()->info ("TriangulateProcess finished. All polygons have been triangulated.");
+ else DefaultLogger::get()->debug("TriangulateProcess finished. There was nothing to be done.");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Test whether a point p2 is on the left side of the line formed by p0-p1
+inline bool OnLeftSideOfLine(const aiVector2D& p0, const aiVector2D& p1,const aiVector2D& p2)
+{
+ return ( (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y) ) > 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Test whether a point is inside a given triangle in R2
+inline bool PointInTriangle2D(const aiVector2D& p0, const aiVector2D& p1,const aiVector2D& p2, const aiVector2D& pp)
+{
+ // Point in triangle test using baryzentric coordinates
+ const aiVector2D v0 = p1 - p0;
+ const aiVector2D v1 = p2 - p0;
+ const aiVector2D v2 = pp - p0;
+
+ float dot00 = v0 * v0;
+ float dot01 = v0 * v1;
+ float dot02 = v0 * v2;
+ float dot11 = v1 * v1;
+ float dot12 = v1 * v2;
+
+ const float 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);
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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;
+ }
+
+ // the output mesh will contain triangles, but no polys anymore
+ pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON;
+
+ // Find out how many output faces we'll get
+ unsigned int numOut = 0, max_out = 0;
+ for ( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
+ aiFace& face = pMesh->mFaces[a];
+ 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
+ assert(numOut != pMesh->mNumFaces);
+
+ aiVector3D* nor_out = NULL;
+ if (!pMesh->mNormals && pMesh->mPrimitiveTypes == aiPrimitiveType_POLYGON) {
+ nor_out = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+ }
+
+ aiFace* out = new aiFace[numOut], *curOut = out;
+ std::vector<aiVector3D> temp_verts(max_out+2); /* temporary storage for vertices */
+
+ // 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
+
+ // use boost::scoped_array to avoid slow std::vector<bool> specialiations
+ boost::scoped_array<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
+
+ // 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;
+ }
+ // quadrilaterals can't have ears. trifanning will always work
+ else if ( face.mNumIndices == 4) {
+ aiFace& nface = *curOut++;
+ nface.mNumIndices = 3;
+ nface.mIndices = face.mIndices;
+
+ aiFace& sface = *curOut++;
+ sface.mNumIndices = 3;
+ sface.mIndices = new unsigned int[3];
+
+ sface.mIndices[0] = face.mIndices[0];
+ sface.mIndices[1] = face.mIndices[2];
+ sface.mIndices[2] = face.mIndices[3];
+ }
+ 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 trifanning. However, LightWave is probably the only
+ // modeller to make extensive use of highly concave monster polygons ...
+ // so we need to apply the full 'ear cutting' algorithm.
+
+ // RERQUIREMENT: polygon is expected to be simple and *nearly* planar.
+ // We project it onto a plane to get 2d data. Working in R3 would
+ // also be possible but it's more difficult to implement.
+
+ // Collect all vertices of of the polygon.
+ aiVector3D* verts = pMesh->mVertices;
+ for (tmp = 0; tmp < max; ++tmp) {
+ temp_verts[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_verts.front().x,&temp_verts.front().y,&temp_verts.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;
+ }
+
+ //
+ // 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-1?next=0:next)];++next) {}
+ if (next < ear) {
+ if (++num_found == 2) {
+ break;
+ }
+ }
+ const aiVector2D* pnt1 = (const aiVector2D*)&temp_verts[ear],
+ *pnt0 = (const aiVector2D*)&temp_verts[prev],
+ *pnt2 = (const aiVector2D*)&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 (OnLeftSideOfLine (*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 refering 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 = * ((aiVector2D*) &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 someting wrong ... but we don't give up yet.
+ //
+
+ // Instead we're continuting with the standard trifanning algorithm which we'd
+ // use if we had only convex polygons. That's life.
+ DefaultLogger::get()->error("Failed to triangulate polygon (no ear found). Probably not a simple polygon?");
+
+ 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] = idx[0];
+ nface.mIndices[1] = idx[tmp+1];
+ nface.mIndices[2] = idx[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] = idx[prev];
+ nface.mIndices[1] = idx[ear];
+ nface.mIndices[2] = idx[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;
+ nface.mIndices = face.mIndices;
+
+ for (tmp = 0; done[tmp]; ++tmp) {}
+ idx[0] = idx[tmp];
+
+ for (++tmp; done[tmp]; ++tmp) {}
+ idx[1] = idx[tmp];
+
+ for (++tmp; done[tmp]; ++tmp) {}
+ idx[2] = idx[tmp];
+ }
+ }
+ face.mIndices = NULL; /* prevent unintended deletion of our awesome results. would be a pity */
+ }
+
+ // 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/src/3rdparty/assimp/code/TriangulateProcess.h b/src/3rdparty/assimp/code/TriangulateProcess.h
new file mode 100644
index 000000000..7a680e5aa
--- /dev/null
+++ b/src/3rdparty/assimp/code/TriangulateProcess.h
@@ -0,0 +1,98 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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
+{
+ friend class Importer;
+ friend class ::TriangulateProcessTest; // grant the unit test full access to us
+
+protected:
+ /** Constructor to be privately used by Importer */
+ TriangulateProcess();
+
+ /** Destructor, private as well */
+ ~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);
+
+protected:
+ // -------------------------------------------------------------------
+ /** 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/src/3rdparty/assimp/code/UnrealLoader.cpp b/src/3rdparty/assimp/code/UnrealLoader.cpp
new file mode 100644
index 000000000..402a0a881
--- /dev/null
+++ b/src/3rdparty/assimp/code/UnrealLoader.cpp
@@ -0,0 +1,430 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file UnrealLoader.cpp
+ * @brief Implementation of the UNREAL (*.3D) importer class
+ *
+ * Sources:
+ * http://local.wasp.uwa.edu.au/~pbourke/dataformats/unreal/
+ */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_3D_IMPORTER
+
+#include "UnrealLoader.h"
+#include "StreamReader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+#include "ConvertToLHProcess.h"
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+UnrealImporter::UnrealImporter()
+: configFrameID (0)
+, configHandleFlags (true)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+UnrealImporter::~UnrealImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool UnrealImporter::CanRead( const std::string& pFile, IOSystem*, bool) const
+{
+ return SimpleExtensionCheck(pFile,"3d","uc");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a string of all file extensions supported
+void UnrealImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("3d");
+ extensions.insert("uc");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void UnrealImporter::SetupProperties(const Importer* pImp)
+{
+ // The
+ // AI_CONFIG_IMPORT_UNREAL_KEYFRAME option overrides the
+ // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_UNREAL_KEYFRAME,0xffffffff);
+ if (0xffffffff == configFrameID) {
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+ }
+
+ // AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS, default is true
+ configHandleFlags = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS,1));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void UnrealImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ // For any of the 3 files being passed get the three correct paths
+ // First of all, determine file extension
+ std::string::size_type pos = pFile.find_last_of('.');
+ std::string extension = GetExtension(pFile);
+
+ std::string d_path,a_path,uc_path;
+ if (extension == "3d") {
+ // jjjj_d.3d
+ // jjjj_a.3d
+ pos = pFile.find_last_of('_');
+ if (std::string::npos == pos) {
+ throw DeadlyImportError("UNREAL: Unexpected naming scheme");
+ }
+ extension = pFile.substr(0,pos);
+ }
+ else {
+ extension = pFile.substr(0,pos);
+ }
+
+ // build proper paths
+ d_path = extension+"_d.3d";
+ a_path = extension+"_a.3d";
+ uc_path = extension+".uc";
+
+ DefaultLogger::get()->debug("UNREAL: data file is " + d_path);
+ DefaultLogger::get()->debug("UNREAL: aniv file is " + a_path);
+ DefaultLogger::get()->debug("UNREAL: uc file is " + uc_path);
+
+ // and open the files ... we can't live without them
+ IOStream* p = pIOHandler->Open(d_path);
+ if (!p)
+ throw DeadlyImportError("UNREAL: Unable to open _d file");
+ StreamReaderLE d_reader(pIOHandler->Open(d_path));
+
+ const uint16_t numTris = d_reader.GetI2();
+ const uint16_t numVert = d_reader.GetI2();
+ d_reader.IncPtr(44);
+ if (!numTris || numVert < 3)
+ throw DeadlyImportError("UNREAL: Invalid number of vertices/triangles");
+
+ // maximum texture index
+ unsigned int maxTexIdx = 0;
+
+ // collect triangles
+ std::vector<Unreal::Triangle> triangles(numTris);
+ for (std::vector<Unreal::Triangle>::iterator it = triangles.begin(), end = triangles.end();it != end; ++it) {
+ Unreal::Triangle& tri = *it;
+
+ for (unsigned int i = 0; i < 3;++i) {
+
+ tri.mVertex[i] = d_reader.GetI2();
+ if (tri.mVertex[i] >= numTris) {
+ DefaultLogger::get()->warn("UNREAL: vertex index out of range");
+ tri.mVertex[i] = 0;
+ }
+ }
+ tri.mType = d_reader.GetI1();
+
+ // handle mesh flagss?
+ if (configHandleFlags)
+ tri.mType = Unreal::MF_NORMAL_OS;
+ else {
+ // ignore MOD and MASKED for the moment, treat them as two-sided
+ if (tri.mType == Unreal::MF_NORMAL_MOD_TS || tri.mType == Unreal::MF_NORMAL_MASKED_TS)
+ tri.mType = Unreal::MF_NORMAL_TS;
+ }
+ d_reader.IncPtr(1);
+
+ for (unsigned int i = 0; i < 3;++i)
+ for (unsigned int i2 = 0; i2 < 2;++i2)
+ tri.mTex[i][i2] = d_reader.GetI1();
+
+ tri.mTextureNum = d_reader.GetI1();
+ maxTexIdx = std::max(maxTexIdx,(unsigned int)tri.mTextureNum);
+ d_reader.IncPtr(1);
+ }
+
+ p = pIOHandler->Open(a_path);
+ if (!p)
+ throw DeadlyImportError("UNREAL: Unable to open _a file");
+ StreamReaderLE a_reader(pIOHandler->Open(a_path));
+
+ // read number of frames
+ const uint32_t numFrames = a_reader.GetI2();
+ if (configFrameID >= numFrames)
+ throw DeadlyImportError("UNREAL: The requested frame does not exist");
+
+ int16_t st = a_reader.GetI2();
+ if (st != numVert*4)
+ throw DeadlyImportError("UNREAL: Unexpected aniv file length");
+
+ // skip to our frame
+ a_reader.IncPtr(configFrameID *numVert*4);
+
+ // collect vertices
+ std::vector<aiVector3D> vertices(numVert);
+ for (std::vector<aiVector3D>::iterator it = vertices.begin(), end = vertices.end(); it != end; ++it) {
+ int32_t val = a_reader.GetI4();
+ Unreal::DecompressVertex(*it,val);
+ }
+
+ // list of textures.
+ std::vector< std::pair<unsigned int, std::string> > textures;
+
+ // allocate the output scene
+ aiNode* nd = pScene->mRootNode = new aiNode();
+ nd->mName.Set("<UnrealRoot>");
+
+ // we can live without the uc file if necessary
+ boost::scoped_ptr<IOStream> pb (pIOHandler->Open(uc_path));
+ if (pb.get()) {
+
+ std::vector<char> _data;
+ TextFileToBuffer(pb.get(),_data);
+ const char* data = &_data[0];
+
+ std::vector< std::pair< std::string,std::string > > tempTextures;
+
+ // do a quick search in the UC file for some known, usually texture-related, tags
+ for (;*data;++data) {
+ if (TokenMatchI(data,"#exec",5)) {
+ SkipSpacesAndLineEnd(&data);
+
+ // #exec TEXTURE IMPORT [...] NAME=jjjjj [...] FILE=jjjj.pcx [...]
+ if (TokenMatchI(data,"TEXTURE",7)) {
+ SkipSpacesAndLineEnd(&data);
+
+ if (TokenMatchI(data,"IMPORT",6)) {
+ tempTextures.push_back(std::pair< std::string,std::string >());
+ std::pair< std::string,std::string >& me = tempTextures.back();
+ for (;!IsLineEnd(*data);++data) {
+ if (!::ASSIMP_strincmp(data,"NAME=",5)) {
+ const char *d = data+=5;
+ for (;!IsSpaceOrNewLine(*data);++data) {}
+ me.first = std::string(d,(size_t)(data-d));
+ }
+ else if (!::ASSIMP_strincmp(data,"FILE=",5)) {
+ const char *d = data+=5;
+ for (;!IsSpaceOrNewLine(*data);++data) {}
+ me.second = std::string(d,(size_t)(data-d));
+ }
+ }
+ if (!me.first.length() || !me.second.length())
+ tempTextures.pop_back();
+ }
+ }
+ // #exec MESHMAP SETTEXTURE MESHMAP=box NUM=1 TEXTURE=Jtex1
+ // #exec MESHMAP SCALE MESHMAP=box X=0.1 Y=0.1 Z=0.2
+ else if (TokenMatchI(data,"MESHMAP",7)) {
+ SkipSpacesAndLineEnd(&data);
+
+ if (TokenMatchI(data,"SETTEXTURE",10)) {
+
+ textures.push_back(std::pair<unsigned int, std::string>());
+ std::pair<unsigned int, std::string>& me = textures.back();
+
+ for (;!IsLineEnd(*data);++data) {
+ if (!::ASSIMP_strincmp(data,"NUM=",4)) {
+ data += 4;
+ me.first = strtol10(data,&data);
+ }
+ else if (!::ASSIMP_strincmp(data,"TEXTURE=",8)) {
+ data += 8;
+ const char *d = data;
+ for (;!IsSpaceOrNewLine(*data);++data) {}
+ me.second = std::string(d,(size_t)(data-d));
+
+ // try to find matching path names, doesn't care if we don't find them
+ for (std::vector< std::pair< std::string,std::string > >::const_iterator it = tempTextures.begin();
+ it != tempTextures.end(); ++it) {
+ if ((*it).first == me.second) {
+ me.second = (*it).second;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else if (TokenMatchI(data,"SCALE",5)) {
+
+ for (;!IsLineEnd(*data);++data) {
+ if (data[0] == 'X' && data[1] == '=') {
+ data = fast_atof_move(data+2,(float&)nd->mTransformation.a1);
+ }
+ else if (data[0] == 'Y' && data[1] == '=') {
+ data = fast_atof_move(data+2,(float&)nd->mTransformation.b2);
+ }
+ else if (data[0] == 'Z' && data[1] == '=') {
+ data = fast_atof_move(data+2,(float&)nd->mTransformation.c3);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ DefaultLogger::get()->error("Unable to open .uc file");
+ }
+
+ std::vector<Unreal::TempMat> materials;
+ materials.reserve(textures.size()*2+5);
+
+ // find out how many output meshes and materials we'll have and build material indices
+ for (std::vector<Unreal::Triangle>::iterator it = triangles.begin(), end = triangles.end();it != end; ++it) {
+ Unreal::Triangle& tri = *it;
+ Unreal::TempMat mat(tri);
+ std::vector<Unreal::TempMat>::iterator nt = std::find(materials.begin(),materials.end(),mat);
+ if (nt == materials.end()) {
+ // add material
+ tri.matIndex = materials.size();
+ mat.numFaces = 1;
+ materials.push_back(mat);
+
+ ++pScene->mNumMeshes;
+ }
+ else {
+ tri.matIndex = static_cast<unsigned int>(nt-materials.begin());
+ ++nt->numFaces;
+ }
+ }
+
+ if (!pScene->mNumMeshes) {
+ throw DeadlyImportError("UNREAL: Unable to find valid mesh data");
+ }
+
+ // allocate meshes and bind them to the node graph
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = pScene->mNumMeshes];
+
+ nd->mNumMeshes = pScene->mNumMeshes;
+ nd->mMeshes = new unsigned int[nd->mNumMeshes];
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i) {
+ aiMesh* m = pScene->mMeshes[i] = new aiMesh();
+ m->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ const unsigned int num = materials[i].numFaces;
+ m->mFaces = new aiFace [num];
+ m->mVertices = new aiVector3D [num*3];
+ m->mTextureCoords[0] = new aiVector3D [num*3];
+
+ nd->mMeshes[i] = i;
+
+ // create materials, too
+ MaterialHelper* mat = new MaterialHelper();
+ pScene->mMaterials[i] = mat;
+
+ // all white by default - texture rulez
+ aiColor3D color(1.f,1.f,1.f);
+
+ aiString s;
+ ::sprintf(s.data,"mat%i_tx%i_",i,materials[i].tex);
+
+ // set the two-sided flag
+ if (materials[i].type == Unreal::MF_NORMAL_TS) {
+ const int twosided = 1;
+ mat->AddProperty(&twosided,1,AI_MATKEY_TWOSIDED);
+ ::strcat(s.data,"ts_");
+ }
+ else ::strcat(s.data,"os_");
+
+ // make TRANS faces 90% opaque that RemRedundantMaterials won't catch us
+ if (materials[i].type == Unreal::MF_NORMAL_TRANS_TS) {
+ const float opac = 0.9f;
+ mat->AddProperty(&opac,1,AI_MATKEY_OPACITY);
+ ::strcat(s.data,"tran_");
+ }
+ else ::strcat(s.data,"opaq_");
+
+ // a special name for the weapon attachment point
+ if (materials[i].type == Unreal::MF_WEAPON_PLACEHOLDER) {
+ s.length = ::sprintf(s.data,"$WeaponTag$");
+ color = aiColor3D(0.f,0.f,0.f);
+ }
+
+ // set color and name
+ mat->AddProperty(&color,1,AI_MATKEY_COLOR_DIFFUSE);
+ s.length = ::strlen(s.data);
+ mat->AddProperty(&s,AI_MATKEY_NAME);
+
+ // set texture, if any
+ const unsigned int tex = materials[i].tex;
+ for (std::vector< std::pair< unsigned int, std::string > >::const_iterator it = textures.begin();it != textures.end();++it) {
+ if ((*it).first == tex) {
+ s.Set((*it).second);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ break;
+ }
+ }
+ }
+
+ // fill them.
+ for (std::vector<Unreal::Triangle>::iterator it = triangles.begin(), end = triangles.end();it != end; ++it) {
+ Unreal::Triangle& tri = *it;
+ Unreal::TempMat mat(tri);
+ std::vector<Unreal::TempMat>::iterator nt = std::find(materials.begin(),materials.end(),mat);
+
+ aiMesh* mesh = pScene->mMeshes[nt-materials.begin()];
+ aiFace& f = mesh->mFaces[mesh->mNumFaces++];
+ f.mIndices = new unsigned int[f.mNumIndices = 3];
+
+ for (unsigned int i = 0; i < 3;++i,mesh->mNumVertices++) {
+ f.mIndices[i] = mesh->mNumVertices;
+
+ mesh->mVertices[mesh->mNumVertices] = vertices[ tri.mVertex[i] ];
+ mesh->mTextureCoords[0][mesh->mNumVertices] = aiVector3D( tri.mTex[i][0] / 255.f, 1.f - tri.mTex[i][1] / 255.f, 0.f);
+ }
+ }
+
+ // convert to RH
+ MakeLeftHandedProcess hero;
+ hero.Execute(pScene);
+
+ FlipWindingOrderProcess flipper;
+ flipper.Execute(pScene);
+}
+
+#endif // !! ASSIMP_BUILD_NO_3D_IMPORTER
diff --git a/src/3rdparty/assimp/code/UnrealLoader.h b/src/3rdparty/assimp/code/UnrealLoader.h
new file mode 100644
index 000000000..647246028
--- /dev/null
+++ b/src/3rdparty/assimp/code/UnrealLoader.h
@@ -0,0 +1,204 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file UnrealLoader.h
+ * @brief Declaration of the .3d (UNREAL) importer class.
+ */
+#ifndef INCLUDED_AI_3D_LOADER_H
+#define INCLUDED_AI_3D_LOADER_H
+
+#include "BaseImporter.h"
+namespace Assimp {
+namespace Unreal {
+
+ /*
+ 0 = Normal one-sided
+ 1 = Normal two-sided
+ 2 = Translucent two-sided
+ 3 = Masked two-sided
+ 4 = Modulation blended two-sided
+ 8 = Placeholder triangle for weapon positioning (invisible)
+ */
+enum MeshFlags {
+ MF_NORMAL_OS = 0,
+ MF_NORMAL_TS = 1,
+ MF_NORMAL_TRANS_TS = 2,
+ MF_NORMAL_MASKED_TS = 3,
+ MF_NORMAL_MOD_TS = 4,
+ MF_WEAPON_PLACEHOLDER = 8
+};
+
+ // a single triangle
+struct Triangle {
+ uint16_t mVertex[3]; // Vertex indices
+ char mType; // James' Mesh Type
+ char mColor; // Color for flat and Gourand Shaded
+ unsigned char mTex[3][2]; // Texture UV coordinates
+ unsigned char mTextureNum; // Source texture offset
+ char mFlags; // Unreal Mesh Flags (unused)
+
+ unsigned int matIndex;
+};
+
+// temporary representation for a material
+struct TempMat {
+ TempMat()
+ : numFaces (0)
+ {}
+
+ TempMat(const Triangle& in)
+ : type ((Unreal::MeshFlags)in.mType)
+ , tex (in.mTextureNum)
+ , numFaces (0)
+ {}
+
+ // type of mesh
+ Unreal::MeshFlags type;
+
+ // index of texture
+ unsigned int tex;
+
+ // number of faces using us
+ unsigned int numFaces;
+
+ // for std::find
+ bool operator == (const TempMat& o ) {
+ return (tex == o.tex && type == o.type);
+ }
+};
+
+struct Vertex
+{
+ int32_t X : 11;
+ int32_t Y : 11;
+ int32_t Z : 10;
+};
+
+ // UNREAL vertex compression
+inline void CompressVertex(const aiVector3D& v, uint32_t& out)
+{
+ union {
+ Vertex n;
+ int32_t t;
+ };
+ n.X = (int32_t)v.x;
+ n.Y = (int32_t)v.y;
+ n.Z = (int32_t)v.z;
+ out = t;
+}
+
+ // UNREAL vertex decompression
+inline void DecompressVertex(aiVector3D& v, int32_t in)
+{
+ union {
+ Vertex n;
+ int32_t i;
+ };
+ i = in;
+
+ v.x = (float)n.X;
+ v.y = (float)n.Y;
+ v.z = (float)n.Z;
+}
+
+} // end namespace Unreal
+
+// ---------------------------------------------------------------------------
+/** @brief Importer class to load UNREAL files (*.3d)
+*/
+class UnrealImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ UnrealImporter();
+
+ /** Destructor, private as well */
+ ~UnrealImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** @brief Returns whether we can handle the format of the given file
+ *
+ * See BaseImporter::CanRead() for details.
+ **/
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** @brief Called by Importer::GetExtensionList()
+ *
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+
+ // -------------------------------------------------------------------
+ /** @brief Setup properties for the importer
+ *
+ * See BaseImporter::SetupProperties() for details
+ */
+ void SetupProperties(const Importer* pImp);
+
+
+ // -------------------------------------------------------------------
+ /** @brief Imports the given file into the given scene structure.
+ *
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+ //! frame to be loaded
+ uint32_t configFrameID;
+
+ //! process surface flags
+ bool configHandleFlags;
+
+}; // !class UnrealImporter
+
+} // end of namespace Assimp
+#endif // AI_UNREALIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/ValidateDataStructure.cpp b/src/3rdparty/assimp/code/ValidateDataStructure.cpp
new file mode 100644
index 000000000..1bda92eba
--- /dev/null
+++ b/src/3rdparty/assimp/code/ValidateDataStructure.cpp
@@ -0,0 +1,972 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file ValidateDataStructure.cpp
+ * @brief Implementation of the post processing step to validate
+ * the data structure returned by Assimp.
+ */
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "ValidateDataStructure.h"
+#include "BaseImporter.h"
+#include "fast_atof.h"
+#include "ProcessHelper.h"
+
+// CRT headers
+#include <stdarg.h>
+
+#if defined(__QNXNTO__)
+#include <stdio.h>
+#endif
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ValidateDSProcess::ValidateDSProcess() : mScene(0)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// 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);
+#ifdef _DEBUG
+ aiAssert( szBuffer,__LINE__,__FILE__ );
+#endif
+ 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);
+ DefaultLogger::get()->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[%i] is NULL (aiScene::%s is %i)",
+ 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)
+ {
+ this->ReportError("aiScene::%s[%i] has the same name as "
+ "aiScene::%s[%i]",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 (!res) {
+ ReportError("aiScene::%s[%i] has no corresponding node in the scene graph (%s)",
+ firstName,i,array[i]->mName.data);
+ }
+ else if (1 != res) {
+ ReportError("aiScene::%s[%i]: there are more than one nodes with %s as name",
+ firstName,i,array[i]->mName.data);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void ValidateDSProcess::Execute( aiScene* pScene)
+{
+ this->mScene = pScene;
+ DefaultLogger::get()->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");
+ DefaultLogger::get()->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);
+ case 1:
+ if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT))
+ {
+ ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimtiveTypes "
+ "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::mPrimtiveTypes "
+ "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::mPrimtiveTypes "
+ "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::mPrimtiveTypes "
+ "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 contains no vertices");
+ }
+
+ 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 contains no faces");
+ }
+
+ // 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 ) && 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);
+ }
+ boost::scoped_array<float> afSum(NULL);
+ 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)
+ {
+ ReportError("aiMesh::mBones[%i] has the same name as "
+ "aiMesh::mBones[%i]",i,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];
+ if (!::strcmp(prop->mKey.data,"$tex.file") && prop->mSemantic == 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
+ uint32_t len;
+ if (prop->mDataLength < 5 || prop->mDataLength < 4 + (len=*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,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,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,sizeof(int));
+ }
+ }
+ // TODO: check whether there is a key with an unknown name ...
+ }
+
+ // make some more specific tests
+ float 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.01f)) {
+ 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)
+ {
+ this->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)
+ {
+ this->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");
+ if (pNode != mScene->mRootNode && !pNode->mParent)
+ this->ReportError("A node has no valid parent (aiNode::mParent is NULL)");
+
+ this->Validate(&pNode->mName);
+
+ // validate all meshes
+ if (pNode->mNumMeshes)
+ {
+ if (!pNode->mMeshes)
+ {
+ ReportError("aiNode::mMeshes is NULL (aiNode::mNumMeshes is %i)",
+ 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 (maximum is %i)",
+ pNode->mMeshes[i],mScene->mNumMeshes-1);
+ }
+ if (abHadMesh[pNode->mMeshes[i]])
+ {
+ ReportError("aiNode::mMeshes[%i] is already referenced by this node (value: %i)",
+ i,pNode->mMeshes[i]);
+ }
+ abHadMesh[pNode->mMeshes[i]] = true;
+ }
+ }
+ if (pNode->mNumChildren)
+ {
+ if (!pNode->mChildren) {
+ ReportError("aiNode::mChildren is NULL (aiNode::mNumChildren is %i)",
+ 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)
+ {
+ this->ReportError("aiString::length is too large (%i, maximum is %i)",
+ 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/src/3rdparty/assimp/code/ValidateDataStructure.h b/src/3rdparty/assimp/code/ValidateDataStructure.h
new file mode 100644
index 000000000..e9aa12fb4
--- /dev/null
+++ b/src/3rdparty/assimp/code/ValidateDataStructure.h
@@ -0,0 +1,187 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiTypes.h"
+#include "BaseProcess.h"
+
+struct aiBone;
+struct aiMesh;
+struct aiAnimation;
+struct aiNodeAnim;
+struct aiTexture;
+struct aiMaterial;
+struct aiNode;
+struct aiString;
+
+namespace Assimp {
+
+// --------------------------------------------------------------------------------------
+/** Validates the whole ASSIMP scene data structure for correctness.
+ * ImportErrorException is thrown of the scene is corrupt.*/
+// --------------------------------------------------------------------------------------
+class ASSIMP_API ValidateDSProcess : public BaseProcess
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ ValidateDSProcess();
+
+ /** Destructor, private as well */
+ ~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,...);
+
+
+ // -------------------------------------------------------------------
+ /** Report a validation warning. This won't throw an exception,
+ * control will return to the callera.
+ * @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 whethr 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/src/3rdparty/assimp/code/Vertex.h b/src/3rdparty/assimp/code/Vertex.h
new file mode 100644
index 000000000..9db675dcf
--- /dev/null
+++ b/src/3rdparty/assimp/code/Vertex.h
@@ -0,0 +1,304 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/** @file Defines a helper class to represent an interleaved vertex */
+#ifndef AI_VERTEX_H_INC
+#define AI_VERTEX_H_INC
+
+#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&,float);
+ friend Vertex operator - (const Vertex&,float);
+ friend Vertex operator * (const Vertex&,float);
+ friend Vertex operator / (const Vertex&,float);
+
+ friend Vertex operator + (float, const Vertex&);
+ friend Vertex operator - (float, const Vertex&);
+ friend Vertex operator * (float, const Vertex&);
+ friend Vertex operator / (float, 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];
+ }
+ }
+
+public:
+
+ Vertex& operator += (const Vertex& v) {
+ *this = *this+v;
+ return *this;
+ }
+
+ Vertex& operator -= (const Vertex& v) {
+ *this = *this-v;
+ return *this;
+ }
+
+
+
+ Vertex& operator += (float v) {
+ *this = *this+v;
+ return *this;
+ }
+
+ Vertex& operator -= (float v) {
+ *this = *this-v;
+ return *this;
+ }
+
+ Vertex& operator *= (float v) {
+ *this = *this*v;
+ return *this;
+ }
+
+ Vertex& operator /= (float 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, float f) {
+ // this is a heavy task for the compiler to optimize ... *pray*
+
+ Vertex res;
+ res.position = op<aiVector3D,float,aiVector3D>()(v0.position,f);
+ res.normal = op<aiVector3D,float,aiVector3D>()(v0.normal,f);
+ res.tangent = op<aiVector3D,float,aiVector3D>()(v0.tangent,f);
+ res.bitangent = op<aiVector3D,float,aiVector3D>()(v0.bitangent,f);
+
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ res.texcoords[i] = op<aiVector3D,float,aiVector3D>()(v0.texcoords[i],f);
+ }
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
+ res.colors[i] = op<aiColor4D,float,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(float f, const Vertex& v0) {
+ // this is a heavy task for the compiler to optimize ... *pray*
+
+ Vertex res;
+ res.position = op<float,aiVector3D,aiVector3D>()(f,v0.position);
+ res.normal = op<float,aiVector3D,aiVector3D>()(f,v0.normal);
+ res.tangent = op<float,aiVector3D,aiVector3D>()(f,v0.tangent);
+ res.bitangent = op<float,aiVector3D,aiVector3D>()(f,v0.bitangent);
+
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ res.texcoords[i] = op<float,aiVector3D,aiVector3D>()(f,v0.texcoords[i]);
+ }
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
+ res.colors[i] = op<float,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,float f) {
+ return Vertex::BinaryOp<Intern::plus>(v0,f);
+}
+
+AI_FORCE_INLINE Vertex operator - (const Vertex& v0,float f) {
+ return Vertex::BinaryOp<Intern::minus>(v0,f);
+}
+
+AI_FORCE_INLINE Vertex operator * (const Vertex& v0,float f) {
+ return Vertex::BinaryOp<Intern::multiplies>(v0,f);
+}
+
+AI_FORCE_INLINE Vertex operator / (const Vertex& v0,float f) {
+ return Vertex::BinaryOp<Intern::divides>(v0,f);
+}
+
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE Vertex operator + (float f,const Vertex& v0) {
+ return Vertex::BinaryOp<Intern::plus>(f,v0);
+}
+
+AI_FORCE_INLINE Vertex operator - (float f,const Vertex& v0) {
+ return Vertex::BinaryOp<Intern::minus>(f,v0);
+}
+
+AI_FORCE_INLINE Vertex operator * (float f,const Vertex& v0) {
+ return Vertex::BinaryOp<Intern::multiplies>(f,v0);
+}
+
+AI_FORCE_INLINE Vertex operator / (float f,const Vertex& v0) {
+ return Vertex::BinaryOp<Intern::divides>(f,v0);
+}
+
+}
+#endif
diff --git a/src/3rdparty/assimp/code/VertexTriangleAdjacency.cpp b/src/3rdparty/assimp/code/VertexTriangleAdjacency.cpp
new file mode 100644
index 000000000..07ab5090d
--- /dev/null
+++ b/src/3rdparty/assimp/code/VertexTriangleAdjacency.cpp
@@ -0,0 +1,132 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the VertexTriangleAdjacency helper class
+ */
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "VertexTriangleAdjacency.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(3 == pcFace->mNumIndices);
+ iNumVertices = std::max(iNumVertices,pcFace->mIndices[0]);
+ iNumVertices = std::max(iNumVertices,pcFace->mIndices[1]);
+ iNumVertices = std::max(iNumVertices,pcFace->mIndices[2]);
+ }
+ }
+ 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)
+ {
+ pi[pcFace->mIndices[0]]++;
+ pi[pcFace->mIndices[1]]++;
+ pi[pcFace->mIndices[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 int idx = pcFace->mIndices[0];
+ mAdjacencyTable[pi[idx]++] = iSum;
+
+ idx = pcFace->mIndices[1];
+ mAdjacencyTable[pi[idx]++] = iSum;
+
+ idx = pcFace->mIndices[2];
+ mAdjacencyTable[pi[idx]++] = 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/src/3rdparty/assimp/code/VertexTriangleAdjacency.h b/src/3rdparty/assimp/code/VertexTriangleAdjacency.h
new file mode 100644
index 000000000..d4e1d4e6e
--- /dev/null
+++ b/src/3rdparty/assimp/code/VertexTriangleAdjacency.h
@@ -0,0 +1,120 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 "../include/aiTypes.h"
+#include "../include/aiAssert.h"
+
+struct aiMesh;
+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();
+
+
+public:
+
+ // ----------------------------------------------------------------------------
+ /** @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 < iNumVertices);
+ 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 < iNumVertices && NULL != mLiveTriangles);
+ return mLiveTriangles[iVertIndex];
+ }
+
+
+public:
+
+ //! Offset table
+ unsigned int* mOffsetTable;
+
+ //! Adjacency table
+ unsigned int* mAdjacencyTable;
+
+ //! Table containing the number of referenced triangles per vertex
+ unsigned int* mLiveTriangles;
+};
+}
+
+#endif // !! AI_VTADJACENCY_H_INC
diff --git a/src/3rdparty/assimp/code/Win32DebugLogStream.h b/src/3rdparty/assimp/code/Win32DebugLogStream.h
new file mode 100644
index 000000000..f7f0df324
--- /dev/null
+++ b/src/3rdparty/assimp/code/Win32DebugLogStream.h
@@ -0,0 +1,50 @@
+#ifndef AI_WIN32DEBUGLOGSTREAM_H_INC
+#define AI_WIN32DEBUGLOGSTREAM_H_INC
+
+#ifdef WIN32
+
+#include "../include/LogStream.h"
+#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);
+};
+
+// ---------------------------------------------------------------------------
+// Default constructor
+inline Win32DebugLogStream::Win32DebugLogStream()
+{}
+
+// ---------------------------------------------------------------------------
+// Default constructor
+inline Win32DebugLogStream::~Win32DebugLogStream()
+{}
+
+// ---------------------------------------------------------------------------
+// Write method
+inline void Win32DebugLogStream::write(const char* message)
+{
+ OutputDebugStringA( message);
+}
+
+// ---------------------------------------------------------------------------
+} // Namespace Assimp
+
+#endif // ! WIN32
+#endif // guard
diff --git a/src/3rdparty/assimp/code/XFileHelper.h b/src/3rdparty/assimp/code/XFileHelper.h
new file mode 100644
index 000000000..6922bae6d
--- /dev/null
+++ b/src/3rdparty/assimp/code/XFileHelper.h
@@ -0,0 +1,200 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file Defines the helper data structures for importing XFiles */
+#ifndef AI_XFILEHELPER_H_INC
+#define AI_XFILEHELPER_H_INC
+
+#include <string>
+#include <vector>
+
+#include "../include/aiTypes.h"
+#include "../include/aiQuaternion.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+namespace Assimp
+{
+namespace XFile
+{
+
+/** Helper structure representing a XFile mesh face */
+struct Face
+{
+ std::vector<unsigned int> mIndices;
+};
+
+/** Helper structure representing a texture filename inside a material and its potential source */
+struct TexEntry
+{
+ std::string mName;
+ bool mIsNormalMap; // true if the texname was specified in a NormalmapFilename tag
+
+ TexEntry() { mIsNormalMap = false; }
+ TexEntry( const std::string& pName, bool pIsNormalMap = false)
+ : mName( pName), mIsNormalMap( pIsNormalMap)
+ { /* done */ }
+};
+
+/** Helper structure representing a XFile material */
+struct Material
+{
+ std::string mName;
+ bool mIsReference; // if true, mName holds a name by which the actual material can be found in the material list
+ aiColor4D mDiffuse;
+ float mSpecularExponent;
+ aiColor3D mSpecular;
+ aiColor3D mEmissive;
+ std::vector<TexEntry> mTextures;
+
+ Material() : mSpecularExponent(0.0f) { mIsReference = false; }
+};
+
+/** Helper structure to represent a bone weight */
+struct BoneWeight
+{
+ unsigned int mVertex;
+ float mWeight;
+};
+
+/** Helper structure to represent a bone in a mesh */
+struct Bone
+{
+ std::string mName;
+ std::vector<BoneWeight> mWeights;
+ aiMatrix4x4 mOffsetMatrix;
+};
+
+/** Helper structure to represent an XFile mesh */
+struct Mesh
+{
+ std::vector<aiVector3D> mPositions;
+ std::vector<Face> mPosFaces;
+ std::vector<aiVector3D> mNormals;
+ std::vector<Face> mNormFaces;
+ unsigned int mNumTextures;
+ std::vector<aiVector2D> mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ unsigned int mNumColorSets;
+ std::vector<aiColor4D> mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
+
+ std::vector<unsigned int> mFaceMaterials;
+ std::vector<Material> mMaterials;
+
+ std::vector<Bone> mBones;
+
+ Mesh() { mNumTextures = 0; mNumColorSets = 0; }
+};
+
+/** Helper structure to represent a XFile frame */
+struct Node
+{
+ std::string mName;
+ aiMatrix4x4 mTrafoMatrix;
+ Node* mParent;
+ std::vector<Node*> mChildren;
+ std::vector<Mesh*> mMeshes;
+
+ Node() { mParent = NULL; }
+ Node( Node* pParent) { mParent = pParent; }
+ ~Node()
+ {
+ for ( unsigned int a = 0; a < mChildren.size(); a++)
+ delete mChildren[a];
+ for ( unsigned int a = 0; a < mMeshes.size(); a++)
+ delete mMeshes[a];
+ }
+};
+
+struct MatrixKey
+{
+ double mTime;
+ aiMatrix4x4 mMatrix;
+};
+
+/** Helper structure representing a single animated bone in a XFile */
+struct AnimBone
+{
+ std::string mBoneName;
+ std::vector<aiVectorKey> mPosKeys; // either three separate key sequences for position, rotation, scaling
+ std::vector<aiQuatKey> mRotKeys;
+ std::vector<aiVectorKey> mScaleKeys;
+ std::vector<MatrixKey> mTrafoKeys; // or a combined key sequence of transformation matrices.
+};
+
+/** Helper structure to represent an animation set in a XFile */
+struct Animation
+{
+ std::string mName;
+ std::vector<AnimBone*> mAnims;
+
+ ~Animation()
+ {
+ for ( unsigned int a = 0; a < mAnims.size(); a++)
+ delete mAnims[a];
+ }
+};
+
+/** Helper structure analogue to aiScene */
+struct Scene
+{
+ Node* mRootNode;
+
+ std::vector<Mesh*> mGlobalMeshes; // global meshes found outside of any frames
+ std::vector<Material> mGlobalMaterials; // global materials found outside of any meshes.
+
+ std::vector<Animation*> mAnims;
+ unsigned int mAnimTicksPerSecond;
+
+ Scene() { mRootNode = NULL; mAnimTicksPerSecond = 0; }
+ ~Scene()
+ {
+ delete mRootNode;
+ for ( unsigned int a = 0; a < mGlobalMeshes.size(); a++)
+ delete mGlobalMeshes[a];
+ for ( unsigned int a = 0; a < mAnims.size(); a++)
+ delete mAnims[a];
+ }
+};
+
+} // end of namespace XFile
+} // end of namespace Assimp
+
+#endif // AI_XFILEHELPER_H_INC
diff --git a/src/3rdparty/assimp/code/XFileImporter.cpp b/src/3rdparty/assimp/code/XFileImporter.cpp
new file mode 100644
index 000000000..1abfb1f2b
--- /dev/null
+++ b/src/3rdparty/assimp/code/XFileImporter.cpp
@@ -0,0 +1,672 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file XFileImporter.cpp
+ * @brief Implementation of the XFile importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_X_IMPORTER
+
+#include "XFileImporter.h"
+#include "XFileParser.h"
+#include "ConvertToLHProcess.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+XFileImporter::XFileImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+XFileImporter::~XFileImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ std::string extension = GetExtension(pFile);
+ if (extension == "x") {
+ return true;
+ }
+ if (!extension.length() || checkSig) {
+ uint32_t token[1];
+ token[0] = AI_MAKE_MAGIC("xof ");
+ return CheckMagicToken(pIOHandler,pFile,token,1,0);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("x");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+ // read file into memory
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open file " + pFile + ".");
+
+ size_t fileSize = file->FileSize();
+ if ( fileSize < 16)
+ throw DeadlyImportError( "XFile is too small.");
+
+ // need to clear members - this method might be called multiple
+ // times on a single XFileImporter instance.
+ mImportedMats.clear();
+
+ // in the hope that binary files will never start with a BOM ...
+ mBuffer.resize( fileSize);
+ file->Read( &mBuffer.front(), 1, fileSize);
+ ConvertToUTF8(mBuffer);
+
+ // parse the file into a temporary representation
+ XFileParser parser( mBuffer);
+
+ // and create the proper return structures out of it
+ CreateDataRepresentationFromImport( pScene, parser.GetImportedData());
+
+ // if nothing came from it, report it as error
+ if ( !pScene->mRootNode)
+ throw DeadlyImportError( "XFile is ill-formatted - no content imported.");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructs the return data structure out of the imported data.
+void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, const XFile::Scene* pData)
+{
+ // Read the global materials first so that meshes referring to them can find them later
+ ConvertMaterials( pScene, pData->mGlobalMaterials);
+
+ // copy nodes, extracting meshes and materials on the way
+ pScene->mRootNode = CreateNodes( pScene, NULL, pData->mRootNode);
+
+ // extract animations
+ CreateAnimations( pScene, pData);
+
+ // read the global meshes that were stored outside of any node
+ if ( pData->mGlobalMeshes.size() > 0)
+ {
+ // create a root node to hold them if there isn't any, yet
+ if ( pScene->mRootNode == NULL)
+ {
+ pScene->mRootNode = new aiNode;
+ pScene->mRootNode->mName.Set( "$dummy_node");
+ }
+
+ // convert all global meshes and store them in the root node.
+ // If there was one before, the global meshes now suddenly have its transformation matrix...
+ // Don't know what to do there, I don't want to insert another node under the present root node
+ // just to avoid this.
+ CreateMeshes( pScene, pScene->mRootNode, pData->mGlobalMeshes);
+ }
+
+ // Convert everything to OpenGL space... it's the same operation as the conversion back, so we can reuse the step directly
+ MakeLeftHandedProcess convertProcess;
+ convertProcess.Execute( pScene);
+
+ FlipWindingOrderProcess flipper;
+ flipper.Execute(pScene);
+
+ // finally: create a dummy material if not material was imported
+ if ( pScene->mNumMaterials == 0)
+ {
+ pScene->mNumMaterials = 1;
+ // create the Material
+ Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
+ int shadeMode = (int) aiShadingMode_Gouraud;
+ mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
+ // material colours
+ int specExp = 1;
+
+ aiColor3D clr = aiColor3D( 0, 0, 0);
+ mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_EMISSIVE);
+ mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_SPECULAR);
+
+ clr = aiColor3D( 0.5f, 0.5f, 0.5f);
+ mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS);
+
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = mat;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively creates scene nodes from the imported hierarchy.
+aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode)
+{
+ if ( !pNode)
+ return NULL;
+
+ // create node
+ aiNode* node = new aiNode;
+ node->mName.length = pNode->mName.length();
+ node->mParent = pParent;
+ memcpy( node->mName.data, pNode->mName.c_str(), pNode->mName.length());
+ node->mName.data[node->mName.length] = 0;
+ node->mTransformation = pNode->mTrafoMatrix;
+
+ // convert meshes from the source node
+ CreateMeshes( pScene, node, pNode->mMeshes);
+
+ // handle childs
+ if ( pNode->mChildren.size() > 0)
+ {
+ node->mNumChildren = (unsigned int)pNode->mChildren.size();
+ node->mChildren = new aiNode* [node->mNumChildren];
+
+ for ( unsigned int a = 0; a < pNode->mChildren.size(); a++)
+ node->mChildren[a] = CreateNodes( pScene, node, pNode->mChildren[a]);
+ }
+
+ return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the meshes for the given node.
+void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector<XFile::Mesh*>& pMeshes)
+{
+ if ( pMeshes.size() == 0)
+ return;
+
+ // create a mesh for each mesh-material combination in the source node
+ std::vector<aiMesh*> meshes;
+ for ( unsigned int a = 0; a < pMeshes.size(); a++)
+ {
+ const XFile::Mesh* sourceMesh = pMeshes[a];
+ // first convert its materials so that we can find them when searching by name afterwards
+ ConvertMaterials( pScene, sourceMesh->mMaterials);
+
+ unsigned int numMaterials = std::max( (unsigned int)sourceMesh->mMaterials.size(), 1u);
+ for ( unsigned int b = 0; b < numMaterials; b++)
+ {
+ // collect the faces belonging to this material
+ std::vector<unsigned int> faces;
+ unsigned int numVertices = 0;
+ if ( sourceMesh->mFaceMaterials.size() > 0)
+ {
+ // if there is a per-face material defined, select the faces with the corresponding material
+ for ( unsigned int c = 0; c < sourceMesh->mFaceMaterials.size(); c++)
+ {
+ if ( sourceMesh->mFaceMaterials[c] == b)
+ {
+ faces.push_back( c);
+ numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size();
+ }
+ }
+ } else
+ {
+ // if there is no per-face material, place everything into one mesh
+ for ( unsigned int c = 0; c < sourceMesh->mPosFaces.size(); c++)
+ {
+ faces.push_back( c);
+ numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size();
+ }
+ }
+
+ // no faces/vertices using this material? strange...
+ if ( numVertices == 0)
+ continue;
+
+ // create a submesh using this material
+ aiMesh* mesh = new aiMesh;
+ meshes.push_back( mesh);
+
+ // find the material by name in the scene's material list. Either own material
+ // or referenced material, it should already be found there
+ if ( sourceMesh->mFaceMaterials.size() > 0)
+ {
+ std::map<std::string, unsigned int>::const_iterator matIt = mImportedMats.find( sourceMesh->mMaterials[b].mName);
+ if ( matIt == mImportedMats.end())
+ mesh->mMaterialIndex = 0;
+ else
+ mesh->mMaterialIndex = matIt->second;
+ } else
+ {
+ mesh->mMaterialIndex = 0;
+ }
+
+ // Create properly sized data arrays in the mesh. We store unique vertices per face,
+ // as specified
+ mesh->mNumVertices = numVertices;
+ mesh->mVertices = new aiVector3D[numVertices];
+ mesh->mNumFaces = (unsigned int)faces.size();
+ mesh->mFaces = new aiFace[mesh->mNumFaces];
+
+ // normals?
+ if ( sourceMesh->mNormals.size() > 0)
+ mesh->mNormals = new aiVector3D[numVertices];
+ // texture coords
+ for ( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; c++)
+ {
+ if ( sourceMesh->mTexCoords[c].size() > 0)
+ mesh->mTextureCoords[c] = new aiVector3D[numVertices];
+ }
+ // vertex colors
+ for ( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; c++)
+ {
+ if ( sourceMesh->mColors[c].size() > 0)
+ mesh->mColors[c] = new aiColor4D[numVertices];
+ }
+
+ // now collect the vertex data of all data streams present in the imported mesh
+ unsigned int newIndex = 0;
+ std::vector<unsigned int> orgPoints; // from which original point each new vertex stems
+ orgPoints.resize( numVertices, 0);
+
+ for ( unsigned int c = 0; c < faces.size(); c++)
+ {
+ unsigned int f = faces[c]; // index of the source face
+ const XFile::Face& pf = sourceMesh->mPosFaces[f]; // position source face
+
+ // create face. either triangle or triangle fan depending on the index count
+ aiFace& df = mesh->mFaces[c]; // destination face
+ df.mNumIndices = (unsigned int)pf.mIndices.size();
+ df.mIndices = new unsigned int[ df.mNumIndices];
+
+ // collect vertex data for indices of this face
+ for ( unsigned int d = 0; d < df.mNumIndices; d++)
+ {
+ df.mIndices[d] = newIndex;
+ orgPoints[newIndex] = pf.mIndices[d];
+
+ // Position
+ mesh->mVertices[newIndex] = sourceMesh->mPositions[pf.mIndices[d]];
+ // Normal, if present
+ if ( mesh->HasNormals())
+ mesh->mNormals[newIndex] = sourceMesh->mNormals[sourceMesh->mNormFaces[f].mIndices[d]];
+
+ // texture coord sets
+ for ( unsigned int e = 0; e < AI_MAX_NUMBER_OF_TEXTURECOORDS; e++)
+ {
+ if ( mesh->HasTextureCoords( e))
+ {
+ aiVector2D tex = sourceMesh->mTexCoords[e][pf.mIndices[d]];
+ mesh->mTextureCoords[e][newIndex] = aiVector3D( tex.x, 1.0f - tex.y, 0.0f);
+ }
+ }
+ // vertex color sets
+ for ( unsigned int e = 0; e < AI_MAX_NUMBER_OF_COLOR_SETS; e++)
+ if ( mesh->HasVertexColors( e))
+ mesh->mColors[e][newIndex] = sourceMesh->mColors[e][pf.mIndices[d]];
+
+ newIndex++;
+ }
+ }
+
+ // there should be as much new vertices as we calculated before
+ assert( newIndex == numVertices);
+
+ // convert all bones of the source mesh which influence vertices in this newly created mesh
+ const std::vector<XFile::Bone>& bones = sourceMesh->mBones;
+ std::vector<aiBone*> newBones;
+ for ( unsigned int c = 0; c < bones.size(); c++)
+ {
+ const XFile::Bone& obone = bones[c];
+ // set up a vertex-linear array of the weights for quick searching if a bone influences a vertex
+ std::vector<float> oldWeights( sourceMesh->mPositions.size(), 0.0f);
+ for ( unsigned int d = 0; d < obone.mWeights.size(); d++)
+ oldWeights[obone.mWeights[d].mVertex] = obone.mWeights[d].mWeight;
+
+ // collect all vertex weights that influence a vertex in the new mesh
+ std::vector<aiVertexWeight> newWeights;
+ newWeights.reserve( numVertices);
+ for ( unsigned int d = 0; d < orgPoints.size(); d++)
+ {
+ // does the new vertex stem from an old vertex which was influenced by this bone?
+ float w = oldWeights[orgPoints[d]];
+ if ( w > 0.0f)
+ newWeights.push_back( aiVertexWeight( d, w));
+ }
+
+ // if the bone has no weights in the newly created mesh, ignore it
+ if ( newWeights.size() == 0)
+ continue;
+
+ // create
+ aiBone* nbone = new aiBone;
+ newBones.push_back( nbone);
+ // copy name and matrix
+ nbone->mName.Set( obone.mName);
+ nbone->mOffsetMatrix = obone.mOffsetMatrix;
+ nbone->mNumWeights = (unsigned int)newWeights.size();
+ nbone->mWeights = new aiVertexWeight[nbone->mNumWeights];
+ for ( unsigned int d = 0; d < newWeights.size(); d++)
+ nbone->mWeights[d] = newWeights[d];
+ }
+
+ // store the bones in the mesh
+ mesh->mNumBones = (unsigned int)newBones.size();
+ if ( newBones.size() > 0)
+ {
+ mesh->mBones = new aiBone*[mesh->mNumBones];
+ std::copy( newBones.begin(), newBones.end(), mesh->mBones);
+ }
+ }
+ }
+
+ // reallocate scene mesh array to be large enough
+ aiMesh** prevArray = pScene->mMeshes;
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes + meshes.size()];
+ if ( prevArray)
+ {
+ memcpy( pScene->mMeshes, prevArray, pScene->mNumMeshes * sizeof( aiMesh*));
+ delete [] prevArray;
+ }
+
+ // allocate mesh index array in the node
+ pNode->mNumMeshes = (unsigned int)meshes.size();
+ pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
+
+ // store all meshes in the mesh library of the scene and store their indices in the node
+ for ( unsigned int a = 0; a < meshes.size(); a++)
+ {
+ pScene->mMeshes[pScene->mNumMeshes] = meshes[a];
+ pNode->mMeshes[a] = pScene->mNumMeshes;
+ pScene->mNumMeshes++;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts the animations from the given imported data and creates them in the scene.
+void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData)
+{
+ std::vector<aiAnimation*> newAnims;
+
+ for ( unsigned int a = 0; a < pData->mAnims.size(); a++)
+ {
+ const XFile::Animation* anim = pData->mAnims[a];
+ // some exporters mock me with empty animation tags.
+ if ( anim->mAnims.size() == 0)
+ continue;
+
+ // create a new animation to hold the data
+ aiAnimation* nanim = new aiAnimation;
+ newAnims.push_back( nanim);
+ nanim->mName.Set( anim->mName);
+ // duration will be determined by the maximum length
+ nanim->mDuration = 0;
+ nanim->mTicksPerSecond = pData->mAnimTicksPerSecond;
+ nanim->mNumChannels = (unsigned int)anim->mAnims.size();
+ nanim->mChannels = new aiNodeAnim*[nanim->mNumChannels];
+
+ for ( unsigned int b = 0; b < anim->mAnims.size(); b++)
+ {
+ const XFile::AnimBone* bone = anim->mAnims[b];
+ aiNodeAnim* nbone = new aiNodeAnim;
+ nbone->mNodeName.Set( bone->mBoneName);
+ nanim->mChannels[b] = nbone;
+
+ // keyframes are given as combined transformation matrix keys
+ if ( bone->mTrafoKeys.size() > 0)
+ {
+ nbone->mNumPositionKeys = (unsigned int)bone->mTrafoKeys.size();
+ nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys];
+ nbone->mNumRotationKeys = (unsigned int)bone->mTrafoKeys.size();
+ nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys];
+ nbone->mNumScalingKeys = (unsigned int)bone->mTrafoKeys.size();
+ nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys];
+
+ for ( unsigned int c = 0; c < bone->mTrafoKeys.size(); c++)
+ {
+ // deconstruct each matrix into separate position, rotation and scaling
+ double time = bone->mTrafoKeys[c].mTime;
+ aiMatrix4x4 trafo = bone->mTrafoKeys[c].mMatrix;
+
+ // extract position
+ aiVector3D pos( trafo.a4, trafo.b4, trafo.c4);
+
+ nbone->mPositionKeys[c].mTime = time;
+ nbone->mPositionKeys[c].mValue = pos;
+
+ // extract scaling
+ aiVector3D scale;
+ scale.x = aiVector3D( trafo.a1, trafo.b1, trafo.c1).Length();
+ scale.y = aiVector3D( trafo.a2, trafo.b2, trafo.c2).Length();
+ scale.z = aiVector3D( trafo.a3, trafo.b3, trafo.c3).Length();
+ nbone->mScalingKeys[c].mTime = time;
+ nbone->mScalingKeys[c].mValue = scale;
+
+ // reconstruct rotation matrix without scaling
+ aiMatrix3x3 rotmat(
+ trafo.a1 / scale.x, trafo.a2 / scale.y, trafo.a3 / scale.z,
+ trafo.b1 / scale.x, trafo.b2 / scale.y, trafo.b3 / scale.z,
+ trafo.c1 / scale.x, trafo.c2 / scale.y, trafo.c3 / scale.z);
+
+ // and convert it into a quaternion
+ nbone->mRotationKeys[c].mTime = time;
+ nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat);
+ }
+
+ // longest lasting key sequence determines duration
+ nanim->mDuration = std::max( nanim->mDuration, bone->mTrafoKeys.back().mTime);
+ } else
+ {
+ // separate key sequences for position, rotation, scaling
+ nbone->mNumPositionKeys = (unsigned int)bone->mPosKeys.size();
+ nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys];
+ for ( unsigned int c = 0; c < nbone->mNumPositionKeys; c++)
+ {
+ aiVector3D pos = bone->mPosKeys[c].mValue;
+
+ nbone->mPositionKeys[c].mTime = bone->mPosKeys[c].mTime;
+ nbone->mPositionKeys[c].mValue = pos;
+ }
+
+ // rotation
+ nbone->mNumRotationKeys = (unsigned int)bone->mRotKeys.size();
+ nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys];
+ for ( unsigned int c = 0; c < nbone->mNumRotationKeys; c++)
+ {
+ aiMatrix3x3 rotmat = bone->mRotKeys[c].mValue.GetMatrix();
+
+ nbone->mRotationKeys[c].mTime = bone->mRotKeys[c].mTime;
+ nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat);
+ nbone->mRotationKeys[c].mValue.w *= -1.0f; // needs quat inversion
+ }
+
+ // scaling
+ nbone->mNumScalingKeys = (unsigned int)bone->mScaleKeys.size();
+ nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys];
+ for ( unsigned int c = 0; c < nbone->mNumScalingKeys; c++)
+ nbone->mScalingKeys[c] = bone->mScaleKeys[c];
+
+ // longest lasting key sequence determines duration
+ if ( bone->mPosKeys.size() > 0)
+ nanim->mDuration = std::max( nanim->mDuration, bone->mPosKeys.back().mTime);
+ if ( bone->mRotKeys.size() > 0)
+ nanim->mDuration = std::max( nanim->mDuration, bone->mRotKeys.back().mTime);
+ if ( bone->mScaleKeys.size() > 0)
+ nanim->mDuration = std::max( nanim->mDuration, bone->mScaleKeys.back().mTime);
+ }
+ }
+ }
+
+ // store all converted animations in the scene
+ if ( newAnims.size() > 0)
+ {
+ pScene->mNumAnimations = (unsigned int)newAnims.size();
+ pScene->mAnimations = new aiAnimation* [pScene->mNumAnimations];
+ for ( unsigned int a = 0; a < newAnims.size(); a++)
+ pScene->mAnimations[a] = newAnims[a];
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts all materials in the given array and stores them in the scene's material list.
+void XFileImporter::ConvertMaterials( aiScene* pScene, const std::vector<XFile::Material>& pMaterials)
+{
+ // count the non-referrer materials in the array
+ unsigned int numMaterials = 0;
+ for ( unsigned int a = 0; a < pMaterials.size(); a++)
+ if ( !pMaterials[a].mIsReference)
+ numMaterials++;
+
+ if ( numMaterials == 0)
+ return;
+
+ // resize the scene's material list to offer enough space for the new materials
+ aiMaterial** prevMats = pScene->mMaterials;
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials + numMaterials];
+ if ( prevMats)
+ {
+ memcpy( pScene->mMaterials, prevMats, pScene->mNumMaterials * sizeof( aiMaterial*));
+ delete [] prevMats;
+ }
+
+ // convert all the materials given in the array
+ for ( unsigned int a = 0; a < pMaterials.size(); a++)
+ {
+ const XFile::Material& oldMat = pMaterials[a];
+ if ( oldMat.mIsReference)
+ continue;
+
+ Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
+ aiString name;
+ name.Set( oldMat.mName);
+ mat->AddProperty( &name, AI_MATKEY_NAME);
+
+ // Shading model: hardcoded to PHONG, there is no such information in an XFile
+ // FIX (aramis): If the specular exponent is 0, use gouraud shading. This is a bugfix
+ // for some models in the SDK (e.g. good old tiny.x)
+ int shadeMode = (int)oldMat.mSpecularExponent == 0.0f
+ ? aiShadingMode_Gouraud : aiShadingMode_Phong;
+
+ mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
+ // material colours
+ // FIX: Setup this as ambient not as emissive color
+ mat->AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_AMBIENT);
+ mat->AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mat->AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
+ mat->AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
+
+
+ // texture, if there is one
+ if (1 == oldMat.mTextures.size())
+ {
+ const XFile::TexEntry& otex = oldMat.mTextures.back();
+ if (otex.mName.length())
+ {
+ // if there is only one texture assume it contains the diffuse color
+ aiString tex( otex.mName);
+ if ( otex.mIsNormalMap)
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS(0));
+ else
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ }
+ else
+ {
+ // Otherwise ... try to search for typical strings in the
+ // texture's file name like 'bump' or 'diffuse'
+ unsigned int iHM = 0,iNM = 0,iDM = 0,iSM = 0,iAM = 0,iEM = 0;
+ for ( unsigned int b = 0; b < oldMat.mTextures.size(); b++)
+ {
+ const XFile::TexEntry& otex = oldMat.mTextures[b];
+ std::string sz = otex.mName;
+ if (!sz.length())continue;
+
+
+ // find the file name
+ //const size_t iLen = sz.length();
+ std::string::size_type s = sz.find_last_of("\\/");
+ if (std::string::npos == s)
+ s = 0;
+
+ // cut off the file extension
+ std::string::size_type sExt = sz.find_last_of('.');
+ if (std::string::npos != sExt){
+ sz[sExt] = '\0';
+ }
+
+ // convert to lower case for easier comparision
+ for ( unsigned int c = 0; c < sz.length(); c++)
+ if ( isalpha( sz[c]))
+ sz[c] = tolower( sz[c]);
+
+
+ // Place texture filename property under the corresponding name
+ aiString tex( oldMat.mTextures[b].mName);
+
+ // bump map
+ if (std::string::npos != sz.find("bump", s) || std::string::npos != sz.find("height", s))
+ {
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_HEIGHT(iHM++));
+ } else
+ if (otex.mIsNormalMap || std::string::npos != sz.find( "normal", s) || std::string::npos != sz.find("nm", s))
+ {
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS(iNM++));
+ } else
+ if (std::string::npos != sz.find( "spec", s) || std::string::npos != sz.find( "glanz", s))
+ {
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_SPECULAR(iSM++));
+ } else
+ if (std::string::npos != sz.find( "ambi", s) || std::string::npos != sz.find( "env", s))
+ {
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_AMBIENT(iAM++));
+ } else
+ if (std::string::npos != sz.find( "emissive", s) || std::string::npos != sz.find( "self", s))
+ {
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_EMISSIVE(iEM++));
+ } else
+ {
+ // Assume it is a diffuse texture
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(iDM++));
+ }
+ }
+ }
+
+ pScene->mMaterials[pScene->mNumMaterials] = mat;
+ mImportedMats[oldMat.mName] = pScene->mNumMaterials;
+ pScene->mNumMaterials++;
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_X_IMPORTER
+
diff --git a/src/3rdparty/assimp/code/XFileImporter.h b/src/3rdparty/assimp/code/XFileImporter.h
new file mode 100644
index 000000000..4dbce4b04
--- /dev/null
+++ b/src/3rdparty/assimp/code/XFileImporter.h
@@ -0,0 +1,159 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file XFileImporter.h
+ * @brief Definition of the XFile importer class.
+ */
+#ifndef AI_XFILEIMPORTER_H_INC
+#define AI_XFILEIMPORTER_H_INC
+
+#include <map>
+
+#include "XFileHelper.h"
+#include "BaseImporter.h"
+
+#include "../include/aiTypes.h"
+
+struct aiNode;
+
+namespace Assimp {
+
+namespace XFile {
+struct Scene;
+struct Node;
+}
+
+// ---------------------------------------------------------------------------
+/** The XFileImporter is a worker class capable of importing a scene from a
+ * DirectX file .x
+ */
+class XFileImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ XFileImporter();
+
+ /** Destructor, private as well */
+ ~XFileImporter();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details. */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool CheckSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /** Constructs the return data structure out of the imported data.
+ * @param pScene The scene to construct the return data in.
+ * @param pData The imported data in the internal temporary
+ * representation.
+ */
+ void CreateDataRepresentationFromImport( aiScene* pScene,
+ const XFile::Scene* pData);
+
+ // -------------------------------------------------------------------
+ /** Recursively creates scene nodes from the imported hierarchy.
+ * The meshes and materials of the nodes will be extracted on the way.
+ * @param pScene The scene to construct the return data in.
+ * @param pParent The parent node where to create new child nodes
+ * @param pNode The temporary node to copy.
+ * @return The created node
+ */
+ aiNode* CreateNodes( aiScene* pScene, aiNode* pParent,
+ const XFile::Node* pNode);
+
+ // -------------------------------------------------------------------
+ /** Converts all meshes in the given mesh array. Each mesh is splitted
+ * up per material, the indices of the generated meshes are stored in
+ * the node structure.
+ * @param pScene The scene to construct the return data in.
+ * @param pNode The target node structure that references the
+ * constructed meshes.
+ * @param pMeshes The array of meshes to convert
+ */
+ void CreateMeshes( aiScene* pScene, aiNode* pNode,
+ const std::vector<XFile::Mesh*>& pMeshes);
+
+ // -------------------------------------------------------------------
+ /** Converts the animations from the given imported data and creates
+ * them in the scene.
+ * @param pScene The scene to hold to converted animations
+ * @param pData The data to read the animations from
+ */
+ void CreateAnimations( aiScene* pScene, const XFile::Scene* pData);
+
+ // -------------------------------------------------------------------
+ /** Converts all materials in the given array and stores them in the
+ * scene's material list.
+ * @param pScene The scene to hold the converted materials.
+ * @param pMaterials The material array to convert.
+ */
+ void ConvertMaterials( aiScene* pScene,
+ const std::vector<XFile::Material>& pMaterials);
+
+protected:
+ /** Buffer to hold the loaded file */
+ std::vector<char> mBuffer;
+
+ /** Imported materials: index in the scene's material list by name */
+ std::map<std::string, unsigned int> mImportedMats;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_BASEIMPORTER_H_INC
diff --git a/src/3rdparty/assimp/code/XFileParser.cpp b/src/3rdparty/assimp/code/XFileParser.cpp
new file mode 100644
index 000000000..229366ff5
--- /dev/null
+++ b/src/3rdparty/assimp/code/XFileParser.cpp
@@ -0,0 +1,1434 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the XFile parser helper class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_X_IMPORTER
+
+#include "XFileParser.h"
+#include "XFileHelper.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+using namespace Assimp::XFile;
+
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_X
+
+# ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+# include <zlib.h>
+# else
+# include "../contrib/zlib/zlib.h"
+# endif
+
+// Magic identifier for MSZIP compressed data
+#define MSZIP_MAGIC 0x4B43
+#define MSZIP_BLOCK 32786
+
+// ------------------------------------------------------------------------------------------------
+// Dummy memory wrappers for use with zlib
+static void* dummy_alloc (void* /*opaque*/, unsigned int items, unsigned int size) {
+ return ::operator new(items*size);
+}
+
+static void dummy_free (void* /*opaque*/, void* address) {
+ return ::operator delete(address);
+}
+
+#endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
+
+// ------------------------------------------------------------------------------------------------
+// Constructor. Creates a data structure out of the XFile given in the memory block.
+XFileParser::XFileParser( const std::vector<char>& pBuffer)
+{
+ mMajorVersion = mMinorVersion = 0;
+ mIsBinaryFormat = false;
+ mBinaryNumCount = 0;
+ P = End = NULL;
+ mLineNumber = 0;
+ mScene = NULL;
+
+ // vector to store uncompressed file for INFLATE'd X files
+ std::vector<char> uncompressed;
+
+ // set up memory pointers
+ P = &pBuffer.front();
+ End = P + pBuffer.size();
+
+ // check header
+ if ( strncmp( P, "xof ", 4) != 0)
+ throw DeadlyImportError( "Header mismatch, file is not an XFile.");
+
+ // read version. It comes in a four byte format such as "0302"
+ mMajorVersion = (unsigned int)(P[4] - 48) * 10 + (unsigned int)(P[5] - 48);
+ mMinorVersion = (unsigned int)(P[6] - 48) * 10 + (unsigned int)(P[7] - 48);
+
+ bool compressed = false;
+
+ // txt - pure ASCII text format
+ if ( strncmp( P + 8, "txt ", 4) == 0)
+ mIsBinaryFormat = false;
+
+ // bin - Binary format
+ else if ( strncmp( P + 8, "bin ", 4) == 0)
+ mIsBinaryFormat = true;
+
+ // tzip - Inflate compressed text format
+ else if ( strncmp( P + 8, "tzip", 4) == 0)
+ {
+ mIsBinaryFormat = false;
+ compressed = true;
+ }
+ // bzip - Inflate compressed binary format
+ else if ( strncmp( P + 8, "bzip", 4) == 0)
+ {
+ mIsBinaryFormat = true;
+ compressed = true;
+ }
+ else ThrowException( boost::str(boost::format("Unsupported xfile format '%c%c%c%c'")
+ % P[8] % P[9] % P[10] % P[11]));
+
+ // float size
+ mBinaryFloatSize = (unsigned int)(P[12] - 48) * 1000
+ + (unsigned int)(P[13] - 48) * 100
+ + (unsigned int)(P[14] - 48) * 10
+ + (unsigned int)(P[15] - 48);
+
+ if ( mBinaryFloatSize != 32 && mBinaryFloatSize != 64)
+ ThrowException( boost::str( boost::format( "Unknown float size %1% specified in xfile header.")
+ % mBinaryFloatSize));
+
+ P += 16;
+
+ // If this is a compressed X file, apply the inflate algorithm to it
+ if (compressed)
+ {
+#ifdef ASSIMP_BUILD_NO_COMPRESSED_X
+ throw DeadlyImportError("Assimp was built without compressed X support");
+#else
+ /* ///////////////////////////////////////////////////////////////////////
+ * COMPRESSED X FILE FORMAT
+ * ///////////////////////////////////////////////////////////////////////
+ * [xhead]
+ * 2 major
+ * 2 minor
+ * 4 type // bzip,tzip
+ * [mszip_master_head]
+ * 4 unkn // checksum?
+ * 2 unkn // flags? (seems to be constant)
+ * [mszip_head]
+ * 2 ofs // offset to next section
+ * 2 magic // 'CK'
+ * ... ofs bytes of data
+ * ... next mszip_head
+ *
+ * http://www.kdedevelopers.org/node/3181 has been very helpful.
+ * ///////////////////////////////////////////////////////////////////////
+ */
+
+ // build a zlib stream
+ z_stream stream;
+ stream.opaque = NULL;
+ stream.zalloc = &dummy_alloc;
+ stream.zfree = &dummy_free;
+ stream.data_type = (mIsBinaryFormat ? Z_BINARY : Z_ASCII);
+
+ // initialize the inflation algorithm
+ ::inflateInit2(&stream, -MAX_WBITS);
+
+ // skip unknown data (checksum, flags?)
+ P += 6;
+
+ // First find out how much storage we'll need. Count sections.
+ const char* P1 = P;
+ unsigned int est_out = 0;
+ while (P1 < End)
+ {
+ // read next offset
+ uint16_t ofs = *((uint16_t*)P1);
+ AI_SWAP2(ofs); P1 += 2;
+
+ if (ofs >= MSZIP_BLOCK)
+ throw DeadlyImportError("X: Invalid offset to next MSZIP compressed block");
+
+ // check magic word
+ uint16_t magic = *((uint16_t*)P1);
+ AI_SWAP2(magic); P1 += 2;
+
+ if (magic != MSZIP_MAGIC)
+ throw DeadlyImportError("X: Unsupported compressed format, expected MSZIP header");
+
+ // and advance to the next offset
+ P1 += ofs;
+ est_out += MSZIP_BLOCK; // one decompressed block is 32786 in size
+ }
+
+ // Allocate storage and do the actual uncompressing
+ uncompressed.resize(est_out);
+ char* out = &uncompressed.front();
+ while (P < End)
+ {
+ uint16_t ofs = *((uint16_t*)P);
+ AI_SWAP2(ofs);
+ P += 4;
+
+ // push data to the stream
+ stream.next_in = (Bytef*)P;
+ stream.avail_in = ofs;
+ stream.next_out = (Bytef*)out;
+ stream.avail_out = MSZIP_BLOCK;
+
+ // and decompress the data ....
+ int ret = ::inflate( &stream, Z_SYNC_FLUSH );
+ if (ret != Z_OK && ret != Z_STREAM_END)
+ throw DeadlyImportError("X: Failed to decompress MSZIP-compressed data");
+
+ ::inflateReset( &stream );
+ ::inflateSetDictionary( &stream, (const Bytef*)out , MSZIP_BLOCK - stream.avail_out );
+
+ // and advance to the next offset
+ out += MSZIP_BLOCK - stream.avail_out;
+ P += ofs;
+ }
+
+ // terminate zlib
+ ::inflateEnd(&stream);
+
+ // ok, update pointers to point to the uncompressed file data
+ P = &uncompressed[0];
+ End = out;
+
+ // FIXME: we don't need the compressed data anymore, could release
+ // it already for better memory usage. Consider breaking const-co.
+ DefaultLogger::get()->info("Successfully decompressed MSZIP-compressed file");
+#endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
+ }
+ else
+ {
+ // start reading here
+ ReadUntilEndOfLine();
+ }
+
+ mScene = new Scene;
+ ParseFile();
+
+ // filter the imported hierarchy for some degenerated cases
+ if ( mScene->mRootNode) {
+ FilterHierarchy( mScene->mRootNode);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor. Destroys all imported data along with it
+XFileParser::~XFileParser()
+{
+ // kill everything we created
+ delete mScene;
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseFile()
+{
+ bool running = true;
+ while ( running )
+ {
+ // read name of next object
+ std::string objectName = GetNextToken();
+ if (objectName.length() == 0)
+ break;
+
+ // parse specific object
+ if ( objectName == "template")
+ ParseDataObjectTemplate();
+ else
+ if ( objectName == "Frame")
+ ParseDataObjectFrame( NULL);
+ else
+ if ( objectName == "Mesh")
+ {
+ // some meshes have no frames at all
+ Mesh* mesh = new Mesh;
+ ParseDataObjectMesh( mesh);
+ mScene->mGlobalMeshes.push_back( mesh);
+ } else
+ if ( objectName == "AnimTicksPerSecond")
+ ParseDataObjectAnimTicksPerSecond();
+ else
+ if ( objectName == "AnimationSet")
+ ParseDataObjectAnimationSet();
+ else
+ if ( objectName == "Material")
+ {
+ // Material outside of a mesh or node
+ Material material;
+ ParseDataObjectMaterial( &material);
+ mScene->mGlobalMaterials.push_back( material);
+ } else
+ if ( objectName == "}")
+ {
+ // whatever?
+ DefaultLogger::get()->warn("} found in dataObject");
+ } else
+ {
+ // unknown format
+ DefaultLogger::get()->warn("Unknown data object in animation of .x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectTemplate()
+{
+ // parse a template data object. Currently not stored.
+ std::string name;
+ readHeadOfDataObject( &name);
+
+ // read GUID
+ std::string guid = GetNextToken();
+
+ // read and ignore data members
+ bool running = true;
+ while ( running )
+ {
+ std::string s = GetNextToken();
+
+ if ( s == "}")
+ break;
+
+ if ( s.length() == 0)
+ ThrowException( "Unexpected end of file reached while parsing template definition");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectFrame( Node* pParent)
+{
+ // A coordinate frame, or "frame of reference." The Frame template
+ // is open and can contain any object. The Direct3D extensions (D3DX)
+ // mesh-loading functions recognize Mesh, FrameTransformMatrix, and
+ // Frame template instances as child objects when loading a Frame
+ // instance.
+ std::string name;
+ readHeadOfDataObject(&name);
+
+ // create a named node and place it at its parent, if given
+ Node* node = new Node( pParent);
+ node->mName = name;
+ if ( pParent)
+ {
+ pParent->mChildren.push_back( node);
+ } else
+ {
+ // there might be multiple root nodes
+ if ( mScene->mRootNode != NULL)
+ {
+ // place a dummy root if not there
+ if ( mScene->mRootNode->mName != "$dummy_root")
+ {
+ Node* exroot = mScene->mRootNode;
+ mScene->mRootNode = new Node( NULL);
+ mScene->mRootNode->mName = "$dummy_root";
+ mScene->mRootNode->mChildren.push_back( exroot);
+ exroot->mParent = mScene->mRootNode;
+ }
+ // put the new node as its child instead
+ mScene->mRootNode->mChildren.push_back( node);
+ node->mParent = mScene->mRootNode;
+ } else
+ {
+ // it's the first node imported. place it as root
+ mScene->mRootNode = node;
+ }
+ }
+
+ // Now inside a frame.
+ // read tokens until closing brace is reached.
+ bool running = true;
+ while ( running )
+ {
+ std::string objectName = GetNextToken();
+ if (objectName.size() == 0)
+ ThrowException( "Unexpected end of file reached while parsing frame");
+
+ if ( objectName == "}")
+ break; // frame finished
+ else
+ if ( objectName == "Frame")
+ ParseDataObjectFrame( node); // child frame
+ else
+ if ( objectName == "FrameTransformMatrix")
+ ParseDataObjectTransformationMatrix( node->mTrafoMatrix);
+ else
+ if ( objectName == "Mesh")
+ {
+ Mesh* mesh = new Mesh;
+ node->mMeshes.push_back( mesh);
+ ParseDataObjectMesh( mesh);
+ } else
+ {
+ DefaultLogger::get()->warn("Unknown data object in frame in x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectTransformationMatrix( aiMatrix4x4& pMatrix)
+{
+ // read header, we're not interested if it has a name
+ readHeadOfDataObject();
+
+ // read its components
+ pMatrix.a1 = ReadFloat(); pMatrix.b1 = ReadFloat();
+ pMatrix.c1 = ReadFloat(); pMatrix.d1 = ReadFloat();
+ pMatrix.a2 = ReadFloat(); pMatrix.b2 = ReadFloat();
+ pMatrix.c2 = ReadFloat(); pMatrix.d2 = ReadFloat();
+ pMatrix.a3 = ReadFloat(); pMatrix.b3 = ReadFloat();
+ pMatrix.c3 = ReadFloat(); pMatrix.d3 = ReadFloat();
+ pMatrix.a4 = ReadFloat(); pMatrix.b4 = ReadFloat();
+ pMatrix.c4 = ReadFloat(); pMatrix.d4 = ReadFloat();
+
+ // trailing symbols
+ CheckForSemicolon();
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMesh( Mesh* pMesh)
+{
+ std::string name;
+ readHeadOfDataObject( &name);
+
+ // read vertex count
+ unsigned int numVertices = ReadInt();
+ pMesh->mPositions.resize( numVertices);
+
+ // read vertices
+ for ( unsigned int a = 0; a < numVertices; a++)
+ pMesh->mPositions[a] = ReadVector3();
+
+ // read position faces
+ unsigned int numPosFaces = ReadInt();
+ pMesh->mPosFaces.resize( numPosFaces);
+ for ( unsigned int a = 0; a < numPosFaces; a++)
+ {
+ unsigned int numIndices = ReadInt();
+ if ( numIndices < 3)
+ ThrowException( boost::str( boost::format( "Invalid index count %1% for face %2%.") % numIndices % a));
+
+ // read indices
+ Face& face = pMesh->mPosFaces[a];
+ for ( unsigned int b = 0; b < numIndices; b++)
+ face.mIndices.push_back( ReadInt());
+ CheckForSeparator();
+ }
+
+ // here, other data objects may follow
+ bool running = true;
+ while ( running )
+ {
+ std::string objectName = GetNextToken();
+
+ if ( objectName.size() == 0)
+ ThrowException( "Unexpected end of file while parsing mesh structure");
+ else
+ if ( objectName == "}")
+ break; // mesh finished
+ else
+ if ( objectName == "MeshNormals")
+ ParseDataObjectMeshNormals( pMesh);
+ else
+ if ( objectName == "MeshTextureCoords")
+ ParseDataObjectMeshTextureCoords( pMesh);
+ else
+ if ( objectName == "MeshVertexColors")
+ ParseDataObjectMeshVertexColors( pMesh);
+ else
+ if ( objectName == "MeshMaterialList")
+ ParseDataObjectMeshMaterialList( pMesh);
+ else
+ if ( objectName == "VertexDuplicationIndices")
+ ParseUnknownDataObject(); // we'll ignore vertex duplication indices
+ else
+ if ( objectName == "XSkinMeshHeader")
+ ParseDataObjectSkinMeshHeader( pMesh);
+ else
+ if ( objectName == "SkinWeights")
+ ParseDataObjectSkinWeights( pMesh);
+ else
+ {
+ DefaultLogger::get()->warn("Unknown data object in mesh in x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectSkinWeights( Mesh *pMesh)
+{
+ readHeadOfDataObject();
+
+ std::string transformNodeName;
+ GetNextTokenAsString( transformNodeName);
+
+ pMesh->mBones.push_back( Bone());
+ Bone& bone = pMesh->mBones.back();
+ bone.mName = transformNodeName;
+
+ // read vertex weights
+ unsigned int numWeights = ReadInt();
+ bone.mWeights.reserve( numWeights);
+
+ for ( unsigned int a = 0; a < numWeights; a++)
+ {
+ BoneWeight weight;
+ weight.mVertex = ReadInt();
+ bone.mWeights.push_back( weight);
+ }
+
+ // read vertex weights
+ for ( unsigned int a = 0; a < numWeights; a++)
+ bone.mWeights[a].mWeight = ReadFloat();
+
+ // read matrix offset
+ bone.mOffsetMatrix.a1 = ReadFloat(); bone.mOffsetMatrix.b1 = ReadFloat();
+ bone.mOffsetMatrix.c1 = ReadFloat(); bone.mOffsetMatrix.d1 = ReadFloat();
+ bone.mOffsetMatrix.a2 = ReadFloat(); bone.mOffsetMatrix.b2 = ReadFloat();
+ bone.mOffsetMatrix.c2 = ReadFloat(); bone.mOffsetMatrix.d2 = ReadFloat();
+ bone.mOffsetMatrix.a3 = ReadFloat(); bone.mOffsetMatrix.b3 = ReadFloat();
+ bone.mOffsetMatrix.c3 = ReadFloat(); bone.mOffsetMatrix.d3 = ReadFloat();
+ bone.mOffsetMatrix.a4 = ReadFloat(); bone.mOffsetMatrix.b4 = ReadFloat();
+ bone.mOffsetMatrix.c4 = ReadFloat(); bone.mOffsetMatrix.d4 = ReadFloat();
+
+ CheckForSemicolon();
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectSkinMeshHeader( Mesh* /*pMesh*/ )
+{
+ readHeadOfDataObject();
+
+ /*unsigned int maxSkinWeightsPerVertex =*/ ReadInt();
+ /*unsigned int maxSkinWeightsPerFace =*/ ReadInt();
+ /*unsigned int numBonesInMesh = */ReadInt();
+
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMeshNormals( Mesh* pMesh)
+{
+ readHeadOfDataObject();
+
+ // read count
+ unsigned int numNormals = ReadInt();
+ pMesh->mNormals.resize( numNormals);
+
+ // read normal vectors
+ for ( unsigned int a = 0; a < numNormals; a++)
+ pMesh->mNormals[a] = ReadVector3();
+
+ // read normal indices
+ unsigned int numFaces = ReadInt();
+ if ( numFaces != pMesh->mPosFaces.size())
+ ThrowException( "Normal face count does not match vertex face count.");
+
+ for ( unsigned int a = 0; a < numFaces; a++)
+ {
+ unsigned int numIndices = ReadInt();
+ pMesh->mNormFaces.push_back( Face());
+ Face& face = pMesh->mNormFaces.back();
+
+ for ( unsigned int b = 0; b < numIndices; b++)
+ face.mIndices.push_back( ReadInt());
+
+ CheckForSeparator();
+ }
+
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMeshTextureCoords( Mesh* pMesh)
+{
+ readHeadOfDataObject();
+ std::vector<aiVector2D>& coords = pMesh->mTexCoords[pMesh->mNumTextures++];
+
+ unsigned int numCoords = ReadInt();
+ if ( numCoords != pMesh->mPositions.size())
+ ThrowException( "Texture coord count does not match vertex count");
+
+ coords.resize( numCoords);
+ for ( unsigned int a = 0; a < numCoords; a++)
+ coords[a] = ReadVector2();
+
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMeshVertexColors( Mesh* pMesh)
+{
+ readHeadOfDataObject();
+ std::vector<aiColor4D>& colors = pMesh->mColors[pMesh->mNumColorSets++];
+
+ unsigned int numColors = ReadInt();
+ if ( numColors != pMesh->mPositions.size())
+ ThrowException( "Vertex color count does not match vertex count");
+
+ colors.resize( numColors, aiColor4D( 0, 0, 0, 1));
+ for ( unsigned int a = 0; a < numColors; a++)
+ {
+ unsigned int index = ReadInt();
+ if ( index >= pMesh->mPositions.size())
+ ThrowException( "Vertex color index out of bounds");
+
+ colors[index] = ReadRGBA();
+ // HACK: (thom) Maxon Cinema XPort plugin puts a third separator here, kwxPort puts a comma.
+ // Ignore gracefully.
+ if ( !mIsBinaryFormat)
+ {
+ FindNextNoneWhiteSpace();
+ if ( *P == ';' || *P == ',')
+ P++;
+ }
+ }
+
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMeshMaterialList( Mesh* pMesh)
+{
+ readHeadOfDataObject();
+
+ // read material count
+ /*unsigned int numMaterials =*/ ReadInt();
+ // read non triangulated face material index count
+ unsigned int numMatIndices = ReadInt();
+
+ // some models have a material index count of 1... to be able to read them we
+ // replicate this single material index on every face
+ if ( numMatIndices != pMesh->mPosFaces.size() && numMatIndices != 1)
+ ThrowException( "Per-Face material index count does not match face count.");
+
+ // read per-face material indices
+ for ( unsigned int a = 0; a < numMatIndices; a++)
+ pMesh->mFaceMaterials.push_back( ReadInt());
+
+ // in version 03.02, the face indices end with two semicolons.
+ // commented out version check, as version 03.03 exported from blender also has 2 semicolons
+ if ( !mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2)
+ {
+ if ( *P == ';')
+ ++P;
+ }
+
+ // if there was only a single material index, replicate it on all faces
+ while ( pMesh->mFaceMaterials.size() < pMesh->mPosFaces.size())
+ pMesh->mFaceMaterials.push_back( pMesh->mFaceMaterials.front());
+
+ // read following data objects
+ bool running = true;
+ while ( running )
+ {
+ std::string objectName = GetNextToken();
+ if ( objectName.size() == 0)
+ ThrowException( "Unexpected end of file while parsing mesh material list.");
+ else
+ if ( objectName == "}")
+ break; // material list finished
+ else
+ if ( objectName == "{")
+ {
+ // template materials
+ std::string matName = GetNextToken();
+ Material material;
+ material.mIsReference = true;
+ material.mName = matName;
+ pMesh->mMaterials.push_back( material);
+
+ CheckForClosingBrace(); // skip }
+ } else
+ if ( objectName == "Material")
+ {
+ pMesh->mMaterials.push_back( Material());
+ ParseDataObjectMaterial( &pMesh->mMaterials.back());
+ } else
+ if ( objectName == ";")
+ {
+ // ignore
+ } else
+ {
+ DefaultLogger::get()->warn("Unknown data object in material list in x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMaterial( Material* pMaterial)
+{
+ std::string matName;
+ readHeadOfDataObject( &matName);
+ if ( matName.empty())
+ matName = std::string( "material") + boost::lexical_cast<std::string>( mLineNumber);
+ pMaterial->mName = matName;
+ pMaterial->mIsReference = false;
+
+ // read material values
+ pMaterial->mDiffuse = ReadRGBA();
+ pMaterial->mSpecularExponent = ReadFloat();
+ pMaterial->mSpecular = ReadRGB();
+ pMaterial->mEmissive = ReadRGB();
+
+ // read other data objects
+ bool running = true;
+ while ( running )
+ {
+ std::string objectName = GetNextToken();
+ if ( objectName.size() == 0)
+ ThrowException( "Unexpected end of file while parsing mesh material");
+ else
+ if ( objectName == "}")
+ break; // material finished
+ else
+ if ( objectName == "TextureFilename" || objectName == "TextureFileName")
+ {
+ // some exporters write "TextureFileName" instead.
+ std::string texname;
+ ParseDataObjectTextureFilename( texname);
+ pMaterial->mTextures.push_back( TexEntry( texname));
+ } else
+ if ( objectName == "NormalmapFilename" || objectName == "NormalmapFileName")
+ {
+ // one exporter writes out the normal map in a separate filename tag
+ std::string texname;
+ ParseDataObjectTextureFilename( texname);
+ pMaterial->mTextures.push_back( TexEntry( texname, true));
+ } else
+ {
+ DefaultLogger::get()->warn("Unknown data object in material in x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectAnimTicksPerSecond()
+{
+ readHeadOfDataObject();
+ mScene->mAnimTicksPerSecond = ReadInt();
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectAnimationSet()
+{
+ std::string animName;
+ readHeadOfDataObject( &animName);
+
+ Animation* anim = new Animation;
+ mScene->mAnims.push_back( anim);
+ anim->mName = animName;
+
+ bool running = true;
+ while ( running )
+ {
+ std::string objectName = GetNextToken();
+ if ( objectName.length() == 0)
+ ThrowException( "Unexpected end of file while parsing animation set.");
+ else
+ if ( objectName == "}")
+ break; // animation set finished
+ else
+ if ( objectName == "Animation")
+ ParseDataObjectAnimation( anim);
+ else
+ {
+ DefaultLogger::get()->warn("Unknown data object in animation set in x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectAnimation( Animation* pAnim)
+{
+ readHeadOfDataObject();
+ AnimBone* banim = new AnimBone;
+ pAnim->mAnims.push_back( banim);
+
+ bool running = true;
+ while ( running )
+ {
+ std::string objectName = GetNextToken();
+
+ if ( objectName.length() == 0)
+ ThrowException( "Unexpected end of file while parsing animation.");
+ else
+ if ( objectName == "}")
+ break; // animation finished
+ else
+ if ( objectName == "AnimationKey")
+ ParseDataObjectAnimationKey( banim);
+ else
+ if ( objectName == "AnimationOptions")
+ ParseUnknownDataObject(); // not interested
+ else
+ if ( objectName == "{")
+ {
+ // read frame name
+ banim->mBoneName = GetNextToken();
+ CheckForClosingBrace();
+ } else
+ {
+ DefaultLogger::get()->warn("Unknown data object in animation in x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectAnimationKey( AnimBone* pAnimBone)
+{
+ readHeadOfDataObject();
+
+ // read key type
+ unsigned int keyType = ReadInt();
+
+ // read number of keys
+ unsigned int numKeys = ReadInt();
+
+ for ( unsigned int a = 0; a < numKeys; a++)
+ {
+ // read time
+ unsigned int time = ReadInt();
+
+ // read keys
+ switch( keyType)
+ {
+ case 0: // rotation quaternion
+ {
+ // read count
+ if ( ReadInt() != 4)
+ ThrowException( "Invalid number of arguments for quaternion key in animation");
+
+ aiQuatKey key;
+ key.mTime = double( time);
+ key.mValue.w = ReadFloat();
+ key.mValue.x = ReadFloat();
+ key.mValue.y = ReadFloat();
+ key.mValue.z = ReadFloat();
+ pAnimBone->mRotKeys.push_back( key);
+
+ CheckForSemicolon();
+ break;
+ }
+
+ case 1: // scale vector
+ case 2: // position vector
+ {
+ // read count
+ if ( ReadInt() != 3)
+ ThrowException( "Invalid number of arguments for vector key in animation");
+
+ aiVectorKey key;
+ key.mTime = double( time);
+ key.mValue = ReadVector3();
+
+ if ( keyType == 2)
+ pAnimBone->mPosKeys.push_back( key);
+ else
+ pAnimBone->mScaleKeys.push_back( key);
+
+ break;
+ }
+
+ case 3: // combined transformation matrix
+ case 4: // denoted both as 3 or as 4
+ {
+ // read count
+ if ( ReadInt() != 16)
+ ThrowException( "Invalid number of arguments for matrix key in animation");
+
+ // read matrix
+ MatrixKey key;
+ key.mTime = double( time);
+ key.mMatrix.a1 = ReadFloat(); key.mMatrix.b1 = ReadFloat();
+ key.mMatrix.c1 = ReadFloat(); key.mMatrix.d1 = ReadFloat();
+ key.mMatrix.a2 = ReadFloat(); key.mMatrix.b2 = ReadFloat();
+ key.mMatrix.c2 = ReadFloat(); key.mMatrix.d2 = ReadFloat();
+ key.mMatrix.a3 = ReadFloat(); key.mMatrix.b3 = ReadFloat();
+ key.mMatrix.c3 = ReadFloat(); key.mMatrix.d3 = ReadFloat();
+ key.mMatrix.a4 = ReadFloat(); key.mMatrix.b4 = ReadFloat();
+ key.mMatrix.c4 = ReadFloat(); key.mMatrix.d4 = ReadFloat();
+ pAnimBone->mTrafoKeys.push_back( key);
+
+ CheckForSemicolon();
+ break;
+ }
+
+ default:
+ ThrowException( boost::str( boost::format( "Unknown key type %1% in animation.") % keyType));
+ break;
+ } // end switch
+
+ // key separator
+ CheckForSeparator();
+ }
+
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectTextureFilename( std::string& pName)
+{
+ readHeadOfDataObject();
+ GetNextTokenAsString( pName);
+ CheckForClosingBrace();
+
+ // FIX: some files (e.g. AnimationTest.x) have "" as texture file name
+ if (!pName.length())
+ {
+ DefaultLogger::get()->warn("Length of texture file name is zero. Skipping this texture.");
+ }
+
+ // some exporters write double backslash paths out. We simply replace them if we find them
+ while ( pName.find( "\\\\") != std::string::npos)
+ pName.replace( pName.find( "\\\\"), 2, "\\");
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseUnknownDataObject()
+{
+ // find opening delimiter
+ bool running = true;
+ while ( running )
+ {
+ std::string t = GetNextToken();
+ if ( t.length() == 0)
+ ThrowException( "Unexpected end of file while parsing unknown segment.");
+
+ if ( t == "{")
+ break;
+ }
+
+ unsigned int counter = 1;
+
+ // parse until closing delimiter
+ while ( counter > 0)
+ {
+ std::string t = GetNextToken();
+
+ if ( t.length() == 0)
+ ThrowException( "Unexpected end of file while parsing unknown segment.");
+
+ if ( t == "{")
+ ++counter;
+ else
+ if ( t == "}")
+ --counter;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+//! checks for closing curly brace
+void XFileParser::CheckForClosingBrace()
+{
+ if ( GetNextToken() != "}")
+ ThrowException( "Closing brace expected.");
+}
+
+// ------------------------------------------------------------------------------------------------
+//! checks for one following semicolon
+void XFileParser::CheckForSemicolon()
+{
+ if ( mIsBinaryFormat)
+ return;
+
+ if ( GetNextToken() != ";")
+ ThrowException( "Semicolon expected.");
+}
+
+// ------------------------------------------------------------------------------------------------
+//! checks for a separator char, either a ',' or a ';'
+void XFileParser::CheckForSeparator()
+{
+ if ( mIsBinaryFormat)
+ return;
+
+ std::string token = GetNextToken();
+ if ( token != "," && token != ";")
+ ThrowException( "Separator character (';' or ',') expected.");
+}
+
+// ------------------------------------------------------------------------------------------------
+// tests and possibly consumes a separator char, but does nothing if there was no separator
+void XFileParser::TestForSeparator()
+{
+ if ( mIsBinaryFormat)
+ return;
+
+ FindNextNoneWhiteSpace();
+ if ( P >= End)
+ return;
+
+ // test and skip
+ if ( *P == ';' || *P == ',')
+ P++;
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::readHeadOfDataObject( std::string* poName)
+{
+ std::string nameOrBrace = GetNextToken();
+ if ( nameOrBrace != "{")
+ {
+ if ( poName)
+ *poName = nameOrBrace;
+
+ if ( GetNextToken() != "{")
+ ThrowException( "Opening brace expected.");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+std::string XFileParser::GetNextToken()
+{
+ std::string s;
+
+ // process binary-formatted file
+ if ( mIsBinaryFormat)
+ {
+ // in binary mode it will only return NAME and STRING token
+ // and (correctly) skip over other tokens.
+
+ unsigned int tok = ReadBinWord();
+ unsigned int len;
+
+ // standalone tokens
+ switch( tok)
+ {
+ case 1:
+ // name token
+ len = ReadBinDWord();
+ s = std::string(P, len);
+ P += len;
+ return s;
+ case 2:
+ // string token
+ len = ReadBinDWord();
+ s = std::string(P, len);
+ P += (len + 2);
+ return s;
+ case 3:
+ // integer token
+ P += 4;
+ return "<integer>";
+ case 5:
+ // GUID token
+ P += 16;
+ return "<guid>";
+ case 6:
+ len = ReadBinDWord();
+ P += (len * 4);
+ return "<int_list>";
+ case 7:
+ len = ReadBinDWord();
+ P += (len * mBinaryFloatSize);
+ return "<flt_list>";
+ case 0x0a:
+ return "{";
+ case 0x0b:
+ return "}";
+ case 0x0c:
+ return "(";
+ case 0x0d:
+ return ")";
+ case 0x0e:
+ return "[";
+ case 0x0f:
+ return "]";
+ case 0x10:
+ return "<";
+ case 0x11:
+ return ">";
+ case 0x12:
+ return ".";
+ case 0x13:
+ return ",";
+ case 0x14:
+ return ";";
+ case 0x1f:
+ return "template";
+ case 0x28:
+ return "WORD";
+ case 0x29:
+ return "DWORD";
+ case 0x2a:
+ return "FLOAT";
+ case 0x2b:
+ return "DOUBLE";
+ case 0x2c:
+ return "CHAR";
+ case 0x2d:
+ return "UCHAR";
+ case 0x2e:
+ return "SWORD";
+ case 0x2f:
+ return "SDWORD";
+ case 0x30:
+ return "void";
+ case 0x31:
+ return "string";
+ case 0x32:
+ return "unicode";
+ case 0x33:
+ return "cstring";
+ case 0x34:
+ return "array";
+ }
+ }
+ // process text-formatted file
+ else
+ {
+ FindNextNoneWhiteSpace();
+ if ( P >= End)
+ return s;
+
+ while ( (P < End) && !isspace( (unsigned char) *P))
+ {
+ // either keep token delimiters when already holding a token, or return if first valid char
+ if ( *P == ';' || *P == '}' || *P == '{' || *P == ',')
+ {
+ if ( !s.size())
+ s.append( P++, 1);
+ break; // stop for delimiter
+ }
+ s.append( P++, 1);
+ }
+ }
+ return s;
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::FindNextNoneWhiteSpace()
+{
+ if ( mIsBinaryFormat)
+ return;
+
+ bool running = true;
+ while ( running )
+ {
+ while ( P < End && isspace( (unsigned char) *P))
+ {
+ if ( *P == '\n')
+ mLineNumber++;
+ ++P;
+ }
+
+ if ( P >= End)
+ return;
+
+ // check if this is a comment
+ if ( (P[0] == '/' && P[1] == '/') || P[0] == '#')
+ ReadUntilEndOfLine();
+ else
+ break;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::GetNextTokenAsString( std::string& poString)
+{
+ if ( mIsBinaryFormat)
+ {
+ poString = GetNextToken();
+ return;
+ }
+
+ FindNextNoneWhiteSpace();
+ if ( P >= End)
+ ThrowException( "Unexpected end of file while parsing string");
+
+ if ( *P != '"')
+ ThrowException( "Expected quotation mark.");
+ ++P;
+
+ while ( P < End && *P != '"')
+ poString.append( P++, 1);
+
+ if ( P >= End-1)
+ ThrowException( "Unexpected end of file while parsing string");
+
+ if ( P[1] != ';' || P[0] != '"')
+ ThrowException( "Expected quotation mark and semicolon at the end of a string.");
+ P+=2;
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ReadUntilEndOfLine()
+{
+ if ( mIsBinaryFormat)
+ return;
+
+ while ( P < End)
+ {
+ if ( *P == '\n' || *P == '\r')
+ {
+ ++P; mLineNumber++;
+ return;
+ }
+
+ ++P;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned short XFileParser::ReadBinWord()
+{
+ const unsigned char* q = (const unsigned char*) P;
+ unsigned short tmp = q[0] | (q[1] << 8);
+ P += 2;
+ return tmp;
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned int XFileParser::ReadBinDWord()
+{
+ const unsigned char* q = (const unsigned char*) P;
+ unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24);
+ P += 4;
+ return tmp;
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned int XFileParser::ReadInt()
+{
+ if ( mIsBinaryFormat)
+ {
+ if ( mBinaryNumCount == 0)
+ {
+ unsigned short tmp = ReadBinWord(); // 0x06 or 0x03
+ if ( tmp == 0x06) // array of ints follows
+ mBinaryNumCount = ReadBinDWord();
+ else // single int follows
+ mBinaryNumCount = 1;
+ }
+
+ --mBinaryNumCount;
+ return ReadBinDWord();
+ } else
+ {
+ FindNextNoneWhiteSpace();
+
+ // TODO: consider using strtol10s instead???
+
+ // check preceeding minus sign
+ bool isNegative = false;
+ if ( *P == '-')
+ {
+ isNegative = true;
+ P++;
+ }
+
+ // at least one digit expected
+ if ( !isdigit( *P))
+ ThrowException( "Number expected.");
+
+ // read digits
+ unsigned int number = 0;
+ while ( P < End)
+ {
+ if ( !isdigit( *P))
+ break;
+ number = number * 10 + (*P - 48);
+ P++;
+ }
+
+ CheckForSeparator();
+ return isNegative ? ((unsigned int) -int( number)) : number;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+float XFileParser::ReadFloat()
+{
+ if ( mIsBinaryFormat)
+ {
+ if ( mBinaryNumCount == 0)
+ {
+ unsigned short tmp = ReadBinWord(); // 0x07 or 0x42
+ if ( tmp == 0x07) // array of floats following
+ mBinaryNumCount = ReadBinDWord();
+ else // single float following
+ mBinaryNumCount = 1;
+ }
+
+ --mBinaryNumCount;
+ if ( mBinaryFloatSize == 8)
+ {
+ float result = (float) (*(double*) P);
+ P += 8;
+ return result;
+ } else
+ {
+ float result = *(float*) P;
+ P += 4;
+ return result;
+ }
+ }
+
+ // text version
+ FindNextNoneWhiteSpace();
+ // check for various special strings to allow reading files from faulty exporters
+ // I mean you, Blender!
+ if ( strncmp( P, "-1.#IND00", 9) == 0 || strncmp( P, "1.#IND00", 8) == 0)
+ {
+ P += 9;
+ CheckForSeparator();
+ return 0.0f;
+ } else
+ if ( strncmp( P, "1.#QNAN0", 8) == 0)
+ {
+ P += 8;
+ CheckForSeparator();
+ return 0.0f;
+ }
+
+ float result = 0.0f;
+ P = fast_atof_move( P, result);
+
+ CheckForSeparator();
+
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector2D XFileParser::ReadVector2()
+{
+ aiVector2D vector;
+ vector.x = ReadFloat();
+ vector.y = ReadFloat();
+ TestForSeparator();
+
+ return vector;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector3D XFileParser::ReadVector3()
+{
+ aiVector3D vector;
+ vector.x = ReadFloat();
+ vector.y = ReadFloat();
+ vector.z = ReadFloat();
+ TestForSeparator();
+
+ return vector;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiColor4D XFileParser::ReadRGBA()
+{
+ aiColor4D color;
+ color.r = ReadFloat();
+ color.g = ReadFloat();
+ color.b = ReadFloat();
+ color.a = ReadFloat();
+ TestForSeparator();
+
+ return color;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiColor3D XFileParser::ReadRGB()
+{
+ aiColor3D color;
+ color.r = ReadFloat();
+ color.g = ReadFloat();
+ color.b = ReadFloat();
+ TestForSeparator();
+
+ return color;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Throws an exception with a line number and the given text.
+void XFileParser::ThrowException( const std::string& pText)
+{
+ if ( mIsBinaryFormat)
+ throw DeadlyImportError( pText);
+ else
+ throw DeadlyImportError( boost::str( boost::format( "Line %d: %s") % mLineNumber % pText));
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Filters the imported hierarchy for some degenerated cases that some exporters produce.
+void XFileParser::FilterHierarchy( XFile::Node* pNode)
+{
+ // if the node has just a single unnamed child containing a mesh, remove
+ // the anonymous node inbetween. The 3DSMax kwXport plugin seems to produce this
+ // mess in some cases
+ if ( pNode->mChildren.size() == 1)
+ {
+ XFile::Node* child = pNode->mChildren.front();
+ if ( child->mName.length() == 0 && child->mMeshes.size() > 0)
+ {
+ // transfer its meshes to us
+ for ( unsigned int a = 0; a < child->mMeshes.size(); a++)
+ pNode->mMeshes.push_back( child->mMeshes[a]);
+ child->mMeshes.clear();
+
+ // then kill it
+ delete child;
+ pNode->mChildren.clear();
+ }
+ }
+
+ // recurse
+ for ( unsigned int a = 0; a < pNode->mChildren.size(); a++)
+ FilterHierarchy( pNode->mChildren[a]);
+}
+
+#endif // !! ASSIMP_BUILD_NO_X_IMPORTER
diff --git a/src/3rdparty/assimp/code/XFileParser.h b/src/3rdparty/assimp/code/XFileParser.h
new file mode 100644
index 000000000..8000c133b
--- /dev/null
+++ b/src/3rdparty/assimp/code/XFileParser.h
@@ -0,0 +1,162 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Helper class to parse a XFile into a temporary structure */
+#ifndef AI_XFILEPARSER_H_INC
+#define AI_XFILEPARSER_H_INC
+
+#include <string>
+#include <vector>
+
+#include "../include/aiTypes.h"
+
+namespace Assimp
+{
+ namespace XFile
+ {
+ struct Node;
+ struct Mesh;
+ struct Scene;
+ struct Material;
+ struct Animation;
+ struct AnimBone;
+ }
+
+/** The XFileParser reads a XFile either in text or binary form and builds a temporary
+ * data structure out of it.
+ */
+class XFileParser
+{
+public:
+ /** Constructor. Creates a data structure out of the XFile given in the memory block.
+ * @param pBuffer Memory buffer containing the XFile
+ */
+ XFileParser( const std::vector<char>& pBuffer);
+
+ /** Destructor. Destroys all imported data along with it */
+ ~XFileParser();
+
+ /** Returns the temporary representation of the imported data */
+ const XFile::Scene* GetImportedData() const { return mScene; }
+
+protected:
+ void ParseFile();
+ void ParseDataObjectTemplate();
+ void ParseDataObjectFrame( XFile::Node *pParent);
+ void ParseDataObjectTransformationMatrix( aiMatrix4x4& pMatrix);
+ void ParseDataObjectMesh( XFile::Mesh* pMesh);
+ void ParseDataObjectSkinWeights( XFile::Mesh* pMesh);
+ void ParseDataObjectSkinMeshHeader( XFile::Mesh* pMesh);
+ void ParseDataObjectMeshNormals( XFile::Mesh* pMesh);
+ void ParseDataObjectMeshTextureCoords( XFile::Mesh* pMesh);
+ void ParseDataObjectMeshVertexColors( XFile::Mesh* pMesh);
+ void ParseDataObjectMeshMaterialList( XFile::Mesh* pMesh);
+ void ParseDataObjectMaterial( XFile::Material* pMaterial);
+ void ParseDataObjectAnimTicksPerSecond();
+ void ParseDataObjectAnimationSet();
+ void ParseDataObjectAnimation( XFile::Animation* pAnim);
+ void ParseDataObjectAnimationKey( XFile::AnimBone *pAnimBone);
+ void ParseDataObjectTextureFilename( std::string& pName);
+ void ParseUnknownDataObject();
+
+ //! places pointer to next begin of a token, and ignores comments
+ void FindNextNoneWhiteSpace();
+
+ //! returns next parseable token. Returns empty string if no token there
+ std::string GetNextToken();
+
+ //! reads header of dataobject including the opening brace.
+ //! returns false if error happened, and writes name of object
+ //! if there is one
+ void readHeadOfDataObject( std::string* poName = NULL);
+
+ //! checks for closing curly brace, throws exception if not there
+ void CheckForClosingBrace();
+
+ //! checks for one following semicolon, throws exception if not there
+ void CheckForSemicolon();
+
+ //! checks for a separator char, either a ',' or a ';'
+ void CheckForSeparator();
+
+ /// tests and possibly consumes a separator char, but does nothing if there was no separator
+ void TestForSeparator();
+
+ //! reads a x file style string
+ void GetNextTokenAsString( std::string& poString);
+
+ void ReadUntilEndOfLine();
+
+ unsigned short ReadBinWord();
+ unsigned int ReadBinDWord();
+ unsigned int ReadInt();
+ float ReadFloat();
+ aiVector2D ReadVector2();
+ aiVector3D ReadVector3();
+ aiColor3D ReadRGB();
+ aiColor4D ReadRGBA();
+
+ /** Throws an exception with a line number and the given text. */
+ void ThrowException( const std::string& pText);
+
+ /** Filters the imported hierarchy for some degenerated cases that some exporters produce.
+ * @param pData The sub-hierarchy to filter
+ */
+ void FilterHierarchy( XFile::Node* pNode);
+
+protected:
+ unsigned int mMajorVersion, mMinorVersion; ///< version numbers
+ bool mIsBinaryFormat; ///< true if the file is in binary, false if it's in text form
+ unsigned int mBinaryFloatSize; ///< float size, either 32 or 64 bits
+ // counter for number arrays in binary format
+ unsigned int mBinaryNumCount;
+
+ const char* P;
+ const char* End;
+
+ /// Line number when reading in text format
+ unsigned int mLineNumber;
+
+ /// Imported data
+ XFile::Scene* mScene;
+};
+
+}
+#endif // AI_XFILEPARSER_H_INC
diff --git a/src/3rdparty/assimp/code/aiAssert.cpp b/src/3rdparty/assimp/code/aiAssert.cpp
new file mode 100644
index 000000000..cf82660be
--- /dev/null
+++ b/src/3rdparty/assimp/code/aiAssert.cpp
@@ -0,0 +1,72 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#include "AssimpPCH.h"
+#include "../include/aiAssert.h"
+#ifdef _WIN32
+#ifndef __GNUC__
+# include "crtdbg.h"
+#endif // ndef gcc
+#endif // _WIN32
+
+#if defined(__QNXNTO__)
+#include <stdlib.h>
+#endif
+
+// Set a breakpoint using win32, else line, file and message will be returned and progam ends with
+// errrocode = 1
+AI_WONT_RETURN void Assimp::aiAssert (const std::string &message, unsigned int uiLine, const std::string &file)
+{
+ // moved expression testing out of the function and into the macro to avoid constructing
+ // two std::string on every single ai_assert test
+// if (!expression)
+ {
+ // FIX (Aramis): changed std::cerr to std::cout that the message appears in VS' output window ...
+ std::cout << "File :" << file << ", line " << uiLine << " : " << message << std::endl;
+
+#ifdef _WIN32
+#ifndef __GNUC__
+ // Set breakpoint
+ __debugbreak();
+#endif //ndef gcc
+#else
+ exit (1);
+#endif
+ }
+}
diff --git a/src/3rdparty/assimp/code/assbin_chunks.h b/src/3rdparty/assimp/code/assbin_chunks.h
new file mode 100644
index 000000000..342b7879c
--- /dev/null
+++ b/src/3rdparty/assimp/code/assbin_chunks.h
@@ -0,0 +1,196 @@
+
+#ifndef INCLUDED_ASSBIN_CHUNKS_H
+#define INCLUDED_ASSBIN_CHUNKS_H
+
+#define ASSBIN_VERSION_MAJOR 1
+#define ASSBIN_VERSION_MINOR 0
+
+/**
+@page assfile .ASS File formats
+
+@section over Overview
+Assimp provides its own interchange format, which is intended to applications which need
+to serialize 3D-models and to reload them quickly. Assimp's file formats are designed to
+be read by Assimp itself. They encode additional information needed by Assimp to optimize
+its postprocessing pipeline. If you once apply specific steps to a scene, then save it
+and reread it from an ASS format using the same post processing settings, they won't
+be executed again.
+
+The format comes in two flavours: XML and binary - both of them hold a complete dump of
+the 'aiScene' data structure returned by the APIs. The focus for the binary format
+(<tt>.assbin</tt>) is fast loading. Optional deflate compression helps reduce file size. The XML
+flavour, <tt>.assxml</tt> or simply .xml, is just a plain-to-xml conversion of aiScene.
+
+ASSBIN is Assimp's binary interchange format. assimp_cmd (<tt>&lt;root&gt;/tools/assimp_cmd</tt>) is able to
+write it and the core library provides a loader for it.
+
+@section assxml XML File format
+
+The format is pretty much self-explanatory due to its similarity to the in-memory aiScene structure.
+With few exceptions, C structures are wrapped in XML elements.
+
+The DTD for ASSXML can be found in <tt>&lt;root&gt;/doc/AssXML_Scheme.xml</tt>. Or have look
+at the output files generated by assimp_cmd.
+
+@section assbin Binary file format
+
+The ASSBIN file format is composed of chunks to represent the hierarchical aiScene data structure.
+This makes the format extensible and allows backward-compatibility with future data structure
+versions. The <tt>&lt;root&gt;/code/assbin_chunks.h</tt> header contains some magic constants
+for use by stand-alone ASSBIN loaders. Also, Assimp's own file writer can be found
+in <tt>&lt;root&gt;/tools/assimp_cmd/WriteDumb.cpp</tt> (yes, the 'b' is no typo ...).
+
+@verbatim
+
+-------------------------------------------------------------------------------
+1. File structure:
+-------------------------------------------------------------------------------
+
+----------------------
+| Header (500 bytes) |
+----------------------
+| Variable chunks |
+----------------------
+
+-------------------------------------------------------------------------------
+2. Definitions:
+-------------------------------------------------------------------------------
+
+integer is four bytes wide, stored in little-endian byte order.
+short is two bytes wide, stored in little-endian byte order.
+byte is a single byte.
+string is an integer n followed by n UTF-8 characters, not terminated by zero
+float is an IEEE 754 single-precision floating-point value
+double is an IEEE 754 double-precision floating-point value
+t[n] is an array of n elements of type t
+
+-------------------------------------------------------------------------------
+2. Header:
+-------------------------------------------------------------------------------
+
+byte[44] Magic identification string for ASSBIN files.
+ 'ASSIMP.binary'
+
+integer Major version of the Assimp library which wrote the file
+integer Minor version of the Assimp library which wrote the file
+ match these against ASSBIN_VERSION_MAJOR and ASSBIN_VERSION_MINOR
+
+integer SVN revision of the Assimp library (intended for our internal
+ debugging - if you write Ass files from your own APPs, set this value to 0.
+integer Assimp compile flags
+
+short 0 for normal files, 1 for shortened dumps for regression tests
+ these should have the file extension assbin.regress
+
+short 1 if the data after the header is compressed with the DEFLATE algorithm,
+ 0 for uncompressed files.
+ For compressed files, the first integer after the header is
+ always the uncompressed data size
+
+byte[256] Zero-terminated source file name, UTF-8
+byte[128] Zero-terminated command line parameters passed to assimp_cmd, UTF-8
+
+byte[64] Reserved for future use
+---> Total length: 512 bytes
+
+-------------------------------------------------------------------------------
+3. Chunks:
+-------------------------------------------------------------------------------
+
+integer Magic chunk ID (ASSBIN_CHUNK_XXX)
+integer Chunk data length, in bytes
+ (unknown chunks are possible, a good reader skips over them)
+
+byte[n] length-of-chunk bytes of data, depending on the chunk type
+
+Chunks can contain nested chunks. Nested chunks are ALWAYS at the end of the chunk,
+their size is included in length-of-chunk.
+
+The chunk layout for all ASSIMP data structures is derived from their C declarations.
+The general 'rule' to get from Assimp headers to the serialized layout is:
+
+ 1. POD members (i.e. aiMesh::mPrimitiveTypes, aiMesh::mNumVertices),
+ in order of declaration.
+
+ 2. Array-members (aiMesh::mFaces, aiMesh::mVertices, aiBone::mWeights),
+ in order of declaration.
+
+ 2. Object array members (i.e aiMesh::mBones, aiScene::mMeshes) are stored in
+ subchunks directly following the data written in 1.) and 2.)
+
+
+ Of course, there are some exceptions to this general order:
+
+[[aiScene]]
+
+ - The root node holding the scene structure is naturally stored in
+ a ASSBIN_CHUNK_AINODE subchunk following 1.) and 2.) (which is
+ empty for aiScene).
+
+[[aiMesh]]
+
+ - mTextureCoords and mNumUVComponents are serialized as follows:
+
+ [number of used uv channels times]
+ integer mNumUVComponents[n]
+ float mTextureCoords[n][mNumUVComponents[n]]
+
+ -> more than AI_MAX_TEXCOORD_CHANNELS can be stored. This allows Assimp
+ builds with different settings for AI_MAX_TEXCOORD_CHANNELS to exchange
+ data. Unlike the in-memory format, only the used components of the
+ UV coordinates are written to disk. If mNumUVComponents[0] is 1, the
+ corresponding mTextureCoords array consists of mNumTextureCoords*1
+ single floats.
+
+ - The array member block of aiMesh is prefixed with an integer that specifies
+ the kinds of vertex components actually present in the mesh. This is a
+ bitwise combination of the ASSBIN_MESH_HAS_xxx constants.
+
+[[aiFace]]
+
+ - mNumIndices is stored as short
+ - mIndices are written as short, if aiMesh::mNumVertices<65536
+
+[[aiNode]]
+
+ - mParent is ommitted
+
+[[aiLight]]
+
+ - mAttenuationXXX not written if aiLight::mType == aiLightSource_DIRECTIONAL
+ - mAngleXXX not written if aiLight::mType != aiLightSource_SPOT
+
+[[aiMaterial]]
+
+ - mNumAllocated is ommitted, for obvious reasons :-)
+
+
+ @endverbatim*/
+
+
+#define ASSBIN_HEADER_LENGTH 512
+
+// these are the magic chunk identifiers for the binary ASS file format
+#define ASSBIN_CHUNK_AICAMERA 0x1234
+#define ASSBIN_CHUNK_AILIGHT 0x1235
+#define ASSBIN_CHUNK_AITEXTURE 0x1236
+#define ASSBIN_CHUNK_AIMESH 0x1237
+#define ASSBIN_CHUNK_AINODEANIM 0x1238
+#define ASSBIN_CHUNK_AISCENE 0x1239
+#define ASSBIN_CHUNK_AIBONE 0x123a
+#define ASSBIN_CHUNK_AIANIMATION 0x123b
+#define ASSBIN_CHUNK_AINODE 0x123c
+#define ASSBIN_CHUNK_AIMATERIAL 0x123d
+#define ASSBIN_CHUNK_AIMATERIALPROPERTY 0x123e
+
+#define ASSBIN_MESH_HAS_POSITIONS 0x1
+#define ASSBIN_MESH_HAS_NORMALS 0x2
+#define ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS 0x4
+#define ASSBIN_MESH_HAS_TEXCOORD_BASE 0x100
+#define ASSBIN_MESH_HAS_COLOR_BASE 0x10000
+
+#define ASSBIN_MESH_HAS_TEXCOORD(n) (ASSBIN_MESH_HAS_TEXCOORD_BASE << n)
+#define ASSBIN_MESH_HAS_COLOR(n) (ASSBIN_MESH_HAS_COLOR_BASE << n)
+
+
+#endif // INCLUDED_ASSBIN_CHUNKS_H
diff --git a/src/3rdparty/assimp/code/fast_atof.h b/src/3rdparty/assimp/code/fast_atof.h
new file mode 100644
index 000000000..a584efb70
--- /dev/null
+++ b/src/3rdparty/assimp/code/fast_atof.h
@@ -0,0 +1,306 @@
+// 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 strtol10_64
+// to ensure long numbers are handled correctly
+// ------------------------------------------------------------------------------------
+
+
+#ifndef __FAST_A_TO_F_H_INCLUDED__
+#define __FAST_A_TO_F_H_INCLUDED__
+
+#include <math.h>
+
+namespace Assimp
+{
+
+const float fast_atof_table[16] = { // we write [16] here instead of [] to work around a swig bug
+ 0.f,
+ 0.1f,
+ 0.01f,
+ 0.001f,
+ 0.0001f,
+ 0.00001f,
+ 0.000001f,
+ 0.0000001f,
+ 0.00000001f,
+ 0.000000001f,
+ 0.0000000001f,
+ 0.00000000001f,
+ 0.000000000001f,
+ 0.0000000000001f,
+ 0.00000000000001f,
+ 0.000000000000001f
+};
+
+
+// ------------------------------------------------------------------------------------
+// Convert a string in decimal format to a number
+// ------------------------------------------------------------------------------------
+inline unsigned int strtol10( const char* in, const char** out=0)
+{
+ unsigned int value = 0;
+
+ bool running = true;
+ while ( running )
+ {
+ 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 strtol8( const char* in, const char** out=0)
+{
+ unsigned int value = 0;
+
+ bool running = true;
+ while ( running )
+ {
+ 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 strtol16( const char* in, const char** out=0)
+{
+ unsigned int value = 0;
+
+ bool running = true;
+ while ( running )
+ {
+ 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 0xffffffff if the input is not hex
+// ------------------------------------------------------------------------------------
+inline unsigned int HexDigitToDecimal(char in)
+{
+ unsigned int out = 0xffffffff;
+ 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 0xffffffff if the input is not a hex digit
+ return out;
+}
+
+// ------------------------------------------------------------------------------------
+// Convert a hex-encoded octet (2 characters processed)
+// Return value is 0xffffffff if the input is not hex
+// ------------------------------------------------------------------------------------
+inline uint8_t HexOctetToDecimal(const char* in)
+{
+ return ((uint8_t)HexDigitToDecimal(in[0])<<4)+(uint8_t)HexDigitToDecimal(in[1]);
+}
+
+
+// ------------------------------------------------------------------------------------
+// signed variant of strtol10
+// ------------------------------------------------------------------------------------
+inline int strtol10s( const char* in, const char** out=0)
+{
+ bool inv = (*in=='-');
+ if (inv || *in=='+')
+ ++in;
+
+ int value = strtol10(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 strtol_cppstyle( const char* in, const char** out=0)
+{
+ if ('0' == in[0])
+ {
+ return 'x' == in[1] ? strtol16(in+2,out) : strtol8(in+1,out);
+ }
+ return strtol10(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 strtol10_64( const char* in, const char** out=0, unsigned int* max_inout=0)
+{
+ unsigned int cur = 0;
+ uint64_t value = 0;
+
+ bool running = true;
+ while ( running )
+ {
+ if ( *in < '0' || *in > '9' )
+ break;
+
+ const uint64_t new_value = ( value * 10 ) + ( *in - '0' );
+
+ if (new_value < value) /* numeric overflow, we rely on you */
+ return value;
+
+ 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;
+}
+
+// Number of relevant decimals for floating-point parsing.
+#define AI_FAST_ATOF_RELAVANT_DECIMALS 6
+
+// ------------------------------------------------------------------------------------
+//! 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.
+// ------------------------------------------------------------------------------------
+inline const char* fast_atof_move( const char* c, float& out)
+{
+ float f;
+
+ bool inv = (*c=='-');
+ if (inv || *c=='+')
+ ++c;
+
+ f = (float) strtol10_64 ( c, &c);
+ if (*c == '.' || (c[0] == ',' && (c[1] >= '0' || c[1] <= '9'))) // allow for commas, too
+ {
+ ++c;
+
+ // NOTE: The original implementation is highly unaccurate 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 = (double) strtol10_64 ( c, &c, &diff );
+
+ pl *= fast_atof_table[diff];
+ f += (float)pl;
+ }
+
+ // 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;
+ bool einv = (*c=='-');
+ if (einv || *c=='+')
+ ++c;
+
+ float exp = (float)strtol10_64(c, &c);
+ if (einv)
+ exp *= -1.0f;
+
+ f *= pow(10.0f, exp);
+ }
+
+ if (inv)
+ f *= -1.0f;
+
+ out = f;
+ return c;
+}
+
+// ------------------------------------------------------------------------------------
+// The same but more human.
+inline float fast_atof(const char* c)
+{
+ float ret;
+ fast_atof_move(c, ret);
+ return ret;
+}
+
+
+inline float fast_atof( const char* c, const char** cout)
+{
+ float ret;
+ *cout = fast_atof_move(c, ret);
+
+ return ret;
+}
+
+inline float fast_atof( const char** inout)
+{
+ float ret;
+ *inout = fast_atof_move(*inout, ret);
+
+ return ret;
+}
+
+} // end of namespace Assimp
+
+#endif
+
diff --git a/src/3rdparty/assimp/code/irrXMLWrapper.h b/src/3rdparty/assimp/code/irrXMLWrapper.h
new file mode 100644
index 000000000..af7516683
--- /dev/null
+++ b/src/3rdparty/assimp/code/irrXMLWrapper.h
@@ -0,0 +1,131 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef INCLUDED_AI_IRRXML_WRAPPER
+#define INCLUDED_AI_IRRXML_WRAPPER
+
+// some long includes ....
+#include "./../contrib/irrXML/irrXML.h"
+#include "./../include/IOStream.h"
+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
+ * boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+ * if ( file.get() == NULL) {
+ * throw DeadlyImportError( "Failed to open file " + pFile + ".");
+ * }
+ *
+ * // generate a XML reader for it
+ * boost::scoped_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
+ 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 as to map the whole file into
+ // memory.
+
+ data.resize(stream->FileSize());
+ stream->Read(&data[0],data.size(),1);
+
+ 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 = 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/src/3rdparty/assimp/code/makefile b/src/3rdparty/assimp/code/makefile
new file mode 100644
index 000000000..5710158da
--- /dev/null
+++ b/src/3rdparty/assimp/code/makefile
@@ -0,0 +1,111 @@
+### USE OF THIS MAKEFILE IS NOT RECOMMENDED.
+### It is no longer maintained. Use CMAKE instead.
+
+# Makefile for Open Asset Import Library (GNU-make)
+# aramis_acg@users.sourceforge.net
+
+#
+# Usage: make <target> <macros>
+
+# TARGETS:
+# all Build a shared so from the whole library
+# clean Cleanup object files, prepare for rebuild
+# static Build a static library (*.a)
+# install SDK will be installed to /usr/bin/assimp
+
+# MACROS: (make clean before you change one)
+# NOBOOST=1 Build against boost workaround
+# SINGLETHREADED=1 Build single-threaded library
+# DEBUG=1 Build debug build of library
+
+# C++ object files
+OBJECTS := $(patsubst %.cpp,%.o, $(wildcard *.cpp))
+OBJECTS += $(patsubst %.cpp,%.o, $(wildcard extra/*.cpp))
+OBJECTS += $(patsubst %.cpp,%.o, $(wildcard ./../contrib/irrXML/*.cpp))
+
+# C object files
+OBJECTSC := $(patsubst %.c,%.oc, $(wildcard ./../contrib/zlib/*.c))
+OBJECTSC += $(patsubst %.c,%.oc, $(wildcard ./../contrib/ConvertUTF/*.c))
+OBJECTSC += $(patsubst %.c,%.oc, $(wildcard ./../contrib/unzip/*.c))
+
+# Directory for install
+INSTALLDIR = /usr/bin/assimp
+
+# Include flags for gcc
+INCLUDEFLAGS =
+
+# Preprocessor defines for gcc
+DEFINEFLAGS =
+
+# Suffix for the output binary, represents build type
+NAMESUFFIX =
+
+# Output path for binaries
+BINPATH = ../bin/gcc
+INCPATH = ../include
+
+# GCC compiler flags
+CPPFLAGS=-Wall
+
+# Setup environment for noboost build
+ifeq ($(NOBOOST),1)
+ SINGLETHREADED = 1
+ INCLUDEFLAGS += -IBoostWorkaround/
+ DEFINEFLAGS += -DASSIMP_BUILD_BOOST_WORKAROUND
+# NAMESUFFIX += -noboost
+# else
+# INCLUDEFLAGS += -I"C:/Program Files/boost/boost_1_35_0"
+endif
+
+# Setup environment for st build
+ifeq ($(SINGLETHREADED),1)
+ DEFINEFLAGS += -DASSIMP_BUILD_SINGLETHREADED
+# NAMESUFFIX += -st
+endif
+
+# Setup environment for debug build
+ifeq ($(DEBUG),1)
+ DEFINEFLAGS += -D_DEBUG -DDEBUG
+# NAMESUFFIX += -debug
+else
+ CPPFLAGS += -O3
+ DEFINEFLAGS += -DNDEBUG -D_NDEBUG
+endif
+
+OUTPUT_NAME = dummy
+
+# Output name of shared library
+SHARED_TARGET = $(BINPATH)/libassimp$(NAMESUFFIX).so
+
+# Output name of static library
+STATIC = $(BINPATH)/libassimp$(NAMESUFFIX).a
+
+# target: all
+# usage : build a shared library (*.so)
+all: $(SHARED_TARGET)
+
+$(SHARED_TARGET): $(OBJECTS) $(OBJECTSC)
+ gcc -o $@ $(OBJECTS) $(OBJECTSC) -shared -lstdc++
+%.o:%.cpp
+ $(CXX) -g -c $(CPPFLAGS) $? -o $@ $(INCLUDEFLAGS) $(DEFINEFLAGS) -fPIC
+%.oc:%.c
+ $(CXX) -x c -g -c -ansi $(CPPFLAGS) $? -o $@ -fPIC
+
+# target: clean
+# usage : cleanup all object files, prepare for a rebuild
+.PHONY: clean
+clean:
+ -rm -f $(OBJECTS) $(OBJECTSC) $(TARGET)
+
+# target: static
+# usage : build a static library (*.a)
+static: $(STATIC)
+$(STATIC): $(OBJECTS) $(OBJECTSC)
+ ar rcs $@ $(OBJECTS) $(OBJECTSC)
+
+install:
+ mkdir -p $(INSTALLDIR)
+ mkdir -p $(INSTALLDIR)/include
+ mkdir -p $(INSTALLDIR)/lib
+ cp $(BINPATH)/libassimp$(NAMESUFFIX).* $(INSTALLDIR)/lib
+ cp $(INCPATH)/* $(INSTALLDIR)/include
diff --git a/src/3rdparty/assimp/code/makefile.mingw b/src/3rdparty/assimp/code/makefile.mingw
new file mode 100644
index 000000000..c0b7bde05
--- /dev/null
+++ b/src/3rdparty/assimp/code/makefile.mingw
@@ -0,0 +1,105 @@
+### USE OF THIS MAKEFILE IS NOT RECOMMENDED.
+### It is no longer maintained. Use CMAKE instead.
+
+# ---------------------------------------------------------------------------
+# Makefile for Open Asset Import Library (MinGW32-make)
+# aramis_acg@users.sourceforge.net
+# - just a quick'n'dirty one, could be buggy ...
+#
+# Usage: mingw32-make -f makefile.mingw <target> <macros>
+
+# TARGETS:
+# all Build a shared so from the whole library
+# clean Cleanup object files, prepare for rebuild
+# static Build a static library (*.a)
+
+# MACROS: (make clean before you change one)
+# NOBOOST=1 Build against boost workaround
+# SINGLETHREADED=1 Build single-threaded library
+# DEBUG=1 Build debug build of library
+#
+# ---------------------------------------------------------------------------
+
+# C++ object files
+OBJECTS := $(patsubst %.cpp,%.o, $(wildcard *.cpp))
+OBJECTS += $(patsubst %.cpp,%.o, $(wildcard extra/*.cpp))
+OBJECTS += $(patsubst %.cpp,%.o, $(wildcard ./../contrib/irrXML/*.cpp))
+
+# C object files
+OBJECTSC := $(patsubst %.c,%.oc, $(wildcard ./../contrib/zlib/*.c))
+OBJECTSC += $(patsubst %.c,%.oc, $(wildcard ./../contrib/ConvertUTF/*.c))
+OBJECTSC += $(patsubst %.c,%.oc, $(wildcard ./../contrib/unzip/*.c))
+
+# Include flags for gcc
+INCLUDEFLAGS =
+
+# Preprocessor defines for gcc
+DEFINEFLAGS =
+
+# Suffix for the output binary, represents build type
+NAMESUFFIX =
+
+# Output path for binaries
+BINPATH = ../bin/mingw/
+
+# GCC compiler flags
+CPPFLAGS=-Wall
+
+# Setup environment for noboost build
+ifeq ($(NOBOOST),1)
+ SINGLETHREADED = 1
+ INCLUDEFLAGS += -I./BoostWorkaround/
+ DEFINEFLAGS += -DASSIMP_BUILD_BOOST_WORKAROUND
+# NAMESUFFIX += -noboost
+else
+ # adjust this manually if your boost is stored elsewhere
+ INCLUDEFLAGS += -I"C:/Program Files/boost/boost_1_38"
+ #INCLUDEFLAGS += -I"$(BOOST_DIR)"
+
+endif
+
+# Setup environment for st build
+ifeq ($(SINGLETHREADED),1)
+ DEFINEFLAGS += -DASSIMP_BUILD_SINGLETHREADED
+# NAMESUFFIX += -st
+endif
+
+# Setup environment for debug build
+ifeq ($(DEBUG),1)
+ DEFINEFLAGS += -D_DEBUG -DDEBUG
+ CPPFLAGS += -g
+# NAMESUFFIX += -debug
+else
+ CPPFLAGS += -O2 -s
+ DEFINEFLAGS += -DNDEBUG -D_NDEBUG
+endif
+
+# Output name of shared library
+SHARED_TARGET = $(BINPATH)/libassimp$(NAMESUFFIX).so
+
+# Output name of static library
+STATIC = $(BINPATH)/libassimp$(NAMESUFFIX).a
+
+# target: all
+# usage : build a shared library (*.so)
+all: $(SHARED_TARGET)
+
+$(SHARED_TARGET): $(OBJECTS) $(OBJECTSC)
+ gcc -o $@ $(OBJECTS) $(OBJECTSC) -shared -lstdc++
+%.o:%.cpp
+ $(CXX) -c $(CPPFLAGS) $? -o $@ $(INCLUDEFLAGS) $(DEFINEFLAGS)
+%.oc:%.c
+ $(CXX) -x c -c -ansi $(CPPFLAGS) $? -o $@
+
+# target: clean
+# usage : cleanup all object files, prepare for a rebuild
+.PHONY: clean
+clean:
+ -del *.o .\..\contrib\irrXML\*.o .\..\contrib\zlib\*.oc .\..\contrib\unzip\*.oc .\..\contrib\ConvertUTF\*.oc
+
+# target: static
+# usage : build a static library (*.a)
+static: $(STATIC)
+$(STATIC): $(OBJECTS) $(OBJECTSC)
+ ar rcs $@ $(OBJECTS) $(OBJECTSC)
+
diff --git a/src/3rdparty/assimp/code/pstdint.h b/src/3rdparty/assimp/code/pstdint.h
new file mode 100644
index 000000000..0c1cec84e
--- /dev/null
+++ b/src/3rdparty/assimp/code/pstdint.h
@@ -0,0 +1,729 @@
+/* A portable stdint.h
+ ****************************************************************************
+ * BSD License:
+ ****************************************************************************
+ *
+ * Copyright (c) 2005-2007 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.10
+ *
+ * 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 most compiler vendors have decided not to
+ * implement the C99 standard, and the next C++ language standard
+ * (which has a lot more mindshare these days) will be a long time in
+ * coming and its unknown whether or not it will include stdint.h or
+ * how much adoption it will have. Either way, it will be a long time
+ * before all compilers come with a stdint.h and it also does nothing
+ * for the extremely large number of compilers available today which
+ * do not include this file, or anything comparable to it.
+ *
+ * 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. 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 verison, 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_INT8_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
+ *
+ */
+
+#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(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) )) && !defined (_PSTDINT_H_INCLUDED)
+#include <stdint.h>
+#define _PSTDINT_H_INCLUDED
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "ll"
+# endif
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER "l"
+# 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_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 "20"
+# 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_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
+
+/*
+ * 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
+
+#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
+#ifndef uint8_t
+# 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
+#ifndef int8_t
+# 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
+#ifndef uint16_t
+#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
+#ifndef int16_t
+#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
+#ifndef uint32_t
+#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
+#ifndef int32_t
+#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__)
+# 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 "20"
+#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
+
+/*
+ * 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__)
+# 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)
+# define STDINT_H_UINTPTR_T_DEFINED
+#endif
+
+#ifndef STDINT_H_UINTPTR_T_DEFINED
+# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64)
+# 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)
+# define stdint_intptr_bits 32
+# elif defined (__INTEL_COMPILER)
+/* TODO -- what will Intel do about x86-64? */
+# 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
+
diff --git a/src/3rdparty/assimp/code/qnan.h b/src/3rdparty/assimp/code/qnan.h
new file mode 100644
index 000000000..c21dc86a1
--- /dev/null
+++ b/src/3rdparty/assimp/code/qnan.h
@@ -0,0 +1,110 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file 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
+
+// ---------------------------------------------------------------------------
+/** 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;
+} ;
+
+// ---------------------------------------------------------------------------
+/** 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
+ return (reinterpret_cast<_IEEESingle*>(&in)->IEEE.Exp == (1u << 8)-1 &&
+ reinterpret_cast<_IEEESingle*>(&in)->IEEE.Frac);
+}
+
+// ---------------------------------------------------------------------------
+/** Check whether a float is NOT qNaN.
+ * @param in Input value */
+AI_FORCE_INLINE bool is_not_qnan(float in)
+{
+ return !is_qnan(in);
+}
+
+// ---------------------------------------------------------------------------
+/** @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)
+{
+ return (reinterpret_cast<_IEEESingle*>(&in)->IEEE.Exp == (1u << 8)-1);
+}
+
+// ---------------------------------------------------------------------------
+/** @brief Get a fresh qnan. */
+AI_FORCE_INLINE float get_qnan()
+{
+ return std::numeric_limits<float>::quiet_NaN();
+}
+
+#endif // !! AI_QNAN_H_INCLUDED
diff --git a/src/3rdparty/assimp/code/res/assimp.rc b/src/3rdparty/assimp/code/res/assimp.rc
new file mode 100644
index 000000000..5b3b5c06b
--- /dev/null
+++ b/src/3rdparty/assimp/code/res/assimp.rc
@@ -0,0 +1,80 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+#include "..\..\revision.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Deutsch (Deutschland) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU)
+#ifdef _WIN32
+LANGUAGE LANG_GERMAN, SUBLANG_GERMAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,1,SVNRevision, 0
+ PRODUCTVERSION 1,1,SVNRevision,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x7L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040704b0"
+ BEGIN
+ VALUE "Comments", "Licensed under a 3-clause BSD license"
+ VALUE "CompanyName", "ASSIMP Development Team"
+ VALUE "FileDescription", "Open Asset Import Library"
+ VALUE "FileVersion", 1,1,SVNRevision,0
+ VALUE "InternalName", "assimp "
+ VALUE "LegalCopyright", "Copyright (C) 2006-2010"
+ VALUE "OriginalFilename", "assimpNN.dll"
+ VALUE "ProductName", "Open Asset Import Library"
+ VALUE "ProductVersion", 1,1,SVNRevision,0
+ ,0
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x407, 1200
+ END
+END
+
+#endif // Deutsch (Deutschland) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/src/3rdparty/assimp/code/res/resource.h b/src/3rdparty/assimp/code/res/resource.h
new file mode 100644
index 000000000..a2542fea1
--- /dev/null
+++ b/src/3rdparty/assimp/code/res/resource.h
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by assimp.rc
+
+// Nchste Standardwerte fr neue Objekte
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/src/3rdparty/assimp/contrib/ConvertUTF/ConvertUTF.c b/src/3rdparty/assimp/contrib/ConvertUTF/ConvertUTF.c
new file mode 100644
index 000000000..aeb6735fb
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/ConvertUTF/ConvertUTF.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Source code file.
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Sept 2001: fixed const & error conditions per
+ mods suggested by S. Parent & A. Lillich.
+ June 2002: Tim Dodd added detection and handling of incomplete
+ source sequences, enhanced error detection, added casts
+ to eliminate compiler warnings.
+ July 2003: slight mods to back out aggressive FFFE detection.
+ Jan 2004: updated switches in from-UTF8 conversions.
+ Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
+
+ See the header file "ConvertUTF.h" for complete documentation.
+
+------------------------------------------------------------------------ */
+
+
+#include "ConvertUTF.h"
+#ifdef CVTUTF_DEBUG
+#include <stdio.h>
+#endif
+
+static const int halfShift = 10; /* used for shifting by 10 bits */
+
+static const UTF32 halfBase = 0x0010000UL;
+static const UTF32 halfMask = 0x3FFUL;
+
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+#define false 0
+#define true 1
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF16 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF32* source = *sourceStart;
+ UTF16* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ if (target >= targetEnd) {
+ result = targetExhausted; break;
+ }
+ ch = *source++;
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_LEGAL_UTF32) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ --source; /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF32 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16* source = *sourceStart;
+ UTF32* target = *targetStart;
+ UTF32 ch, ch2;
+ while (source < sourceEnd) {
+ const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd) {
+ ch2 = *source;
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ } else if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ if (target >= targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ *target++ = ch;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+#ifdef CVTUTF_DEBUG
+if (result == sourceIllegal) {
+ fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
+ fflush(stderr);
+}
+#endif
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 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,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,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,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,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+
+/*
+ * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+ * into the first byte, depending on how many bytes follow. There are
+ * as many entries in this table as there are UTF-8 sequence types.
+ * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
+ * for *legal* UTF-8 will be 4 or fewer bytes total.
+ */
+static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+/* --------------------------------------------------------------------- */
+
+/* The interface converts a whole buffer to avoid function-call overhead.
+ * Constants have been gathered. Loops & conditionals have been removed as
+ * much as possible for efficiency, in favor of drop-through switches.
+ * (See "Note A" at the bottom of the file for equivalent code.)
+ * If your compiler supports it, the "isLegalUTF8" call can be turned
+ * into an inline function.
+ */
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF8 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16* source = *sourceStart;
+ UTF8* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd) {
+ UTF32 ch2 = *source;
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ } else if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ /* Figure out how many bytes the result will require */
+ if (ch < (UTF32)0x80) { bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
+ } else if (ch < (UTF32)0x110000) { bytesToWrite = 4;
+ } else { bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ target -= bytesToWrite; result = targetExhausted; break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns false. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+
+static Boolean isLegalUTF8(const UTF8 *source, int length) {
+ UTF8 a;
+ const UTF8 *srcptr = source+length;
+ switch (length) {
+ default: return false;
+ /* Everything else falls through when "true"... */
+ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 2: if ((a = (*--srcptr)) > 0xBF) return false;
+
+ switch (*source) {
+ /* no fall-through in this inner switch */
+ case 0xE0: if (a < 0xA0) return false; break;
+ case 0xED: if (a > 0x9F) return false; break;
+ case 0xF0: if (a < 0x90) return false; break;
+ case 0xF4: if (a > 0x8F) return false; break;
+ default: if (a < 0x80) return false;
+ }
+
+ case 1: if (*source >= 0x80 && *source < 0xC2) return false;
+ }
+ if (*source > 0xF4) return false;
+ return true;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Exported function to return whether a UTF-8 sequence is legal or not.
+ * This is not used here; it's just exported.
+ */
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
+ int length = trailingBytesForUTF8[*source]+1;
+ if (source+length > sourceEnd) {
+ return false;
+ }
+ return isLegalUTF8(source, length);
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF16 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8* source = *sourceStart;
+ UTF16* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ result = sourceExhausted; break;
+ }
+ /* Do this check whether lenient or strict */
+ if (! isLegalUTF8(source, extraBytesToRead+1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (target >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_UTF16) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ source -= (extraBytesToRead+1); /* return to the start */
+ break; /* Bail out; shouldn't continue */
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF8 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF32* source = *sourceStart;
+ UTF8* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ ch = *source++;
+ if (flags == strictConversion ) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ /*
+ * Figure out how many bytes the result will require. Turn any
+ * illegally large UTF32 things (> Plane 17) into replacement chars.
+ */
+ if (ch < (UTF32)0x80) { bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
+ } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4;
+ } else { bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ result = sourceIllegal;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ --source; /* Back up source pointer! */
+ target -= bytesToWrite; result = targetExhausted; break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF32 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8* source = *sourceStart;
+ UTF32* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ result = sourceExhausted; break;
+ }
+ /* Do this check whether lenient or strict */
+ if (! isLegalUTF8(source, extraBytesToRead+1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5: ch += *source++; ch <<= 6;
+ case 4: ch += *source++; ch <<= 6;
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (target >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up the source pointer! */
+ result = targetExhausted; break;
+ }
+ if (ch <= UNI_MAX_LEGAL_UTF32) {
+ /*
+ * UTF-16 surrogate values are illegal in UTF-32, and anything
+ * over Plane 17 (> 0x10FFFF) is illegal.
+ */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = ch;
+ }
+ } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
+ result = sourceIllegal;
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* ---------------------------------------------------------------------
+
+ Note A.
+ The fall-through switches in UTF-8 reading code save a
+ temp variable, some decrements & conditionals. The switches
+ are equivalent to the following loop:
+ {
+ int tmpBytesToRead = extraBytesToRead+1;
+ do {
+ ch += *source++;
+ --tmpBytesToRead;
+ if (tmpBytesToRead) ch <<= 6;
+ } while (tmpBytesToRead > 0);
+ }
+ In UTF-8 writing code, the switches on "bytesToWrite" are
+ similarly unrolled loops.
+
+ --------------------------------------------------------------------- */
diff --git a/src/3rdparty/assimp/contrib/ConvertUTF/ConvertUTF.h b/src/3rdparty/assimp/contrib/ConvertUTF/ConvertUTF.h
new file mode 100644
index 000000000..76e801ece
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/ConvertUTF/ConvertUTF.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+#ifndef CONVERTUTF_H
+#define CONVERTUTF_H
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Header file.
+
+ Several funtions are included here, forming a complete set of
+ conversions between the three formats. UTF-7 is not included
+ here, but is handled in a separate source file.
+
+ Each of these routines takes pointers to input buffers and output
+ buffers. The input buffers are const.
+
+ Each routine converts the text between *sourceStart and sourceEnd,
+ putting the result into the buffer between *targetStart and
+ targetEnd. Note: the end pointers are *after* the last item: e.g.
+ *(sourceEnd - 1) is the last item.
+
+ The return result indicates whether the conversion was successful,
+ and if not, whether the problem was in the source or target buffers.
+ (Only the first encountered problem is indicated.)
+
+ After the conversion, *sourceStart and *targetStart are both
+ updated to point to the end of last text successfully converted in
+ the respective buffers.
+
+ Input parameters:
+ sourceStart - pointer to a pointer to the source buffer.
+ The contents of this are modified on return so that
+ it points at the next thing to be converted.
+ targetStart - similarly, pointer to pointer to the target buffer.
+ sourceEnd, targetEnd - respectively pointers to the ends of the
+ two buffers, for overflow checking only.
+
+ These conversion functions take a ConversionFlags argument. When this
+ flag is set to strict, both irregular sequences and isolated surrogates
+ will cause an error. When the flag is set to lenient, both irregular
+ sequences and isolated surrogates are converted.
+
+ Whether the flag is strict or lenient, all illegal sequences will cause
+ an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
+ or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
+ must check for illegal sequences.
+
+ When the flag is set to lenient, characters over 0x10FFFF are converted
+ to the replacement character; otherwise (when the flag is set to strict)
+ they constitute an error.
+
+ Output parameters:
+ The value "sourceIllegal" is returned from some routines if the input
+ sequence is malformed. When "sourceIllegal" is returned, the source
+ value will point to the illegal value that caused the problem. E.g.,
+ in UTF-8 when a sequence is malformed, it points to the start of the
+ malformed sequence.
+
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Fixes & updates, Sept 2001.
+
+------------------------------------------------------------------------ */
+
+/* ---------------------------------------------------------------------
+ The following 4 definitions are compiler-specific.
+ The C standard does not guarantee that wchar_t has at least
+ 16 bits, so wchar_t is no less portable than unsigned short!
+ All should be unsigned values to avoid sign extension during
+ bit mask & shift operations.
+------------------------------------------------------------------------ */
+
+typedef unsigned long UTF32; /* at least 32 bits */
+typedef unsigned short UTF16; /* at least 16 bits */
+typedef unsigned char UTF8; /* typically 8 bits */
+typedef unsigned char Boolean; /* 0 or 1 */
+
+/* Some fundamental constants */
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_MAX_BMP (UTF32)0x0000FFFF
+#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
+#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
+#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
+
+typedef enum {
+ conversionOK, /* conversion successful */
+ sourceExhausted, /* partial character in source, but hit end */
+ targetExhausted, /* insuff. room in target for conversion */
+ sourceIllegal /* source sequence is illegal/malformed */
+} ConversionResult;
+
+typedef enum {
+ strictConversion = 0,
+ lenientConversion
+} ConversionFlags;
+
+/* This is for C++ and does no harm in C */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ConversionResult ConvertUTF8toUTF16 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF16toUTF8 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF8toUTF32 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF32toUTF8 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF16toUTF32 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF32toUTF16 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
+
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+#endif // CONVERTUTF_H
diff --git a/src/3rdparty/assimp/contrib/ConvertUTF/readme.txt b/src/3rdparty/assimp/contrib/ConvertUTF/readme.txt
new file mode 100644
index 000000000..71f4de981
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/ConvertUTF/readme.txt
@@ -0,0 +1,43 @@
+
+The accompanying C source code file "ConvertUTF.c" and the associated header
+file "ConvertUTF.h" provide for conversion between various transformation
+formats of Unicode characters. The following conversions are supported:
+
+ UTF-32 to UTF-16
+ UTF-32 to UTF-8
+ UTF-16 to UTF-32
+ UTF-16 to UTF-8
+ UTF-8 to UTF-16
+ UTF-8 to UTF-32
+
+In addition, there is a test harness which runs various tests.
+
+The files "CVTUTF7.C" and "CVTUTF7.H" are for archival and historical purposes
+only. They have not been updated to Unicode 3.0 or later and should be
+considered obsolescent. "CVTUTF7.C" contains two functions that can convert
+between UCS2 (i.e., the BMP characters only) and UTF-7. Surrogates are
+not supported, the code has not been tested, and should be considered
+unsuitable for general purpose use.
+
+Please submit any bug reports about these programs here:
+
+ http://www.unicode.org/unicode/reporting.html
+
+Version 1.0: initial version.
+
+Version 1.1: corrected some minor problems; added stricter checks.
+
+Version 1.2: corrected switch statements associated with "extraBytesToRead"
+ in 4 & 5 byte cases, in functions for conversion from UTF8.
+ Note: formally, the 4 & 5 byte cases are illegal in the latest
+ UTF8, but the table and this code has always catered for those,
+ cases since at one time they were legal.
+
+Version 1.3: Updated UTF-8 legality check;
+ updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions
+ Updated UTF-8 legality tests in harness.c
+
+
+Last update: October 19, 2004
+
+
diff --git a/src/3rdparty/assimp/contrib/irrXML/CXMLReaderImpl.h b/src/3rdparty/assimp/contrib/irrXML/CXMLReaderImpl.h
new file mode 100644
index 000000000..3f85bea6a
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/irrXML/CXMLReaderImpl.h
@@ -0,0 +1,809 @@
+// Copyright (C) 2002-2005 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/or irrXML.h
+
+#ifndef __ICXML_READER_IMPL_H_INCLUDED__
+#define __ICXML_READER_IMPL_H_INCLUDED__
+
+#include "irrXML.h"
+#include "irrString.h"
+#include "irrArray.h"
+
+using namespace Assimp;
+
+#ifdef _DEBUG
+#define IRR_DEBUGPRINT(x) printf((x));
+#else // _DEBUG
+#define IRR_DEBUGPRINT(x)
+#endif // _DEBUG
+
+
+namespace irr
+{
+namespace io
+{
+
+
+//! implementation of the IrrXMLReader
+template<class char_type, class superclass>
+class CXMLReaderImpl : public IIrrXMLReader<char_type, superclass>
+{
+public:
+
+ //! Constructor
+ CXMLReaderImpl(IFileReadCallBack* callback, bool deleteCallBack = true)
+ : TextData(0), P(0), TextBegin(0), TextSize(0), CurrentNodeType(EXN_NONE),
+ SourceFormat(ETF_ASCII), TargetFormat(ETF_ASCII), IsEmptyElement(true)
+ {
+ if (!callback)
+ return;
+
+ storeTargetFormat();
+
+ // read whole xml file
+
+ readFile(callback);
+
+ // clean up
+
+ if (deleteCallBack)
+ delete callback;
+
+ // create list with special characters
+
+ createSpecialCharacterList();
+
+ // set pointer to text begin
+ P = TextBegin;
+ }
+
+
+ //! Destructor
+ virtual ~CXMLReaderImpl()
+ {
+ delete [] TextData;
+ }
+
+
+ //! Reads forward to the next xml node.
+ //! \return Returns false, if there was no further node.
+ virtual bool read()
+ {
+ // if not end reached, parse the node
+ if (P && (unsigned int)(P - TextBegin) < TextSize - 1 && *P != 0)
+ {
+ parseCurrentNode();
+ return true;
+ }
+
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return false;
+ }
+
+
+ //! Returns the type of the current XML node.
+ virtual EXML_NODE getNodeType() const
+ {
+ return CurrentNodeType;
+ }
+
+
+ //! Returns attribute count of the current XML node.
+ virtual int getAttributeCount() const
+ {
+ return Attributes.size();
+ }
+
+
+ //! Returns name of an attribute.
+ virtual const char_type* getAttributeName(int idx) const
+ {
+ if (idx < 0 || idx >= (int)Attributes.size())
+ return 0;
+
+ return Attributes[idx].Name.c_str();
+ }
+
+
+ //! Returns the value of an attribute.
+ virtual const char_type* getAttributeValue(int idx) const
+ {
+ if (idx < 0 || idx >= (int)Attributes.size())
+ return 0;
+
+ return Attributes[idx].Value.c_str();
+ }
+
+
+ //! Returns the value of an attribute.
+ virtual const char_type* getAttributeValue(const char_type* name) const
+ {
+ const SAttribute* attr = getAttributeByName(name);
+ if (!attr)
+ return 0;
+
+ return attr->Value.c_str();
+ }
+
+
+ //! Returns the value of an attribute
+ virtual const char_type* getAttributeValueSafe(const char_type* name) const
+ {
+ const SAttribute* attr = getAttributeByName(name);
+ if (!attr)
+ return EmptyString.c_str();
+
+ return attr->Value.c_str();
+ }
+
+
+
+ //! Returns the value of an attribute as integer.
+ int getAttributeValueAsInt(const char_type* name) const
+ {
+ return (int)getAttributeValueAsFloat(name);
+ }
+
+
+ //! Returns the value of an attribute as integer.
+ int getAttributeValueAsInt(int idx) const
+ {
+ return (int)getAttributeValueAsFloat(idx);
+ }
+
+
+ //! Returns the value of an attribute as float.
+ float getAttributeValueAsFloat(const char_type* name) const
+ {
+ const SAttribute* attr = getAttributeByName(name);
+ if (!attr)
+ return 0;
+
+ core::stringc c = attr->Value.c_str();
+ return fast_atof(c.c_str());
+ }
+
+
+ //! Returns the value of an attribute as float.
+ float getAttributeValueAsFloat(int idx) const
+ {
+ const char_type* attrvalue = getAttributeValue(idx);
+ if (!attrvalue)
+ return 0;
+
+ core::stringc c = attrvalue;
+ return fast_atof(c.c_str());
+ }
+
+
+ //! Returns the name of the current node.
+ virtual const char_type* getNodeName() const
+ {
+ return NodeName.c_str();
+ }
+
+
+ //! Returns data of the current node.
+ virtual const char_type* getNodeData() const
+ {
+ return NodeName.c_str();
+ }
+
+
+ //! Returns if an element is an empty element, like <foo />
+ virtual bool isEmptyElement() const
+ {
+ return IsEmptyElement;
+ }
+
+ //! Returns format of the source xml file.
+ virtual ETEXT_FORMAT getSourceFormat() const
+ {
+ return SourceFormat;
+ }
+
+ //! Returns format of the strings returned by the parser.
+ virtual ETEXT_FORMAT getParserFormat() const
+ {
+ return TargetFormat;
+ }
+
+private:
+
+ // Reads the current xml node
+ void parseCurrentNode()
+ {
+ char_type* start = P;
+
+ // more forward until '<' found
+ while (*P != L'<' && *P)
+ ++P;
+
+ if (!*P)
+ return;
+
+ if (P - start > 0)
+ {
+ // we found some text, store it
+ if (setText(start, P))
+ return;
+ }
+
+ ++P;
+
+ // based on current token, parse and report next element
+ switch(*P)
+ {
+ case L'/':
+ parseClosingXMLElement();
+ break;
+ case L'?':
+ ignoreDefinition();
+ break;
+ case L'!':
+ if (!parseCDATA())
+ parseComment();
+ break;
+ default:
+ parseOpeningXMLElement();
+ break;
+ }
+ }
+
+
+ //! sets the state that text was found. Returns true if set should be set
+ bool setText(char_type* start, char_type* end)
+ {
+ // check if text is more than 2 characters, and if not, check if there is
+ // only white space, so that this text won't be reported
+ if (end - start < 3)
+ {
+ char_type* p = start;
+ for (; p != end; ++p)
+ if (!isWhiteSpace(*p))
+ break;
+
+ if (p == end)
+ return false;
+ }
+
+ // set current text to the parsed text, and replace xml special characters
+ core::string<char_type> s(start, (int)(end - start));
+ NodeName = replaceSpecialCharacters(s);
+
+ // current XML node type is text
+ CurrentNodeType = EXN_TEXT;
+
+ return true;
+ }
+
+
+
+ //! ignores an xml definition like <?xml something />
+ void ignoreDefinition()
+ {
+ CurrentNodeType = EXN_UNKNOWN;
+
+ // move until end marked with '>' reached
+ while (*P != L'>')
+ ++P;
+
+ ++P;
+ }
+
+
+ //! parses a comment
+ void parseComment()
+ {
+ CurrentNodeType = EXN_COMMENT;
+ P += 1;
+
+ char_type *pCommentBegin = P;
+
+ int count = 1;
+
+ // move until end of comment reached
+ while (count)
+ {
+ if (*P == L'>')
+ --count;
+ else
+ if (*P == L'<')
+ ++count;
+
+ ++P;
+ }
+
+ P -= 3;
+ NodeName = core::string<char_type>(pCommentBegin+2, (int)(P - pCommentBegin-2));
+ P += 3;
+ }
+
+
+ //! parses an opening xml element and reads attributes
+ void parseOpeningXMLElement()
+ {
+ CurrentNodeType = EXN_ELEMENT;
+ IsEmptyElement = false;
+ Attributes.clear();
+
+ // find name
+ const char_type* startName = P;
+
+ // find end of element
+ while (*P != L'>' && !isWhiteSpace(*P))
+ ++P;
+
+ const char_type* endName = P;
+
+ // find Attributes
+ while (*P != L'>')
+ {
+ if (isWhiteSpace(*P))
+ ++P;
+ else
+ {
+ if (*P != L'/')
+ {
+ // we've got an attribute
+
+ // read the attribute names
+ const char_type* attributeNameBegin = P;
+
+ while (!isWhiteSpace(*P) && *P != L'=')
+ ++P;
+
+ const char_type* attributeNameEnd = P;
+ ++P;
+
+ // read the attribute value
+ // check for quotes and single quotes, thx to murphy
+ while ( (*P != L'\"') && (*P != L'\'') && *P)
+ ++P;
+
+ if (!*P) // malformatted xml file
+ return;
+
+ const char_type attributeQuoteChar = *P;
+
+ ++P;
+ const char_type* attributeValueBegin = P;
+
+ while (*P != attributeQuoteChar && *P)
+ ++P;
+
+ if (!*P) // malformatted xml file
+ return;
+
+ const char_type* attributeValueEnd = P;
+ ++P;
+
+ SAttribute attr;
+ attr.Name = core::string<char_type>(attributeNameBegin,
+ (int)(attributeNameEnd - attributeNameBegin));
+
+ core::string<char_type> s(attributeValueBegin,
+ (int)(attributeValueEnd - attributeValueBegin));
+
+ attr.Value = replaceSpecialCharacters(s);
+ Attributes.push_back(attr);
+ }
+ else
+ {
+ // tag is closed directly
+ ++P;
+ IsEmptyElement = true;
+ break;
+ }
+ }
+ }
+
+ // check if this tag is closing directly
+ if (endName > startName && *(endName-1) == L'/')
+ {
+ // directly closing tag
+ IsEmptyElement = true;
+ endName--;
+ }
+
+ NodeName = core::string<char_type>(startName, (int)(endName - startName));
+
+ ++P;
+ }
+
+
+ //! parses an closing xml tag
+ void parseClosingXMLElement()
+ {
+ CurrentNodeType = EXN_ELEMENT_END;
+ IsEmptyElement = false;
+ Attributes.clear();
+
+ ++P;
+ const char_type* pBeginClose = P;
+
+ while (*P != L'>')
+ ++P;
+
+ // remove trailing whitespace, if any
+ while ( isspace( P[-1]))
+ --P;
+
+ NodeName = core::string<char_type>(pBeginClose, (int)(P - pBeginClose));
+ ++P;
+ }
+
+ //! parses a possible CDATA section, returns false if begin was not a CDATA section
+ bool parseCDATA()
+ {
+ if (*(P+1) != L'[')
+ return false;
+
+ CurrentNodeType = EXN_CDATA;
+
+ // skip '<![CDATA['
+ int count=0;
+ while ( *P && count<8 )
+ {
+ ++P;
+ ++count;
+ }
+
+ if (!*P)
+ return true;
+
+ char_type *cDataBegin = P;
+ char_type *cDataEnd = 0;
+
+ // find end of CDATA
+ while (*P && !cDataEnd)
+ {
+ if (*P == L'>' &&
+ (*(P-1) == L']') &&
+ (*(P-2) == L']'))
+ {
+ cDataEnd = P - 2;
+ }
+
+ ++P;
+ }
+
+ if ( cDataEnd )
+ NodeName = core::string<char_type>(cDataBegin, (int)(cDataEnd - cDataBegin));
+ else
+ NodeName = "";
+
+ return true;
+ }
+
+
+ // structure for storing attribute-name pairs
+ struct SAttribute
+ {
+ core::string<char_type> Name;
+ core::string<char_type> Value;
+ };
+
+ // finds a current attribute by name, returns 0 if not found
+ const SAttribute* getAttributeByName(const char_type* name) const
+ {
+ if (!name)
+ return 0;
+
+ core::string<char_type> n = name;
+
+ for (int i=0; i<(int)Attributes.size(); ++i)
+ if (Attributes[i].Name == n)
+ return &Attributes[i];
+
+ return 0;
+ }
+
+ // replaces xml special characters in a string and creates a new one
+ core::string<char_type> replaceSpecialCharacters(
+ core::string<char_type>& origstr)
+ {
+ int pos = origstr.findFirst(L'&');
+ int oldPos = 0;
+
+ if (pos == -1)
+ return origstr;
+
+ core::string<char_type> newstr;
+
+ while (pos != -1 && pos < origstr.size()-2)
+ {
+ // check if it is one of the special characters
+
+ int specialChar = -1;
+ for (int i=0; i<(int)SpecialCharacters.size(); ++i)
+ {
+ const char_type* p = &origstr.c_str()[pos]+1;
+
+ if (equalsn(&SpecialCharacters[i][1], p, SpecialCharacters[i].size()-1))
+ {
+ specialChar = i;
+ break;
+ }
+ }
+
+ if (specialChar != -1)
+ {
+ newstr.append(origstr.subString(oldPos, pos - oldPos));
+ newstr.append(SpecialCharacters[specialChar][0]);
+ pos += SpecialCharacters[specialChar].size();
+ }
+ else
+ {
+ newstr.append(origstr.subString(oldPos, pos - oldPos + 1));
+ pos += 1;
+ }
+
+ // find next &
+ oldPos = pos;
+ pos = origstr.findNext(L'&', pos);
+ }
+
+ if (oldPos < origstr.size()-1)
+ newstr.append(origstr.subString(oldPos, origstr.size()-oldPos));
+
+ return newstr;
+ }
+
+
+
+ //! reads the xml file and converts it into the wanted character format.
+ bool readFile(IFileReadCallBack* callback)
+ {
+ int size = callback->getSize();
+ size += 4; // We need two terminating 0's at the end.
+ // For ASCII we need 1 0's, for UTF-16 2, for UTF-32 4.
+
+ char* data8 = new char[size];
+
+ if (!callback->read(data8, size-4))
+ {
+ delete [] data8;
+ return false;
+ }
+
+ // add zeros at end
+
+ data8[size-1] = 0;
+ data8[size-2] = 0;
+ data8[size-3] = 0;
+ data8[size-4] = 0;
+
+ char16* data16 = reinterpret_cast<char16*>(data8);
+ char32* data32 = reinterpret_cast<char32*>(data8);
+
+ // now we need to convert the data to the desired target format
+ // based on the byte order mark.
+
+ const unsigned char UTF8[] = {0xEF, 0xBB, 0xBF}; // 0xEFBBBF;
+ const int UTF16_BE = 0xFFFE;
+ const int UTF16_LE = 0xFEFF;
+ const int UTF32_BE = 0xFFFE0000;
+ const int UTF32_LE = 0x0000FEFF;
+
+ // check source for all utf versions and convert to target data format
+
+ if (size >= 4 && data32[0] == (char32)UTF32_BE)
+ {
+ // UTF-32, big endian
+ SourceFormat = ETF_UTF32_BE;
+ convertTextData(data32+1, data8, (size/4)); // data32+1 because we need to skip the header
+ }
+ else
+ if (size >= 4 && data32[0] == (char32)UTF32_LE)
+ {
+ // UTF-32, little endian
+ SourceFormat = ETF_UTF32_LE;
+ convertTextData(data32+1, data8, (size/4)); // data32+1 because we need to skip the header
+ }
+ else
+ if (size >= 2 && data16[0] == UTF16_BE)
+ {
+ // UTF-16, big endian
+ SourceFormat = ETF_UTF16_BE;
+ convertTextData(data16+1, data8, (size/2)); // data16+1 because we need to skip the header
+ }
+ else
+ if (size >= 2 && data16[0] == UTF16_LE)
+ {
+ // UTF-16, little endian
+ SourceFormat = ETF_UTF16_LE;
+ convertTextData(data16+1, data8, (size/2)); // data16+1 because we need to skip the header
+ }
+ else
+ if (size >= 3 && data8[0] == UTF8[0] && data8[1] == UTF8[1] && data8[2] == UTF8[2])
+ {
+ // UTF-8
+ SourceFormat = ETF_UTF8;
+ convertTextData(data8+3, data8, size); // data8+3 because we need to skip the header
+ }
+ else
+ {
+ // ASCII
+ SourceFormat = ETF_ASCII;
+ convertTextData(data8, data8, size);
+ }
+
+ return true;
+ }
+
+
+ //! converts the text file into the desired format.
+ //! \param source: begin of the text (without byte order mark)
+ //! \param pointerToStore: pointer to text data block which can be
+ //! stored or deleted based on the nesessary conversion.
+ //! \param sizeWithoutHeader: Text size in characters without header
+ template<class src_char_type>
+ void convertTextData(src_char_type* source, char* pointerToStore, int sizeWithoutHeader)
+ {
+ // convert little to big endian if necessary
+ if (sizeof(src_char_type) > 1 &&
+ isLittleEndian(TargetFormat) != isLittleEndian(SourceFormat))
+ convertToLittleEndian(source);
+
+ // check if conversion is necessary:
+ if (sizeof(src_char_type) == sizeof(char_type))
+ {
+ // no need to convert
+ TextBegin = (char_type*)source;
+ TextData = (char_type*)pointerToStore;
+ TextSize = sizeWithoutHeader;
+ }
+ else
+ {
+ // convert source into target data format.
+ // TODO: implement a real conversion. This one just
+ // copies bytes. This is a problem when there are
+ // unicode symbols using more than one character.
+
+ TextData = new char_type[sizeWithoutHeader];
+
+ // MSVC debugger complains here about loss of data ...
+
+
+ // FIXME - gcc complains about 'shift width larger than width of type'
+ // for T == unsigned long. Avoid it by messing around volatile ..
+ volatile unsigned int c = 3;
+ const src_char_type cc = (src_char_type)((((uint64_t)1u << (sizeof( char_type)<<c)) - 1));
+ for (int i=0; i<sizeWithoutHeader; ++i)
+ TextData[i] = char_type( source[i] & cc);
+
+ TextBegin = TextData;
+ TextSize = sizeWithoutHeader;
+
+ // delete original data because no longer needed
+ delete [] pointerToStore;
+ }
+ }
+
+ //! converts whole text buffer to little endian
+ template<class src_char_type>
+ void convertToLittleEndian(src_char_type* t)
+ {
+ if (sizeof(src_char_type) == 4)
+ {
+ // 32 bit
+
+ while (*t)
+ {
+ *t = ((*t & 0xff000000) >> 24) |
+ ((*t & 0x00ff0000) >> 8) |
+ ((*t & 0x0000ff00) << 8) |
+ ((*t & 0x000000ff) << 24);
+ ++t;
+ }
+ }
+ else
+ {
+ // 16 bit
+
+ while (*t)
+ {
+ *t = (*t >> 8) | (*t << 8);
+ ++t;
+ }
+ }
+ }
+
+ //! returns if a format is little endian
+ inline bool isLittleEndian(ETEXT_FORMAT f)
+ {
+ return f == ETF_ASCII ||
+ f == ETF_UTF8 ||
+ f == ETF_UTF16_LE ||
+ f == ETF_UTF32_LE;
+ }
+
+
+ //! returns true if a character is whitespace
+ inline bool isWhiteSpace(char_type c)
+ {
+ return (c==' ' || c=='\t' || c=='\n' || c=='\r');
+ }
+
+
+ //! generates a list with xml special characters
+ void createSpecialCharacterList()
+ {
+ // list of strings containing special symbols,
+ // the first character is the special character,
+ // the following is the symbol string without trailing &.
+
+ SpecialCharacters.push_back("&amp;");
+ SpecialCharacters.push_back("<lt;");
+ SpecialCharacters.push_back(">gt;");
+ SpecialCharacters.push_back("\"quot;");
+ SpecialCharacters.push_back("'apos;");
+
+ }
+
+
+ //! compares the first n characters of the strings
+ bool equalsn(const char_type* str1, const char_type* str2, int len)
+ {
+ int i;
+ for (i=0; str1[i] && str2[i] && i < len; ++i)
+ if (str1[i] != str2[i])
+ return false;
+
+ // if one (or both) of the strings was smaller then they
+ // are only equal if they have the same lenght
+ return (i == len) || (str1[i] == 0 && str2[i] == 0);
+ }
+
+
+ //! stores the target text format
+ void storeTargetFormat()
+ {
+ // get target format. We could have done this using template specialization,
+ // but VisualStudio 6 don't like it and we want to support it.
+
+ switch(sizeof(char_type))
+ {
+ case 1:
+ TargetFormat = ETF_UTF8;
+ break;
+ case 2:
+ TargetFormat = ETF_UTF16_LE;
+ break;
+ case 4:
+ TargetFormat = ETF_UTF32_LE;
+ break;
+ default:
+ TargetFormat = ETF_ASCII; // should never happen.
+ }
+ }
+
+
+ // instance variables:
+
+ char_type* TextData; // data block of the text file
+ char_type* P; // current point in text to parse
+ char_type* TextBegin; // start of text to parse
+ unsigned int TextSize; // size of text to parse in characters, not bytes
+
+ EXML_NODE CurrentNodeType; // type of the currently parsed node
+ ETEXT_FORMAT SourceFormat; // source format of the xml file
+ ETEXT_FORMAT TargetFormat; // output format of this parser
+
+ core::string<char_type> NodeName; // name of the node currently in
+ core::string<char_type> EmptyString; // empty string to be returned by getSafe() methods
+
+ bool IsEmptyElement; // is the currently parsed node empty?
+
+ core::array< core::string<char_type> > SpecialCharacters; // see createSpecialCharacterList()
+
+ core::array<SAttribute> Attributes; // attributes of current element
+
+}; // end CXMLReaderImpl
+
+
+} // end namespace
+} // end namespace
+
+#endif
diff --git a/src/3rdparty/assimp/contrib/irrXML/heapsort.h b/src/3rdparty/assimp/contrib/irrXML/heapsort.h
new file mode 100644
index 000000000..0409f9152
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/irrXML/heapsort.h
@@ -0,0 +1,73 @@
+// Copyright (C) 2002-2005 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#ifndef __IRR_HEAPSORT_H_INCLUDED__
+#define __IRR_HEAPSORT_H_INCLUDED__
+
+#include "irrTypes.h"
+
+namespace irr
+{
+namespace core
+{
+
+//! Sinks an element into the heap.
+template<class T>
+inline void heapsink(T*array, s32 element, s32 max)
+{
+ while ((element<<1) < max) // there is a left child
+ {
+ s32 j = (element<<1);
+
+ if (j+1 < max && array[j] < array[j+1])
+ j = j+1; // take right child
+
+ if (array[element] < array[j])
+ {
+ T t = array[j]; // swap elements
+ array[j] = array[element];
+ array[element] = t;
+ element = j;
+ }
+ else
+ return;
+ }
+}
+
+
+//! Sorts an array with size 'size' using heapsort.
+template<class T>
+inline void heapsort(T* array_, s32 size)
+{
+ // for heapsink we pretent this is not c++, where
+ // arrays start with index 0. So we decrease the array pointer,
+ // the maximum always +2 and the element always +1
+
+ T* virtualArray = array_ - 1;
+ s32 virtualSize = size + 2;
+ s32 i;
+
+ // build heap
+
+ for (i=((size-1)/2); i>=0; --i)
+ heapsink(virtualArray, i+1, virtualSize-1);
+
+ // sort array
+
+ for (i=size-1; i>=0; --i)
+ {
+ T t = array_[0];
+ array_[0] = array_[i];
+ array_[i] = t;
+ heapsink(virtualArray, 1, i + 1);
+ }
+}
+
+} // end namespace core
+} // end namespace irr
+
+
+
+#endif
+
diff --git a/src/3rdparty/assimp/contrib/irrXML/irrArray.h b/src/3rdparty/assimp/contrib/irrXML/irrArray.h
new file mode 100644
index 000000000..8c762f43c
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/irrXML/irrArray.h
@@ -0,0 +1,444 @@
+// Copyright (C) 2002-2005 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
+
+#ifndef __IRR_ARRAY_H_INCLUDED__
+#define __IRR_ARRAY_H_INCLUDED__
+
+#include "irrTypes.h"
+#include "heapsort.h"
+
+namespace irr
+{
+namespace core
+{
+
+//! Self reallocating template array (like stl vector) with additional features.
+/** Some features are: Heap sorting, binary search methods, easier debugging.
+*/
+template <class T>
+class array
+{
+
+public:
+
+ array()
+ : data(0), allocated(0), used(0),
+ free_when_destroyed(true), is_sorted(true)
+ {
+ }
+
+ //! Constructs a array and allocates an initial chunk of memory.
+ //! \param start_count: Amount of elements to allocate.
+ array(u32 start_count)
+ : data(0), allocated(0), used(0),
+ free_when_destroyed(true), is_sorted(true)
+ {
+ reallocate(start_count);
+ }
+
+
+ //! Copy constructor
+ array(const array<T>& other)
+ : data(0)
+ {
+ *this = other;
+ }
+
+
+
+ //! Destructor. Frees allocated memory, if set_free_when_destroyed
+ //! was not set to false by the user before.
+ ~array()
+ {
+ if (free_when_destroyed)
+ delete [] data;
+ }
+
+
+
+ //! Reallocates the array, make it bigger or smaller.
+ //! \param new_size: New size of array.
+ void reallocate(u32 new_size)
+ {
+ T* old_data = data;
+
+ data = new T[new_size];
+ allocated = new_size;
+
+ s32 end = used < new_size ? used : new_size;
+ for (s32 i=0; i<end; ++i)
+ data[i] = old_data[i];
+
+ if (allocated < used)
+ used = allocated;
+
+ delete [] old_data;
+ }
+
+ //! Adds an element at back of array. If the array is to small to
+ //! add this new element, the array is made bigger.
+ //! \param element: Element to add at the back of the array.
+ void push_back(const T& element)
+ {
+ if (used + 1 > allocated)
+ {
+ // reallocate(used * 2 +1);
+ // this doesn't work if the element is in the same array. So
+ // we'll copy the element first to be sure we'll get no data
+ // corruption
+
+ T e;
+ e = element; // copy element
+ reallocate(used * 2 +1); // increase data block
+ data[used++] = e; // push_back
+ is_sorted = false;
+ return;
+ }
+
+ data[used++] = element;
+ is_sorted = false;
+ }
+
+
+ //! Adds an element at the front of the array. If the array is to small to
+ //! add this new element, the array is made bigger. Please note that this
+ //! is slow, because the whole array needs to be copied for this.
+ //! \param element: Element to add at the back of the array.
+ void push_front(const T& element)
+ {
+ if (used + 1 > allocated)
+ reallocate(used * 2 +1);
+
+ for (int i=(int)used; i>0; --i)
+ data[i] = data[i-1];
+
+ data[0] = element;
+ is_sorted = false;
+ ++used;
+ }
+
+
+ //! Insert item into array at specified position. Please use this
+ //! only if you know what you are doing (possible performance loss).
+ //! The preferred method of adding elements should be push_back().
+ //! \param element: Element to be inserted
+ //! \param index: Where position to insert the new element.
+ void insert(const T& element, u32 index=0)
+ {
+ _IRR_DEBUG_BREAK_IF(index>used) // access violation
+
+ if (used + 1 > allocated)
+ reallocate(used * 2 +1);
+
+ for (u32 i=used++; i>index; i--)
+ data[i] = data[i-1];
+
+ data[index] = element;
+ is_sorted = false;
+ }
+
+
+
+
+ //! Clears the array and deletes all allocated memory.
+ void clear()
+ {
+ delete [] data;
+ data = 0;
+ used = 0;
+ allocated = 0;
+ is_sorted = true;
+ }
+
+
+
+ //! Sets pointer to new array, using this as new workspace.
+ //! \param newPointer: Pointer to new array of elements.
+ //! \param size: Size of the new array.
+ void set_pointer(T* newPointer, u32 size)
+ {
+ delete [] data;
+ data = newPointer;
+ allocated = size;
+ used = size;
+ is_sorted = false;
+ }
+
+
+
+ //! Sets if the array should delete the memory it used.
+ //! \param f: If true, the array frees the allocated memory in its
+ //! destructor, otherwise not. The default is true.
+ void set_free_when_destroyed(bool f)
+ {
+ free_when_destroyed = f;
+ }
+
+
+
+ //! Sets the size of the array.
+ //! \param usedNow: Amount of elements now used.
+ void set_used(u32 usedNow)
+ {
+ if (allocated < usedNow)
+ reallocate(usedNow);
+
+ used = usedNow;
+ }
+
+
+
+ //! Assignement operator
+ void operator=(const array<T>& other)
+ {
+ if (data)
+ delete [] data;
+
+ //if (allocated < other.allocated)
+ if (other.allocated == 0)
+ data = 0;
+ else
+ data = new T[other.allocated];
+
+ used = other.used;
+ free_when_destroyed = other.free_when_destroyed;
+ is_sorted = other.is_sorted;
+ allocated = other.allocated;
+
+ for (u32 i=0; i<other.used; ++i)
+ data[i] = other.data[i];
+ }
+
+
+ //! Direct access operator
+ T& operator [](u32 index)
+ {
+ _IRR_DEBUG_BREAK_IF(index>=used) // access violation
+
+ return data[index];
+ }
+
+
+
+ //! Direct access operator
+ const T& operator [](u32 index) const
+ {
+ _IRR_DEBUG_BREAK_IF(index>=used) // access violation
+
+ return data[index];
+ }
+
+ //! Gets last frame
+ const T& getLast() const
+ {
+ _IRR_DEBUG_BREAK_IF(!used) // access violation
+
+ return data[used-1];
+ }
+
+ //! Gets last frame
+ T& getLast()
+ {
+ _IRR_DEBUG_BREAK_IF(!used) // access violation
+
+ return data[used-1];
+ }
+
+
+ //! Returns a pointer to the array.
+ //! \return Pointer to the array.
+ T* pointer()
+ {
+ return data;
+ }
+
+
+
+ //! Returns a const pointer to the array.
+ //! \return Pointer to the array.
+ const T* const_pointer() const
+ {
+ return data;
+ }
+
+
+
+ //! Returns size of used array.
+ //! \return Size of elements in the array.
+ u32 size() const
+ {
+ return used;
+ }
+
+
+
+ //! Returns amount memory allocated.
+ //! \return Returns amount of memory allocated. The amount of bytes
+ //! allocated would be allocated_size() * sizeof(ElementsUsed);
+ u32 allocated_size() const
+ {
+ return allocated;
+ }
+
+
+
+ //! Returns true if array is empty
+ //! \return True if the array is empty, false if not.
+ bool empty() const
+ {
+ return used == 0;
+ }
+
+
+
+ //! Sorts the array using heapsort. There is no additional memory waste and
+ //! the algorithm performs (O) n log n in worst case.
+ void sort()
+ {
+ if (is_sorted || used<2)
+ return;
+
+ heapsort(data, used);
+ is_sorted = true;
+ }
+
+
+
+ //! Performs a binary search for an element, returns -1 if not found.
+ //! The array will be sorted before the binary search if it is not
+ //! already sorted.
+ //! \param element: Element to search for.
+ //! \return Returns position of the searched element if it was found,
+ //! otherwise -1 is returned.
+ s32 binary_search(const T& element)
+ {
+ return binary_search(element, 0, used-1);
+ }
+
+
+
+ //! Performs a binary search for an element, returns -1 if not found.
+ //! The array will be sorted before the binary search if it is not
+ //! already sorted.
+ //! \param element: Element to search for.
+ //! \param left: First left index
+ //! \param right: Last right index.
+ //! \return Returns position of the searched element if it was found,
+ //! otherwise -1 is returned.
+ s32 binary_search(const T& element, s32 left, s32 right)
+ {
+ if (!used)
+ return -1;
+
+ sort();
+
+ s32 m;
+
+ do
+ {
+ m = (left+right)>>1;
+
+ if (element < data[m])
+ right = m - 1;
+ else
+ left = m + 1;
+
+ } while ((element < data[m] || data[m] < element) && left<=right);
+
+ // this last line equals to:
+ // " while ((element != array[m]) && left<=right);"
+ // but we only want to use the '<' operator.
+ // the same in next line, it is "(element == array[m])"
+
+ if (!(element < data[m]) && !(data[m] < element))
+ return m;
+
+ return -1;
+ }
+
+
+ //! Finds an element in linear time, which is very slow. Use
+ //! binary_search for faster finding. Only works if =operator is implemented.
+ //! \param element: Element to search for.
+ //! \return Returns position of the searched element if it was found,
+ //! otherwise -1 is returned.
+ s32 linear_search(T& element)
+ {
+ for (u32 i=0; i<used; ++i)
+ if (!(element < data[i]) && !(data[i] < element))
+ return (s32)i;
+
+ return -1;
+ }
+
+
+ //! Finds an element in linear time, which is very slow. Use
+ //! binary_search for faster finding. Only works if =operator is implemented.
+ //! \param element: Element to search for.
+ //! \return Returns position of the searched element if it was found,
+ //! otherwise -1 is returned.
+ s32 linear_reverse_search(T& element)
+ {
+ for (s32 i=used-1; i>=0; --i)
+ if (data[i] == element)
+ return (s32)i;
+
+ return -1;
+ }
+
+
+
+ //! Erases an element from the array. May be slow, because all elements
+ //! following after the erased element have to be copied.
+ //! \param index: Index of element to be erased.
+ void erase(u32 index)
+ {
+ _IRR_DEBUG_BREAK_IF(index>=used || index<0) // access violation
+
+ for (u32 i=index+1; i<used; ++i)
+ data[i-1] = data[i];
+
+ --used;
+ }
+
+
+ //! Erases some elements from the array. may be slow, because all elements
+ //! following after the erased element have to be copied.
+ //! \param index: Index of the first element to be erased.
+ //! \param count: Amount of elements to be erased.
+ void erase(u32 index, s32 count)
+ {
+ _IRR_DEBUG_BREAK_IF(index>=used || index<0 || count<1 || index+count>used) // access violation
+
+ for (u32 i=index+count; i<used; ++i)
+ data[i-count] = data[i];
+
+ used-= count;
+ }
+
+
+ //! Sets if the array is sorted
+ void set_sorted(bool _is_sorted)
+ {
+ is_sorted = _is_sorted;
+ }
+
+
+ private:
+
+ T* data;
+ u32 allocated;
+ u32 used;
+ bool free_when_destroyed;
+ bool is_sorted;
+};
+
+
+} // end namespace core
+} // end namespace irr
+
+
+
+#endif
+
diff --git a/src/3rdparty/assimp/contrib/irrXML/irrString.h b/src/3rdparty/assimp/contrib/irrXML/irrString.h
new file mode 100644
index 000000000..be8405888
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/irrXML/irrString.h
@@ -0,0 +1,664 @@
+// Copyright (C) 2002-2005 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
+
+#ifndef __IRR_STRING_H_INCLUDED__
+#define __IRR_STRING_H_INCLUDED__
+
+#include "irrTypes.h"
+
+namespace irr
+{
+namespace core
+{
+
+//! Very simple string class with some useful features.
+/** string<c8> and string<wchar_t> work both with unicode AND ascii,
+so you can assign unicode to string<c8> and ascii to string<wchar_t>
+(and the other way round) if your ever would want to.
+Note that the conversation between both is not done using an encoding.
+
+Known bugs:
+Special characters like '', '' and '' are ignored in the
+methods make_upper, make_lower and equals_ignore_case.
+*/
+template <class T>
+class string
+{
+public:
+
+ //! Default constructor
+ string()
+ : array(0), allocated(1), used(1)
+ {
+ array = new T[1];
+ array[0] = 0x0;
+ }
+
+
+
+ //! Constructor
+ string(const string<T>& other)
+ : array(0), allocated(0), used(0)
+ {
+ *this = other;
+ }
+
+
+ //! Constructs a string from an int
+ string(int number)
+ : array(0), allocated(0), used(0)
+ {
+ // store if negative and make positive
+
+ bool negative = false;
+ if (number < 0)
+ {
+ number *= -1;
+ negative = true;
+ }
+
+ // temporary buffer for 16 numbers
+
+ c8 tmpbuf[16];
+ tmpbuf[15] = 0;
+ s32 idx = 15;
+
+ // special case '0'
+
+ if (!number)
+ {
+ tmpbuf[14] = '0';
+ *this = &tmpbuf[14];
+ return;
+ }
+
+ // add numbers
+
+ while (number && idx)
+ {
+ idx--;
+ tmpbuf[idx] = (c8)('0' + (number % 10));
+ number = number / 10;
+ }
+
+ // add sign
+
+ if (negative)
+ {
+ idx--;
+ tmpbuf[idx] = '-';
+ }
+
+ *this = &tmpbuf[idx];
+ }
+
+
+
+ //! Constructor for copying a string from a pointer with a given lenght
+ template <class B>
+ string(const B* c, s32 lenght)
+ : array(0), allocated(0), used(0)
+ {
+ if (!c)
+ return;
+
+ allocated = used = lenght+1;
+ array = new T[used];
+
+ for (s32 l = 0; l<lenght; ++l)
+ array[l] = (T)c[l];
+
+ array[lenght] = 0;
+ }
+
+
+
+ //! Constructor for unicode and ascii strings
+ template <class B>
+ string(const B* c)
+ : array(0),allocated(0), used(0)
+ {
+ *this = c;
+ }
+
+
+
+ //! destructor
+ ~string()
+ {
+ delete [] array;
+ }
+
+
+
+ //! Assignment operator
+ string<T>& operator=(const string<T>& other)
+ {
+ if (this == &other)
+ return *this;
+
+ delete [] array;
+ allocated = used = other.size()+1;
+ array = new T[used];
+
+ const T* p = other.c_str();
+ for (s32 i=0; i<used; ++i, ++p)
+ array[i] = *p;
+
+ return *this;
+ }
+
+
+
+ //! Assignment operator for strings, ascii and unicode
+ template <class B>
+ string<T>& operator=(const B* c)
+ {
+ if (!c)
+ {
+ if (!array)
+ {
+ array = new T[1];
+ allocated = 1;
+ used = 1;
+ }
+ array[0] = 0x0;
+ return *this;
+ }
+
+ if ((void*)c == (void*)array)
+ return *this;
+
+ s32 len = 0;
+ const B* p = c;
+ while (*p)
+ {
+ ++len;
+ ++p;
+ }
+
+ // we'll take the old string for a while, because the new string could be
+ // a part of the current string.
+ T* oldArray = array;
+
+ allocated = used = len+1;
+ array = new T[used];
+
+ for (s32 l = 0; l<len+1; ++l)
+ array[l] = (T)c[l];
+
+ delete [] oldArray;
+ return *this;
+ }
+
+ //! Add operator for other strings
+ string<T> operator+(const string<T>& other)
+ {
+ string<T> str(*this);
+ str.append(other);
+
+ return str;
+ }
+
+ //! Add operator for strings, ascii and unicode
+ template <class B>
+ string<T> operator+(const B* c)
+ {
+ string<T> str(*this);
+ str.append(c);
+
+ return str;
+ }
+
+
+
+ //! Direct access operator
+ T& operator [](const s32 index) const
+ {
+ _IRR_DEBUG_BREAK_IF(index>=used) // bad index
+
+ return array[index];
+ }
+
+
+ //! Comparison operator
+ bool operator ==(const T* str) const
+ {
+ int i;
+ for (i=0; array[i] && str[i]; ++i)
+ if (array[i] != str[i])
+ return false;
+
+ return !array[i] && !str[i];
+ }
+
+
+
+ //! Comparison operator
+ bool operator ==(const string<T>& other) const
+ {
+ for (s32 i=0; array[i] && other.array[i]; ++i)
+ if (array[i] != other.array[i])
+ return false;
+
+ return used == other.used;
+ }
+
+
+
+ //! Is smaller operator
+ bool operator <(const string<T>& other) const
+ {
+ for (s32 i=0; array[i] && other.array[i]; ++i)
+ if (array[i] != other.array[i])
+ return (array[i] < other.array[i]);
+
+ return used < other.used;
+ }
+
+
+
+ //! Equals not operator
+ bool operator !=(const string<T>& other) const
+ {
+ return !(*this == other);
+ }
+
+
+
+ //! Returns length of string
+ /** \return Returns length of the string in characters. */
+ s32 size() const
+ {
+ return used-1;
+ }
+
+
+
+ //! Returns character string
+ /** \return Returns pointer to C-style zero terminated string. */
+ const T* c_str() const
+ {
+ return array;
+ }
+
+
+
+ //! Makes the string lower case.
+ void make_lower()
+ {
+ const T A = (T)'A';
+ const T Z = (T)'Z';
+ const T diff = (T)'a' - A;
+
+ for (s32 i=0; i<used; ++i)
+ {
+ if (array[i]>=A && array[i]<=Z)
+ array[i] += diff;
+ }
+ }
+
+
+
+ //! Makes the string upper case.
+ void make_upper()
+ {
+ const T a = (T)'a';
+ const T z = (T)'z';
+ const T diff = (T)'A' - a;
+
+ for (s32 i=0; i<used; ++i)
+ {
+ if (array[i]>=a && array[i]<=z)
+ array[i] += diff;
+ }
+ }
+
+
+
+ //! Compares the string ignoring case.
+ /** \param other: Other string to compare.
+ \return Returns true if the string are equal ignoring case. */
+ bool equals_ignore_case(const string<T>& other) const
+ {
+ for (s32 i=0; array[i] && other[i]; ++i)
+ if (toLower(array[i]) != toLower(other[i]))
+ return false;
+
+ return used == other.used;
+ }
+
+
+ //! compares the first n characters of the strings
+ bool equalsn(const string<T>& other, int len)
+ {
+ int i;
+ for (i=0; array[i] && other[i] && i < len; ++i)
+ if (array[i] != other[i])
+ return false;
+
+ // if one (or both) of the strings was smaller then they
+ // are only equal if they have the same lenght
+ return (i == len) || (used == other.used);
+ }
+
+
+ //! compares the first n characters of the strings
+ bool equalsn(const T* str, int len)
+ {
+ int i;
+ for (i=0; array[i] && str[i] && i < len; ++i)
+ if (array[i] != str[i])
+ return false;
+
+ // if one (or both) of the strings was smaller then they
+ // are only equal if they have the same lenght
+ return (i == len) || (array[i] == 0 && str[i] == 0);
+ }
+
+
+ //! Appends a character to this string
+ /** \param character: Character to append. */
+ void append(T character)
+ {
+ if (used + 1 > allocated)
+ reallocate((s32)used + 1);
+
+ used += 1;
+
+ array[used-2] = character;
+ array[used-1] = 0;
+ }
+
+ //! Appends a string to this string
+ /** \param other: String to append. */
+ void append(const string<T>& other)
+ {
+ --used;
+
+ s32 len = other.size();
+
+ if (used + len + 1 > allocated)
+ reallocate((s32)used + (s32)len + 1);
+
+ for (s32 l=0; l<len+1; ++l)
+ array[l+used] = other[l];
+
+ used = used + len + 1;
+ }
+
+
+ //! Appends a string of the length l to this string.
+ /** \param other: other String to append to this string.
+ \param length: How much characters of the other string to add to this one. */
+ void append(const string<T>& other, s32 length)
+ {
+ s32 len = other.size();
+
+ if (len < length)
+ {
+ append(other);
+ return;
+ }
+
+ len = length;
+ --used;
+
+ if (used + len > allocated)
+ reallocate((s32)used + (s32)len);
+
+ for (s32 l=0; l<len; ++l)
+ array[l+used] = other[l];
+
+ used = used + len;
+ }
+
+
+ //! Reserves some memory.
+ /** \param count: Amount of characters to reserve. */
+ void reserve(s32 count)
+ {
+ if (count < allocated)
+ return;
+
+ reallocate(count);
+ }
+
+
+ //! finds first occurrence of character in string
+ /** \param c: Character to search for.
+ \return Returns position where the character has been found,
+ or -1 if not found. */
+ s32 findFirst(T c) const
+ {
+ for (s32 i=0; i<used; ++i)
+ if (array[i] == c)
+ return i;
+
+ return -1;
+ }
+
+ //! finds first occurrence of a character of a list in string
+ /** \param c: List of strings to find. For example if the method
+ should find the first occurance of 'a' or 'b', this parameter should be "ab".
+ \param count: Amount of characters in the list. Ususally,
+ this should be strlen(ofParameter1)
+ \return Returns position where one of the character has been found,
+ or -1 if not found. */
+ s32 findFirstChar(T* c, int count) const
+ {
+ for (s32 i=0; i<used; ++i)
+ for (int j=0; j<count; ++j)
+ if (array[i] == c[j])
+ return i;
+
+ return -1;
+ }
+
+
+ //! Finds first position of a character not in a given list.
+ /** \param c: List of characters not to find. For example if the method
+ should find the first occurance of a character not 'a' or 'b', this parameter should be "ab".
+ \param count: Amount of characters in the list. Ususally,
+ this should be strlen(ofParameter1)
+ \return Returns position where the character has been found,
+ or -1 if not found. */
+ template <class B>
+ s32 findFirstCharNotInList(B* c, int count) const
+ {
+ for (int i=0; i<used; ++i)
+ {
+ int j;
+ for (j=0; j<count; ++j)
+ if (array[i] == c[j])
+ break;
+
+ if (j==count)
+ return i;
+ }
+
+ return -1;
+ }
+
+ //! Finds last position of a character not in a given list.
+ /** \param c: List of characters not to find. For example if the method
+ should find the first occurance of a character not 'a' or 'b', this parameter should be "ab".
+ \param count: Amount of characters in the list. Ususally,
+ this should be strlen(ofParameter1)
+ \return Returns position where the character has been found,
+ or -1 if not found. */
+ template <class B>
+ s32 findLastCharNotInList(B* c, int count) const
+ {
+ for (int i=used-2; i>=0; --i)
+ {
+ int j;
+ for (j=0; j<count; ++j)
+ if (array[i] == c[j])
+ break;
+
+ if (j==count)
+ return i;
+ }
+
+ return -1;
+ }
+
+ //! finds next occurrence of character in string
+ /** \param c: Character to search for.
+ \param startPos: Position in string to start searching.
+ \return Returns position where the character has been found,
+ or -1 if not found. */
+ s32 findNext(T c, s32 startPos) const
+ {
+ for (s32 i=startPos; i<used; ++i)
+ if (array[i] == c)
+ return i;
+
+ return -1;
+ }
+
+
+ //! finds last occurrence of character in string
+ //! \param c: Character to search for.
+ //! \return Returns position where the character has been found,
+ //! or -1 if not found.
+ s32 findLast(T c) const
+ {
+ for (s32 i=used-1; i>=0; --i)
+ if (array[i] == c)
+ return i;
+
+ return -1;
+ }
+
+
+ //! Returns a substring
+ //! \param begin: Start of substring.
+ //! \param length: Length of substring.
+ string<T> subString(s32 begin, s32 length)
+ {
+ if (length <= 0)
+ return string<T>("");
+
+ string<T> o;
+ o.reserve(length+1);
+
+ for (s32 i=0; i<length; ++i)
+ o.array[i] = array[i+begin];
+
+ o.array[length] = 0;
+ o.used = o.allocated;
+
+ return o;
+ }
+
+
+ void operator += (T c)
+ {
+ append(c);
+ }
+
+ void operator += (const string<T>& other)
+ {
+ append(other);
+ }
+
+ void operator += (int i)
+ {
+ append(string<T>(i));
+ }
+
+ //! replaces all characters of a special type with another one
+ void replace(T toReplace, T replaceWith)
+ {
+ for (s32 i=0; i<used; ++i)
+ if (array[i] == toReplace)
+ array[i] = replaceWith;
+ }
+
+ //! trims the string.
+ /** Removes whitespace from begin and end of the string. */
+ void trim()
+ {
+ const char whitespace[] = " \t\n";
+ const int whitespacecount = 3;
+
+ // find start and end of real string without whitespace
+ int begin = findFirstCharNotInList(whitespace, whitespacecount);
+ if (begin == -1)
+ return;
+
+ int end = findLastCharNotInList(whitespace, whitespacecount);
+ if (end == -1)
+ return;
+
+ *this = subString(begin, (end +1) - begin);
+ }
+
+
+ //! Erases a character from the string. May be slow, because all elements
+ //! following after the erased element have to be copied.
+ //! \param index: Index of element to be erased.
+ void erase(int index)
+ {
+ _IRR_DEBUG_BREAK_IF(index>=used || index<0) // access violation
+
+ for (int i=index+1; i<used; ++i)
+ array[i-1] = array[i];
+
+ --used;
+ }
+
+
+
+private:
+
+ //! Returns a character converted to lower case
+ T toLower(const T& t) const
+ {
+ if (t>=(T)'A' && t<=(T)'Z')
+ return t + ((T)'a' - (T)'A');
+ else
+ return t;
+ }
+
+ //! Reallocate the array, make it bigger or smaler
+ void reallocate(s32 new_size)
+ {
+ T* old_array = array;
+
+ array = new T[new_size];
+ allocated = new_size;
+
+ s32 amount = used < new_size ? used : new_size;
+ for (s32 i=0; i<amount; ++i)
+ array[i] = old_array[i];
+
+ if (allocated < used)
+ used = allocated;
+
+ delete [] old_array;
+ }
+
+
+ //--- member variables
+
+ T* array;
+ s32 allocated;
+ s32 used;
+};
+
+
+//! Typedef for character strings
+typedef string<irr::c8> stringc;
+
+//! Typedef for wide character strings
+typedef string<wchar_t> stringw;
+
+} // end namespace core
+} // end namespace irr
+
+#endif
+
diff --git a/src/3rdparty/assimp/contrib/irrXML/irrTypes.h b/src/3rdparty/assimp/contrib/irrXML/irrTypes.h
new file mode 100644
index 000000000..0911a2d7c
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/irrXML/irrTypes.h
@@ -0,0 +1,108 @@
+// Copyright (C) 2002-2005 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#ifndef __IRR_TYPES_H_INCLUDED__
+#define __IRR_TYPES_H_INCLUDED__
+
+namespace irr
+{
+
+//! 8 bit unsigned variable.
+/** This is a typedef for unsigned char, it ensures portability of the engine. */
+typedef unsigned char u8;
+
+//! 8 bit signed variable.
+/** This is a typedef for signed char, it ensures portability of the engine. */
+typedef signed char s8;
+
+//! 8 bit character variable.
+/** This is a typedef for char, it ensures portability of the engine. */
+typedef char c8;
+
+
+
+//! 16 bit unsigned variable.
+/** This is a typedef for unsigned short, it ensures portability of the engine. */
+typedef unsigned short u16;
+
+//! 16 bit signed variable.
+/** This is a typedef for signed short, it ensures portability of the engine. */
+typedef signed short s16;
+
+
+
+//! 32 bit unsigned variable.
+/** This is a typedef for unsigned int, it ensures portability of the engine. */
+typedef unsigned int u32;
+
+//! 32 bit signed variable.
+/** This is a typedef for signed int, it ensures portability of the engine. */
+typedef signed int s32;
+
+
+
+// 64 bit signed variable.
+// This is a typedef for __int64, it ensures portability of the engine.
+// This type is currently not used by the engine and not supported by compilers
+// other than Microsoft Compilers, so it is outcommented.
+//typedef __int64 s64;
+
+
+
+//! 32 bit floating point variable.
+/** This is a typedef for float, it ensures portability of the engine. */
+typedef float f32;
+
+//! 64 bit floating point variable.
+/** This is a typedef for double, it ensures portability of the engine. */
+typedef double f64;
+
+
+} // end namespace
+
+
+// define the wchar_t type if not already built in.
+#ifdef _MSC_VER
+#ifndef _WCHAR_T_DEFINED
+//! A 16 bit wide character type.
+/**
+ Defines the wchar_t-type.
+ In VS6, its not possible to tell
+ the standard compiler to treat wchar_t as a built-in type, and
+ sometimes we just don't want to include the huge stdlib.h or wchar.h,
+ so we'll use this.
+*/
+typedef unsigned short wchar_t;
+#define _WCHAR_T_DEFINED
+#endif // wchar is not defined
+#endif // microsoft compiler
+
+//! define a break macro for debugging only in Win32 mode.
+// WORKAROUND (ASSIMP): remove __asm
+#if defined(WIN32) && defined(_MSC_VER) && defined(_DEBUG)
+#if defined(_M_IX86)
+#define _IRR_DEBUG_BREAK_IF( _CONDITION_ ) /*if (_CONDITION_) {_asm int 3}*/
+#else
+#define _IRR_DEBUG_BREAK_IF( _CONDITION_ )
+#endif
+#else
+#define _IRR_DEBUG_BREAK_IF( _CONDITION_ )
+#endif
+
+//! Defines a small statement to work around a microsoft compiler bug.
+/** The microsft compiler 7.0 - 7.1 has a bug:
+When you call unmanaged code that returns a bool type value of false from managed code,
+the return value may appear as true. See
+http://support.microsoft.com/default.aspx?kbid=823071 for details.
+Compiler version defines: VC6.0 : 1200, VC7.0 : 1300, VC7.1 : 1310, VC8.0 : 1400*/
+
+// WORKAROUND (ASSIMP): remove __asm
+#if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER > 1299) && (_MSC_VER < 1400)
+#define _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX /*__asm mov eax,100*/
+#else
+#define _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX
+#endif // _IRR_MANAGED_MARSHALLING_BUGFIX
+
+#endif // __IRR_TYPES_H_INCLUDED__
+
diff --git a/src/3rdparty/assimp/contrib/irrXML/irrXML.cpp b/src/3rdparty/assimp/contrib/irrXML/irrXML.cpp
new file mode 100644
index 000000000..406408f0a
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/irrXML/irrXML.cpp
@@ -0,0 +1,152 @@
+// Copyright (C) 2002-2005 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/or irrXML.h
+
+// Need to include Assimp, too. We're using Assimp's version of fast_atof
+// so we need stdint.h. But no PCH.
+
+#include "./../../code/AssimpPCH.h"
+
+#include "irrXML.h"
+#include "irrString.h"
+#include "irrArray.h"
+#include "./../../code/fast_atof.h"
+#include "CXMLReaderImpl.h"
+
+namespace irr
+{
+namespace io
+{
+
+//! Implementation of the file read callback for ordinary files
+class CFileReadCallBack : public IFileReadCallBack
+{
+public:
+
+ //! construct from filename
+ CFileReadCallBack(const char* filename)
+ : File(0), Size(0), Close(true)
+ {
+ // open file
+ File = fopen(filename, "rb");
+
+ if (File)
+ getFileSize();
+ }
+
+ //! construct from FILE pointer
+ CFileReadCallBack(FILE* file)
+ : File(file), Size(0), Close(false)
+ {
+ if (File)
+ getFileSize();
+ }
+
+ //! destructor
+ virtual ~CFileReadCallBack()
+ {
+ if (Close && File)
+ fclose(File);
+ }
+
+ //! Reads an amount of bytes from the file.
+ virtual int read(void* buffer, int sizeToRead)
+ {
+ if (!File)
+ return 0;
+
+ return (int)fread(buffer, 1, sizeToRead, File);
+ }
+
+ //! Returns size of file in bytes
+ virtual int getSize()
+ {
+ return Size;
+ }
+
+private:
+
+ //! retrieves the file size of the open file
+ void getFileSize()
+ {
+ fseek(File, 0, SEEK_END);
+ Size = ftell(File);
+ fseek(File, 0, SEEK_SET);
+ }
+
+ FILE* File;
+ int Size;
+ bool Close;
+
+}; // end class CFileReadCallBack
+
+
+
+// FACTORY FUNCTIONS:
+
+
+//! Creates an instance of an UFT-8 or ASCII character xml parser.
+IrrXMLReader* createIrrXMLReader(const char* filename)
+{
+ return new CXMLReaderImpl<char, IXMLBase>(new CFileReadCallBack(filename));
+}
+
+
+//! Creates an instance of an UFT-8 or ASCII character xml parser.
+IrrXMLReader* createIrrXMLReader(FILE* file)
+{
+ return new CXMLReaderImpl<char, IXMLBase>(new CFileReadCallBack(file));
+}
+
+
+//! Creates an instance of an UFT-8 or ASCII character xml parser.
+IrrXMLReader* createIrrXMLReader(IFileReadCallBack* callback)
+{
+ return new CXMLReaderImpl<char, IXMLBase>(callback, false);
+}
+
+
+//! Creates an instance of an UTF-16 xml parser.
+IrrXMLReaderUTF16* createIrrXMLReaderUTF16(const char* filename)
+{
+ return new CXMLReaderImpl<char16, IXMLBase>(new CFileReadCallBack(filename));
+}
+
+
+//! Creates an instance of an UTF-16 xml parser.
+IrrXMLReaderUTF16* createIrrXMLReaderUTF16(FILE* file)
+{
+ return new CXMLReaderImpl<char16, IXMLBase>(new CFileReadCallBack(file));
+}
+
+
+//! Creates an instance of an UTF-16 xml parser.
+IrrXMLReaderUTF16* createIrrXMLReaderUTF16(IFileReadCallBack* callback)
+{
+ return new CXMLReaderImpl<char16, IXMLBase>(callback, false);
+}
+
+
+//! Creates an instance of an UTF-32 xml parser.
+IrrXMLReaderUTF32* createIrrXMLReaderUTF32(const char* filename)
+{
+ return new CXMLReaderImpl<char32, IXMLBase>(new CFileReadCallBack(filename));
+}
+
+
+//! Creates an instance of an UTF-32 xml parser.
+IrrXMLReaderUTF32* createIrrXMLReaderUTF32(FILE* file)
+{
+ return new CXMLReaderImpl<char32, IXMLBase>(new CFileReadCallBack(file));
+}
+
+
+//! Creates an instance of an UTF-32 xml parser.
+IrrXMLReaderUTF32* createIrrXMLReaderUTF32(IFileReadCallBack* callback)
+{
+ return new CXMLReaderImpl<char32, IXMLBase>(callback, false);
+}
+
+
+} // end namespace io
+} // end namespace irr
diff --git a/src/3rdparty/assimp/contrib/irrXML/irrXML.h b/src/3rdparty/assimp/contrib/irrXML/irrXML.h
new file mode 100644
index 000000000..e5fcbe083
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/irrXML/irrXML.h
@@ -0,0 +1,540 @@
+// Copyright (C) 2002-2005 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/or irrXML.h
+
+#ifndef __IRR_XML_H_INCLUDED__
+#define __IRR_XML_H_INCLUDED__
+
+#include <stdio.h>
+
+/** \mainpage irrXML 1.2 API documentation
+ <div align="center"><img src="logobig.png" ></div>
+
+ \section intro Introduction
+
+ Welcome to the irrXML API documentation.
+ Here you'll find any information you'll need to develop applications with
+ irrXML. If you look for a tutorial on how to start, take a look at the \ref irrxmlexample,
+ at the homepage of irrXML at <A HREF="http://xml.irrlicht3d.org" >xml.irrlicht3d.org</A>
+ or into the SDK in the directory \example.
+
+ irrXML is intended to be a high speed and easy-to-use XML Parser for C++, and
+ this documentation is an important part of it. If you have any questions or
+ suggestions, just send a email to the author of the engine, Nikolaus Gebhardt
+ (niko (at) irrlicht3d.org). For more informations about this parser, see \ref history.
+
+ \section features Features
+
+ irrXML provides forward-only, read-only
+ access to a stream of non validated XML data. It was fully implemented by
+ Nikolaus Gebhardt. Its current features are:
+
+ - It it fast as lighting and has very low memory usage. It was
+ developed with the intention of being used in 3D games, as it already has been.
+ - irrXML is very small: It only consists of 60 KB of code and can be added easily
+ to your existing project.
+ - Of course, it is platform independent and works with lots of compilers.
+ - It is able to parse ASCII, UTF-8, UTF-16 and UTF-32 text files, both in
+ little and big endian format.
+ - Independent of the input file format, the parser can return all strings in ASCII, UTF-8,
+ UTF-16 and UTF-32 format.
+ - With its optional file access abstraction it has the advantage that it can read not
+ only from files but from any type of data (memory, network, ...). For example when
+ used with the Irrlicht Engine, it directly reads from compressed .zip files.
+ - Just like the Irrlicht Engine for which it was originally created, it is extremely easy
+ to use.
+ - It has no external dependencies, it does not even need the STL.
+
+ Although irrXML has some strenghts, it currently also has the following limitations:
+
+ - The input xml file is not validated and assumed to be correct.
+
+ \section irrxmlexample Example
+
+ The following code demonstrates the basic usage of irrXML. A simple xml
+ file like this is parsed:
+ \code
+ <?xml version="1.0"?>
+ <config>
+ <!-- This is a config file for the mesh viewer -->
+ <model file="dwarf.dea" />
+ <messageText caption="Irrlicht Engine Mesh Viewer">
+ Welcome to the Mesh Viewer of the &quot;Irrlicht Engine&quot;.
+ </messageText>
+ </config>
+ \endcode
+
+ The code for parsing this file would look like this:
+ \code
+ #include <irrXML.h>
+ using namespace irr; // irrXML is located in the namespace irr::io
+ using namespace io;
+
+ #include <string> // we use STL strings to store data in this example
+
+ void main()
+ {
+ // create the reader using one of the factory functions
+
+ IrrXMLReader* xml = createIrrXMLReader("config.xml");
+
+ // strings for storing the data we want to get out of the file
+ std::string modelFile;
+ std::string messageText;
+ std::string caption;
+
+ // parse the file until end reached
+
+ while (xml && xml->read())
+ {
+ switch(xml->getNodeType())
+ {
+ case EXN_TEXT:
+ // in this xml file, the only text which occurs is the messageText
+ messageText = xml->getNodeData();
+ break;
+ case EXN_ELEMENT:
+ {
+ if (!strcmp("model", xml->getNodeName()))
+ modelFile = xml->getAttributeValue("file");
+ else
+ if (!strcmp("messageText", xml->getNodeName()))
+ caption = xml->getAttributeValue("caption");
+ }
+ break;
+ }
+ }
+
+ // delete the xml parser after usage
+ delete xml;
+ }
+ \endcode
+
+ \section howto How to use
+
+ Simply add the source files in the /src directory of irrXML to your project. Done.
+
+ \section license License
+
+ The irrXML license is based on the zlib license. Basicly, this means you can do with
+ irrXML whatever you want:
+
+ Copyright (C) 2002-2005 Nikolaus Gebhardt
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
+ \section history History
+
+ As lots of references in this documentation and the source show, this xml
+ parser has originally been a part of the
+ <A HREF="http://irrlicht.sourceforge.net" >Irrlicht Engine</A>. But because
+ the parser has become very useful with the latest release, people asked for a
+ separate version of it, to be able to use it in non Irrlicht projects. With
+ irrXML 1.0, this has now been done.
+*/
+
+namespace irr
+{
+namespace io
+{
+ //! Enumeration of all supported source text file formats
+ enum ETEXT_FORMAT
+ {
+ //! ASCII, file without byte order mark, or not a text file
+ ETF_ASCII,
+
+ //! UTF-8 format
+ ETF_UTF8,
+
+ //! UTF-16 format, big endian
+ ETF_UTF16_BE,
+
+ //! UTF-16 format, little endian
+ ETF_UTF16_LE,
+
+ //! UTF-32 format, big endian
+ ETF_UTF32_BE,
+
+ //! UTF-32 format, little endian
+ ETF_UTF32_LE
+ };
+
+
+ //! Enumeration for all xml nodes which are parsed by IrrXMLReader
+ enum EXML_NODE
+ {
+ //! No xml node. This is usually the node if you did not read anything yet.
+ EXN_NONE,
+
+ //! A xml element, like <foo>
+ EXN_ELEMENT,
+
+ //! End of an xml element, like </foo>
+ EXN_ELEMENT_END,
+
+ //! Text within a xml element: <foo> this is the text. </foo>
+ EXN_TEXT,
+
+ //! An xml comment like &lt;!-- I am a comment --&gt; or a DTD definition.
+ EXN_COMMENT,
+
+ //! An xml cdata section like &lt;![CDATA[ this is some CDATA ]]&gt;
+ EXN_CDATA,
+
+ //! Unknown element.
+ EXN_UNKNOWN
+ };
+
+ //! Callback class for file read abstraction.
+ /** With this, it is possible to make the xml parser read in other things
+ than just files. The Irrlicht engine is using this for example to
+ read xml from compressed .zip files. To make the parser read in
+ any other data, derive a class from this interface, implement the
+ two methods to read your data and give a pointer to an instance of
+ your implementation when calling createIrrXMLReader(),
+ createIrrXMLReaderUTF16() or createIrrXMLReaderUTF32() */
+ class IFileReadCallBack
+ {
+ public:
+
+ //! virtual destructor
+ virtual ~IFileReadCallBack() {};
+
+ //! Reads an amount of bytes from the file.
+ /** \param buffer: Pointer to buffer where to read bytes will be written to.
+ \param sizeToRead: Amount of bytes to read from the file.
+ \return Returns how much bytes were read. */
+ virtual int read(void* buffer, int sizeToRead) = 0;
+
+ //! Returns size of file in bytes
+ virtual int getSize() = 0;
+ };
+
+ //! Empty class to be used as parent class for IrrXMLReader.
+ /** If you need another class as base class for the xml reader, you can do this by creating
+ the reader using for example new CXMLReaderImpl<char, YourBaseClass>(yourcallback);
+ The Irrlicht Engine for example needs IUnknown as base class for every object to
+ let it automaticly reference countend, hence it replaces IXMLBase with IUnknown.
+ See irrXML.cpp on how this can be done in detail. */
+ class IXMLBase
+ {
+ };
+
+ //! Interface providing easy read access to a XML file.
+ /** You can create an instance of this reader using one of the factory functions
+ createIrrXMLReader(), createIrrXMLReaderUTF16() and createIrrXMLReaderUTF32().
+ If using the parser from the Irrlicht Engine, please use IFileSystem::createXMLReader()
+ instead.
+ For a detailed intro how to use the parser, see \ref irrxmlexample and \ref features.
+
+ The typical usage of this parser looks like this:
+ \code
+ #include <irrXML.h>
+ using namespace irr; // irrXML is located in the namespace irr::io
+ using namespace io;
+
+ void main()
+ {
+ // create the reader using one of the factory functions
+ IrrXMLReader* xml = createIrrXMLReader("config.xml");
+
+ if (xml == 0)
+ return; // file could not be opened
+
+ // parse the file until end reached
+ while (xml->read())
+ {
+ // based on xml->getNodeType(), do something.
+ }
+
+ // delete the xml parser after usage
+ delete xml;
+ }
+ \endcode
+ See \ref irrxmlexample for a more detailed example.
+ */
+ template<class char_type, class super_class>
+ class IIrrXMLReader : public super_class
+ {
+ public:
+
+ //! Destructor
+ virtual ~IIrrXMLReader() {};
+
+ //! Reads forward to the next xml node.
+ /** \return Returns false, if there was no further node. */
+ virtual bool read() = 0;
+
+ //! Returns the type of the current XML node.
+ virtual EXML_NODE getNodeType() const = 0;
+
+ //! Returns attribute count of the current XML node.
+ /** This is usually
+ non null if the current node is EXN_ELEMENT, and the element has attributes.
+ \return Returns amount of attributes of this xml node. */
+ virtual int getAttributeCount() const = 0;
+
+ //! Returns name of an attribute.
+ /** \param idx: Zero based index, should be something between 0 and getAttributeCount()-1.
+ \return Name of the attribute, 0 if an attribute with this index does not exist. */
+ virtual const char_type* getAttributeName(int idx) const = 0;
+
+ //! Returns the value of an attribute.
+ /** \param idx: Zero based index, should be something between 0 and getAttributeCount()-1.
+ \return Value of the attribute, 0 if an attribute with this index does not exist. */
+ virtual const char_type* getAttributeValue(int idx) const = 0;
+
+ //! Returns the value of an attribute.
+ /** \param name: Name of the attribute.
+ \return Value of the attribute, 0 if an attribute with this name does not exist. */
+ virtual const char_type* getAttributeValue(const char_type* name) const = 0;
+
+ //! Returns the value of an attribute in a safe way.
+ /** Like getAttributeValue(), but does not
+ return 0 if the attribute does not exist. An empty string ("") is returned then.
+ \param name: Name of the attribute.
+ \return Value of the attribute, and "" if an attribute with this name does not exist */
+ virtual const char_type* getAttributeValueSafe(const char_type* name) const = 0;
+
+ //! Returns the value of an attribute as integer.
+ /** \param name Name of the attribute.
+ \return Value of the attribute as integer, and 0 if an attribute with this name does not exist or
+ the value could not be interpreted as integer. */
+ virtual int getAttributeValueAsInt(const char_type* name) const = 0;
+
+ //! Returns the value of an attribute as integer.
+ /** \param idx: Zero based index, should be something between 0 and getAttributeCount()-1.
+ \return Value of the attribute as integer, and 0 if an attribute with this index does not exist or
+ the value could not be interpreted as integer. */
+ virtual int getAttributeValueAsInt(int idx) const = 0;
+
+ //! Returns the value of an attribute as float.
+ /** \param name: Name of the attribute.
+ \return Value of the attribute as float, and 0 if an attribute with this name does not exist or
+ the value could not be interpreted as float. */
+ virtual float getAttributeValueAsFloat(const char_type* name) const = 0;
+
+ //! Returns the value of an attribute as float.
+ /** \param idx: Zero based index, should be something between 0 and getAttributeCount()-1.
+ \return Value of the attribute as float, and 0 if an attribute with this index does not exist or
+ the value could not be interpreted as float. */
+ virtual float getAttributeValueAsFloat(int idx) const = 0;
+
+ //! Returns the name of the current node.
+ /** Only non null, if the node type is EXN_ELEMENT.
+ \return Name of the current node or 0 if the node has no name. */
+ virtual const char_type* getNodeName() const = 0;
+
+ //! Returns data of the current node.
+ /** Only non null if the node has some
+ data and it is of type EXN_TEXT or EXN_UNKNOWN. */
+ virtual const char_type* getNodeData() const = 0;
+
+ //! Returns if an element is an empty element, like <foo />
+ virtual bool isEmptyElement() const = 0;
+
+ //! Returns format of the source xml file.
+ /** It is not necessary to use
+ this method because the parser will convert the input file format
+ to the format wanted by the user when creating the parser. This
+ method is useful to get/display additional informations. */
+ virtual ETEXT_FORMAT getSourceFormat() const = 0;
+
+ //! Returns format of the strings returned by the parser.
+ /** This will be UTF8 for example when you created a parser with
+ IrrXMLReaderUTF8() and UTF32 when it has been created using
+ IrrXMLReaderUTF32. It should not be necessary to call this
+ method and only exists for informational purposes. */
+ virtual ETEXT_FORMAT getParserFormat() const = 0;
+ };
+
+
+ //! defines the utf-16 type.
+ /** Not using wchar_t for this because
+ wchar_t has 16 bit on windows and 32 bit on other operating systems. */
+ typedef unsigned short char16;
+
+ //! defines the utf-32 type.
+ /** Not using wchar_t for this because
+ wchar_t has 16 bit on windows and 32 bit on other operating systems. */
+ typedef unsigned long char32;
+
+ //! A UTF-8 or ASCII character xml parser.
+ /** This means that all character data will be returned in 8 bit ASCII or UTF-8 by this parser.
+ The file to read can be in any format, it will be converted to UTF-8 if it is not
+ in this format.
+ Create an instance of this with createIrrXMLReader();
+ See IIrrXMLReader for description on how to use it. */
+ typedef IIrrXMLReader<char, IXMLBase> IrrXMLReader;
+
+ //! A UTF-16 xml parser.
+ /** This means that all character data will be returned in UTF-16 by this parser.
+ The file to read can be in any format, it will be converted to UTF-16 if it is not
+ in this format.
+ Create an instance of this with createIrrXMLReaderUTF16();
+ See IIrrXMLReader for description on how to use it. */
+ typedef IIrrXMLReader<char16, IXMLBase> IrrXMLReaderUTF16;
+
+ //! A UTF-32 xml parser.
+ /** This means that all character data will be returned in UTF-32 by this parser.
+ The file to read can be in any format, it will be converted to UTF-32 if it is not
+ in this format.
+ Create an instance of this with createIrrXMLReaderUTF32();
+ See IIrrXMLReader for description on how to use it. */
+ typedef IIrrXMLReader<char32, IXMLBase> IrrXMLReaderUTF32;
+
+
+ //! Creates an instance of an UFT-8 or ASCII character xml parser.
+ /** This means that all character data will be returned in 8 bit ASCII or UTF-8.
+ The file to read can be in any format, it will be converted to UTF-8 if it is not in this format.
+ If you are using the Irrlicht Engine, it is better not to use this function but
+ IFileSystem::createXMLReaderUTF8() instead.
+ \param filename: Name of file to be opened.
+ \return Returns a pointer to the created xml parser. This pointer should be
+ deleted using 'delete' after no longer needed. Returns 0 if an error occured
+ and the file could not be opened. */
+ IrrXMLReader* createIrrXMLReader(const char* filename);
+
+ //! Creates an instance of an UFT-8 or ASCII character xml parser.
+ /** This means that all character data will be returned in 8 bit ASCII or UTF-8. The file to read can
+ be in any format, it will be converted to UTF-8 if it is not in this format.
+ If you are using the Irrlicht Engine, it is better not to use this function but
+ IFileSystem::createXMLReaderUTF8() instead.
+ \param file: Pointer to opened file, must have been opened in binary mode, e.g.
+ using fopen("foo.bar", "wb"); The file will not be closed after it has been read.
+ \return Returns a pointer to the created xml parser. This pointer should be
+ deleted using 'delete' after no longer needed. Returns 0 if an error occured
+ and the file could not be opened. */
+ IrrXMLReader* createIrrXMLReader(FILE* file);
+
+ //! Creates an instance of an UFT-8 or ASCII character xml parser.
+ /** This means that all character data will be returned in 8 bit ASCII or UTF-8. The file to read can
+ be in any format, it will be converted to UTF-8 if it is not in this format.
+ If you are using the Irrlicht Engine, it is better not to use this function but
+ IFileSystem::createXMLReaderUTF8() instead.
+ \param callback: Callback for file read abstraction. Implement your own
+ callback to make the xml parser read in other things than just files. See
+ IFileReadCallBack for more information about this.
+ \return Returns a pointer to the created xml parser. This pointer should be
+ deleted using 'delete' after no longer needed. Returns 0 if an error occured
+ and the file could not be opened. */
+ IrrXMLReader* createIrrXMLReader(IFileReadCallBack* callback);
+
+ //! Creates an instance of an UFT-16 xml parser.
+ /** This means that
+ all character data will be returned in UTF-16. The file to read can
+ be in any format, it will be converted to UTF-16 if it is not in this format.
+ If you are using the Irrlicht Engine, it is better not to use this function but
+ IFileSystem::createXMLReader() instead.
+ \param filename: Name of file to be opened.
+ \return Returns a pointer to the created xml parser. This pointer should be
+ deleted using 'delete' after no longer needed. Returns 0 if an error occured
+ and the file could not be opened. */
+ IrrXMLReaderUTF16* createIrrXMLReaderUTF16(const char* filename);
+
+ //! Creates an instance of an UFT-16 xml parser.
+ /** This means that all character data will be returned in UTF-16. The file to read can
+ be in any format, it will be converted to UTF-16 if it is not in this format.
+ If you are using the Irrlicht Engine, it is better not to use this function but
+ IFileSystem::createXMLReader() instead.
+ \param file: Pointer to opened file, must have been opened in binary mode, e.g.
+ using fopen("foo.bar", "wb"); The file will not be closed after it has been read.
+ \return Returns a pointer to the created xml parser. This pointer should be
+ deleted using 'delete' after no longer needed. Returns 0 if an error occured
+ and the file could not be opened. */
+ IrrXMLReaderUTF16* createIrrXMLReaderUTF16(FILE* file);
+
+ //! Creates an instance of an UFT-16 xml parser.
+ /** This means that all character data will be returned in UTF-16. The file to read can
+ be in any format, it will be converted to UTF-16 if it is not in this format.
+ If you are using the Irrlicht Engine, it is better not to use this function but
+ IFileSystem::createXMLReader() instead.
+ \param callback: Callback for file read abstraction. Implement your own
+ callback to make the xml parser read in other things than just files. See
+ IFileReadCallBack for more information about this.
+ \return Returns a pointer to the created xml parser. This pointer should be
+ deleted using 'delete' after no longer needed. Returns 0 if an error occured
+ and the file could not be opened. */
+ IrrXMLReaderUTF16* createIrrXMLReaderUTF16(IFileReadCallBack* callback);
+
+
+ //! Creates an instance of an UFT-32 xml parser.
+ /** This means that all character data will be returned in UTF-32. The file to read can
+ be in any format, it will be converted to UTF-32 if it is not in this format.
+ If you are using the Irrlicht Engine, it is better not to use this function but
+ IFileSystem::createXMLReader() instead.
+ \param filename: Name of file to be opened.
+ \return Returns a pointer to the created xml parser. This pointer should be
+ deleted using 'delete' after no longer needed. Returns 0 if an error occured
+ and the file could not be opened. */
+ IrrXMLReaderUTF32* createIrrXMLReaderUTF32(const char* filename);
+
+ //! Creates an instance of an UFT-32 xml parser.
+ /** This means that all character data will be returned in UTF-32. The file to read can
+ be in any format, it will be converted to UTF-32 if it is not in this format.
+ if you are using the Irrlicht Engine, it is better not to use this function but
+ IFileSystem::createXMLReader() instead.
+ \param file: Pointer to opened file, must have been opened in binary mode, e.g.
+ using fopen("foo.bar", "wb"); The file will not be closed after it has been read.
+ \return Returns a pointer to the created xml parser. This pointer should be
+ deleted using 'delete' after no longer needed. Returns 0 if an error occured
+ and the file could not be opened. */
+ IrrXMLReaderUTF32* createIrrXMLReaderUTF32(FILE* file);
+
+ //! Creates an instance of an UFT-32 xml parser.
+ /** This means that
+ all character data will be returned in UTF-32. The file to read can
+ be in any format, it will be converted to UTF-32 if it is not in this format.
+ If you are using the Irrlicht Engine, it is better not to use this function but
+ IFileSystem::createXMLReader() instead.
+ \param callback: Callback for file read abstraction. Implement your own
+ callback to make the xml parser read in other things than just files. See
+ IFileReadCallBack for more information about this.
+ \return Returns a pointer to the created xml parser. This pointer should be
+ deleted using 'delete' after no longer needed. Returns 0 if an error occured
+ and the file could not be opened. */
+ IrrXMLReaderUTF32* createIrrXMLReaderUTF32(IFileReadCallBack* callback);
+
+
+ /*! \file irrxml.h
+ \brief Header file of the irrXML, the Irrlicht XML parser.
+
+ This file includes everything needed for using irrXML,
+ the XML parser of the Irrlicht Engine. To use irrXML,
+ you only need to include this file in your project:
+
+ \code
+ #include <irrXML.h>
+ \endcode
+
+ It is also common to use the two namespaces in which irrXML is included,
+ directly after #including irrXML.h:
+
+ \code
+ #include <irrXML.h>
+ using namespace irr;
+ using namespace io;
+ \endcode
+ */
+
+} // end namespace io
+} // end namespace irr
+
+#endif // __IRR_XML_H_INCLUDED__
+
diff --git a/src/3rdparty/assimp/contrib/unzip/crypt.h b/src/3rdparty/assimp/contrib/unzip/crypt.h
new file mode 100644
index 000000000..03a2230f5
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/unzip/crypt.h
@@ -0,0 +1,137 @@
+/* crypt.h -- base code for crypt/uncrypt ZIPfile
+
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This code is a modified version of crypting code in Infozip distribution
+
+ The encryption/decryption parts of this source code (as opposed to the
+ non-echoing password parts) were originally written in Europe. The
+ whole source package can be freely distributed, including from the USA.
+ (Prior to January 2000, re-export from the US was a violation of US law.)
+
+ This encryption code is a direct transcription of the algorithm from
+ Roger Schlafly, described by Phil Katz in the file appnote.txt. This
+ file (appnote.txt) is distributed with the PKZIP program (even in the
+ version without encryption capabilities).
+
+ If you don't need crypting in your application, just define symbols
+ NOCRYPT and NOUNCRYPT.
+
+ This code support the "Traditional PKWARE Encryption".
+
+ The new AES encryption added on Zip format by Winzip (see the page
+ http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong
+ Encryption is not supported.
+*/
+
+#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8))
+
+#define Q_UNUSED(x) (void)x;
+
+/***********************************************************************
+ * Return the next byte in the pseudo-random sequence
+ */
+static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab)
+{
+ unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an
+ * unpredictable manner on 16-bit systems; not a problem
+ * with any known compiler so far, though */
+ Q_UNUSED(pcrc_32_tab);
+
+ temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2;
+ return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
+}
+
+/***********************************************************************
+ * Update the encryption keys with the next byte of plain text
+ */
+static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c)
+{
+ Q_UNUSED(pcrc_32_tab);
+
+ (*(pkeys+0)) = CRC32((*(pkeys+0)), c);
+ (*(pkeys+1)) += (*(pkeys+0)) & 0xff;
+ (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1;
+ {
+ register int keyshift = (int)((*(pkeys+1)) >> 24);
+ (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift);
+ }
+ return c;
+}
+
+
+/***********************************************************************
+ * Initialize the encryption keys and the random header according to
+ * the given password.
+ */
+static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab)
+{
+ *(pkeys+0) = 305419896L;
+ *(pkeys+1) = 591751049L;
+ *(pkeys+2) = 878082192L;
+ while (*passwd != '\0') {
+ update_keys(pkeys,pcrc_32_tab,(int)*passwd);
+ passwd++;
+ }
+}
+
+#define zdecode(pkeys,pcrc_32_tab,c) \
+ (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab)))
+
+#define zencode(pkeys,pcrc_32_tab,c,t) \
+ (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c))
+
+#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED
+
+#define RAND_HEAD_LEN 12
+ /* "last resort" source for second part of crypt seed pattern */
+# ifndef ZCR_SEED2
+# define ZCR_SEED2 3141592654UL /* use PI as default pattern */
+# endif
+
+static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting)
+ const char *passwd; /* password string */
+ unsigned char *buf; /* where to write header */
+ int bufSize;
+ unsigned long* pkeys;
+ const unsigned long* pcrc_32_tab;
+ unsigned long crcForCrypting;
+{
+ int n; /* index in random header */
+ int t; /* temporary */
+ int c; /* random byte */
+ unsigned char header[RAND_HEAD_LEN-2]; /* random header */
+ static unsigned calls = 0; /* ensure different random header each time */
+
+ if (bufSize<RAND_HEAD_LEN)
+ return 0;
+
+ /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the
+ * output of rand() to get less predictability, since rand() is
+ * often poorly implemented.
+ */
+ if (++calls == 1)
+ {
+ srand((unsigned)(time(NULL) ^ ZCR_SEED2));
+ }
+ init_keys(passwd, pkeys, pcrc_32_tab);
+ for (n = 0; n < RAND_HEAD_LEN-2; n++)
+ {
+ c = (rand() >> 7) & 0xff;
+ header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t);
+ }
+ /* Encrypt random header (last two bytes is high word of crc) */
+ init_keys(passwd, pkeys, pcrc_32_tab);
+ for (n = 0; n < RAND_HEAD_LEN-2; n++)
+ {
+ buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t);
+ }
+ buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t);
+ buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t);
+ return n;
+}
+
+#endif
diff --git a/src/3rdparty/assimp/contrib/unzip/ioapi.c b/src/3rdparty/assimp/contrib/unzip/ioapi.c
new file mode 100644
index 000000000..d062a7b12
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/unzip/ioapi.c
@@ -0,0 +1,169 @@
+/* ioapi.c -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define Q_UNUSED(x) (void)x;
+
+# ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+# include <zlib.h>
+# else
+# include "../zlib/zlib.h"
+# endif
+#include "ioapi.h"
+
+
+
+/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+voidpf ZCALLBACK fopen_file_func (
+ voidpf opaque,
+ const char* filename,
+ int mode);
+
+uLong ZCALLBACK fread_file_func (
+ voidpf opaque,
+ voidpf stream,
+ void* buf,
+ uLong size);
+
+uLong ZCALLBACK fwrite_file_func (
+ voidpf opaque,
+ voidpf stream,
+ const void* buf,
+ uLong size);
+
+long ZCALLBACK ftell_file_func (
+ voidpf opaque,
+ voidpf stream);
+
+long ZCALLBACK fseek_file_func (
+ voidpf opaque,
+ voidpf stream,
+ uLong offset,
+ int origin);
+
+int ZCALLBACK fclose_file_func (
+ voidpf opaque,
+ voidpf stream);
+
+int ZCALLBACK ferror_file_func (
+ voidpf opaque,
+ voidpf stream);
+
+
+voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode)
+{
+ FILE* file = NULL;
+ const char* mode_fopen = NULL;
+ Q_UNUSED(opaque);
+ if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
+ mode_fopen = "rb";
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
+ mode_fopen = "r+b";
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_CREATE)
+ mode_fopen = "wb";
+
+ if ((filename!=NULL) && (mode_fopen != NULL))
+ file = fopen(filename, mode_fopen);
+ return file;
+}
+
+
+uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size)
+{
+ uLong ret = 0;
+ Q_UNUSED(opaque);
+ ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream);
+ return ret;
+}
+
+
+uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size)
+{
+ uLong ret = 0;
+ Q_UNUSED(opaque);
+ ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream);
+ return ret;
+}
+
+long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream)
+{
+ long ret = 0;
+ Q_UNUSED(opaque);
+ ret = ftell((FILE *)stream);
+ return ret;
+}
+
+long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin)
+{
+ int fseek_origin=0;
+ long ret = 0;
+ Q_UNUSED(opaque);
+ switch (origin)
+ {
+ case ZLIB_FILEFUNC_SEEK_CUR :
+ fseek_origin = SEEK_CUR;
+ break;
+ case ZLIB_FILEFUNC_SEEK_END :
+ fseek_origin = SEEK_END;
+ break;
+ case ZLIB_FILEFUNC_SEEK_SET :
+ fseek_origin = SEEK_SET;
+ break;
+ default: return -1;
+ }
+ ret = 0;
+ fseek((FILE *)stream, offset, fseek_origin);
+ return ret;
+}
+
+int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream)
+{
+ int ret;
+ Q_UNUSED(opaque);
+ ret = fclose((FILE *)stream);
+ return ret;
+}
+
+int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream)
+{
+ int ret;
+ Q_UNUSED(opaque);
+ ret = ferror((FILE *)stream);
+ return ret;
+}
+
+void fill_fopen_filefunc (pzlib_filefunc_def)
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ pzlib_filefunc_def->zopen_file = fopen_file_func;
+ pzlib_filefunc_def->zread_file = fread_file_func;
+ pzlib_filefunc_def->zwrite_file = fwrite_file_func;
+ pzlib_filefunc_def->ztell_file = ftell_file_func;
+ pzlib_filefunc_def->zseek_file = fseek_file_func;
+ pzlib_filefunc_def->zclose_file = fclose_file_func;
+ pzlib_filefunc_def->zerror_file = ferror_file_func;
+ pzlib_filefunc_def->opaque = NULL;
+}
diff --git a/src/3rdparty/assimp/contrib/unzip/ioapi.h b/src/3rdparty/assimp/contrib/unzip/ioapi.h
new file mode 100644
index 000000000..06fdd15e9
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/unzip/ioapi.h
@@ -0,0 +1,75 @@
+/* ioapi.h -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#ifndef _ZLIBIOAPI_H
+#define _ZLIBIOAPI_H
+
+
+#define ZLIB_FILEFUNC_SEEK_CUR (1)
+#define ZLIB_FILEFUNC_SEEK_END (2)
+#define ZLIB_FILEFUNC_SEEK_SET (0)
+
+#define ZLIB_FILEFUNC_MODE_READ (1)
+#define ZLIB_FILEFUNC_MODE_WRITE (2)
+#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3)
+
+#define ZLIB_FILEFUNC_MODE_EXISTING (4)
+#define ZLIB_FILEFUNC_MODE_CREATE (8)
+
+
+#ifndef ZCALLBACK
+
+#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
+#define ZCALLBACK CALLBACK
+#else
+#define ZCALLBACK
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef voidpf (ZCALLBACK *open_file_func) (voidpf opaque, const char* filename, int mode);
+typedef uLong (ZCALLBACK *read_file_func) (voidpf opaque, voidpf stream, void* buf, uLong size);
+typedef uLong (ZCALLBACK *write_file_func)(voidpf opaque, voidpf stream, const void* buf, uLong size);
+typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream);
+typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uLong offset, int origin);
+typedef int (ZCALLBACK *close_file_func) (voidpf opaque, voidpf stream);
+typedef int (ZCALLBACK *testerror_file_func) (voidpf opaque, voidpf stream);
+
+typedef struct zlib_filefunc_def_s
+{
+ open_file_func zopen_file;
+ read_file_func zread_file;
+ write_file_func zwrite_file;
+ tell_file_func ztell_file;
+ seek_file_func zseek_file;
+ close_file_func zclose_file;
+ testerror_file_func zerror_file;
+ voidpf opaque;
+} zlib_filefunc_def;
+
+
+
+void fill_fopen_filefunc (zlib_filefunc_def* pzlib_filefunc_def);
+
+#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size))
+#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size))
+#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream))
+#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode))
+#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream))
+#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream))
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/3rdparty/assimp/contrib/unzip/unzip.c b/src/3rdparty/assimp/contrib/unzip/unzip.c
new file mode 100644
index 000000000..b6c048369
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/unzip/unzip.c
@@ -0,0 +1,1604 @@
+/* unzip.c -- IO for uncompress .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ Read unzip.h for more info
+*/
+
+/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of
+compatibility with older software. The following is from the original crypt.c. Code
+woven in by Terry Thorsen 1/2003.
+*/
+/*
+ Copyright (c) 1990-2000 Info-ZIP. All rights reserved.
+
+ See the accompanying file LICENSE, version 2000-Apr-09 or later
+ (the contents of which are also included in zip.h) for terms of use.
+ If, for some reason, all these files are missing, the Info-ZIP license
+ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
+*/
+/*
+ crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h]
+
+ The encryption/decryption parts of this source code (as opposed to the
+ non-echoing password parts) were originally written in Europe. The
+ whole source package can be freely distributed, including from the USA.
+ (Prior to January 2000, re-export from the US was a violation of US law.)
+ */
+
+/*
+ This encryption code is a direct transcription of the algorithm from
+ Roger Schlafly, described by Phil Katz in the file appnote.txt. This
+ file (appnote.txt) is distributed with the PKZIP program (even in the
+ version without encryption capabilities).
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "./unzip.h"
+
+#ifdef STDC
+# include <stddef.h>
+# include <string.h>
+# include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+ extern int errno;
+#else
+# include <errno.h>
+#endif
+
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+
+#ifndef CASESENSITIVITYDEFAULT_NO
+# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES)
+# define CASESENSITIVITYDEFAULT_NO
+# endif
+#endif
+
+
+#ifndef UNZ_BUFSIZE
+#define UNZ_BUFSIZE (16384)
+#endif
+
+#ifndef UNZ_MAXFILENAMEINZIP
+#define UNZ_MAXFILENAMEINZIP (256)
+#endif
+
+#ifndef ALLOC
+# define ALLOC(size) (malloc(size))
+#endif
+#ifndef TRYFREE
+# define TRYFREE(p) {if (p) free(p);}
+#endif
+
+#define SIZECENTRALDIRITEM (0x2e)
+#define SIZEZIPLOCALHEADER (0x1e)
+
+
+
+
+const char unz_copyright[] =
+ " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
+
+/* unz_file_info_interntal contain internal info about a file in zipfile*/
+typedef struct unz_file_info_internal_s
+{
+ uLong offset_curfile;/* relative offset of local header 4 bytes */
+} unz_file_info_internal;
+
+
+/* file_in_zip_read_info_s contain internal information about a file in zipfile,
+ when reading and decompress it */
+typedef struct
+{
+ char *read_buffer; /* internal buffer for compressed data */
+ z_stream stream; /* zLib stream structure for inflate */
+
+ uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/
+ uLong stream_initialised; /* flag set if stream structure is initialised*/
+
+ uLong offset_local_extrafield;/* offset of the local extra field */
+ uInt size_local_extrafield;/* size of the local extra field */
+ uLong pos_local_extrafield; /* position in the local extra field in read*/
+
+ uLong crc32; /* crc32 of all data uncompressed */
+ uLong crc32_wait; /* crc32 we must obtain after decompress all */
+ uLong rest_read_compressed; /* number of byte to be decompressed */
+ uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/
+ zlib_filefunc_def z_filefunc;
+ voidpf filestream; /* io structore of the zipfile */
+ uLong compression_method; /* compression method (0==store) */
+ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+ int raw;
+} file_in_zip_read_info_s;
+
+
+/* unz_s contain internal information about the zipfile
+*/
+typedef struct
+{
+ zlib_filefunc_def z_filefunc;
+ voidpf filestream; /* io structore of the zipfile */
+ unz_global_info gi; /* public global information */
+ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+ uLong num_file; /* number of the current file in the zipfile*/
+ uLong pos_in_central_dir; /* pos of the current file in the central dir*/
+ uLong current_file_ok; /* flag about the usability of the current file*/
+ uLong central_pos; /* position of the beginning of the central dir*/
+
+ uLong size_central_dir; /* size of the central directory */
+ uLong offset_central_dir; /* offset of start of central directory with
+ respect to the starting disk number */
+
+ unz_file_info cur_file_info; /* public info about the current file in zip*/
+ unz_file_info_internal cur_file_info_internal; /* private info about it*/
+ file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current
+ file if we are decompressing it */
+ int encrypted;
+# ifndef NOUNCRYPT
+ unsigned long keys[3]; /* keys defining the pseudo-random sequence */
+ const unsigned long* pcrc_32_tab;
+# endif
+} unz_s;
+
+
+#ifndef NOUNCRYPT
+#include "crypt.h"
+#endif
+
+/* ===========================================================================
+ Read a byte from a gz_stream; update next_in and avail_in. Return EOF
+ for end of file.
+ IN assertion: the stream s has been sucessfully opened for reading.
+*/
+
+
+local int unzlocal_getByte (
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ int *pi);
+
+local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ int *pi;
+{
+ unsigned char c;
+ int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1);
+ if (err==1)
+ {
+ *pi = (int)c;
+ return UNZ_OK;
+ }
+ else
+ {
+ if (ZERROR(*pzlib_filefunc_def,filestream))
+ return UNZ_ERRNO;
+ else
+ return UNZ_EOF;
+ }
+}
+
+
+/* ===========================================================================
+ Reads a long in LSB order from the given gz_stream. Sets
+*/
+local int unzlocal_getShort (
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX);
+
+local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x ;
+ int i;
+ int err;
+
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==UNZ_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+local int unzlocal_getLong (
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX);
+
+local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x ;
+ int i;
+ int err;
+
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<16;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<24;
+
+ if (err==UNZ_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+
+/* My own strcmpi / strcasecmp */
+local int strcmpcasenosensitive_internal (fileName1,fileName2)
+ const char* fileName1;
+ const char* fileName2;
+{
+ for (;;)
+ {
+ char c1=*(fileName1++);
+ char c2=*(fileName2++);
+ if ((c1>='a') && (c1<='z'))
+ c1 -= 0x20;
+ if ((c2>='a') && (c2<='z'))
+ c2 -= 0x20;
+ if (c1=='\0')
+ return ((c2=='\0') ? 0 : -1);
+ if (c2=='\0')
+ return 1;
+ if (c1<c2)
+ return -1;
+ if (c1>c2)
+ return 1;
+ }
+}
+
+
+#ifdef CASESENSITIVITYDEFAULT_NO
+#define CASESENSITIVITYDEFAULTVALUE 2
+#else
+#define CASESENSITIVITYDEFAULTVALUE 1
+#endif
+
+#ifndef STRCMPCASENOSENTIVEFUNCTION
+#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal
+#endif
+
+/*
+ Compare two filename (fileName1,fileName2).
+ If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
+ If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
+ or strcasecmp)
+ If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
+ (like 1 on Unix, 2 on Windows)
+
+*/
+extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity)
+ const char* fileName1;
+ const char* fileName2;
+ int iCaseSensitivity;
+{
+ if (iCaseSensitivity==0)
+ iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE;
+
+ if (iCaseSensitivity==1)
+ return strcmp(fileName1,fileName2);
+
+ return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2);
+}
+
+#ifndef BUFREADCOMMENT
+#define BUFREADCOMMENT (0x400)
+#endif
+
+/*
+ Locate the Central directory of a zipfile (at the end, just before
+ the global comment)
+*/
+local uLong unzlocal_SearchCentralDir (
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream);
+
+local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+{
+ unsigned char* buf;
+ uLong uSizeFile;
+ uLong uBackRead;
+ uLong uMaxBack=0xffff; /* maximum size of global comment */
+ uLong uPosFound=0;
+
+ if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+ return 0;
+
+
+ uSizeFile = ZTELL(*pzlib_filefunc_def,filestream);
+
+ if (uMaxBack>uSizeFile)
+ uMaxBack = uSizeFile;
+
+ buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
+ if (buf==NULL)
+ return 0;
+
+ uBackRead = 4;
+ while (uBackRead<uMaxBack)
+ {
+ uLong uReadSize,uReadPos ;
+ int i;
+ if (uBackRead+BUFREADCOMMENT>uMaxBack)
+ uBackRead = uMaxBack;
+ else
+ uBackRead+=BUFREADCOMMENT;
+ uReadPos = uSizeFile-uBackRead ;
+
+ uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
+ (BUFREADCOMMENT+4) : (uSizeFile-uReadPos);
+ if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ break;
+
+ if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
+ break;
+
+ for (i=(int)uReadSize-3; (i--)>0;)
+ if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
+ ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
+ {
+ uPosFound = uReadPos+i;
+ break;
+ }
+
+ if (uPosFound!=0)
+ break;
+ }
+ TRYFREE(buf);
+ return uPosFound;
+}
+
+/*
+ Open a Zip file. path contain the full pathname (by example,
+ on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer
+ "zlib/zlib114.zip".
+ If the zipfile cannot be opened (file doesn't exist or in not valid), the
+ return value is NULL.
+ Else, the return value is a unzFile Handle, usable with other function
+ of this unzip package.
+*/
+extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def)
+ const char *path;
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ unz_s us;
+ unz_s *s;
+ uLong central_pos,uL;
+
+ uLong number_disk; /* number of the current dist, used for
+ spaning ZIP, unsupported, always 0*/
+ uLong number_disk_with_CD; /* number the the disk with central dir, used
+ for spaning ZIP, unsupported, always 0*/
+ uLong number_entry_CD; /* total number of entries in
+ the central dir
+ (same than number_entry on nospan) */
+
+ int err=UNZ_OK;
+
+ if (unz_copyright[0]!=' ')
+ return NULL;
+
+ if (pzlib_filefunc_def==NULL)
+ fill_fopen_filefunc(&us.z_filefunc);
+ else
+ us.z_filefunc = *pzlib_filefunc_def;
+
+ us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque,
+ path,
+ ZLIB_FILEFUNC_MODE_READ |
+ ZLIB_FILEFUNC_MODE_EXISTING);
+ if (us.filestream==NULL)
+ return NULL;
+
+ central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream);
+ if (central_pos==0)
+ err=UNZ_ERRNO;
+
+ if (ZSEEK(us.z_filefunc, us.filestream,
+ central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=UNZ_ERRNO;
+
+ /* the signature, already checked */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* number of this disk */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* number of the disk with the start of the central directory */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* total number of entries in the central dir on this disk */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* total number of entries in the central dir */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if ((number_entry_CD!=us.gi.number_entry) ||
+ (number_disk_with_CD!=0) ||
+ (number_disk!=0))
+ err=UNZ_BADZIPFILE;
+
+ /* size of the central directory */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* offset of start of central directory with respect to the
+ starting disk number */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* zipfile comment length */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if ((central_pos<us.offset_central_dir+us.size_central_dir) &&
+ (err==UNZ_OK))
+ err=UNZ_BADZIPFILE;
+
+ if (err!=UNZ_OK)
+ {
+ ZCLOSE(us.z_filefunc, us.filestream);
+ return NULL;
+ }
+
+ us.byte_before_the_zipfile = central_pos -
+ (us.offset_central_dir+us.size_central_dir);
+ us.central_pos = central_pos;
+ us.pfile_in_zip_read = NULL;
+ us.encrypted = 0;
+
+
+ s=(unz_s*)ALLOC(sizeof(unz_s));
+ *s=us;
+ unzGoToFirstFile((unzFile)s);
+ return (unzFile)s;
+}
+
+
+extern unzFile ZEXPORT unzOpen (path)
+ const char *path;
+{
+ return unzOpen2(path, NULL);
+}
+
+/*
+ Close a ZipFile opened with unzipOpen.
+ If there is files inside the .Zip opened with unzipOpenCurrentFile (see later),
+ these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+ return UNZ_OK if there is no problem. */
+extern int ZEXPORT unzClose (file)
+ unzFile file;
+{
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ if (s->pfile_in_zip_read!=NULL)
+ unzCloseCurrentFile(file);
+
+ ZCLOSE(s->z_filefunc, s->filestream);
+ TRYFREE(s);
+ return UNZ_OK;
+}
+
+
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem. */
+extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info)
+ unzFile file;
+ unz_global_info *pglobal_info;
+{
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ *pglobal_info=s->gi;
+ return UNZ_OK;
+}
+
+
+/*
+ Translate date/time from Dos format to tm_unz (readable more easilty)
+*/
+local void unzlocal_DosDateToTmuDate (ulDosDate, ptm)
+ uLong ulDosDate;
+ tm_unz* ptm;
+{
+ uLong uDate;
+ uDate = (uLong)(ulDosDate>>16);
+ ptm->tm_mday = (uInt)(uDate&0x1f) ;
+ ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ;
+ ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ;
+
+ ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800);
+ ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ;
+ ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ;
+}
+
+/*
+ Get Info about the current file in the zipfile, with internal only info
+*/
+local int unzlocal_GetCurrentFileInfoInternal (unzFile file,
+ unz_file_info *pfile_info,
+ unz_file_info_internal
+ *pfile_info_internal,
+ char *szFileName,
+ uLong fileNameBufferSize,
+ void *extraField,
+ uLong extraFieldBufferSize,
+ char *szComment,
+ uLong commentBufferSize);
+
+local int unzlocal_GetCurrentFileInfoInternal (file,
+ pfile_info,
+ pfile_info_internal,
+ szFileName, fileNameBufferSize,
+ extraField, extraFieldBufferSize,
+ szComment, commentBufferSize)
+ unzFile file;
+ unz_file_info *pfile_info;
+ unz_file_info_internal *pfile_info_internal;
+ char *szFileName;
+ uLong fileNameBufferSize;
+ void *extraField;
+ uLong extraFieldBufferSize;
+ char *szComment;
+ uLong commentBufferSize;
+{
+ unz_s* s;
+ unz_file_info file_info;
+ unz_file_info_internal file_info_internal;
+ int err=UNZ_OK;
+ uLong uMagic;
+ long lSeek=0;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (ZSEEK(s->z_filefunc, s->filestream,
+ s->pos_in_central_dir+s->byte_before_the_zipfile,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=UNZ_ERRNO;
+
+
+ /* we check the magic */
+ if (err==UNZ_OK)
+ {
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if (uMagic!=0x02014b50)
+ err=UNZ_BADZIPFILE;
+ }
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date);
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ lSeek+=file_info.size_filename;
+ if ((err==UNZ_OK) && (szFileName!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_filename<fileNameBufferSize)
+ {
+ *(szFileName+file_info.size_filename)='\0';
+ uSizeRead = file_info.size_filename;
+ }
+ else
+ uSizeRead = fileNameBufferSize;
+
+ if ((file_info.size_filename>0) && (fileNameBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ lSeek -= uSizeRead;
+ }
+
+
+ if ((err==UNZ_OK) && (extraField!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_file_extra<extraFieldBufferSize)
+ uSizeRead = file_info.size_file_extra;
+ else
+ uSizeRead = extraFieldBufferSize;
+
+ if (lSeek!=0)
+ {
+ if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
+ lSeek=0;
+ else
+ err=UNZ_ERRNO;
+ }
+ if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ lSeek += file_info.size_file_extra - uSizeRead;
+ }
+ else
+ lSeek+=file_info.size_file_extra;
+
+
+ if ((err==UNZ_OK) && (szComment!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_file_comment<commentBufferSize)
+ {
+ *(szComment+file_info.size_file_comment)='\0';
+ uSizeRead = file_info.size_file_comment;
+ }
+ else
+ uSizeRead = commentBufferSize;
+
+ if (lSeek!=0)
+ {
+ if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
+ lSeek=0;
+ else
+ err=UNZ_ERRNO;
+ }
+ if ((file_info.size_file_comment>0) && (commentBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ lSeek+=file_info.size_file_comment - uSizeRead;
+ }
+ else
+ lSeek+=file_info.size_file_comment;
+
+ if ((err==UNZ_OK) && (pfile_info!=NULL))
+ *pfile_info=file_info;
+
+ if ((err==UNZ_OK) && (pfile_info_internal!=NULL))
+ *pfile_info_internal=file_info_internal;
+
+ return err;
+}
+
+
+
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem.
+*/
+extern int ZEXPORT unzGetCurrentFileInfo (file,
+ pfile_info,
+ szFileName, fileNameBufferSize,
+ extraField, extraFieldBufferSize,
+ szComment, commentBufferSize)
+ unzFile file;
+ unz_file_info *pfile_info;
+ char *szFileName;
+ uLong fileNameBufferSize;
+ void *extraField;
+ uLong extraFieldBufferSize;
+ char *szComment;
+ uLong commentBufferSize;
+{
+ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,
+ szFileName,fileNameBufferSize,
+ extraField,extraFieldBufferSize,
+ szComment,commentBufferSize);
+}
+
+/*
+ Set the current file of the zipfile to the first file.
+ return UNZ_OK if there is no problem
+*/
+extern int ZEXPORT unzGoToFirstFile (file)
+ unzFile file;
+{
+ int err=UNZ_OK;
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ s->pos_in_central_dir=s->offset_central_dir;
+ s->num_file=0;
+ err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+/*
+ Set the current file of the zipfile to the next file.
+ return UNZ_OK if there is no problem
+ return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
+*/
+extern int ZEXPORT unzGoToNextFile (file)
+ unzFile file;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+ if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */
+ if (s->num_file+1==s->gi.number_entry)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename +
+ s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ;
+ s->num_file++;
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+
+/*
+ Try locate the file szFileName in the zipfile.
+ For the iCaseSensitivity signification, see unzipStringFileNameCompare
+
+ return value :
+ UNZ_OK if the file is found. It becomes the current file.
+ UNZ_END_OF_LIST_OF_FILE if the file is not found
+*/
+extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity)
+ unzFile file;
+ const char *szFileName;
+ int iCaseSensitivity;
+{
+ unz_s* s;
+ int err;
+
+ /* We remember the 'current' position in the file so that we can jump
+ * back there if we fail.
+ */
+ unz_file_info cur_file_infoSaved;
+ unz_file_info_internal cur_file_info_internalSaved;
+ uLong num_fileSaved;
+ uLong pos_in_central_dirSaved;
+
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+
+ if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP)
+ return UNZ_PARAMERROR;
+
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ /* Save the current state */
+ num_fileSaved = s->num_file;
+ pos_in_central_dirSaved = s->pos_in_central_dir;
+ cur_file_infoSaved = s->cur_file_info;
+ cur_file_info_internalSaved = s->cur_file_info_internal;
+
+ err = unzGoToFirstFile(file);
+
+ while (err == UNZ_OK)
+ {
+ char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1];
+ err = unzGetCurrentFileInfo(file,NULL,
+ szCurrentFileName,sizeof(szCurrentFileName)-1,
+ NULL,0,NULL,0);
+ if (err == UNZ_OK)
+ {
+ if (unzStringFileNameCompare(szCurrentFileName,
+ szFileName,iCaseSensitivity)==0)
+ return UNZ_OK;
+ err = unzGoToNextFile(file);
+ }
+ }
+
+ /* We failed, so restore the state of the 'current file' to where we
+ * were.
+ */
+ s->num_file = num_fileSaved ;
+ s->pos_in_central_dir = pos_in_central_dirSaved ;
+ s->cur_file_info = cur_file_infoSaved;
+ s->cur_file_info_internal = cur_file_info_internalSaved;
+ return err;
+}
+
+
+/*
+///////////////////////////////////////////
+// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net)
+// I need random access
+//
+// Further optimization could be realized by adding an ability
+// to cache the directory in memory. The goal being a single
+// comprehensive file read to put the file I need in a memory.
+*/
+
+/*
+typedef struct unz_file_pos_s
+{
+ uLong pos_in_zip_directory; // offset in file
+ uLong num_of_file; // # of file
+} unz_file_pos;
+*/
+
+extern int ZEXPORT unzGetFilePos(file, file_pos)
+ unzFile file;
+ unz_file_pos* file_pos;
+{
+ unz_s* s;
+
+ if (file==NULL || file_pos==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ file_pos->pos_in_zip_directory = s->pos_in_central_dir;
+ file_pos->num_of_file = s->num_file;
+
+ return UNZ_OK;
+}
+
+extern int ZEXPORT unzGoToFilePos(file, file_pos)
+ unzFile file;
+ unz_file_pos* file_pos;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL || file_pos==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ /* jump to the right spot */
+ s->pos_in_central_dir = file_pos->pos_in_zip_directory;
+ s->num_file = file_pos->num_of_file;
+
+ /* set the current file */
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ /* return results */
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+/*
+// Unzip Helper Functions - should be here?
+///////////////////////////////////////////
+*/
+
+/*
+ Read the local header of the current zipfile
+ Check the coherency of the local header and info in the end of central
+ directory about this file
+ store in *piSizeVar the size of extra info in local header
+ (filename and size of extra field data)
+*/
+local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar,
+ poffset_local_extrafield,
+ psize_local_extrafield)
+ unz_s* s;
+ uInt* piSizeVar;
+ uLong *poffset_local_extrafield;
+ uInt *psize_local_extrafield;
+{
+ uLong uMagic,uData,uFlags;
+ uLong size_filename;
+ uLong size_extra_field;
+ int err=UNZ_OK;
+
+ *piSizeVar = 0;
+ *poffset_local_extrafield = 0;
+ *psize_local_extrafield = 0;
+
+ if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile +
+ s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+
+ if (err==UNZ_OK)
+ {
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if (uMagic!=0x04034b50)
+ err=UNZ_BADZIPFILE;
+ }
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
+ err=UNZ_ERRNO;
+/*
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion))
+ err=UNZ_BADZIPFILE;
+*/
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method))
+ err=UNZ_BADZIPFILE;
+
+ if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) &&
+ (s->cur_file_info.compression_method!=Z_DEFLATED))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename))
+ err=UNZ_BADZIPFILE;
+
+ *piSizeVar += (uInt)size_filename;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK)
+ err=UNZ_ERRNO;
+ *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile +
+ SIZEZIPLOCALHEADER + size_filename;
+ *psize_local_extrafield = (uInt)size_extra_field;
+
+ *piSizeVar += (uInt)size_extra_field;
+
+ return err;
+}
+
+/*
+ Open for reading data the current file in the zipfile.
+ If there is no error and the file is opened, the return value is UNZ_OK.
+*/
+extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password)
+ unzFile file;
+ int* method;
+ int* level;
+ int raw;
+ const char* password;
+{
+ int err=UNZ_OK;
+ uInt iSizeVar;
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ uLong offset_local_extrafield; /* offset of the local extra field */
+ uInt size_local_extrafield; /* size of the local extra field */
+# ifndef NOUNCRYPT
+ char source[12];
+# else
+ if (password != NULL)
+ return UNZ_PARAMERROR;
+# endif
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_PARAMERROR;
+
+ if (s->pfile_in_zip_read != NULL)
+ unzCloseCurrentFile(file);
+
+ if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar,
+ &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK)
+ return UNZ_BADZIPFILE;
+
+ pfile_in_zip_read_info = (file_in_zip_read_info_s*)
+ ALLOC(sizeof(file_in_zip_read_info_s));
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_INTERNALERROR;
+
+ pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE);
+ pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield;
+ pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield;
+ pfile_in_zip_read_info->pos_local_extrafield=0;
+ pfile_in_zip_read_info->raw=raw;
+
+ if (pfile_in_zip_read_info->read_buffer==NULL)
+ {
+ TRYFREE(pfile_in_zip_read_info);
+ return UNZ_INTERNALERROR;
+ }
+
+ pfile_in_zip_read_info->stream_initialised=0;
+
+ if (method!=NULL)
+ *method = (int)s->cur_file_info.compression_method;
+
+ if (level!=NULL)
+ {
+ *level = 6;
+ switch (s->cur_file_info.flag & 0x06)
+ {
+ case 6 : *level = 1; break;
+ case 4 : *level = 2; break;
+ case 2 : *level = 9; break;
+ }
+ }
+
+ if ((s->cur_file_info.compression_method!=0) &&
+ (s->cur_file_info.compression_method!=Z_DEFLATED))
+ err=UNZ_BADZIPFILE;
+
+ pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc;
+ pfile_in_zip_read_info->crc32=0;
+ pfile_in_zip_read_info->compression_method =
+ s->cur_file_info.compression_method;
+ pfile_in_zip_read_info->filestream=s->filestream;
+ pfile_in_zip_read_info->z_filefunc=s->z_filefunc;
+ pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile;
+
+ pfile_in_zip_read_info->stream.total_out = 0;
+
+ if ((s->cur_file_info.compression_method==Z_DEFLATED) &&
+ (!raw))
+ {
+ pfile_in_zip_read_info->stream.zalloc = (alloc_func)0;
+ pfile_in_zip_read_info->stream.zfree = (free_func)0;
+ pfile_in_zip_read_info->stream.opaque = (voidpf)0;
+ pfile_in_zip_read_info->stream.next_in = (voidpf)0;
+ pfile_in_zip_read_info->stream.avail_in = 0;
+
+ err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS);
+ if (err == Z_OK)
+ pfile_in_zip_read_info->stream_initialised=1;
+ else
+ {
+ TRYFREE(pfile_in_zip_read_info);
+ return err;
+ }
+ /* windowBits is passed < 0 to tell that there is no zlib header.
+ * Note that in this case inflate *requires* an extra "dummy" byte
+ * after the compressed stream in order to complete decompression and
+ * return Z_STREAM_END.
+ * In unzip, i don't wait absolutely Z_STREAM_END because I known the
+ * size of both compressed and uncompressed data
+ */
+ }
+ pfile_in_zip_read_info->rest_read_compressed =
+ s->cur_file_info.compressed_size ;
+ pfile_in_zip_read_info->rest_read_uncompressed =
+ s->cur_file_info.uncompressed_size ;
+
+
+ pfile_in_zip_read_info->pos_in_zipfile =
+ s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER +
+ iSizeVar;
+
+ pfile_in_zip_read_info->stream.avail_in = (uInt)0;
+
+ s->pfile_in_zip_read = pfile_in_zip_read_info;
+
+# ifndef NOUNCRYPT
+ if (password != NULL)
+ {
+ int i;
+ s->pcrc_32_tab = get_crc_table();
+ init_keys(password,s->keys,s->pcrc_32_tab);
+ if (ZSEEK(s->z_filefunc, s->filestream,
+ s->pfile_in_zip_read->pos_in_zipfile +
+ s->pfile_in_zip_read->byte_before_the_zipfile,
+ SEEK_SET)!=0)
+ return UNZ_INTERNALERROR;
+ if (ZREAD(s->z_filefunc, s->filestream,source, 12)<12)
+ return UNZ_INTERNALERROR;
+
+ for (i = 0; i<12; i++)
+ zdecode(s->keys,s->pcrc_32_tab,source[i]);
+
+ s->pfile_in_zip_read->pos_in_zipfile+=12;
+ s->encrypted=1;
+ }
+# endif
+
+
+ return UNZ_OK;
+}
+
+extern int ZEXPORT unzOpenCurrentFile (file)
+ unzFile file;
+{
+ return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL);
+}
+
+extern int ZEXPORT unzOpenCurrentFilePassword (file, password)
+ unzFile file;
+ const char* password;
+{
+ return unzOpenCurrentFile3(file, NULL, NULL, 0, password);
+}
+
+extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw)
+ unzFile file;
+ int* method;
+ int* level;
+ int raw;
+{
+ return unzOpenCurrentFile3(file, method, level, raw, NULL);
+}
+
+/*
+ Read bytes from the current file.
+ buf contain buffer where data must be copied
+ len the size of buf.
+
+ return the number of byte copied if somes bytes are copied
+ return 0 if the end of file was reached
+ return <0 with error code if there is an error
+ (UNZ_ERRNO for IO error, or zLib error for uncompress error)
+*/
+extern int ZEXPORT unzReadCurrentFile (file, buf, len)
+ unzFile file;
+ voidp buf;
+ unsigned len;
+{
+ int err=UNZ_OK;
+ uInt iRead = 0;
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+
+ if ((pfile_in_zip_read_info->read_buffer == NULL))
+ return UNZ_END_OF_LIST_OF_FILE;
+ if (len==0)
+ return 0;
+
+ pfile_in_zip_read_info->stream.next_out = (Bytef*)buf;
+
+ pfile_in_zip_read_info->stream.avail_out = (uInt)len;
+
+ if ((len>pfile_in_zip_read_info->rest_read_uncompressed) &&
+ (!(pfile_in_zip_read_info->raw)))
+ pfile_in_zip_read_info->stream.avail_out =
+ (uInt)pfile_in_zip_read_info->rest_read_uncompressed;
+
+ if ((len>pfile_in_zip_read_info->rest_read_compressed+
+ pfile_in_zip_read_info->stream.avail_in) &&
+ (pfile_in_zip_read_info->raw))
+ pfile_in_zip_read_info->stream.avail_out =
+ (uInt)pfile_in_zip_read_info->rest_read_compressed+
+ pfile_in_zip_read_info->stream.avail_in;
+
+ while (pfile_in_zip_read_info->stream.avail_out>0)
+ {
+ if ((pfile_in_zip_read_info->stream.avail_in==0) &&
+ (pfile_in_zip_read_info->rest_read_compressed>0))
+ {
+ uInt uReadThis = UNZ_BUFSIZE;
+ if (pfile_in_zip_read_info->rest_read_compressed<uReadThis)
+ uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed;
+ if (uReadThis == 0)
+ return UNZ_EOF;
+ if (ZSEEK(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->pos_in_zipfile +
+ pfile_in_zip_read_info->byte_before_the_zipfile,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+ if (ZREAD(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->read_buffer,
+ uReadThis)!=uReadThis)
+ return UNZ_ERRNO;
+
+
+# ifndef NOUNCRYPT
+ if (s->encrypted)
+ {
+ uInt i;
+ for (i=0;i<uReadThis;i++)
+ pfile_in_zip_read_info->read_buffer[i] =
+ zdecode(s->keys,s->pcrc_32_tab,
+ pfile_in_zip_read_info->read_buffer[i]);
+ }
+# endif
+
+
+ pfile_in_zip_read_info->pos_in_zipfile += uReadThis;
+
+ pfile_in_zip_read_info->rest_read_compressed-=uReadThis;
+
+ pfile_in_zip_read_info->stream.next_in =
+ (Bytef*)pfile_in_zip_read_info->read_buffer;
+ pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis;
+ }
+
+ if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw))
+ {
+ uInt uDoCopy,i ;
+
+ if ((pfile_in_zip_read_info->stream.avail_in == 0) &&
+ (pfile_in_zip_read_info->rest_read_compressed == 0))
+ return (iRead==0) ? UNZ_EOF : iRead;
+
+ if (pfile_in_zip_read_info->stream.avail_out <
+ pfile_in_zip_read_info->stream.avail_in)
+ uDoCopy = pfile_in_zip_read_info->stream.avail_out ;
+ else
+ uDoCopy = pfile_in_zip_read_info->stream.avail_in ;
+
+ for (i=0;i<uDoCopy;i++)
+ *(pfile_in_zip_read_info->stream.next_out+i) =
+ *(pfile_in_zip_read_info->stream.next_in+i);
+
+ pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,
+ pfile_in_zip_read_info->stream.next_out,
+ uDoCopy);
+ pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy;
+ pfile_in_zip_read_info->stream.avail_in -= uDoCopy;
+ pfile_in_zip_read_info->stream.avail_out -= uDoCopy;
+ pfile_in_zip_read_info->stream.next_out += uDoCopy;
+ pfile_in_zip_read_info->stream.next_in += uDoCopy;
+ pfile_in_zip_read_info->stream.total_out += uDoCopy;
+ iRead += uDoCopy;
+ }
+ else
+ {
+ uLong uTotalOutBefore,uTotalOutAfter;
+ const Bytef *bufBefore;
+ uLong uOutThis;
+ int flush=Z_SYNC_FLUSH;
+
+ uTotalOutBefore = pfile_in_zip_read_info->stream.total_out;
+ bufBefore = pfile_in_zip_read_info->stream.next_out;
+
+ /*
+ if ((pfile_in_zip_read_info->rest_read_uncompressed ==
+ pfile_in_zip_read_info->stream.avail_out) &&
+ (pfile_in_zip_read_info->rest_read_compressed == 0))
+ flush = Z_FINISH;
+ */
+ err=inflate(&pfile_in_zip_read_info->stream,flush);
+
+ if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL))
+ err = Z_DATA_ERROR;
+
+ uTotalOutAfter = pfile_in_zip_read_info->stream.total_out;
+ uOutThis = uTotalOutAfter-uTotalOutBefore;
+
+ pfile_in_zip_read_info->crc32 =
+ crc32(pfile_in_zip_read_info->crc32,bufBefore,
+ (uInt)(uOutThis));
+
+ pfile_in_zip_read_info->rest_read_uncompressed -=
+ uOutThis;
+
+ iRead += (uInt)(uTotalOutAfter - uTotalOutBefore);
+
+ if (err==Z_STREAM_END)
+ return (iRead==0) ? UNZ_EOF : iRead;
+ if (err!=Z_OK)
+ break;
+ }
+ }
+
+ if (err==Z_OK)
+ return iRead;
+ return err;
+}
+
+
+/*
+ Give the current position in uncompressed data
+*/
+extern z_off_t ZEXPORT unztell (file)
+ unzFile file;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ return (z_off_t)pfile_in_zip_read_info->stream.total_out;
+}
+
+
+/*
+ return 1 if the end of file was reached, 0 elsewhere
+*/
+extern int ZEXPORT unzeof (file)
+ unzFile file;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ if (pfile_in_zip_read_info->rest_read_uncompressed == 0)
+ return 1;
+ else
+ return 0;
+}
+
+
+
+/*
+ Read extra field from the current file (opened by unzOpenCurrentFile)
+ This is the local-header version of the extra field (sometimes, there is
+ more info in the local-header version than in the central-header)
+
+ if buf==NULL, it return the size of the local extra field that can be read
+
+ if buf!=NULL, len is the size of the buffer, the extra header is copied in
+ buf.
+ the return value is the number of bytes copied in buf, or (if <0)
+ the error code
+*/
+extern int ZEXPORT unzGetLocalExtrafield (file,buf,len)
+ unzFile file;
+ voidp buf;
+ unsigned len;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ uInt read_now;
+ uLong size_to_read;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ size_to_read = (pfile_in_zip_read_info->size_local_extrafield -
+ pfile_in_zip_read_info->pos_local_extrafield);
+
+ if (buf==NULL)
+ return (int)size_to_read;
+
+ if (len>size_to_read)
+ read_now = (uInt)size_to_read;
+ else
+ read_now = (uInt)len ;
+
+ if (read_now==0)
+ return 0;
+
+ if (ZSEEK(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->offset_local_extrafield +
+ pfile_in_zip_read_info->pos_local_extrafield,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+ if (ZREAD(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ buf,read_now)!=read_now)
+ return UNZ_ERRNO;
+
+ return (int)read_now;
+}
+
+/*
+ Close the file in zip opened with unzipOpenCurrentFile
+ Return UNZ_CRCERROR if all the file was read but the CRC is not good
+*/
+extern int ZEXPORT unzCloseCurrentFile (file)
+ unzFile file;
+{
+ int err=UNZ_OK;
+
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+
+ if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) &&
+ (!pfile_in_zip_read_info->raw))
+ {
+ if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait)
+ err=UNZ_CRCERROR;
+ }
+
+
+ TRYFREE(pfile_in_zip_read_info->read_buffer);
+ pfile_in_zip_read_info->read_buffer = NULL;
+ if (pfile_in_zip_read_info->stream_initialised)
+ inflateEnd(&pfile_in_zip_read_info->stream);
+
+ pfile_in_zip_read_info->stream_initialised = 0;
+ TRYFREE(pfile_in_zip_read_info);
+
+ s->pfile_in_zip_read=NULL;
+
+ return err;
+}
+
+
+/*
+ Get the global comment string of the ZipFile, in the szComment buffer.
+ uSizeBuf is the size of the szComment buffer.
+ return the number of byte copied or an error code <0
+*/
+extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf)
+ unzFile file;
+ char *szComment;
+ uLong uSizeBuf;
+{
+ unz_s* s;
+ uLong uReadThis ;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ uReadThis = uSizeBuf;
+ if (uReadThis>s->gi.size_comment)
+ uReadThis = s->gi.size_comment;
+
+ if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+ if (uReadThis>0)
+ {
+ *szComment='\0';
+ if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis)
+ return UNZ_ERRNO;
+ }
+
+ if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment))
+ *(szComment+s->gi.size_comment)='\0';
+ return (int)uReadThis;
+}
+
+/* Additions by RX '2004 */
+extern uLong ZEXPORT unzGetOffset (file)
+ unzFile file;
+{
+ unz_s* s;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return 0;
+ if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff)
+ if (s->num_file==s->gi.number_entry)
+ return 0;
+ return s->pos_in_central_dir;
+}
+
+extern int ZEXPORT unzSetOffset (file, pos)
+ unzFile file;
+ uLong pos;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ s->pos_in_central_dir = pos;
+ s->num_file = s->gi.number_entry; /* hack */
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
diff --git a/src/3rdparty/assimp/contrib/unzip/unzip.h b/src/3rdparty/assimp/contrib/unzip/unzip.h
new file mode 100644
index 000000000..2f0d6ee56
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/unzip/unzip.h
@@ -0,0 +1,358 @@
+/* unzip.h -- IO for uncompress .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g
+ WinZip, InfoZip tools and compatible.
+
+ Multi volume ZipFile (span) are not supported.
+ Encryption compatible with pkzip 2.04g only supported
+ Old compressions used by old PKZip 1.x are not supported
+
+
+ I WAIT FEEDBACK at mail info@winimage.com
+ Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution
+
+ Condition of use and distribution are the same than zlib :
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+
+*/
+
+/* for more info about .ZIP format, see
+ http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip
+ http://www.info-zip.org/pub/infozip/doc/
+ PkWare has also a specification at :
+ ftp://ftp.pkware.com/probdesc.zip
+*/
+
+#ifndef _unz_H
+#define _unz_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ZLIB_H
+# ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+# include <zlib.h>
+# else
+# include "../zlib/zlib.h"
+# endif
+#endif
+
+#ifndef _ZLIBIOAPI_H
+#include "ioapi.h"
+#endif
+
+#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
+/* like the STRICT of WIN32, we define a pointer that cannot be converted
+ from (void*) without cast */
+typedef struct TagunzFile__ { int unused; } unzFile__;
+typedef unzFile__ *unzFile;
+#else
+typedef voidp unzFile;
+#endif
+
+
+#define UNZ_OK (0)
+#define UNZ_END_OF_LIST_OF_FILE (-100)
+#define UNZ_ERRNO (Z_ERRNO)
+#define UNZ_EOF (0)
+#define UNZ_PARAMERROR (-102)
+#define UNZ_BADZIPFILE (-103)
+#define UNZ_INTERNALERROR (-104)
+#define UNZ_CRCERROR (-105)
+
+/* tm_unz contain date/time info */
+typedef struct tm_unz_s
+{
+ uInt tm_sec; /* seconds after the minute - [0,59] */
+ uInt tm_min; /* minutes after the hour - [0,59] */
+ uInt tm_hour; /* hours since midnight - [0,23] */
+ uInt tm_mday; /* day of the month - [1,31] */
+ uInt tm_mon; /* months since January - [0,11] */
+ uInt tm_year; /* years - [1980..2044] */
+} tm_unz;
+
+/* unz_global_info structure contain global data about the ZIPfile
+ These data comes from the end of central dir */
+typedef struct unz_global_info_s
+{
+ uLong number_entry; /* total number of entries in
+ the central dir on this disk */
+ uLong size_comment; /* size of the global comment of the zipfile */
+} unz_global_info;
+
+
+/* unz_file_info contain information about a file in the zipfile */
+typedef struct unz_file_info_s
+{
+ uLong version; /* version made by 2 bytes */
+ uLong version_needed; /* version needed to extract 2 bytes */
+ uLong flag; /* general purpose bit flag 2 bytes */
+ uLong compression_method; /* compression method 2 bytes */
+ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
+ uLong crc; /* crc-32 4 bytes */
+ uLong compressed_size; /* compressed size 4 bytes */
+ uLong uncompressed_size; /* uncompressed size 4 bytes */
+ uLong size_filename; /* filename length 2 bytes */
+ uLong size_file_extra; /* extra field length 2 bytes */
+ uLong size_file_comment; /* file comment length 2 bytes */
+
+ uLong disk_num_start; /* disk number start 2 bytes */
+ uLong internal_fa; /* internal file attributes 2 bytes */
+ uLong external_fa; /* external file attributes 4 bytes */
+
+ tm_unz tmu_date;
+} unz_file_info;
+
+extern int ZEXPORT unzStringFileNameCompare (const char* fileName1,
+ const char* fileName2,
+ int iCaseSensitivity);
+/*
+ Compare two filename (fileName1,fileName2).
+ If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
+ If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
+ or strcasecmp)
+ If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
+ (like 1 on Unix, 2 on Windows)
+*/
+
+
+extern unzFile ZEXPORT unzOpen (const char *path);
+/*
+ Open a Zip file. path contain the full pathname (by example,
+ on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer
+ "zlib/zlib113.zip".
+ If the zipfile cannot be opened (file don't exist or in not valid), the
+ return value is NULL.
+ Else, the return value is a unzFile Handle, usable with other function
+ of this unzip package.
+*/
+
+extern unzFile ZEXPORT unzOpen2 (const char *path,
+ zlib_filefunc_def* pzlib_filefunc_def);
+/*
+ Open a Zip file, like unzOpen, but provide a set of file low level API
+ for read/write the zip file (see ioapi.h)
+*/
+
+extern int ZEXPORT unzClose (unzFile file);
+/*
+ Close a ZipFile opened with unzipOpen.
+ If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
+ these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+ return UNZ_OK if there is no problem. */
+
+extern int ZEXPORT unzGetGlobalInfo (unzFile file,
+ unz_global_info *pglobal_info);
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem. */
+
+
+extern int ZEXPORT unzGetGlobalComment (unzFile file,
+ char *szComment,
+ uLong uSizeBuf);
+/*
+ Get the global comment string of the ZipFile, in the szComment buffer.
+ uSizeBuf is the size of the szComment buffer.
+ return the number of byte copied or an error code <0
+*/
+
+
+/***************************************************************************/
+/* Unzip package allow you browse the directory of the zipfile */
+
+extern int ZEXPORT unzGoToFirstFile (unzFile file);
+/*
+ Set the current file of the zipfile to the first file.
+ return UNZ_OK if there is no problem
+*/
+
+extern int ZEXPORT unzGoToNextFile (unzFile file);
+/*
+ Set the current file of the zipfile to the next file.
+ return UNZ_OK if there is no problem
+ return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
+*/
+
+extern int ZEXPORT unzLocateFile (unzFile file,
+ const char *szFileName,
+ int iCaseSensitivity);
+/*
+ Try locate the file szFileName in the zipfile.
+ For the iCaseSensitivity signification, see unzStringFileNameCompare
+
+ return value :
+ UNZ_OK if the file is found. It becomes the current file.
+ UNZ_END_OF_LIST_OF_FILE if the file is not found
+*/
+
+
+/* ****************************************** */
+/* Ryan supplied functions */
+/* unz_file_info contain information about a file in the zipfile */
+typedef struct unz_file_pos_s
+{
+ uLong pos_in_zip_directory; /* offset in zip file directory */
+ uLong num_of_file; /* # of file */
+} unz_file_pos;
+
+extern int ZEXPORT unzGetFilePos(
+ unzFile file,
+ unz_file_pos* file_pos);
+
+extern int ZEXPORT unzGoToFilePos(
+ unzFile file,
+ unz_file_pos* file_pos);
+
+/* ****************************************** */
+
+extern int ZEXPORT unzGetCurrentFileInfo (unzFile file,
+ unz_file_info *pfile_info,
+ char *szFileName,
+ uLong fileNameBufferSize,
+ void *extraField,
+ uLong extraFieldBufferSize,
+ char *szComment,
+ uLong commentBufferSize);
+/*
+ Get Info about the current file
+ if pfile_info!=NULL, the *pfile_info structure will contain somes info about
+ the current file
+ if szFileName!=NULL, the filemane string will be copied in szFileName
+ (fileNameBufferSize is the size of the buffer)
+ if extraField!=NULL, the extra field information will be copied in extraField
+ (extraFieldBufferSize is the size of the buffer).
+ This is the Central-header version of the extra field
+ if szComment!=NULL, the comment string of the file will be copied in szComment
+ (commentBufferSize is the size of the buffer)
+*/
+
+/***************************************************************************/
+/* for reading the content of the current zipfile, you can open it, read data
+ from it, and close it (you can close it before reading all the file)
+ */
+
+extern int ZEXPORT unzOpenCurrentFile (unzFile file);
+/*
+ Open for reading data the current file in the zipfile.
+ If there is no error, the return value is UNZ_OK.
+*/
+
+extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file,
+ const char* password);
+/*
+ Open for reading data the current file in the zipfile.
+ password is a crypting password
+ If there is no error, the return value is UNZ_OK.
+*/
+
+extern int ZEXPORT unzOpenCurrentFile2 (unzFile file,
+ int* method,
+ int* level,
+ int raw);
+/*
+ Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
+ if raw==1
+ *method will receive method of compression, *level will receive level of
+ compression
+ note : you can set level parameter as NULL (if you did not want known level,
+ but you CANNOT set method parameter as NULL
+*/
+
+extern int ZEXPORT unzOpenCurrentFile3 (unzFile file,
+ int* method,
+ int* level,
+ int raw,
+ const char* password);
+/*
+ Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
+ if raw==1
+ *method will receive method of compression, *level will receive level of
+ compression
+ note : you can set level parameter as NULL (if you did not want known level,
+ but you CANNOT set method parameter as NULL
+*/
+
+
+extern int ZEXPORT unzCloseCurrentFile (unzFile file);
+/*
+ Close the file in zip opened with unzOpenCurrentFile
+ Return UNZ_CRCERROR if all the file was read but the CRC is not good
+*/
+
+extern int ZEXPORT unzReadCurrentFile (unzFile file,
+ voidp buf,
+ unsigned len);
+/*
+ Read bytes from the current file (opened by unzOpenCurrentFile)
+ buf contain buffer where data must be copied
+ len the size of buf.
+
+ return the number of byte copied if somes bytes are copied
+ return 0 if the end of file was reached
+ return <0 with error code if there is an error
+ (UNZ_ERRNO for IO error, or zLib error for uncompress error)
+*/
+
+extern z_off_t ZEXPORT unztell (unzFile file);
+/*
+ Give the current position in uncompressed data
+*/
+
+extern int ZEXPORT unzeof (unzFile file);
+/*
+ return 1 if the end of file was reached, 0 elsewhere
+*/
+
+extern int ZEXPORT unzGetLocalExtrafield (unzFile file,
+ voidp buf,
+ unsigned len);
+/*
+ Read extra field from the current file (opened by unzOpenCurrentFile)
+ This is the local-header version of the extra field (sometimes, there is
+ more info in the local-header version than in the central-header)
+
+ if buf==NULL, it return the size of the local extra field
+
+ if buf!=NULL, len is the size of the buffer, the extra header is copied in
+ buf.
+ the return value is the number of bytes copied in buf, or (if <0)
+ the error code
+*/
+
+/***************************************************************************/
+
+/* Get the current file offset */
+extern uLong ZEXPORT unzGetOffset (unzFile file);
+
+/* Set the current file offset */
+extern int ZEXPORT unzSetOffset (unzFile file, uLong pos);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _unz_H */
diff --git a/src/3rdparty/assimp/contrib/zlib/README b/src/3rdparty/assimp/contrib/zlib/README
new file mode 100644
index 000000000..d4219bf88
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/README
@@ -0,0 +1,115 @@
+ZLIB DATA COMPRESSION LIBRARY
+
+zlib 1.2.5 is a general purpose data compression library. All the code is
+thread safe. The data format used by the zlib library is described by RFCs
+(Request for Comments) 1950 to 1952 in the files
+http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format)
+and rfc1952.txt (gzip format).
+
+All functions of the compression library are documented in the file zlib.h
+(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example
+of the library is given in the file example.c which also tests that the library
+is working correctly. Another example is given in the file minigzip.c. The
+compression library itself is composed of all source files except example.c and
+minigzip.c.
+
+To compile all files and run the test program, follow the instructions given at
+the top of Makefile.in. In short "./configure; make test", and if that goes
+well, "make install" should work for most flavors of Unix. For Windows, use one
+of the special makefiles in win32/ or contrib/vstudio/ . For VMS, use
+make_vms.com.
+
+Questions about zlib should be sent to <zlib@gzip.org>, or to Gilles Vollant
+<info@winimage.com> for the Windows DLL version. The zlib home page is
+http://zlib.net/ . Before reporting a problem, please check this site to
+verify that you have the latest version of zlib; otherwise get the latest
+version and check whether the problem still exists or not.
+
+PLEASE read the zlib FAQ http://zlib.net/zlib_faq.html before asking for help.
+
+Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997
+issue of Dr. Dobb's Journal; a copy of the article is available at
+http://marknelson.us/1997/01/01/zlib-engine/ .
+
+The changes made in version 1.2.5 are documented in the file ChangeLog.
+
+Unsupported third party contributions are provided in directory contrib/ .
+
+zlib is available in Java using the java.util.zip package, documented at
+http://java.sun.com/developer/technicalArticles/Programming/compression/ .
+
+A Perl interface to zlib written by Paul Marquess <pmqs@cpan.org> is available
+at CPAN (Comprehensive Perl Archive Network) sites, including
+http://search.cpan.org/~pmqs/IO-Compress-Zlib/ .
+
+A Python interface to zlib written by A.M. Kuchling <amk@amk.ca> is
+available in Python 1.5 and later versions, see
+http://www.python.org/doc/lib/module-zlib.html .
+
+zlib is built into tcl: http://wiki.tcl.tk/4610 .
+
+An experimental package to read and write files in .zip format, written on top
+of zlib by Gilles Vollant <info@winimage.com>, is available in the
+contrib/minizip directory of zlib.
+
+
+Notes for some targets:
+
+- For Windows DLL versions, please see win32/DLL_FAQ.txt
+
+- For 64-bit Irix, deflate.c must be compiled without any optimization. With
+ -O, one libpng test fails. The test works in 32 bit mode (with the -n32
+ compiler flag). The compiler bug has been reported to SGI.
+
+- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works
+ when compiled with cc.
+
+- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is
+ necessary to get gzprintf working correctly. This is done by configure.
+
+- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with
+ other compilers. Use "make test" to check your compiler.
+
+- gzdopen is not supported on RISCOS or BEOS.
+
+- For PalmOs, see http://palmzlib.sourceforge.net/
+
+
+Acknowledgments:
+
+ The deflate format used by zlib was defined by Phil Katz. The deflate and
+ zlib specifications were written by L. Peter Deutsch. Thanks to all the
+ people who reported problems and suggested various improvements in zlib; they
+ are too numerous to cite here.
+
+Copyright notice:
+
+ (C) 1995-2010 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+If you use the zlib library in a product, we would appreciate *not* receiving
+lengthy legal documents to sign. The sources are provided for free but without
+warranty of any kind. The library has been entirely written by Jean-loup
+Gailly and Mark Adler; it does not include third-party code.
+
+If you redistribute modified sources, we would appreciate that you include in
+the file ChangeLog history information documenting your changes. Please read
+the FAQ for more information on the distribution of modified source versions.
diff --git a/src/3rdparty/assimp/contrib/zlib/adler32.c b/src/3rdparty/assimp/contrib/zlib/adler32.c
new file mode 100644
index 000000000..65ad6a5ad
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/adler32.c
@@ -0,0 +1,169 @@
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-2007 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#include "zutil.h"
+
+#define local static
+
+local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2);
+
+#define BASE 65521UL /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;}
+#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf) DO8(buf,0); DO8(buf,8);
+
+/* use NO_DIVIDE if your processor does not do division in hardware */
+#ifdef NO_DIVIDE
+# define MOD(a) \
+ do { \
+ if (a >= (BASE << 16)) a -= (BASE << 16); \
+ if (a >= (BASE << 15)) a -= (BASE << 15); \
+ if (a >= (BASE << 14)) a -= (BASE << 14); \
+ if (a >= (BASE << 13)) a -= (BASE << 13); \
+ if (a >= (BASE << 12)) a -= (BASE << 12); \
+ if (a >= (BASE << 11)) a -= (BASE << 11); \
+ if (a >= (BASE << 10)) a -= (BASE << 10); \
+ if (a >= (BASE << 9)) a -= (BASE << 9); \
+ if (a >= (BASE << 8)) a -= (BASE << 8); \
+ if (a >= (BASE << 7)) a -= (BASE << 7); \
+ if (a >= (BASE << 6)) a -= (BASE << 6); \
+ if (a >= (BASE << 5)) a -= (BASE << 5); \
+ if (a >= (BASE << 4)) a -= (BASE << 4); \
+ if (a >= (BASE << 3)) a -= (BASE << 3); \
+ if (a >= (BASE << 2)) a -= (BASE << 2); \
+ if (a >= (BASE << 1)) a -= (BASE << 1); \
+ if (a >= BASE) a -= BASE; \
+ } while (0)
+# define MOD4(a) \
+ do { \
+ if (a >= (BASE << 4)) a -= (BASE << 4); \
+ if (a >= (BASE << 3)) a -= (BASE << 3); \
+ if (a >= (BASE << 2)) a -= (BASE << 2); \
+ if (a >= (BASE << 1)) a -= (BASE << 1); \
+ if (a >= BASE) a -= BASE; \
+ } while (0)
+#else
+# define MOD(a) a %= BASE
+# define MOD4(a) a %= BASE
+#endif
+
+/* ========================================================================= */
+uLong ZEXPORT adler32(adler, buf, len)
+ uLong adler;
+ const Bytef *buf;
+ uInt len;
+{
+ unsigned long sum2;
+ unsigned n;
+
+ /* split Adler-32 into component sums */
+ sum2 = (adler >> 16) & 0xffff;
+ adler &= 0xffff;
+
+ /* in case user likes doing a byte at a time, keep it fast */
+ if (len == 1) {
+ adler += buf[0];
+ if (adler >= BASE)
+ adler -= BASE;
+ sum2 += adler;
+ if (sum2 >= BASE)
+ sum2 -= BASE;
+ return adler | (sum2 << 16);
+ }
+
+ /* initial Adler-32 value (deferred check for len == 1 speed) */
+ if (buf == Z_NULL)
+ return 1L;
+
+ /* in case short lengths are provided, keep it somewhat fast */
+ if (len < 16) {
+ while (len--) {
+ adler += *buf++;
+ sum2 += adler;
+ }
+ if (adler >= BASE)
+ adler -= BASE;
+ MOD4(sum2); /* only added so many BASE's */
+ return adler | (sum2 << 16);
+ }
+
+ /* do length NMAX blocks -- requires just one modulo operation */
+ while (len >= NMAX) {
+ len -= NMAX;
+ n = NMAX / 16; /* NMAX is divisible by 16 */
+ do {
+ DO16(buf); /* 16 sums unrolled */
+ buf += 16;
+ } while (--n);
+ MOD(adler);
+ MOD(sum2);
+ }
+
+ /* do remaining bytes (less than NMAX, still just one modulo) */
+ if (len) { /* avoid modulos if none remaining */
+ while (len >= 16) {
+ len -= 16;
+ DO16(buf);
+ buf += 16;
+ }
+ while (len--) {
+ adler += *buf++;
+ sum2 += adler;
+ }
+ MOD(adler);
+ MOD(sum2);
+ }
+
+ /* return recombined sums */
+ return adler | (sum2 << 16);
+}
+
+/* ========================================================================= */
+local uLong adler32_combine_(adler1, adler2, len2)
+ uLong adler1;
+ uLong adler2;
+ z_off64_t len2;
+{
+ unsigned long sum1;
+ unsigned long sum2;
+ unsigned rem;
+
+ /* the derivation of this formula is left as an exercise for the reader */
+ rem = (unsigned)(len2 % BASE);
+ sum1 = adler1 & 0xffff;
+ sum2 = rem * sum1;
+ MOD(sum2);
+ sum1 += (adler2 & 0xffff) + BASE - 1;
+ sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem;
+ if (sum1 >= BASE) sum1 -= BASE;
+ if (sum1 >= BASE) sum1 -= BASE;
+ if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1);
+ if (sum2 >= BASE) sum2 -= BASE;
+ return sum1 | (sum2 << 16);
+}
+
+/* ========================================================================= */
+uLong ZEXPORT adler32_combine(adler1, adler2, len2)
+ uLong adler1;
+ uLong adler2;
+ z_off_t len2;
+{
+ return adler32_combine_(adler1, adler2, len2);
+}
+
+uLong ZEXPORT adler32_combine64(adler1, adler2, len2)
+ uLong adler1;
+ uLong adler2;
+ z_off64_t len2;
+{
+ return adler32_combine_(adler1, adler2, len2);
+}
diff --git a/src/3rdparty/assimp/contrib/zlib/compress.c b/src/3rdparty/assimp/contrib/zlib/compress.c
new file mode 100644
index 000000000..ea4dfbe9d
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/compress.c
@@ -0,0 +1,80 @@
+/* compress.c -- compress a memory buffer
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least 0.1% larger than sourceLen plus
+ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+ int level;
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+#ifdef MAXSEG_64K
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+#endif
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+ stream.opaque = (voidpf)0;
+
+ err = deflateInit(&stream, level);
+ if (err != Z_OK) return err;
+
+ err = deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ deflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = deflateEnd(&stream);
+ return err;
+}
+
+/* ===========================================================================
+ */
+int ZEXPORT compress (dest, destLen, source, sourceLen)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+{
+ return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
+}
+
+/* ===========================================================================
+ If the default memLevel or windowBits for deflateInit() is changed, then
+ this function needs to be updated.
+ */
+uLong ZEXPORT compressBound (sourceLen)
+ uLong sourceLen;
+{
+ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
+ (sourceLen >> 25) + 13;
+}
diff --git a/src/3rdparty/assimp/contrib/zlib/crc32.c b/src/3rdparty/assimp/contrib/zlib/crc32.c
new file mode 100644
index 000000000..91be372d2
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/crc32.c
@@ -0,0 +1,442 @@
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-2006, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster
+ * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing
+ * tables for updating the shift register in one step with three exclusive-ors
+ * instead of four steps with four exclusive-ors. This results in about a
+ * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.
+ */
+
+/* @(#) $Id$ */
+
+/*
+ Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore
+ protection on the static variables used to control the first-use generation
+ of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should
+ first call get_crc_table() to initialize the tables before allowing more than
+ one thread to use crc32().
+ */
+
+#ifdef MAKECRCH
+# include <stdio.h>
+# ifndef DYNAMIC_CRC_TABLE
+# define DYNAMIC_CRC_TABLE
+# endif /* !DYNAMIC_CRC_TABLE */
+#endif /* MAKECRCH */
+
+#include "zutil.h" /* for STDC and FAR definitions */
+
+#define local static
+
+/* Find a four-byte integer type for crc32_little() and crc32_big(). */
+#ifndef NOBYFOUR
+# ifdef STDC /* need ANSI C limits.h to determine sizes */
+# include <limits.h>
+# define BYFOUR
+# if (UINT_MAX == 0xffffffffUL)
+ typedef unsigned int u4;
+# else
+# if (ULONG_MAX == 0xffffffffUL)
+ typedef unsigned long u4;
+# else
+# if (USHRT_MAX == 0xffffffffUL)
+ typedef unsigned short u4;
+# else
+# undef BYFOUR /* can't find a four-byte integer type! */
+# endif
+# endif
+# endif
+# endif /* STDC */
+#endif /* !NOBYFOUR */
+
+/* Definitions for doing the crc four data bytes at a time. */
+#ifdef BYFOUR
+# define REV(w) ((((w)>>24)&0xff)+(((w)>>8)&0xff00)+ \
+ (((w)&0xff00)<<8)+(((w)&0xff)<<24))
+ local unsigned long crc32_little OF((unsigned long,
+ const unsigned char FAR *, unsigned));
+ local unsigned long crc32_big OF((unsigned long,
+ const unsigned char FAR *, unsigned));
+# define TBLS 8
+#else
+# define TBLS 1
+#endif /* BYFOUR */
+
+/* Local functions for crc concatenation */
+local unsigned long gf2_matrix_times OF((unsigned long *mat,
+ unsigned long vec));
+local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat));
+local uLong crc32_combine_(uLong crc1, uLong crc2, z_off64_t len2);
+
+
+#ifdef DYNAMIC_CRC_TABLE
+
+local volatile int crc_table_empty = 1;
+local unsigned long FAR crc_table[TBLS][256];
+local void make_crc_table OF((void));
+#ifdef MAKECRCH
+ local void write_table OF((FILE *, const unsigned long FAR *));
+#endif /* MAKECRCH */
+/*
+ Generate tables for a byte-wise 32-bit CRC calculation on the polynomial:
+ x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
+
+ Polynomials over GF(2) are represented in binary, one bit per coefficient,
+ with the lowest powers in the most significant bit. Then adding polynomials
+ is just exclusive-or, and multiplying a polynomial by x is a right shift by
+ one. If we call the above polynomial p, and represent a byte as the
+ polynomial q, also with the lowest power in the most significant bit (so the
+ byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
+ where a mod b means the remainder after dividing a by b.
+
+ This calculation is done using the shift-register method of multiplying and
+ taking the remainder. The register is initialized to zero, and for each
+ incoming bit, x^32 is added mod p to the register if the bit is a one (where
+ x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
+ x (which is shifting right by one and adding x^32 mod p if the bit shifted
+ out is a one). We start with the highest power (least significant bit) of
+ q and repeat for all eight bits of q.
+
+ The first table is simply the CRC of all possible eight bit values. This is
+ all the information needed to generate CRCs on data a byte at a time for all
+ combinations of CRC register values and incoming bytes. The remaining tables
+ allow for word-at-a-time CRC calculation for both big-endian and little-
+ endian machines, where a word is four bytes.
+*/
+local void make_crc_table()
+{
+ unsigned long c;
+ int n, k;
+ unsigned long poly; /* polynomial exclusive-or pattern */
+ /* terms of polynomial defining this crc (except x^32): */
+ static volatile int first = 1; /* flag to limit concurrent making */
+ static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+ /* See if another task is already doing this (not thread-safe, but better
+ than nothing -- significantly reduces duration of vulnerability in
+ case the advice about DYNAMIC_CRC_TABLE is ignored) */
+ if (first) {
+ first = 0;
+
+ /* make exclusive-or pattern from polynomial (0xedb88320UL) */
+ poly = 0UL;
+ for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++)
+ poly |= 1UL << (31 - p[n]);
+
+ /* generate a crc for every 8-bit value */
+ for (n = 0; n < 256; n++) {
+ c = (unsigned long)n;
+ for (k = 0; k < 8; k++)
+ c = c & 1 ? poly ^ (c >> 1) : c >> 1;
+ crc_table[0][n] = c;
+ }
+
+#ifdef BYFOUR
+ /* generate crc for each value followed by one, two, and three zeros,
+ and then the byte reversal of those as well as the first table */
+ for (n = 0; n < 256; n++) {
+ c = crc_table[0][n];
+ crc_table[4][n] = REV(c);
+ for (k = 1; k < 4; k++) {
+ c = crc_table[0][c & 0xff] ^ (c >> 8);
+ crc_table[k][n] = c;
+ crc_table[k + 4][n] = REV(c);
+ }
+ }
+#endif /* BYFOUR */
+
+ crc_table_empty = 0;
+ }
+ else { /* not first */
+ /* wait for the other guy to finish (not efficient, but rare) */
+ while (crc_table_empty)
+ ;
+ }
+
+#ifdef MAKECRCH
+ /* write out CRC tables to crc32.h */
+ {
+ FILE *out;
+
+ out = fopen("crc32.h", "w");
+ if (out == NULL) return;
+ fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n");
+ fprintf(out, " * Generated automatically by crc32.c\n */\n\n");
+ fprintf(out, "local const unsigned long FAR ");
+ fprintf(out, "crc_table[TBLS][256] =\n{\n {\n");
+ write_table(out, crc_table[0]);
+# ifdef BYFOUR
+ fprintf(out, "#ifdef BYFOUR\n");
+ for (k = 1; k < 8; k++) {
+ fprintf(out, " },\n {\n");
+ write_table(out, crc_table[k]);
+ }
+ fprintf(out, "#endif\n");
+# endif /* BYFOUR */
+ fprintf(out, " }\n};\n");
+ fclose(out);
+ }
+#endif /* MAKECRCH */
+}
+
+#ifdef MAKECRCH
+local void write_table(out, table)
+ FILE *out;
+ const unsigned long FAR *table;
+{
+ int n;
+
+ for (n = 0; n < 256; n++)
+ fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n],
+ n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", "));
+}
+#endif /* MAKECRCH */
+
+#else /* !DYNAMIC_CRC_TABLE */
+/* ========================================================================
+ * Tables of CRC-32s of all single-byte values, made by make_crc_table().
+ */
+#include "crc32.h"
+#endif /* DYNAMIC_CRC_TABLE */
+
+/* =========================================================================
+ * This function can be used by asm versions of crc32()
+ */
+const unsigned long FAR * ZEXPORT get_crc_table()
+{
+#ifdef DYNAMIC_CRC_TABLE
+ if (crc_table_empty)
+ make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+ return (const unsigned long FAR *)crc_table;
+}
+
+/* ========================================================================= */
+#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
+#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
+
+/* ========================================================================= */
+unsigned long ZEXPORT crc32(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ uInt len;
+{
+ if (buf == Z_NULL) return 0UL;
+
+#ifdef DYNAMIC_CRC_TABLE
+ if (crc_table_empty)
+ make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+
+#ifdef BYFOUR
+ if (sizeof(void *) == sizeof(ptrdiff_t)) {
+ u4 endian;
+
+ endian = 1;
+ if (*((unsigned char *)(&endian)))
+ return crc32_little(crc, buf, len);
+ else
+ return crc32_big(crc, buf, len);
+ }
+#endif /* BYFOUR */
+ crc = crc ^ 0xffffffffUL;
+ while (len >= 8) {
+ DO8;
+ len -= 8;
+ }
+ if (len) do {
+ DO1;
+ } while (--len);
+ return crc ^ 0xffffffffUL;
+}
+
+#ifdef BYFOUR
+
+/* ========================================================================= */
+#define DOLIT4 c ^= *buf4++; \
+ c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \
+ crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24]
+#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4
+
+/* ========================================================================= */
+local unsigned long crc32_little(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ unsigned len;
+{
+ register u4 c;
+ register const u4 FAR *buf4;
+
+ c = (u4)crc;
+ c = ~c;
+ while (len && ((ptrdiff_t)buf & 3)) {
+ c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+ len--;
+ }
+
+ buf4 = (const u4 FAR *)(const void FAR *)buf;
+ while (len >= 32) {
+ DOLIT32;
+ len -= 32;
+ }
+ while (len >= 4) {
+ DOLIT4;
+ len -= 4;
+ }
+ buf = (const unsigned char FAR *)buf4;
+
+ if (len) do {
+ c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+ } while (--len);
+ c = ~c;
+ return (unsigned long)c;
+}
+
+/* ========================================================================= */
+#define DOBIG4 c ^= *++buf4; \
+ c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \
+ crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24]
+#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4
+
+/* ========================================================================= */
+local unsigned long crc32_big(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ unsigned len;
+{
+ register u4 c;
+ register const u4 FAR *buf4;
+
+ c = REV((u4)crc);
+ c = ~c;
+ while (len && ((ptrdiff_t)buf & 3)) {
+ c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+ len--;
+ }
+
+ buf4 = (const u4 FAR *)(const void FAR *)buf;
+ buf4--;
+ while (len >= 32) {
+ DOBIG32;
+ len -= 32;
+ }
+ while (len >= 4) {
+ DOBIG4;
+ len -= 4;
+ }
+ buf4++;
+ buf = (const unsigned char FAR *)buf4;
+
+ if (len) do {
+ c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+ } while (--len);
+ c = ~c;
+ return (unsigned long)(REV(c));
+}
+
+#endif /* BYFOUR */
+
+#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */
+
+/* ========================================================================= */
+local unsigned long gf2_matrix_times(mat, vec)
+ unsigned long *mat;
+ unsigned long vec;
+{
+ unsigned long sum;
+
+ sum = 0;
+ while (vec) {
+ if (vec & 1)
+ sum ^= *mat;
+ vec >>= 1;
+ mat++;
+ }
+ return sum;
+}
+
+/* ========================================================================= */
+local void gf2_matrix_square(square, mat)
+ unsigned long *square;
+ unsigned long *mat;
+{
+ int n;
+
+ for (n = 0; n < GF2_DIM; n++)
+ square[n] = gf2_matrix_times(mat, mat[n]);
+}
+
+/* ========================================================================= */
+local uLong crc32_combine_(crc1, crc2, len2)
+ uLong crc1;
+ uLong crc2;
+ z_off64_t len2;
+{
+ int n;
+ unsigned long row;
+ unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */
+ unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */
+
+ /* degenerate case (also disallow negative lengths) */
+ if (len2 <= 0)
+ return crc1;
+
+ /* put operator for one zero bit in odd */
+ odd[0] = 0xedb88320UL; /* CRC-32 polynomial */
+ row = 1;
+ for (n = 1; n < GF2_DIM; n++) {
+ odd[n] = row;
+ row <<= 1;
+ }
+
+ /* put operator for two zero bits in even */
+ gf2_matrix_square(even, odd);
+
+ /* put operator for four zero bits in odd */
+ gf2_matrix_square(odd, even);
+
+ /* apply len2 zeros to crc1 (first square will put the operator for one
+ zero byte, eight zero bits, in even) */
+ do {
+ /* apply zeros operator for this bit of len2 */
+ gf2_matrix_square(even, odd);
+ if (len2 & 1)
+ crc1 = gf2_matrix_times(even, crc1);
+ len2 >>= 1;
+
+ /* if no more bits set, then done */
+ if (len2 == 0)
+ break;
+
+ /* another iteration of the loop with odd and even swapped */
+ gf2_matrix_square(odd, even);
+ if (len2 & 1)
+ crc1 = gf2_matrix_times(odd, crc1);
+ len2 >>= 1;
+
+ /* if no more bits set, then done */
+ } while (len2 != 0);
+
+ /* return combined crc */
+ crc1 ^= crc2;
+ return crc1;
+}
+
+/* ========================================================================= */
+uLong ZEXPORT crc32_combine(crc1, crc2, len2)
+ uLong crc1;
+ uLong crc2;
+ z_off_t len2;
+{
+ return crc32_combine_(crc1, crc2, len2);
+}
+
+uLong ZEXPORT crc32_combine64(crc1, crc2, len2)
+ uLong crc1;
+ uLong crc2;
+ z_off64_t len2;
+{
+ return crc32_combine_(crc1, crc2, len2);
+}
diff --git a/src/3rdparty/assimp/contrib/zlib/crc32.h b/src/3rdparty/assimp/contrib/zlib/crc32.h
new file mode 100644
index 000000000..8053b6117
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/crc32.h
@@ -0,0 +1,441 @@
+/* crc32.h -- tables for rapid CRC calculation
+ * Generated automatically by crc32.c
+ */
+
+local const unsigned long FAR crc_table[TBLS][256] =
+{
+ {
+ 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
+ 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
+ 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
+ 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
+ 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
+ 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
+ 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
+ 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
+ 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
+ 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
+ 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
+ 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
+ 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
+ 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
+ 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
+ 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
+ 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
+ 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
+ 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
+ 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
+ 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
+ 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
+ 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
+ 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
+ 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
+ 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
+ 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
+ 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
+ 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
+ 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
+ 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
+ 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
+ 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
+ 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
+ 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
+ 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
+ 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
+ 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
+ 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
+ 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
+ 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
+ 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
+ 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
+ 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
+ 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
+ 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
+ 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
+ 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
+ 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
+ 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
+ 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
+ 0x2d02ef8dUL
+#ifdef BYFOUR
+ },
+ {
+ 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,
+ 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,
+ 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,
+ 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,
+ 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,
+ 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,
+ 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,
+ 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,
+ 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,
+ 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,
+ 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,
+ 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,
+ 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,
+ 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,
+ 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,
+ 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,
+ 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,
+ 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,
+ 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,
+ 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,
+ 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,
+ 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,
+ 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,
+ 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,
+ 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,
+ 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,
+ 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,
+ 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,
+ 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,
+ 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,
+ 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,
+ 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,
+ 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,
+ 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,
+ 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,
+ 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,
+ 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,
+ 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,
+ 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,
+ 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,
+ 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,
+ 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,
+ 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,
+ 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,
+ 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,
+ 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,
+ 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,
+ 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,
+ 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,
+ 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,
+ 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,
+ 0x9324fd72UL
+ },
+ {
+ 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,
+ 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,
+ 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,
+ 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,
+ 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,
+ 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,
+ 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,
+ 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,
+ 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,
+ 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,
+ 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,
+ 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,
+ 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,
+ 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,
+ 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,
+ 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,
+ 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,
+ 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,
+ 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,
+ 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,
+ 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,
+ 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,
+ 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,
+ 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,
+ 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,
+ 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,
+ 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,
+ 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,
+ 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,
+ 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,
+ 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,
+ 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,
+ 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,
+ 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,
+ 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,
+ 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,
+ 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,
+ 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,
+ 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,
+ 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,
+ 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,
+ 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,
+ 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,
+ 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,
+ 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,
+ 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,
+ 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,
+ 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,
+ 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,
+ 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,
+ 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,
+ 0xbe9834edUL
+ },
+ {
+ 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,
+ 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,
+ 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,
+ 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,
+ 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,
+ 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,
+ 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,
+ 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,
+ 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,
+ 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,
+ 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,
+ 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,
+ 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,
+ 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,
+ 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,
+ 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,
+ 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,
+ 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,
+ 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,
+ 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,
+ 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,
+ 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,
+ 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,
+ 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,
+ 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,
+ 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,
+ 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,
+ 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,
+ 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,
+ 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,
+ 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,
+ 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,
+ 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,
+ 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,
+ 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,
+ 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,
+ 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,
+ 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,
+ 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,
+ 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,
+ 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,
+ 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,
+ 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,
+ 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,
+ 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,
+ 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,
+ 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,
+ 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,
+ 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,
+ 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,
+ 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,
+ 0xde0506f1UL
+ },
+ {
+ 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,
+ 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,
+ 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,
+ 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,
+ 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,
+ 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,
+ 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,
+ 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,
+ 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,
+ 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,
+ 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,
+ 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,
+ 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,
+ 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,
+ 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,
+ 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,
+ 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,
+ 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,
+ 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,
+ 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,
+ 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,
+ 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,
+ 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,
+ 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,
+ 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,
+ 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,
+ 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,
+ 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,
+ 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,
+ 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,
+ 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,
+ 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,
+ 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,
+ 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,
+ 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,
+ 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,
+ 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,
+ 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,
+ 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,
+ 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,
+ 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,
+ 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,
+ 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,
+ 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,
+ 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,
+ 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,
+ 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,
+ 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,
+ 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,
+ 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,
+ 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,
+ 0x8def022dUL
+ },
+ {
+ 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,
+ 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,
+ 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,
+ 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,
+ 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,
+ 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,
+ 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,
+ 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,
+ 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,
+ 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,
+ 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,
+ 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,
+ 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,
+ 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,
+ 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,
+ 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,
+ 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,
+ 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,
+ 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,
+ 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,
+ 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,
+ 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,
+ 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,
+ 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,
+ 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,
+ 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,
+ 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,
+ 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,
+ 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,
+ 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,
+ 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,
+ 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,
+ 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,
+ 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,
+ 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,
+ 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,
+ 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,
+ 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,
+ 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,
+ 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,
+ 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,
+ 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,
+ 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,
+ 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,
+ 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,
+ 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,
+ 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,
+ 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,
+ 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,
+ 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,
+ 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,
+ 0x72fd2493UL
+ },
+ {
+ 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,
+ 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,
+ 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,
+ 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,
+ 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,
+ 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,
+ 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,
+ 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,
+ 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,
+ 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,
+ 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,
+ 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,
+ 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,
+ 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,
+ 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,
+ 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,
+ 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,
+ 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,
+ 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,
+ 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,
+ 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,
+ 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,
+ 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,
+ 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,
+ 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,
+ 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,
+ 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,
+ 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,
+ 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,
+ 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,
+ 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,
+ 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,
+ 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,
+ 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,
+ 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,
+ 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,
+ 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,
+ 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,
+ 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,
+ 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,
+ 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,
+ 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,
+ 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,
+ 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,
+ 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,
+ 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,
+ 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,
+ 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,
+ 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,
+ 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,
+ 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,
+ 0xed3498beUL
+ },
+ {
+ 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,
+ 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,
+ 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,
+ 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,
+ 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,
+ 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,
+ 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,
+ 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,
+ 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,
+ 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,
+ 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,
+ 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,
+ 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,
+ 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,
+ 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,
+ 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,
+ 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,
+ 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,
+ 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,
+ 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,
+ 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,
+ 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,
+ 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,
+ 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,
+ 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,
+ 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,
+ 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,
+ 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,
+ 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,
+ 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,
+ 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,
+ 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,
+ 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,
+ 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,
+ 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,
+ 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,
+ 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,
+ 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,
+ 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,
+ 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,
+ 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,
+ 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,
+ 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,
+ 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,
+ 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,
+ 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,
+ 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,
+ 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,
+ 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,
+ 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,
+ 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,
+ 0xf10605deUL
+#endif
+ }
+};
diff --git a/src/3rdparty/assimp/contrib/zlib/deflate.c b/src/3rdparty/assimp/contrib/zlib/deflate.c
new file mode 100644
index 000000000..5c4022f3d
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/deflate.c
@@ -0,0 +1,1834 @@
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process depends on being able to identify portions
+ * of the input text which are identical to earlier input (within a
+ * sliding window trailing behind the input currently being processed).
+ *
+ * The most straightforward technique turns out to be the fastest for
+ * most input files: try all possible matches and select the longest.
+ * The key feature of this algorithm is that insertions into the string
+ * dictionary are very simple and thus fast, and deletions are avoided
+ * completely. Insertions are performed at each input character, whereas
+ * string matches are performed only when the previous match ends. So it
+ * is preferable to spend more time in matches to allow very fast string
+ * insertions and avoid deletions. The matching algorithm for small
+ * strings is inspired from that of Rabin & Karp. A brute force approach
+ * is used to find longer strings when a small match has been found.
+ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ * (by Leonid Broukhis).
+ * A previous version of this file used a more sophisticated algorithm
+ * (by Fiala and Greene) which is guaranteed to run in linear amortized
+ * time, but has a larger average cost, uses more memory and is patented.
+ * However the F&G algorithm may be faster for some highly redundant
+ * files if the parameter max_chain_length (described below) is too large.
+ *
+ * ACKNOWLEDGEMENTS
+ *
+ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ * I found it in 'freeze' written by Leonid Broukhis.
+ * Thanks to many people for bug reports and testing.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
+ * Available in http://www.ietf.org/rfc/rfc1951.txt
+ *
+ * A description of the Rabin and Karp algorithm is given in the book
+ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ * Fiala,E.R., and Greene,D.H.
+ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* @(#) $Id$ */
+
+#include "deflate.h"
+
+const char deflate_copyright[] =
+ " deflate 1.2.5 Copyright 1995-2010 Jean-loup Gailly and Mark Adler ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/* ===========================================================================
+ * Function prototypes.
+ */
+typedef enum {
+ need_more, /* block not completed, need more input or more output */
+ block_done, /* block flush performed */
+ finish_started, /* finish started, need only more output at next deflate */
+ finish_done /* finish done, accept no more input or output */
+} block_state;
+
+typedef block_state (*compress_func) OF((deflate_state *s, int flush));
+/* Compression function. Returns the block state after the call. */
+
+local void fill_window OF((deflate_state *s));
+local block_state deflate_stored OF((deflate_state *s, int flush));
+local block_state deflate_fast OF((deflate_state *s, int flush));
+#ifndef FASTEST
+local block_state deflate_slow OF((deflate_state *s, int flush));
+#endif
+local block_state deflate_rle OF((deflate_state *s, int flush));
+local block_state deflate_huff OF((deflate_state *s, int flush));
+local void lm_init OF((deflate_state *s));
+local void putShortMSB OF((deflate_state *s, uInt b));
+local void flush_pending OF((z_streamp strm));
+local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size));
+#ifdef ASMV
+ void match_init OF((void)); /* asm code initialization */
+ uInt longest_match OF((deflate_state *s, IPos cur_match));
+#else
+local uInt longest_match OF((deflate_state *s, IPos cur_match));
+#endif
+
+#ifdef DEBUG
+local void check_match OF((deflate_state *s, IPos start, IPos match,
+ int length));
+#endif
+
+/* ===========================================================================
+ * Local data
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+# define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+typedef struct config_s {
+ ush good_length; /* reduce lazy search above this match length */
+ ush max_lazy; /* do not perform lazy search above this match length */
+ ush nice_length; /* quit search above this match length */
+ ush max_chain;
+ compress_func func;
+} config;
+
+#ifdef FASTEST
+local const config configuration_table[2] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
+/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */
+#else
+local const config configuration_table[10] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
+/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */
+/* 2 */ {4, 5, 16, 8, deflate_fast},
+/* 3 */ {4, 6, 32, 32, deflate_fast},
+
+/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */
+/* 5 */ {8, 16, 32, 32, deflate_slow},
+/* 6 */ {8, 16, 128, 128, deflate_slow},
+/* 7 */ {8, 32, 128, 256, deflate_slow},
+/* 8 */ {32, 128, 258, 1024, deflate_slow},
+/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */
+#endif
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+#ifndef NO_DUMMY_DECL
+struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
+#endif
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
+ * input characters, so that a running hash key can be computed from the
+ * previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * If this file is compiled with -DFASTEST, the compression level is forced
+ * to 1, and no hash chains are maintained.
+ * IN assertion: all calls to to INSERT_STRING are made with consecutive
+ * input characters and the first MIN_MATCH bytes of str are valid
+ * (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#ifdef FASTEST
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ match_head = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+#else
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+#endif
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+ s->head[s->hash_size-1] = NIL; \
+ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ========================================================================= */
+int ZEXPORT deflateInit_(strm, level, version, stream_size)
+ z_streamp strm;
+ int level;
+ const char *version;
+ int stream_size;
+{
+ return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY, version, stream_size);
+ /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
+ version, stream_size)
+ z_streamp strm;
+ int level;
+ int method;
+ int windowBits;
+ int memLevel;
+ int strategy;
+ const char *version;
+ int stream_size;
+{
+ deflate_state *s;
+ int wrap = 1;
+ static const char my_version[] = ZLIB_VERSION;
+
+ ushf *overlay;
+ /* We overlay pending_buf and d_buf+l_buf. This works since the average
+ * output size for (length,distance) codes is <= 24 bits.
+ */
+
+ if (version == Z_NULL || version[0] != my_version[0] ||
+ stream_size != sizeof(z_stream)) {
+ return Z_VERSION_ERROR;
+ }
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->msg = Z_NULL;
+ if (strm->zalloc == (alloc_func)0) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+
+#ifdef FASTEST
+ if (level != 0) level = 1;
+#else
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+
+ if (windowBits < 0) { /* suppress zlib wrapper */
+ wrap = 0;
+ windowBits = -windowBits;
+ }
+#ifdef GZIP
+ else if (windowBits > 15) {
+ wrap = 2; /* write gzip wrapper instead */
+ windowBits -= 16;
+ }
+#endif
+ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
+ windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
+ strategy < 0 || strategy > Z_FIXED) {
+ return Z_STREAM_ERROR;
+ }
+ if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */
+ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+ if (s == Z_NULL) return Z_MEM_ERROR;
+ strm->state = (struct internal_state FAR *)s;
+ s->strm = strm;
+
+ s->wrap = wrap;
+ s->gzhead = Z_NULL;
+ s->w_bits = windowBits;
+ s->w_size = 1 << s->w_bits;
+ s->w_mask = s->w_size - 1;
+
+ s->hash_bits = memLevel + 7;
+ s->hash_size = 1 << s->hash_bits;
+ s->hash_mask = s->hash_size - 1;
+ s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+ s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+ s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
+ s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+ s->high_water = 0; /* nothing written to s->window yet */
+
+ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
+ s->pending_buf = (uchf *) overlay;
+ s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
+
+ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+ s->pending_buf == Z_NULL) {
+ s->status = FINISH_STATE;
+ strm->msg = (char*)ERR_MSG(Z_MEM_ERROR);
+ deflateEnd (strm);
+ return Z_MEM_ERROR;
+ }
+ s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
+ s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
+
+ s->level = level;
+ s->strategy = strategy;
+ s->method = (Byte)method;
+
+ return deflateReset(strm);
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
+ z_streamp strm;
+ const Bytef *dictionary;
+ uInt dictLength;
+{
+ deflate_state *s;
+ uInt length = dictLength;
+ uInt n;
+ IPos hash_head = 0;
+
+ if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL ||
+ strm->state->wrap == 2 ||
+ (strm->state->wrap == 1 && strm->state->status != INIT_STATE))
+ return Z_STREAM_ERROR;
+
+ s = strm->state;
+ if (s->wrap)
+ strm->adler = adler32(strm->adler, dictionary, dictLength);
+
+ if (length < MIN_MATCH) return Z_OK;
+ if (length > s->w_size) {
+ length = s->w_size;
+ dictionary += dictLength - length; /* use the tail of the dictionary */
+ }
+ zmemcpy(s->window, dictionary, length);
+ s->strstart = length;
+ s->block_start = (long)length;
+
+ /* Insert all strings in the hash table (except for the last two bytes).
+ * s->lookahead stays null, so s->ins_h will be recomputed at the next
+ * call of fill_window.
+ */
+ s->ins_h = s->window[0];
+ UPDATE_HASH(s, s->ins_h, s->window[1]);
+ for (n = 0; n <= length - MIN_MATCH; n++) {
+ INSERT_STRING(s, n, hash_head);
+ }
+ if (hash_head) hash_head = 0; /* to make compiler happy */
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateReset (strm)
+ z_streamp strm;
+{
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) {
+ return Z_STREAM_ERROR;
+ }
+
+ strm->total_in = strm->total_out = 0;
+ strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+ strm->data_type = Z_UNKNOWN;
+
+ s = (deflate_state *)strm->state;
+ s->pending = 0;
+ s->pending_out = s->pending_buf;
+
+ if (s->wrap < 0) {
+ s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */
+ }
+ s->status = s->wrap ? INIT_STATE : BUSY_STATE;
+ strm->adler =
+#ifdef GZIP
+ s->wrap == 2 ? crc32(0L, Z_NULL, 0) :
+#endif
+ adler32(0L, Z_NULL, 0);
+ s->last_flush = Z_NO_FLUSH;
+
+ _tr_init(s);
+ lm_init(s);
+
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetHeader (strm, head)
+ z_streamp strm;
+ gz_headerp head;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ if (strm->state->wrap != 2) return Z_STREAM_ERROR;
+ strm->state->gzhead = head;
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflatePrime (strm, bits, value)
+ z_streamp strm;
+ int bits;
+ int value;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ strm->state->bi_valid = bits;
+ strm->state->bi_buf = (ush)(value & ((1 << bits) - 1));
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateParams(strm, level, strategy)
+ z_streamp strm;
+ int level;
+ int strategy;
+{
+ deflate_state *s;
+ compress_func func;
+ int err = Z_OK;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = strm->state;
+
+#ifdef FASTEST
+ if (level != 0) level = 1;
+#else
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+ if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) {
+ return Z_STREAM_ERROR;
+ }
+ func = configuration_table[s->level].func;
+
+ if ((strategy != s->strategy || func != configuration_table[level].func) &&
+ strm->total_in != 0) {
+ /* Flush the last buffer: */
+ err = deflate(strm, Z_BLOCK);
+ }
+ if (s->level != level) {
+ s->level = level;
+ s->max_lazy_match = configuration_table[level].max_lazy;
+ s->good_match = configuration_table[level].good_length;
+ s->nice_match = configuration_table[level].nice_length;
+ s->max_chain_length = configuration_table[level].max_chain;
+ }
+ s->strategy = strategy;
+ return err;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)
+ z_streamp strm;
+ int good_length;
+ int max_lazy;
+ int nice_length;
+ int max_chain;
+{
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = strm->state;
+ s->good_match = good_length;
+ s->max_lazy_match = max_lazy;
+ s->nice_match = nice_length;
+ s->max_chain_length = max_chain;
+ return Z_OK;
+}
+
+/* =========================================================================
+ * For the default windowBits of 15 and memLevel of 8, this function returns
+ * a close to exact, as well as small, upper bound on the compressed size.
+ * They are coded as constants here for a reason--if the #define's are
+ * changed, then this function needs to be changed as well. The return
+ * value for 15 and 8 only works for those exact settings.
+ *
+ * For any setting other than those defaults for windowBits and memLevel,
+ * the value returned is a conservative worst case for the maximum expansion
+ * resulting from using fixed blocks instead of stored blocks, which deflate
+ * can emit on compressed data for some combinations of the parameters.
+ *
+ * This function could be more sophisticated to provide closer upper bounds for
+ * every combination of windowBits and memLevel. But even the conservative
+ * upper bound of about 14% expansion does not seem onerous for output buffer
+ * allocation.
+ */
+uLong ZEXPORT deflateBound(strm, sourceLen)
+ z_streamp strm;
+ uLong sourceLen;
+{
+ deflate_state *s;
+ uLong complen, wraplen;
+ Bytef *str;
+
+ /* conservative upper bound for compressed data */
+ complen = sourceLen +
+ ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;
+
+ /* if can't get parameters, return conservative bound plus zlib wrapper */
+ if (strm == Z_NULL || strm->state == Z_NULL)
+ return complen + 6;
+
+ /* compute wrapper length */
+ s = strm->state;
+ switch (s->wrap) {
+ case 0: /* raw deflate */
+ wraplen = 0;
+ break;
+ case 1: /* zlib wrapper */
+ wraplen = 6 + (s->strstart ? 4 : 0);
+ break;
+ case 2: /* gzip wrapper */
+ wraplen = 18;
+ if (s->gzhead != Z_NULL) { /* user-supplied gzip header */
+ if (s->gzhead->extra != Z_NULL)
+ wraplen += 2 + s->gzhead->extra_len;
+ str = s->gzhead->name;
+ if (str != Z_NULL)
+ do {
+ wraplen++;
+ } while (*str++);
+ str = s->gzhead->comment;
+ if (str != Z_NULL)
+ do {
+ wraplen++;
+ } while (*str++);
+ if (s->gzhead->hcrc)
+ wraplen += 2;
+ }
+ break;
+ default: /* for compiler happiness */
+ wraplen = 6;
+ }
+
+ /* if not default parameters, return conservative bound */
+ if (s->w_bits != 15 || s->hash_bits != 8 + 7)
+ return complen + wraplen;
+
+ /* default settings: return tight bound for that case */
+ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
+ (sourceLen >> 25) + 13 - 6 + wraplen;
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+ deflate_state *s;
+ uInt b;
+{
+ put_byte(s, (Byte)(b >> 8));
+ put_byte(s, (Byte)(b & 0xff));
+}
+
+/* =========================================================================
+ * Flush as much pending output as possible. All deflate() output goes
+ * through this function so some applications may wish to modify it
+ * to avoid allocating a large strm->next_out buffer and copying into it.
+ * (See also read_buf()).
+ */
+local void flush_pending(strm)
+ z_streamp strm;
+{
+ unsigned len = strm->state->pending;
+
+ if (len > strm->avail_out) len = strm->avail_out;
+ if (len == 0) return;
+
+ zmemcpy(strm->next_out, strm->state->pending_out, len);
+ strm->next_out += len;
+ strm->state->pending_out += len;
+ strm->total_out += len;
+ strm->avail_out -= len;
+ strm->state->pending -= len;
+ if (strm->state->pending == 0) {
+ strm->state->pending_out = strm->state->pending_buf;
+ }
+}
+
+/* ========================================================================= */
+int ZEXPORT deflate (strm, flush)
+ z_streamp strm;
+ int flush;
+{
+ int old_flush; /* value of flush param for previous deflate call */
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ flush > Z_BLOCK || flush < 0) {
+ return Z_STREAM_ERROR;
+ }
+ s = strm->state;
+
+ if (strm->next_out == Z_NULL ||
+ (strm->next_in == Z_NULL && strm->avail_in != 0) ||
+ (s->status == FINISH_STATE && flush != Z_FINISH)) {
+ ERR_RETURN(strm, Z_STREAM_ERROR);
+ }
+ if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+ s->strm = strm; /* just in case */
+ old_flush = s->last_flush;
+ s->last_flush = flush;
+
+ /* Write the header */
+ if (s->status == INIT_STATE) {
+#ifdef GZIP
+ if (s->wrap == 2) {
+ strm->adler = crc32(0L, Z_NULL, 0);
+ put_byte(s, 31);
+ put_byte(s, 139);
+ put_byte(s, 8);
+ if (s->gzhead == Z_NULL) {
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, s->level == 9 ? 2 :
+ (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
+ 4 : 0));
+ put_byte(s, OS_CODE);
+ s->status = BUSY_STATE;
+ }
+ else {
+ put_byte(s, (s->gzhead->text ? 1 : 0) +
+ (s->gzhead->hcrc ? 2 : 0) +
+ (s->gzhead->extra == Z_NULL ? 0 : 4) +
+ (s->gzhead->name == Z_NULL ? 0 : 8) +
+ (s->gzhead->comment == Z_NULL ? 0 : 16)
+ );
+ put_byte(s, (Byte)(s->gzhead->time & 0xff));
+ put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff));
+ put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff));
+ put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff));
+ put_byte(s, s->level == 9 ? 2 :
+ (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
+ 4 : 0));
+ put_byte(s, s->gzhead->os & 0xff);
+ if (s->gzhead->extra != Z_NULL) {
+ put_byte(s, s->gzhead->extra_len & 0xff);
+ put_byte(s, (s->gzhead->extra_len >> 8) & 0xff);
+ }
+ if (s->gzhead->hcrc)
+ strm->adler = crc32(strm->adler, s->pending_buf,
+ s->pending);
+ s->gzindex = 0;
+ s->status = EXTRA_STATE;
+ }
+ }
+ else
+#endif
+ {
+ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+ uInt level_flags;
+
+ if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
+ level_flags = 0;
+ else if (s->level < 6)
+ level_flags = 1;
+ else if (s->level == 6)
+ level_flags = 2;
+ else
+ level_flags = 3;
+ header |= (level_flags << 6);
+ if (s->strstart != 0) header |= PRESET_DICT;
+ header += 31 - (header % 31);
+
+ s->status = BUSY_STATE;
+ putShortMSB(s, header);
+
+ /* Save the adler32 of the preset dictionary: */
+ if (s->strstart != 0) {
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ }
+ strm->adler = adler32(0L, Z_NULL, 0);
+ }
+ }
+#ifdef GZIP
+ if (s->status == EXTRA_STATE) {
+ if (s->gzhead->extra != Z_NULL) {
+ uInt beg = s->pending; /* start of bytes to update crc */
+
+ while (s->gzindex < (s->gzhead->extra_len & 0xffff)) {
+ if (s->pending == s->pending_buf_size) {
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ flush_pending(strm);
+ beg = s->pending;
+ if (s->pending == s->pending_buf_size)
+ break;
+ }
+ put_byte(s, s->gzhead->extra[s->gzindex]);
+ s->gzindex++;
+ }
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ if (s->gzindex == s->gzhead->extra_len) {
+ s->gzindex = 0;
+ s->status = NAME_STATE;
+ }
+ }
+ else
+ s->status = NAME_STATE;
+ }
+ if (s->status == NAME_STATE) {
+ if (s->gzhead->name != Z_NULL) {
+ uInt beg = s->pending; /* start of bytes to update crc */
+ int val;
+
+ do {
+ if (s->pending == s->pending_buf_size) {
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ flush_pending(strm);
+ beg = s->pending;
+ if (s->pending == s->pending_buf_size) {
+ val = 1;
+ break;
+ }
+ }
+ val = s->gzhead->name[s->gzindex++];
+ put_byte(s, val);
+ } while (val != 0);
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ if (val == 0) {
+ s->gzindex = 0;
+ s->status = COMMENT_STATE;
+ }
+ }
+ else
+ s->status = COMMENT_STATE;
+ }
+ if (s->status == COMMENT_STATE) {
+ if (s->gzhead->comment != Z_NULL) {
+ uInt beg = s->pending; /* start of bytes to update crc */
+ int val;
+
+ do {
+ if (s->pending == s->pending_buf_size) {
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ flush_pending(strm);
+ beg = s->pending;
+ if (s->pending == s->pending_buf_size) {
+ val = 1;
+ break;
+ }
+ }
+ val = s->gzhead->comment[s->gzindex++];
+ put_byte(s, val);
+ } while (val != 0);
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ if (val == 0)
+ s->status = HCRC_STATE;
+ }
+ else
+ s->status = HCRC_STATE;
+ }
+ if (s->status == HCRC_STATE) {
+ if (s->gzhead->hcrc) {
+ if (s->pending + 2 > s->pending_buf_size)
+ flush_pending(strm);
+ if (s->pending + 2 <= s->pending_buf_size) {
+ put_byte(s, (Byte)(strm->adler & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
+ strm->adler = crc32(0L, Z_NULL, 0);
+ s->status = BUSY_STATE;
+ }
+ }
+ else
+ s->status = BUSY_STATE;
+ }
+#endif
+
+ /* Flush as much pending output as possible */
+ if (s->pending != 0) {
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ /* Since avail_out is 0, deflate will be called again with
+ * more output space, but possibly with both pending and
+ * avail_in equal to zero. There won't be anything to do,
+ * but this is not an error situation so make sure we
+ * return OK instead of BUF_ERROR at next call of deflate:
+ */
+ s->last_flush = -1;
+ return Z_OK;
+ }
+
+ /* Make sure there is something to do and avoid duplicate consecutive
+ * flushes. For repeated and useless calls with Z_FINISH, we keep
+ * returning Z_STREAM_END instead of Z_BUF_ERROR.
+ */
+ } else if (strm->avail_in == 0 && flush <= old_flush &&
+ flush != Z_FINISH) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* User must not provide more input after the first FINISH: */
+ if (s->status == FINISH_STATE && strm->avail_in != 0) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* Start a new block or continue the current one.
+ */
+ if (strm->avail_in != 0 || s->lookahead != 0 ||
+ (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
+ block_state bstate;
+
+ bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) :
+ (s->strategy == Z_RLE ? deflate_rle(s, flush) :
+ (*(configuration_table[s->level].func))(s, flush));
+
+ if (bstate == finish_started || bstate == finish_done) {
+ s->status = FINISH_STATE;
+ }
+ if (bstate == need_more || bstate == finish_started) {
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
+ }
+ return Z_OK;
+ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+ * of deflate should use the same flush parameter to make sure
+ * that the flush is complete. So we don't have to output an
+ * empty block here, this will be done at next call. This also
+ * ensures that for a very small output buffer, we emit at most
+ * one empty block.
+ */
+ }
+ if (bstate == block_done) {
+ if (flush == Z_PARTIAL_FLUSH) {
+ _tr_align(s);
+ } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */
+ _tr_stored_block(s, (char*)0, 0L, 0);
+ /* For a full flush, this empty block will be recognized
+ * as a special marker by inflate_sync().
+ */
+ if (flush == Z_FULL_FLUSH) {
+ CLEAR_HASH(s); /* forget history */
+ if (s->lookahead == 0) {
+ s->strstart = 0;
+ s->block_start = 0L;
+ }
+ }
+ }
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
+ return Z_OK;
+ }
+ }
+ }
+ Assert(strm->avail_out > 0, "bug2");
+
+ if (flush != Z_FINISH) return Z_OK;
+ if (s->wrap <= 0) return Z_STREAM_END;
+
+ /* Write the trailer */
+#ifdef GZIP
+ if (s->wrap == 2) {
+ put_byte(s, (Byte)(strm->adler & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 16) & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 24) & 0xff));
+ put_byte(s, (Byte)(strm->total_in & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 8) & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 16) & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 24) & 0xff));
+ }
+ else
+#endif
+ {
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ }
+ flush_pending(strm);
+ /* If avail_out is zero, the application will call deflate again
+ * to flush the rest.
+ */
+ if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */
+ return s->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateEnd (strm)
+ z_streamp strm;
+{
+ int status;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+
+ status = strm->state->status;
+ if (status != INIT_STATE &&
+ status != EXTRA_STATE &&
+ status != NAME_STATE &&
+ status != COMMENT_STATE &&
+ status != HCRC_STATE &&
+ status != BUSY_STATE &&
+ status != FINISH_STATE) {
+ return Z_STREAM_ERROR;
+ }
+
+ /* Deallocate in reverse order of allocations: */
+ TRY_FREE(strm, strm->state->pending_buf);
+ TRY_FREE(strm, strm->state->head);
+ TRY_FREE(strm, strm->state->prev);
+ TRY_FREE(strm, strm->state->window);
+
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+
+ return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
+}
+
+/* =========================================================================
+ * Copy the source state to the destination state.
+ * To simplify the source, this is not supported for 16-bit MSDOS (which
+ * doesn't have enough memory anyway to duplicate compression states).
+ */
+int ZEXPORT deflateCopy (dest, source)
+ z_streamp dest;
+ z_streamp source;
+{
+#ifdef MAXSEG_64K
+ return Z_STREAM_ERROR;
+#else
+ deflate_state *ds;
+ deflate_state *ss;
+ ushf *overlay;
+
+
+ if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) {
+ return Z_STREAM_ERROR;
+ }
+
+ ss = source->state;
+
+ zmemcpy(dest, source, sizeof(z_stream));
+
+ ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
+ if (ds == Z_NULL) return Z_MEM_ERROR;
+ dest->state = (struct internal_state FAR *) ds;
+ zmemcpy(ds, ss, sizeof(deflate_state));
+ ds->strm = dest;
+
+ ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
+ ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
+ ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
+ overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
+ ds->pending_buf = (uchf *) overlay;
+
+ if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
+ ds->pending_buf == Z_NULL) {
+ deflateEnd (dest);
+ return Z_MEM_ERROR;
+ }
+ /* following zmemcpy do not work for 16-bit MSDOS */
+ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
+ zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos));
+ zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
+ zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
+
+ ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
+ ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
+ ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
+
+ ds->l_desc.dyn_tree = ds->dyn_ltree;
+ ds->d_desc.dyn_tree = ds->dyn_dtree;
+ ds->bl_desc.dyn_tree = ds->bl_tree;
+
+ return Z_OK;
+#endif /* MAXSEG_64K */
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read. All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->next_in buffer and copying from it.
+ * (See also flush_pending()).
+ */
+local int read_buf(strm, buf, size)
+ z_streamp strm;
+ Bytef *buf;
+ unsigned size;
+{
+ unsigned len = strm->avail_in;
+
+ if (len > size) len = size;
+ if (len == 0) return 0;
+
+ strm->avail_in -= len;
+
+ if (strm->state->wrap == 1) {
+ strm->adler = adler32(strm->adler, strm->next_in, len);
+ }
+#ifdef GZIP
+ else if (strm->state->wrap == 2) {
+ strm->adler = crc32(strm->adler, strm->next_in, len);
+ }
+#endif
+ zmemcpy(buf, strm->next_in, len);
+ strm->next_in += len;
+ strm->total_in += len;
+
+ return (int)len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+ deflate_state *s;
+{
+ s->window_size = (ulg)2L*s->w_size;
+
+ CLEAR_HASH(s);
+
+ /* Set the default configuration parameters:
+ */
+ s->max_lazy_match = configuration_table[s->level].max_lazy;
+ s->good_match = configuration_table[s->level].good_length;
+ s->nice_match = configuration_table[s->level].nice_length;
+ s->max_chain_length = configuration_table[s->level].max_chain;
+
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->lookahead = 0;
+ s->match_length = s->prev_length = MIN_MATCH-1;
+ s->match_available = 0;
+ s->ins_h = 0;
+#ifndef FASTEST
+#ifdef ASMV
+ match_init(); /* initialize the asm code */
+#endif
+#endif
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ * OUT assertion: the match length is not greater than s->lookahead.
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+local uInt longest_match(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ unsigned chain_length = s->max_chain_length;/* max hash chain length */
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ int best_len = s->prev_length; /* best match length so far */
+ int nice_match = s->nice_match; /* stop if match long enough */
+ IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+ s->strstart - (IPos)MAX_DIST(s) : NIL;
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+ Posf *prev = s->prev;
+ uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+ /* Compare two bytes at a time. Note: this is not always beneficial.
+ * Try with and without -DUNALIGNED_OK to check.
+ */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+ register ush scan_start = *(ushf*)scan;
+ register ush scan_end = *(ushf*)(scan+best_len-1);
+#else
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+ register Byte scan_end1 = scan[best_len-1];
+ register Byte scan_end = scan[best_len];
+#endif
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ /* Do not waste too much time if we already have a good match: */
+ if (s->prev_length >= s->good_match) {
+ chain_length >>= 2;
+ }
+ /* Do not look for matches beyond the end of the input. This is necessary
+ * to make deflate deterministic.
+ */
+ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ do {
+ Assert(cur_match < s->strstart, "no future");
+ match = s->window + cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2. Note that the checks below
+ * for insufficient lookahead only occur occasionally for performance
+ * reasons. Therefore uninitialized memory will be accessed, and
+ * conditional jumps will be made that depend on those values.
+ * However the length of the match is limited to the lookahead, so
+ * the output of deflate is not affected by the uninitialized values.
+ */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+ /* This code assumes sizeof(unsigned short) == 2. Do not use
+ * UNALIGNED_OK if your compiler uses a different size.
+ */
+ if (*(ushf*)(match+best_len-1) != scan_end ||
+ *(ushf*)match != scan_start) continue;
+
+ /* It is not necessary to compare scan[2] and match[2] since they are
+ * always equal when the other bytes match, given that the hash keys
+ * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+ * strstart+3, +5, ... up to strstart+257. We check for insufficient
+ * lookahead only every 4th comparison; the 128th check will be made
+ * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+ * necessary to put more guard bytes at the end of the window, or
+ * to check more often for insufficient lookahead.
+ */
+ Assert(scan[2] == match[2], "scan[2]?");
+ scan++, match++;
+ do {
+ } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ scan < strend);
+ /* The funny "do {}" generates better code on most compilers */
+
+ /* Here, scan <= window+strstart+257 */
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+ if (*scan == *match) scan++;
+
+ len = (MAX_MATCH - 1) - (int)(strend-scan);
+ scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+ if (match[best_len] != scan_end ||
+ match[best_len-1] != scan_end1 ||
+ *match != *scan ||
+ *++match != scan[1]) continue;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match++;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+ scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+ if (len > best_len) {
+ s->match_start = cur_match;
+ best_len = len;
+ if (len >= nice_match) break;
+#ifdef UNALIGNED_OK
+ scan_end = *(ushf*)(scan+best_len-1);
+#else
+ scan_end1 = scan[best_len-1];
+ scan_end = scan[best_len];
+#endif
+ }
+ } while ((cur_match = prev[cur_match & wmask]) > limit
+ && --chain_length != 0);
+
+ if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
+ return s->lookahead;
+}
+#endif /* ASMV */
+
+#else /* FASTEST */
+
+/* ---------------------------------------------------------------------------
+ * Optimized version for FASTEST only
+ */
+local uInt longest_match(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ Assert(cur_match < s->strstart, "no future");
+
+ match = s->window + cur_match;
+
+ /* Return failure if the match length is less than 2:
+ */
+ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match += 2;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+
+ if (len < MIN_MATCH) return MIN_MATCH - 1;
+
+ s->match_start = cur_match;
+ return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead;
+}
+
+#endif /* FASTEST */
+
+#ifdef DEBUG
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+ deflate_state *s;
+ IPos start, match;
+ int length;
+{
+ /* check that the match is indeed a match */
+ if (zmemcmp(s->window + match,
+ s->window + start, length) != EQUAL) {
+ fprintf(stderr, " start %u, match %u, length %d\n",
+ start, match, length);
+ do {
+ fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
+ } while (--length != 0);
+ z_error("invalid match");
+ }
+ if (z_verbose > 1) {
+ fprintf(stderr,"\\[%d,%d]", start-match, length);
+ do { putc(s->window[start++], stderr); } while (--length != 0);
+ }
+}
+#else
+# define check_match(s, start, match, length)
+#endif /* DEBUG */
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ * At least one byte has been read, or avail_in == 0; reads are
+ * performed for at least two bytes (required for the zip translate_eol
+ * option -- not supported here).
+ */
+local void fill_window(s)
+ deflate_state *s;
+{
+ register unsigned n, m;
+ register Posf *p;
+ unsigned more; /* Amount of free space at the end of the window. */
+ uInt wsize = s->w_size;
+
+ do {
+ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+ /* Deal with !@#$% 64K limit: */
+ if (sizeof(int) <= 2) {
+ if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+ more = wsize;
+
+ } else if (more == (unsigned)(-1)) {
+ /* Very unlikely, but possible on 16 bit machine if
+ * strstart == 0 && lookahead == 1 (input done a byte at time)
+ */
+ more--;
+ }
+ }
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ if (s->strstart >= wsize+MAX_DIST(s)) {
+
+ zmemcpy(s->window, s->window+wsize, (unsigned)wsize);
+ s->match_start -= wsize;
+ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
+ s->block_start -= (long) wsize;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ at the expense of memory usage). We slide even when level == 0
+ to keep the hash table consistent if we switch back to level > 0
+ later. (Using level 0 permanently is not an optimal usage of
+ zlib, so we don't care about this pathological case.)
+ */
+ n = s->hash_size;
+ p = &s->head[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ } while (--n);
+
+ n = wsize;
+#ifndef FASTEST
+ p = &s->prev[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ * its value will never be used.
+ */
+ } while (--n);
+#endif
+ more += wsize;
+ }
+ if (s->strm->avail_in == 0) return;
+
+ /* If there was no sliding:
+ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+ * more == window_size - lookahead - strstart
+ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+ * => more >= window_size - 2*WSIZE + 2
+ * In the BIG_MEM or MMAP case (not yet supported),
+ * window_size == input_size + MIN_LOOKAHEAD &&
+ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+ * Otherwise, window_size == 2*WSIZE so more >= 2.
+ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ Assert(more >= 2, "more < 2");
+
+ n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
+ s->lookahead += n;
+
+ /* Initialize the hash value now that we have some input: */
+ if (s->lookahead >= MIN_MATCH) {
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ }
+ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+ * but this is not important since only literal bytes will be emitted.
+ */
+
+ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+
+ /* If the WIN_INIT bytes after the end of the current data have never been
+ * written, then zero those bytes in order to avoid memory check reports of
+ * the use of uninitialized (or uninitialised as Julian writes) bytes by
+ * the longest match routines. Update the high water mark for the next
+ * time through here. WIN_INIT is set to MAX_MATCH since the longest match
+ * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
+ */
+ if (s->high_water < s->window_size) {
+ ulg curr = s->strstart + (ulg)(s->lookahead);
+ ulg init;
+
+ if (s->high_water < curr) {
+ /* Previous high water mark below current data -- zero WIN_INIT
+ * bytes or up to end of window, whichever is less.
+ */
+ init = s->window_size - curr;
+ if (init > WIN_INIT)
+ init = WIN_INIT;
+ zmemzero(s->window + curr, (unsigned)init);
+ s->high_water = curr + init;
+ }
+ else if (s->high_water < (ulg)curr + WIN_INIT) {
+ /* High water mark at or above current data, but below current data
+ * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
+ * to end of window, whichever is less.
+ */
+ init = (ulg)curr + WIN_INIT - s->high_water;
+ if (init > s->window_size - s->high_water)
+ init = s->window_size - s->high_water;
+ zmemzero(s->window + s->high_water, (unsigned)init);
+ s->high_water += init;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, last) { \
+ _tr_flush_block(s, (s->block_start >= 0L ? \
+ (charf *)&s->window[(unsigned)s->block_start] : \
+ (charf *)Z_NULL), \
+ (ulg)((long)s->strstart - s->block_start), \
+ (last)); \
+ s->block_start = s->strstart; \
+ flush_pending(s->strm); \
+ Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, last) { \
+ FLUSH_BLOCK_ONLY(s, last); \
+ if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \
+}
+
+/* ===========================================================================
+ * Copy without compression as much as possible from the input stream, return
+ * the current block state.
+ * This function does not insert new strings in the dictionary since
+ * uncompressible data is probably not useful. This function is used
+ * only for the level=0 compression option.
+ * NOTE: this function should be optimized to avoid extra copying from
+ * window to pending_buf.
+ */
+local block_state deflate_stored(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
+ * to pending_buf_size, and each stored block has a 5 byte header:
+ */
+ ulg max_block_size = 0xffff;
+ ulg max_start;
+
+ if (max_block_size > s->pending_buf_size - 5) {
+ max_block_size = s->pending_buf_size - 5;
+ }
+
+ /* Copy as much as possible from input to output: */
+ for (;;) {
+ /* Fill the window as much as possible: */
+ if (s->lookahead <= 1) {
+
+ Assert(s->strstart < s->w_size+MAX_DIST(s) ||
+ s->block_start >= (long)s->w_size, "slide too late");
+
+ fill_window(s);
+ if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
+
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+ Assert(s->block_start >= 0L, "block gone");
+
+ s->strstart += s->lookahead;
+ s->lookahead = 0;
+
+ /* Emit a stored block if pending_buf will be full: */
+ max_start = s->block_start + max_block_size;
+ if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
+ /* strstart == 0 is possible when wraparound on 16-bit machine */
+ s->lookahead = (uInt)(s->strstart - max_start);
+ s->strstart = (uInt)max_start;
+ FLUSH_BLOCK(s, 0);
+ }
+ /* Flush if we may have to slide, otherwise block_start may become
+ * negative and the data will be gone:
+ */
+ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
+ FLUSH_BLOCK(s, 0);
+ }
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return the current
+ * block state.
+ * This function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local block_state deflate_fast(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head; /* head of the hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ hash_head = NIL;
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ * At this point we have always match_length < MIN_MATCH
+ */
+ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ s->match_length = longest_match (s, hash_head);
+ /* longest_match() sets match_start */
+ }
+ if (s->match_length >= MIN_MATCH) {
+ check_match(s, s->strstart, s->match_start, s->match_length);
+
+ _tr_tally_dist(s, s->strstart - s->match_start,
+ s->match_length - MIN_MATCH, bflush);
+
+ s->lookahead -= s->match_length;
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+#ifndef FASTEST
+ if (s->match_length <= s->max_insert_length &&
+ s->lookahead >= MIN_MATCH) {
+ s->match_length--; /* string at strstart already in table */
+ do {
+ s->strstart++;
+ INSERT_STRING(s, s->strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead.
+ */
+ } while (--s->match_length != 0);
+ s->strstart++;
+ } else
+#endif
+ {
+ s->strstart += s->match_length;
+ s->match_length = 0;
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+ * matter since it will be recomputed at next deflate call.
+ */
+ }
+ } else {
+ /* No match, output a literal byte */
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ _tr_tally_lit (s, s->window[s->strstart], bflush);
+ s->lookahead--;
+ s->strstart++;
+ }
+ if (bflush) FLUSH_BLOCK(s, 0);
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local block_state deflate_slow(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head; /* head of hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ /* Process the input block. */
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ hash_head = NIL;
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ s->prev_length = s->match_length, s->prev_match = s->match_start;
+ s->match_length = MIN_MATCH-1;
+
+ if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+ s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ s->match_length = longest_match (s, hash_head);
+ /* longest_match() sets match_start */
+
+ if (s->match_length <= 5 && (s->strategy == Z_FILTERED
+#if TOO_FAR <= 32767
+ || (s->match_length == MIN_MATCH &&
+ s->strstart - s->match_start > TOO_FAR)
+#endif
+ )) {
+
+ /* If prev_match is also MIN_MATCH, match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ s->match_length = MIN_MATCH-1;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+ uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+ /* Do not insert strings in hash table beyond this. */
+
+ check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+ _tr_tally_dist(s, s->strstart -1 - s->prev_match,
+ s->prev_length - MIN_MATCH, bflush);
+
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted. If there is not
+ * enough lookahead, the last two strings are not inserted in
+ * the hash table.
+ */
+ s->lookahead -= s->prev_length-1;
+ s->prev_length -= 2;
+ do {
+ if (++s->strstart <= max_insert) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+ } while (--s->prev_length != 0);
+ s->match_available = 0;
+ s->match_length = MIN_MATCH-1;
+ s->strstart++;
+
+ if (bflush) FLUSH_BLOCK(s, 0);
+
+ } else if (s->match_available) {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ if (bflush) {
+ FLUSH_BLOCK_ONLY(s, 0);
+ }
+ s->strstart++;
+ s->lookahead--;
+ if (s->strm->avail_out == 0) return need_more;
+ } else {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ s->match_available = 1;
+ s->strstart++;
+ s->lookahead--;
+ }
+ }
+ Assert (flush != Z_NO_FLUSH, "no flush?");
+ if (s->match_available) {
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ s->match_available = 0;
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+#endif /* FASTEST */
+
+/* ===========================================================================
+ * For Z_RLE, simply look for runs of bytes, generate matches only of distance
+ * one. Do not maintain a hash table. (It will be regenerated if this run of
+ * deflate switches away from Z_RLE.)
+ */
+local block_state deflate_rle(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ int bflush; /* set if current block must be flushed */
+ uInt prev; /* byte at distance one to match */
+ Bytef *scan, *strend; /* scan goes up to strend for length of run */
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the longest encodable run.
+ */
+ if (s->lookahead < MAX_MATCH) {
+ fill_window(s);
+ if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* See how many times the previous byte repeats */
+ s->match_length = 0;
+ if (s->lookahead >= MIN_MATCH && s->strstart > 0) {
+ scan = s->window + s->strstart - 1;
+ prev = *scan;
+ if (prev == *++scan && prev == *++scan && prev == *++scan) {
+ strend = s->window + s->strstart + MAX_MATCH;
+ do {
+ } while (prev == *++scan && prev == *++scan &&
+ prev == *++scan && prev == *++scan &&
+ prev == *++scan && prev == *++scan &&
+ prev == *++scan && prev == *++scan &&
+ scan < strend);
+ s->match_length = MAX_MATCH - (int)(strend - scan);
+ if (s->match_length > s->lookahead)
+ s->match_length = s->lookahead;
+ }
+ }
+
+ /* Emit match if have run of MIN_MATCH or longer, else emit literal */
+ if (s->match_length >= MIN_MATCH) {
+ check_match(s, s->strstart, s->strstart - 1, s->match_length);
+
+ _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush);
+
+ s->lookahead -= s->match_length;
+ s->strstart += s->match_length;
+ s->match_length = 0;
+ } else {
+ /* No match, output a literal byte */
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ _tr_tally_lit (s, s->window[s->strstart], bflush);
+ s->lookahead--;
+ s->strstart++;
+ }
+ if (bflush) FLUSH_BLOCK(s, 0);
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table.
+ * (It will be regenerated if this run of deflate switches away from Huffman.)
+ */
+local block_state deflate_huff(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ int bflush; /* set if current block must be flushed */
+
+ for (;;) {
+ /* Make sure that we have a literal to write. */
+ if (s->lookahead == 0) {
+ fill_window(s);
+ if (s->lookahead == 0) {
+ if (flush == Z_NO_FLUSH)
+ return need_more;
+ break; /* flush the current block */
+ }
+ }
+
+ /* Output a literal byte */
+ s->match_length = 0;
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ _tr_tally_lit (s, s->window[s->strstart], bflush);
+ s->lookahead--;
+ s->strstart++;
+ if (bflush) FLUSH_BLOCK(s, 0);
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
diff --git a/src/3rdparty/assimp/contrib/zlib/deflate.h b/src/3rdparty/assimp/contrib/zlib/deflate.h
new file mode 100644
index 000000000..cbf0d1ea5
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/deflate.h
@@ -0,0 +1,342 @@
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995-2010 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef DEFLATE_H
+#define DEFLATE_H
+
+#include "zutil.h"
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+ trailer creation by deflate(). NO_GZIP would be used to avoid linking in
+ the crc code when it is not needed. For shared libraries, gzip encoding
+ should be left enabled. */
+#ifndef NO_GZIP
+# define GZIP
+#endif
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS 256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES 30
+/* number of distance codes */
+
+#define BL_CODES 19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define INIT_STATE 42
+#define EXTRA_STATE 69
+#define NAME_STATE 73
+#define COMMENT_STATE 91
+#define HCRC_STATE 103
+#define BUSY_STATE 113
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+ union {
+ ush freq; /* frequency count */
+ ush code; /* bit string */
+ } fc;
+ union {
+ ush dad; /* father node in Huffman tree */
+ ush len; /* length of bit string */
+ } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad dl.dad
+#define Len dl.len
+
+typedef struct static_tree_desc_s static_tree_desc;
+
+typedef struct tree_desc_s {
+ ct_data *dyn_tree; /* the dynamic tree */
+ int max_code; /* largest code with non zero frequency */
+ static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct internal_state {
+ z_streamp strm; /* pointer back to this zlib stream */
+ int status; /* as the name implies */
+ Bytef *pending_buf; /* output still pending */
+ ulg pending_buf_size; /* size of pending_buf */
+ Bytef *pending_out; /* next pending byte to output to the stream */
+ uInt pending; /* nb of bytes in the pending buffer */
+ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
+ gz_headerp gzhead; /* gzip header information to write */
+ uInt gzindex; /* where in extra, name, or comment */
+ Byte method; /* STORED (for zip only) or DEFLATED */
+ int last_flush; /* value of flush param for previous deflate call */
+
+ /* used by deflate.c: */
+
+ uInt w_size; /* LZ77 window size (32K by default) */
+ uInt w_bits; /* log2(w_size) (8..16) */
+ uInt w_mask; /* w_size - 1 */
+
+ Bytef *window;
+ /* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least wSize
+ * bytes. With this organization, matches are limited to a distance of
+ * wSize-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size. Also, it limits
+ * the window size to 64K, which is quite useful on MSDOS.
+ * To do: use the user input buffer as sliding window.
+ */
+
+ ulg window_size;
+ /* Actual size of window: 2*wSize, except when the user input buffer
+ * is directly used as sliding window.
+ */
+
+ Posf *prev;
+ /* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+
+ Posf *head; /* Heads of the hash chains or NIL. */
+
+ uInt ins_h; /* hash index of string to be inserted */
+ uInt hash_size; /* number of elements in hash table */
+ uInt hash_bits; /* log2(hash_size) */
+ uInt hash_mask; /* hash_size-1 */
+
+ uInt hash_shift;
+ /* Number of bits by which ins_h must be shifted at each input
+ * step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ * hash_shift * MIN_MATCH >= hash_bits
+ */
+
+ long block_start;
+ /* Window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+
+ uInt match_length; /* length of best match */
+ IPos prev_match; /* previous match */
+ int match_available; /* set if previous match exists */
+ uInt strstart; /* start of string to insert */
+ uInt match_start; /* start of matching string */
+ uInt lookahead; /* number of valid bytes ahead in window */
+
+ uInt prev_length;
+ /* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+ uInt max_chain_length;
+ /* To speed up deflation, hash chains are never searched beyond this
+ * length. A higher limit improves compression ratio but degrades the
+ * speed.
+ */
+
+ uInt max_lazy_match;
+ /* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+# define max_insert_length max_lazy_match
+ /* Insert new strings in the hash table only if the match length is not
+ * greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+ int level; /* compression level (1..9) */
+ int strategy; /* favor or force Huffman coding*/
+
+ uInt good_match;
+ /* Use a faster search when the previous match is longer than this */
+
+ int nice_match; /* Stop searching when current match exceeds this */
+
+ /* used by trees.c: */
+ /* Didn't use ct_data typedef below to supress compiler warning */
+ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
+ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
+
+ struct tree_desc_s l_desc; /* desc. for literal tree */
+ struct tree_desc_s d_desc; /* desc. for distance tree */
+ struct tree_desc_s bl_desc; /* desc. for bit length tree */
+
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
+ int heap_len; /* number of elements in the heap */
+ int heap_max; /* element of largest frequency */
+ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+ uch depth[2*L_CODES+1];
+ /* Depth of each subtree used as tie breaker for trees of equal frequency
+ */
+
+ uchf *l_buf; /* buffer for literals or lengths */
+
+ uInt lit_bufsize;
+ /* Size of match buffer for literals/lengths. There are 4 reasons for
+ * limiting lit_bufsize to 64K:
+ * - frequencies can be kept in 16 bit counters
+ * - if compression is not successful for the first block, all input
+ * data is still in the window so we can still emit a stored block even
+ * when input comes from standard input. (This can also be done for
+ * all blocks if lit_bufsize is not greater than 32K.)
+ * - if compression is not successful for a file smaller than 64K, we can
+ * even emit a stored file instead of a stored block (saving 5 bytes).
+ * This is applicable only for zip (not gzip or zlib).
+ * - creating new Huffman trees less frequently may not provide fast
+ * adaptation to changes in the input data statistics. (Take for
+ * example a binary file with poorly compressible code followed by
+ * a highly compressible string table.) Smaller buffer sizes give
+ * fast adaptation but have of course the overhead of transmitting
+ * trees more frequently.
+ * - I can't count above 4
+ */
+
+ uInt last_lit; /* running index in l_buf */
+
+ ushf *d_buf;
+ /* Buffer for distances. To simplify the code, d_buf and l_buf have
+ * the same number of elements. To use different lengths, an extra flag
+ * array would be necessary.
+ */
+
+ ulg opt_len; /* bit length of current block with optimal trees */
+ ulg static_len; /* bit length of current block with static trees */
+ uInt matches; /* number of string matches in current block */
+ int last_eob_len; /* bit length of EOB code for last block */
+
+#ifdef DEBUG
+ ulg compressed_len; /* total bit length of compressed file mod 2^32 */
+ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */
+#endif
+
+ ush bi_buf;
+ /* Output buffer. bits are inserted starting at the bottom (least
+ * significant bits).
+ */
+ int bi_valid;
+ /* Number of valid bits in bi_buf. All bits above the last valid bit
+ * are always zero.
+ */
+
+ ulg high_water;
+ /* High water mark offset in window for initialized bytes -- bytes above
+ * this are set to zero in order to avoid memory check warnings when
+ * longest match routines access bytes past the input. This is then
+ * updated to the new high water mark.
+ */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+#define WIN_INIT MAX_MATCH
+/* Number of bytes after end of data in window to initialize in order to avoid
+ memory checker errors from longest match routines */
+
+ /* in trees.c */
+void ZLIB_INTERNAL _tr_init OF((deflate_state *s));
+int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
+void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf,
+ ulg stored_len, int last));
+void ZLIB_INTERNAL _tr_align OF((deflate_state *s));
+void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,
+ ulg stored_len, int last));
+
+#define d_code(dist) \
+ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. _dist_code[256] and _dist_code[257] are never
+ * used.
+ */
+
+#ifndef DEBUG
+/* Inline versions of _tr_tally for speed: */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+ extern uch ZLIB_INTERNAL _length_code[];
+ extern uch ZLIB_INTERNAL _dist_code[];
+#else
+ extern const uch ZLIB_INTERNAL _length_code[];
+ extern const uch ZLIB_INTERNAL _dist_code[];
+#endif
+
+# define _tr_tally_lit(s, c, flush) \
+ { uch cc = (c); \
+ s->d_buf[s->last_lit] = 0; \
+ s->l_buf[s->last_lit++] = cc; \
+ s->dyn_ltree[cc].Freq++; \
+ flush = (s->last_lit == s->lit_bufsize-1); \
+ }
+# define _tr_tally_dist(s, distance, length, flush) \
+ { uch len = (length); \
+ ush dist = (distance); \
+ s->d_buf[s->last_lit] = dist; \
+ s->l_buf[s->last_lit++] = len; \
+ dist--; \
+ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
+ s->dyn_dtree[d_code(dist)].Freq++; \
+ flush = (s->last_lit == s->lit_bufsize-1); \
+ }
+#else
+# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
+# define _tr_tally_dist(s, distance, length, flush) \
+ flush = _tr_tally(s, distance, length)
+#endif
+
+#endif /* DEFLATE_H */
diff --git a/src/3rdparty/assimp/contrib/zlib/inffast.c b/src/3rdparty/assimp/contrib/zlib/inffast.c
new file mode 100644
index 000000000..2f1d60b43
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/inffast.c
@@ -0,0 +1,340 @@
+/* inffast.c -- fast decoding
+ * Copyright (C) 1995-2008, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifndef ASMINF
+
+/* Allow machine dependent optimization for post-increment or pre-increment.
+ Based on testing to date,
+ Pre-increment preferred for:
+ - PowerPC G3 (Adler)
+ - MIPS R5000 (Randers-Pehrson)
+ Post-increment preferred for:
+ - none
+ No measurable difference:
+ - Pentium III (Anderson)
+ - M68060 (Nikl)
+ */
+#ifdef POSTINC
+# define OFF 0
+# define PUP(a) *(a)++
+#else
+# define OFF 1
+# define PUP(a) *++(a)
+#endif
+
+/*
+ Decode literal, length, and distance codes and write out the resulting
+ literal and match bytes until either not enough input or output is
+ available, an end-of-block is encountered, or a data error is encountered.
+ When large enough input and output buffers are supplied to inflate(), for
+ example, a 16K input buffer and a 64K output buffer, more than 95% of the
+ inflate execution time is spent in this routine.
+
+ Entry assumptions:
+
+ state->mode == LEN
+ strm->avail_in >= 6
+ strm->avail_out >= 258
+ start >= strm->avail_out
+ state->bits < 8
+
+ On return, state->mode is one of:
+
+ LEN -- ran out of enough output space or enough available input
+ TYPE -- reached end of block code, inflate() to interpret next block
+ BAD -- error in block data
+
+ Notes:
+
+ - The maximum input bits used by a length/distance pair is 15 bits for the
+ length code, 5 bits for the length extra, 15 bits for the distance code,
+ and 13 bits for the distance extra. This totals 48 bits, or six bytes.
+ Therefore if strm->avail_in >= 6, then there is enough input to avoid
+ checking for available input while decoding.
+
+ - The maximum bytes that a single length/distance pair can output is 258
+ bytes, which is the maximum length that can be coded. inflate_fast()
+ requires strm->avail_out >= 258 for each loop to avoid checking for
+ output space.
+ */
+void ZLIB_INTERNAL inflate_fast(strm, start)
+z_streamp strm;
+unsigned start; /* inflate()'s starting value for strm->avail_out */
+{
+ struct inflate_state FAR *state;
+ unsigned char FAR *in; /* local strm->next_in */
+ unsigned char FAR *last; /* while in < last, enough input available */
+ unsigned char FAR *out; /* local strm->next_out */
+ unsigned char FAR *beg; /* inflate()'s initial strm->next_out */
+ unsigned char FAR *end; /* while out < end, enough space available */
+#ifdef INFLATE_STRICT
+ unsigned dmax; /* maximum distance from zlib header */
+#endif
+ unsigned wsize; /* window size or zero if not using window */
+ unsigned whave; /* valid bytes in the window */
+ unsigned wnext; /* window write index */
+ unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */
+ unsigned long hold; /* local strm->hold */
+ unsigned bits; /* local strm->bits */
+ code const FAR *lcode; /* local strm->lencode */
+ code const FAR *dcode; /* local strm->distcode */
+ unsigned lmask; /* mask for first level of length codes */
+ unsigned dmask; /* mask for first level of distance codes */
+ code here; /* retrieved table entry */
+ unsigned op; /* code bits, operation, extra bits, or */
+ /* window position, window bytes to copy */
+ unsigned len; /* match length, unused bytes */
+ unsigned dist; /* match distance */
+ unsigned char FAR *from; /* where to copy match from */
+
+ /* copy state to local variables */
+ state = (struct inflate_state FAR *)strm->state;
+ in = strm->next_in - OFF;
+ last = in + (strm->avail_in - 5);
+ out = strm->next_out - OFF;
+ beg = out - (start - strm->avail_out);
+ end = out + (strm->avail_out - 257);
+#ifdef INFLATE_STRICT
+ dmax = state->dmax;
+#endif
+ wsize = state->wsize;
+ whave = state->whave;
+ wnext = state->wnext;
+ window = state->window;
+ hold = state->hold;
+ bits = state->bits;
+ lcode = state->lencode;
+ dcode = state->distcode;
+ lmask = (1U << state->lenbits) - 1;
+ dmask = (1U << state->distbits) - 1;
+
+ /* decode literals and length/distances until end-of-block or not enough
+ input data or output space */
+ do {
+ if (bits < 15) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ here = lcode[hold & lmask];
+ dolen:
+ op = (unsigned)(here.bits);
+ hold >>= op;
+ bits -= op;
+ op = (unsigned)(here.op);
+ if (op == 0) { /* literal */
+ Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", here.val));
+ PUP(out) = (unsigned char)(here.val);
+ }
+ else if (op & 16) { /* length base */
+ len = (unsigned)(here.val);
+ op &= 15; /* number of extra bits */
+ if (op) {
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ len += (unsigned)hold & ((1U << op) - 1);
+ hold >>= op;
+ bits -= op;
+ }
+ Tracevv((stderr, "inflate: length %u\n", len));
+ if (bits < 15) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ here = dcode[hold & dmask];
+ dodist:
+ op = (unsigned)(here.bits);
+ hold >>= op;
+ bits -= op;
+ op = (unsigned)(here.op);
+ if (op & 16) { /* distance base */
+ dist = (unsigned)(here.val);
+ op &= 15; /* number of extra bits */
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ }
+ dist += (unsigned)hold & ((1U << op) - 1);
+#ifdef INFLATE_STRICT
+ if (dist > dmax) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ hold >>= op;
+ bits -= op;
+ Tracevv((stderr, "inflate: distance %u\n", dist));
+ op = (unsigned)(out - beg); /* max distance in output */
+ if (dist > op) { /* see if copy from window */
+ op = dist - op; /* distance back in window */
+ if (op > whave) {
+ if (state->sane) {
+ strm->msg =
+ (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+ if (len <= op - whave) {
+ do {
+ PUP(out) = 0;
+ } while (--len);
+ continue;
+ }
+ len -= op - whave;
+ do {
+ PUP(out) = 0;
+ } while (--op > whave);
+ if (op == 0) {
+ from = out - dist;
+ do {
+ PUP(out) = PUP(from);
+ } while (--len);
+ continue;
+ }
+#endif
+ }
+ from = window - OFF;
+ if (wnext == 0) { /* very common case */
+ from += wsize - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ else if (wnext < op) { /* wrap around window */
+ from += wsize + wnext - op;
+ op -= wnext;
+ if (op < len) { /* some from end of window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = window - OFF;
+ if (wnext < len) { /* some from start of window */
+ op = wnext;
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ }
+ else { /* contiguous in window */
+ from += wnext - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ while (len > 2) {
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ len -= 3;
+ }
+ if (len) {
+ PUP(out) = PUP(from);
+ if (len > 1)
+ PUP(out) = PUP(from);
+ }
+ }
+ else {
+ from = out - dist; /* copy direct from output */
+ do { /* minimum length is three */
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ len -= 3;
+ } while (len > 2);
+ if (len) {
+ PUP(out) = PUP(from);
+ if (len > 1)
+ PUP(out) = PUP(from);
+ }
+ }
+ }
+ else if ((op & 64) == 0) { /* 2nd level distance code */
+ here = dcode[here.val + (hold & ((1U << op) - 1))];
+ goto dodist;
+ }
+ else {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ }
+ else if ((op & 64) == 0) { /* 2nd level length code */
+ here = lcode[here.val + (hold & ((1U << op) - 1))];
+ goto dolen;
+ }
+ else if (op & 32) { /* end-of-block */
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->mode = TYPE;
+ break;
+ }
+ else {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+ } while (in < last && out < end);
+
+ /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
+ len = bits >> 3;
+ in -= len;
+ bits -= len << 3;
+ hold &= (1U << bits) - 1;
+
+ /* update state and return */
+ strm->next_in = in + OFF;
+ strm->next_out = out + OFF;
+ strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
+ strm->avail_out = (unsigned)(out < end ?
+ 257 + (end - out) : 257 - (out - end));
+ state->hold = hold;
+ state->bits = bits;
+ return;
+}
+
+/*
+ inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
+ - Using bit fields for code structure
+ - Different op definition to avoid & for extra bits (do & for table bits)
+ - Three separate decoding do-loops for direct, window, and wnext == 0
+ - Special case for distance > 1 copies to do overlapped load and store copy
+ - Explicit branch predictions (based on measured branch probabilities)
+ - Deferring match copy and interspersed it with decoding subsequent codes
+ - Swapping literal/length else
+ - Swapping window/direct else
+ - Larger unrolled copy loops (three is about right)
+ - Moving len -= 3 statement into middle of loop
+ */
+
+#endif /* !ASMINF */
diff --git a/src/3rdparty/assimp/contrib/zlib/inffast.h b/src/3rdparty/assimp/contrib/zlib/inffast.h
new file mode 100644
index 000000000..e5c1aa4ca
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/inffast.h
@@ -0,0 +1,11 @@
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-2003, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start));
diff --git a/src/3rdparty/assimp/contrib/zlib/inffixed.h b/src/3rdparty/assimp/contrib/zlib/inffixed.h
new file mode 100644
index 000000000..75ed4b597
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/inffixed.h
@@ -0,0 +1,94 @@
+ /* inffixed.h -- table for decoding fixed codes
+ * Generated automatically by makefixed().
+ */
+
+ /* WARNING: this file should *not* be used by applications. It
+ is part of the implementation of the compression library and
+ is subject to change. Applications should only use zlib.h.
+ */
+
+ static const code lenfix[512] = {
+ {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
+ {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
+ {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
+ {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
+ {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
+ {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
+ {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
+ {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
+ {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
+ {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
+ {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
+ {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
+ {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
+ {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
+ {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
+ {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
+ {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
+ {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
+ {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
+ {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
+ {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
+ {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
+ {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
+ {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
+ {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
+ {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
+ {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
+ {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
+ {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
+ {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
+ {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
+ {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
+ {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
+ {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
+ {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
+ {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
+ {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
+ {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
+ {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
+ {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
+ {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
+ {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
+ {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
+ {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
+ {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
+ {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
+ {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
+ {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
+ {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
+ {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
+ {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
+ {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
+ {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
+ {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
+ {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
+ {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
+ {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
+ {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
+ {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
+ {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
+ {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
+ {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
+ {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
+ {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
+ {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
+ {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
+ {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
+ {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
+ {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
+ {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
+ {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
+ {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
+ {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
+ {0,9,255}
+ };
+
+ static const code distfix[32] = {
+ {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
+ {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
+ {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
+ {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
+ {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
+ {22,5,193},{64,5,0}
+ };
diff --git a/src/3rdparty/assimp/contrib/zlib/inflate.c b/src/3rdparty/assimp/contrib/zlib/inflate.c
new file mode 100644
index 000000000..a8431abea
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/inflate.c
@@ -0,0 +1,1480 @@
+/* inflate.c -- zlib decompression
+ * Copyright (C) 1995-2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * Change history:
+ *
+ * 1.2.beta0 24 Nov 2002
+ * - First version -- complete rewrite of inflate to simplify code, avoid
+ * creation of window when not needed, minimize use of window when it is
+ * needed, make inffast.c even faster, implement gzip decoding, and to
+ * improve code readability and style over the previous zlib inflate code
+ *
+ * 1.2.beta1 25 Nov 2002
+ * - Use pointers for available input and output checking in inffast.c
+ * - Remove input and output counters in inffast.c
+ * - Change inffast.c entry and loop from avail_in >= 7 to >= 6
+ * - Remove unnecessary second byte pull from length extra in inffast.c
+ * - Unroll direct copy to three copies per loop in inffast.c
+ *
+ * 1.2.beta2 4 Dec 2002
+ * - Change external routine names to reduce potential conflicts
+ * - Correct filename to inffixed.h for fixed tables in inflate.c
+ * - Make hbuf[] unsigned char to match parameter type in inflate.c
+ * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset)
+ * to avoid negation problem on Alphas (64 bit) in inflate.c
+ *
+ * 1.2.beta3 22 Dec 2002
+ * - Add comments on state->bits assertion in inffast.c
+ * - Add comments on op field in inftrees.h
+ * - Fix bug in reuse of allocated window after inflateReset()
+ * - Remove bit fields--back to byte structure for speed
+ * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths
+ * - Change post-increments to pre-increments in inflate_fast(), PPC biased?
+ * - Add compile time option, POSTINC, to use post-increments instead (Intel?)
+ * - Make MATCH copy in inflate() much faster for when inflate_fast() not used
+ * - Use local copies of stream next and avail values, as well as local bit
+ * buffer and bit count in inflate()--for speed when inflate_fast() not used
+ *
+ * 1.2.beta4 1 Jan 2003
+ * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings
+ * - Move a comment on output buffer sizes from inffast.c to inflate.c
+ * - Add comments in inffast.c to introduce the inflate_fast() routine
+ * - Rearrange window copies in inflate_fast() for speed and simplification
+ * - Unroll last copy for window match in inflate_fast()
+ * - Use local copies of window variables in inflate_fast() for speed
+ * - Pull out common wnext == 0 case for speed in inflate_fast()
+ * - Make op and len in inflate_fast() unsigned for consistency
+ * - Add FAR to lcode and dcode declarations in inflate_fast()
+ * - Simplified bad distance check in inflate_fast()
+ * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new
+ * source file infback.c to provide a call-back interface to inflate for
+ * programs like gzip and unzip -- uses window as output buffer to avoid
+ * window copying
+ *
+ * 1.2.beta5 1 Jan 2003
+ * - Improved inflateBack() interface to allow the caller to provide initial
+ * input in strm.
+ * - Fixed stored blocks bug in inflateBack()
+ *
+ * 1.2.beta6 4 Jan 2003
+ * - Added comments in inffast.c on effectiveness of POSTINC
+ * - Typecasting all around to reduce compiler warnings
+ * - Changed loops from while (1) or do {} while (1) to for (;;), again to
+ * make compilers happy
+ * - Changed type of window in inflateBackInit() to unsigned char *
+ *
+ * 1.2.beta7 27 Jan 2003
+ * - Changed many types to unsigned or unsigned short to avoid warnings
+ * - Added inflateCopy() function
+ *
+ * 1.2.0 9 Mar 2003
+ * - Changed inflateBack() interface to provide separate opaque descriptors
+ * for the in() and out() functions
+ * - Changed inflateBack() argument and in_func typedef to swap the length
+ * and buffer address return values for the input function
+ * - Check next_in and next_out for Z_NULL on entry to inflate()
+ *
+ * The history for versions after 1.2.0 are in ChangeLog in zlib distribution.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifdef MAKEFIXED
+# ifndef BUILDFIXED
+# define BUILDFIXED
+# endif
+#endif
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+local int updatewindow OF((z_streamp strm, unsigned out));
+#ifdef BUILDFIXED
+ void makefixed OF((void));
+#endif
+local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf,
+ unsigned len));
+
+int ZEXPORT inflateReset(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ strm->total_in = strm->total_out = state->total = 0;
+ strm->msg = Z_NULL;
+ strm->adler = 1; /* to support ill-conceived Java test suite */
+ state->mode = HEAD;
+ state->last = 0;
+ state->havedict = 0;
+ state->dmax = 32768U;
+ state->head = Z_NULL;
+ state->wsize = 0;
+ state->whave = 0;
+ state->wnext = 0;
+ state->hold = 0;
+ state->bits = 0;
+ state->lencode = state->distcode = state->next = state->codes;
+ state->sane = 1;
+ state->back = -1;
+ Tracev((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+int ZEXPORT inflateReset2(strm, windowBits)
+z_streamp strm;
+int windowBits;
+{
+ int wrap;
+ struct inflate_state FAR *state;
+
+ /* get the state */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+
+ /* extract wrap request from windowBits parameter */
+ if (windowBits < 0) {
+ wrap = 0;
+ windowBits = -windowBits;
+ }
+ else {
+ wrap = (windowBits >> 4) + 1;
+#ifdef GUNZIP
+ if (windowBits < 48)
+ windowBits &= 15;
+#endif
+ }
+
+ /* set number of window bits, free window if different */
+ if (windowBits && (windowBits < 8 || windowBits > 15))
+ return Z_STREAM_ERROR;
+ if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) {
+ ZFREE(strm, state->window);
+ state->window = Z_NULL;
+ }
+
+ /* update state and reset the rest of it */
+ state->wrap = wrap;
+ state->wbits = (unsigned)windowBits;
+ return inflateReset(strm);
+}
+
+int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
+z_streamp strm;
+int windowBits;
+const char *version;
+int stream_size;
+{
+ int ret;
+ struct inflate_state FAR *state;
+
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != (int)(sizeof(z_stream)))
+ return Z_VERSION_ERROR;
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+ strm->msg = Z_NULL; /* in case we return an error */
+ if (strm->zalloc == (alloc_func)0) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+ state = (struct inflate_state FAR *)
+ ZALLOC(strm, 1, sizeof(struct inflate_state));
+ if (state == Z_NULL) return Z_MEM_ERROR;
+ Tracev((stderr, "inflate: allocated\n"));
+ strm->state = (struct internal_state FAR *)state;
+ state->window = Z_NULL;
+ ret = inflateReset2(strm, windowBits);
+ if (ret != Z_OK) {
+ ZFREE(strm, state);
+ strm->state = Z_NULL;
+ }
+ return ret;
+}
+
+int ZEXPORT inflateInit_(strm, version, stream_size)
+z_streamp strm;
+const char *version;
+int stream_size;
+{
+ return inflateInit2_(strm, DEF_WBITS, version, stream_size);
+}
+
+int ZEXPORT inflatePrime(strm, bits, value)
+z_streamp strm;
+int bits;
+int value;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (bits < 0) {
+ state->hold = 0;
+ state->bits = 0;
+ return Z_OK;
+ }
+ if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR;
+ value &= (1L << bits) - 1;
+ state->hold += value << state->bits;
+ state->bits += bits;
+ return Z_OK;
+}
+
+/*
+ Return state with length and distance decoding tables and index sizes set to
+ fixed code decoding. Normally this returns fixed tables from inffixed.h.
+ If BUILDFIXED is defined, then instead this routine builds the tables the
+ first time it's called, and returns those tables the first time and
+ thereafter. This reduces the size of the code by about 2K bytes, in
+ exchange for a little execution time. However, BUILDFIXED should not be
+ used for threaded applications, since the rewriting of the tables and virgin
+ may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+ static int virgin = 1;
+ static code *lenfix, *distfix;
+ static code fixed[544];
+
+ /* build fixed huffman tables if first call (may not be thread safe) */
+ if (virgin) {
+ unsigned sym, bits;
+ static code *next;
+
+ /* literal/length table */
+ sym = 0;
+ while (sym < 144) state->lens[sym++] = 8;
+ while (sym < 256) state->lens[sym++] = 9;
+ while (sym < 280) state->lens[sym++] = 7;
+ while (sym < 288) state->lens[sym++] = 8;
+ next = fixed;
+ lenfix = next;
+ bits = 9;
+ inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+ /* distance table */
+ sym = 0;
+ while (sym < 32) state->lens[sym++] = 5;
+ distfix = next;
+ bits = 5;
+ inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+ /* do this just once */
+ virgin = 0;
+ }
+#else /* !BUILDFIXED */
+# include "inffixed.h"
+#endif /* BUILDFIXED */
+ state->lencode = lenfix;
+ state->lenbits = 9;
+ state->distcode = distfix;
+ state->distbits = 5;
+}
+
+#ifdef MAKEFIXED
+#include <stdio.h>
+
+/*
+ Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also
+ defines BUILDFIXED, so the tables are built on the fly. makefixed() writes
+ those tables to stdout, which would be piped to inffixed.h. A small program
+ can simply call makefixed to do this:
+
+ void makefixed(void);
+
+ int main(void)
+ {
+ makefixed();
+ return 0;
+ }
+
+ Then that can be linked with zlib built with MAKEFIXED defined and run:
+
+ a.out > inffixed.h
+ */
+void makefixed()
+{
+ unsigned low, size;
+ struct inflate_state state;
+
+ fixedtables(&state);
+ puts(" /* inffixed.h -- table for decoding fixed codes");
+ puts(" * Generated automatically by makefixed().");
+ puts(" */");
+ puts("");
+ puts(" /* WARNING: this file should *not* be used by applications.");
+ puts(" It is part of the implementation of this library and is");
+ puts(" subject to change. Applications should only use zlib.h.");
+ puts(" */");
+ puts("");
+ size = 1U << 9;
+ printf(" static const code lenfix[%u] = {", size);
+ low = 0;
+ for (;;) {
+ if ((low % 7) == 0) printf("\n ");
+ printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits,
+ state.lencode[low].val);
+ if (++low == size) break;
+ putchar(',');
+ }
+ puts("\n };");
+ size = 1U << 5;
+ printf("\n static const code distfix[%u] = {", size);
+ low = 0;
+ for (;;) {
+ if ((low % 6) == 0) printf("\n ");
+ printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
+ state.distcode[low].val);
+ if (++low == size) break;
+ putchar(',');
+ }
+ puts("\n };");
+}
+#endif /* MAKEFIXED */
+
+/*
+ Update the window with the last wsize (normally 32K) bytes written before
+ returning. If window does not exist yet, create it. This is only called
+ when a window is already in use, or when output has been written during this
+ inflate call, but the end of the deflate stream has not been reached yet.
+ It is also called to create a window for dictionary data when a dictionary
+ is loaded.
+
+ Providing output buffers larger than 32K to inflate() should provide a speed
+ advantage, since only the last 32K of output is copied to the sliding window
+ upon return from inflate(), and since all distances after the first 32K of
+ output will fall in the output data, making match copies simpler and faster.
+ The advantage may be dependent on the size of the processor's data caches.
+ */
+local int updatewindow(strm, out)
+z_streamp strm;
+unsigned out;
+{
+ struct inflate_state FAR *state;
+ unsigned copy, dist;
+
+ state = (struct inflate_state FAR *)strm->state;
+
+ /* if it hasn't been done already, allocate space for the window */
+ if (state->window == Z_NULL) {
+ state->window = (unsigned char FAR *)
+ ZALLOC(strm, 1U << state->wbits,
+ sizeof(unsigned char));
+ if (state->window == Z_NULL) return 1;
+ }
+
+ /* if window not in use yet, initialize */
+ if (state->wsize == 0) {
+ state->wsize = 1U << state->wbits;
+ state->wnext = 0;
+ state->whave = 0;
+ }
+
+ /* copy state->wsize or less output bytes into the circular window */
+ copy = out - strm->avail_out;
+ if (copy >= state->wsize) {
+ zmemcpy(state->window, strm->next_out - state->wsize, state->wsize);
+ state->wnext = 0;
+ state->whave = state->wsize;
+ }
+ else {
+ dist = state->wsize - state->wnext;
+ if (dist > copy) dist = copy;
+ zmemcpy(state->window + state->wnext, strm->next_out - copy, dist);
+ copy -= dist;
+ if (copy) {
+ zmemcpy(state->window, strm->next_out - copy, copy);
+ state->wnext = copy;
+ state->whave = state->wsize;
+ }
+ else {
+ state->wnext += dist;
+ if (state->wnext == state->wsize) state->wnext = 0;
+ if (state->whave < state->wsize) state->whave += dist;
+ }
+ }
+ return 0;
+}
+
+/* Macros for inflate(): */
+
+/* check function to use adler32() for zlib or crc32() for gzip */
+#ifdef GUNZIP
+# define UPDATE(check, buf, len) \
+ (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
+#else
+# define UPDATE(check, buf, len) adler32(check, buf, len)
+#endif
+
+/* check macros for header crc */
+#ifdef GUNZIP
+# define CRC2(check, word) \
+ do { \
+ hbuf[0] = (unsigned char)(word); \
+ hbuf[1] = (unsigned char)((word) >> 8); \
+ check = crc32(check, hbuf, 2); \
+ } while (0)
+
+# define CRC4(check, word) \
+ do { \
+ hbuf[0] = (unsigned char)(word); \
+ hbuf[1] = (unsigned char)((word) >> 8); \
+ hbuf[2] = (unsigned char)((word) >> 16); \
+ hbuf[3] = (unsigned char)((word) >> 24); \
+ check = crc32(check, hbuf, 4); \
+ } while (0)
+#endif
+
+/* Load registers with state in inflate() for speed */
+#define LOAD() \
+ do { \
+ put = strm->next_out; \
+ left = strm->avail_out; \
+ next = strm->next_in; \
+ have = strm->avail_in; \
+ hold = state->hold; \
+ bits = state->bits; \
+ } while (0)
+
+/* Restore state from registers in inflate() */
+#define RESTORE() \
+ do { \
+ strm->next_out = put; \
+ strm->avail_out = left; \
+ strm->next_in = next; \
+ strm->avail_in = have; \
+ state->hold = hold; \
+ state->bits = bits; \
+ } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+ do { \
+ hold = 0; \
+ bits = 0; \
+ } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflate()
+ if there is no input available. */
+#define PULLBYTE() \
+ do { \
+ if (have == 0) goto inf_leave; \
+ have--; \
+ hold += (unsigned long)(*next++) << bits; \
+ bits += 8; \
+ } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator. If there is
+ not enough available input to do that, then return from inflate(). */
+#define NEEDBITS(n) \
+ do { \
+ while (bits < (unsigned)(n)) \
+ PULLBYTE(); \
+ } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+ ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+ do { \
+ hold >>= (n); \
+ bits -= (unsigned)(n); \
+ } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+ do { \
+ hold >>= bits & 7; \
+ bits -= bits & 7; \
+ } while (0)
+
+/* Reverse the bytes in a 32-bit value */
+#define REVERSE(q) \
+ ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
+ (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
+
+/*
+ inflate() uses a state machine to process as much input data and generate as
+ much output data as possible before returning. The state machine is
+ structured roughly as follows:
+
+ for (;;) switch (state) {
+ ...
+ case STATEn:
+ if (not enough input data or output space to make progress)
+ return;
+ ... make progress ...
+ state = STATEm;
+ break;
+ ...
+ }
+
+ so when inflate() is called again, the same case is attempted again, and
+ if the appropriate resources are provided, the machine proceeds to the
+ next state. The NEEDBITS() macro is usually the way the state evaluates
+ whether it can proceed or should return. NEEDBITS() does the return if
+ the requested bits are not available. The typical use of the BITS macros
+ is:
+
+ NEEDBITS(n);
+ ... do something with BITS(n) ...
+ DROPBITS(n);
+
+ where NEEDBITS(n) either returns from inflate() if there isn't enough
+ input left to load n bits into the accumulator, or it continues. BITS(n)
+ gives the low n bits in the accumulator. When done, DROPBITS(n) drops
+ the low n bits off the accumulator. INITBITS() clears the accumulator
+ and sets the number of available bits to zero. BYTEBITS() discards just
+ enough bits to put the accumulator on a byte boundary. After BYTEBITS()
+ and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
+
+ NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
+ if there is no input available. The decoding of variable length codes uses
+ PULLBYTE() directly in order to pull just enough bytes to decode the next
+ code, and no more.
+
+ Some states loop until they get enough input, making sure that enough
+ state information is maintained to continue the loop where it left off
+ if NEEDBITS() returns in the loop. For example, want, need, and keep
+ would all have to actually be part of the saved state in case NEEDBITS()
+ returns:
+
+ case STATEw:
+ while (want < need) {
+ NEEDBITS(n);
+ keep[want++] = BITS(n);
+ DROPBITS(n);
+ }
+ state = STATEx;
+ case STATEx:
+
+ As shown above, if the next state is also the next case, then the break
+ is omitted.
+
+ A state may also return if there is not enough output space available to
+ complete that state. Those states are copying stored data, writing a
+ literal byte, and copying a matching string.
+
+ When returning, a "goto inf_leave" is used to update the total counters,
+ update the check value, and determine whether any progress has been made
+ during that inflate() call in order to return the proper return code.
+ Progress is defined as a change in either strm->avail_in or strm->avail_out.
+ When there is a window, goto inf_leave will update the window with the last
+ output written. If a goto inf_leave occurs in the middle of decompression
+ and there is no window currently, goto inf_leave will create one and copy
+ output to the window for the next call of inflate().
+
+ In this implementation, the flush parameter of inflate() only affects the
+ return code (per zlib.h). inflate() always writes as much as possible to
+ strm->next_out, given the space available and the provided input--the effect
+ documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers
+ the allocation of and copying into a sliding window until necessary, which
+ provides the effect documented in zlib.h for Z_FINISH when the entire input
+ stream available. So the only thing the flush parameter actually does is:
+ when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it
+ will return Z_BUF_ERROR if it has not reached the end of the stream.
+ */
+
+int ZEXPORT inflate(strm, flush)
+z_streamp strm;
+int flush;
+{
+ struct inflate_state FAR *state;
+ unsigned char FAR *next; /* next input */
+ unsigned char FAR *put; /* next output */
+ unsigned have, left; /* available input and output */
+ unsigned long hold; /* bit buffer */
+ unsigned bits; /* bits in bit buffer */
+ unsigned in, out; /* save starting available input and output */
+ unsigned copy; /* number of stored or match bytes to copy */
+ unsigned char FAR *from; /* where to copy match bytes from */
+ code here; /* current decoding table entry */
+ code last; /* parent table entry */
+ unsigned len; /* length to copy for repeats, bits to drop */
+ int ret; /* return code */
+#ifdef GUNZIP
+ unsigned char hbuf[4]; /* buffer for gzip header crc calculation */
+#endif
+ static const unsigned short order[19] = /* permutation of code lengths */
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL ||
+ (strm->next_in == Z_NULL && strm->avail_in != 0))
+ return Z_STREAM_ERROR;
+
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */
+ LOAD();
+ in = have;
+ out = left;
+ ret = Z_OK;
+ for (;;)
+ switch (state->mode) {
+ case HEAD:
+ if (state->wrap == 0) {
+ state->mode = TYPEDO;
+ break;
+ }
+ NEEDBITS(16);
+#ifdef GUNZIP
+ if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */
+ state->check = crc32(0L, Z_NULL, 0);
+ CRC2(state->check, hold);
+ INITBITS();
+ state->mode = FLAGS;
+ break;
+ }
+ state->flags = 0; /* expect zlib header */
+ if (state->head != Z_NULL)
+ state->head->done = -1;
+ if (!(state->wrap & 1) || /* check if zlib header allowed */
+#else
+ if (
+#endif
+ ((BITS(8) << 8) + (hold >> 8)) % 31) {
+ strm->msg = (char *)"incorrect header check";
+ state->mode = BAD;
+ break;
+ }
+ if (BITS(4) != Z_DEFLATED) {
+ strm->msg = (char *)"unknown compression method";
+ state->mode = BAD;
+ break;
+ }
+ DROPBITS(4);
+ len = BITS(4) + 8;
+ if (state->wbits == 0)
+ state->wbits = len;
+ else if (len > state->wbits) {
+ strm->msg = (char *)"invalid window size";
+ state->mode = BAD;
+ break;
+ }
+ state->dmax = 1U << len;
+ Tracev((stderr, "inflate: zlib header ok\n"));
+ strm->adler = state->check = adler32(0L, Z_NULL, 0);
+ state->mode = hold & 0x200 ? DICTID : TYPE;
+ INITBITS();
+ break;
+#ifdef GUNZIP
+ case FLAGS:
+ NEEDBITS(16);
+ state->flags = (int)(hold);
+ if ((state->flags & 0xff) != Z_DEFLATED) {
+ strm->msg = (char *)"unknown compression method";
+ state->mode = BAD;
+ break;
+ }
+ if (state->flags & 0xe000) {
+ strm->msg = (char *)"unknown header flags set";
+ state->mode = BAD;
+ break;
+ }
+ if (state->head != Z_NULL)
+ state->head->text = (int)((hold >> 8) & 1);
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ state->mode = TIME;
+ case TIME:
+ NEEDBITS(32);
+ if (state->head != Z_NULL)
+ state->head->time = hold;
+ if (state->flags & 0x0200) CRC4(state->check, hold);
+ INITBITS();
+ state->mode = OS;
+ case OS:
+ NEEDBITS(16);
+ if (state->head != Z_NULL) {
+ state->head->xflags = (int)(hold & 0xff);
+ state->head->os = (int)(hold >> 8);
+ }
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ state->mode = EXLEN;
+ case EXLEN:
+ if (state->flags & 0x0400) {
+ NEEDBITS(16);
+ state->length = (unsigned)(hold);
+ if (state->head != Z_NULL)
+ state->head->extra_len = (unsigned)hold;
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ }
+ else if (state->head != Z_NULL)
+ state->head->extra = Z_NULL;
+ state->mode = EXTRA;
+ case EXTRA:
+ if (state->flags & 0x0400) {
+ copy = state->length;
+ if (copy > have) copy = have;
+ if (copy) {
+ if (state->head != Z_NULL &&
+ state->head->extra != Z_NULL) {
+ len = state->head->extra_len - state->length;
+ zmemcpy(state->head->extra + len, next,
+ len + copy > state->head->extra_max ?
+ state->head->extra_max - len : copy);
+ }
+ if (state->flags & 0x0200)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ state->length -= copy;
+ }
+ if (state->length) goto inf_leave;
+ }
+ state->length = 0;
+ state->mode = NAME;
+ case NAME:
+ if (state->flags & 0x0800) {
+ if (have == 0) goto inf_leave;
+ copy = 0;
+ do {
+ len = (unsigned)(next[copy++]);
+ if (state->head != Z_NULL &&
+ state->head->name != Z_NULL &&
+ state->length < state->head->name_max)
+ state->head->name[state->length++] = len;
+ } while (len && copy < have);
+ if (state->flags & 0x0200)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ if (len) goto inf_leave;
+ }
+ else if (state->head != Z_NULL)
+ state->head->name = Z_NULL;
+ state->length = 0;
+ state->mode = COMMENT;
+ case COMMENT:
+ if (state->flags & 0x1000) {
+ if (have == 0) goto inf_leave;
+ copy = 0;
+ do {
+ len = (unsigned)(next[copy++]);
+ if (state->head != Z_NULL &&
+ state->head->comment != Z_NULL &&
+ state->length < state->head->comm_max)
+ state->head->comment[state->length++] = len;
+ } while (len && copy < have);
+ if (state->flags & 0x0200)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ if (len) goto inf_leave;
+ }
+ else if (state->head != Z_NULL)
+ state->head->comment = Z_NULL;
+ state->mode = HCRC;
+ case HCRC:
+ if (state->flags & 0x0200) {
+ NEEDBITS(16);
+ if (hold != (state->check & 0xffff)) {
+ strm->msg = (char *)"header crc mismatch";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ }
+ if (state->head != Z_NULL) {
+ state->head->hcrc = (int)((state->flags >> 9) & 1);
+ state->head->done = 1;
+ }
+ strm->adler = state->check = crc32(0L, Z_NULL, 0);
+ state->mode = TYPE;
+ break;
+#endif
+ case DICTID:
+ NEEDBITS(32);
+ strm->adler = state->check = REVERSE(hold);
+ INITBITS();
+ state->mode = DICT;
+ case DICT:
+ if (state->havedict == 0) {
+ RESTORE();
+ return Z_NEED_DICT;
+ }
+ strm->adler = state->check = adler32(0L, Z_NULL, 0);
+ state->mode = TYPE;
+ case TYPE:
+ if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave;
+ case TYPEDO:
+ if (state->last) {
+ BYTEBITS();
+ state->mode = CHECK;
+ break;
+ }
+ NEEDBITS(3);
+ state->last = BITS(1);
+ DROPBITS(1);
+ switch (BITS(2)) {
+ case 0: /* stored block */
+ Tracev((stderr, "inflate: stored block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = STORED;
+ break;
+ case 1: /* fixed block */
+ fixedtables(state);
+ Tracev((stderr, "inflate: fixed codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = LEN_; /* decode codes */
+ if (flush == Z_TREES) {
+ DROPBITS(2);
+ goto inf_leave;
+ }
+ break;
+ case 2: /* dynamic block */
+ Tracev((stderr, "inflate: dynamic codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = TABLE;
+ break;
+ case 3:
+ strm->msg = (char *)"invalid block type";
+ state->mode = BAD;
+ }
+ DROPBITS(2);
+ break;
+ case STORED:
+ BYTEBITS(); /* go to byte boundary */
+ NEEDBITS(32);
+ if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+ strm->msg = (char *)"invalid stored block lengths";
+ state->mode = BAD;
+ break;
+ }
+ state->length = (unsigned)hold & 0xffff;
+ Tracev((stderr, "inflate: stored length %u\n",
+ state->length));
+ INITBITS();
+ state->mode = COPY_;
+ if (flush == Z_TREES) goto inf_leave;
+ case COPY_:
+ state->mode = COPY;
+ case COPY:
+ copy = state->length;
+ if (copy) {
+ if (copy > have) copy = have;
+ if (copy > left) copy = left;
+ if (copy == 0) goto inf_leave;
+ zmemcpy(put, next, copy);
+ have -= copy;
+ next += copy;
+ left -= copy;
+ put += copy;
+ state->length -= copy;
+ break;
+ }
+ Tracev((stderr, "inflate: stored end\n"));
+ state->mode = TYPE;
+ break;
+ case TABLE:
+ NEEDBITS(14);
+ state->nlen = BITS(5) + 257;
+ DROPBITS(5);
+ state->ndist = BITS(5) + 1;
+ DROPBITS(5);
+ state->ncode = BITS(4) + 4;
+ DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+ if (state->nlen > 286 || state->ndist > 30) {
+ strm->msg = (char *)"too many length or distance symbols";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ Tracev((stderr, "inflate: table sizes ok\n"));
+ state->have = 0;
+ state->mode = LENLENS;
+ case LENLENS:
+ while (state->have < state->ncode) {
+ NEEDBITS(3);
+ state->lens[order[state->have++]] = (unsigned short)BITS(3);
+ DROPBITS(3);
+ }
+ while (state->have < 19)
+ state->lens[order[state->have++]] = 0;
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 7;
+ ret = inflate_table(CODES, state->lens, 19, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid code lengths set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: code lengths ok\n"));
+ state->have = 0;
+ state->mode = CODELENS;
+ case CODELENS:
+ while (state->have < state->nlen + state->ndist) {
+ for (;;) {
+ here = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (here.val < 16) {
+ NEEDBITS(here.bits);
+ DROPBITS(here.bits);
+ state->lens[state->have++] = here.val;
+ }
+ else {
+ if (here.val == 16) {
+ NEEDBITS(here.bits + 2);
+ DROPBITS(here.bits);
+ if (state->have == 0) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ len = state->lens[state->have - 1];
+ copy = 3 + BITS(2);
+ DROPBITS(2);
+ }
+ else if (here.val == 17) {
+ NEEDBITS(here.bits + 3);
+ DROPBITS(here.bits);
+ len = 0;
+ copy = 3 + BITS(3);
+ DROPBITS(3);
+ }
+ else {
+ NEEDBITS(here.bits + 7);
+ DROPBITS(here.bits);
+ len = 0;
+ copy = 11 + BITS(7);
+ DROPBITS(7);
+ }
+ if (state->have + copy > state->nlen + state->ndist) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ while (copy--)
+ state->lens[state->have++] = (unsigned short)len;
+ }
+ }
+
+ /* handle error breaks in while */
+ if (state->mode == BAD) break;
+
+ /* check for end-of-block code (better have one) */
+ if (state->lens[256] == 0) {
+ strm->msg = (char *)"invalid code -- missing end-of-block";
+ state->mode = BAD;
+ break;
+ }
+
+ /* build code tables -- note: do not change the lenbits or distbits
+ values here (9 and 6) without reading the comments in inftrees.h
+ concerning the ENOUGH constants, which depend on those values */
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 9;
+ ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid literal/lengths set";
+ state->mode = BAD;
+ break;
+ }
+ state->distcode = (code const FAR *)(state->next);
+ state->distbits = 6;
+ ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+ &(state->next), &(state->distbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid distances set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: codes ok\n"));
+ state->mode = LEN_;
+ if (flush == Z_TREES) goto inf_leave;
+ case LEN_:
+ state->mode = LEN;
+ case LEN:
+ if (have >= 6 && left >= 258) {
+ RESTORE();
+ inflate_fast(strm, out);
+ LOAD();
+ if (state->mode == TYPE)
+ state->back = -1;
+ break;
+ }
+ state->back = 0;
+ for (;;) {
+ here = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (here.op && (here.op & 0xf0) == 0) {
+ last = here;
+ for (;;) {
+ here = state->lencode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ state->back += last.bits;
+ }
+ DROPBITS(here.bits);
+ state->back += here.bits;
+ state->length = (unsigned)here.val;
+ if ((int)(here.op) == 0) {
+ Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", here.val));
+ state->mode = LIT;
+ break;
+ }
+ if (here.op & 32) {
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->back = -1;
+ state->mode = TYPE;
+ break;
+ }
+ if (here.op & 64) {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+ state->extra = (unsigned)(here.op) & 15;
+ state->mode = LENEXT;
+ case LENEXT:
+ if (state->extra) {
+ NEEDBITS(state->extra);
+ state->length += BITS(state->extra);
+ DROPBITS(state->extra);
+ state->back += state->extra;
+ }
+ Tracevv((stderr, "inflate: length %u\n", state->length));
+ state->was = state->length;
+ state->mode = DIST;
+ case DIST:
+ for (;;) {
+ here = state->distcode[BITS(state->distbits)];
+ if ((unsigned)(here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if ((here.op & 0xf0) == 0) {
+ last = here;
+ for (;;) {
+ here = state->distcode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ state->back += last.bits;
+ }
+ DROPBITS(here.bits);
+ state->back += here.bits;
+ if (here.op & 64) {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ state->offset = (unsigned)here.val;
+ state->extra = (unsigned)(here.op) & 15;
+ state->mode = DISTEXT;
+ case DISTEXT:
+ if (state->extra) {
+ NEEDBITS(state->extra);
+ state->offset += BITS(state->extra);
+ DROPBITS(state->extra);
+ state->back += state->extra;
+ }
+#ifdef INFLATE_STRICT
+ if (state->offset > state->dmax) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ Tracevv((stderr, "inflate: distance %u\n", state->offset));
+ state->mode = MATCH;
+ case MATCH:
+ if (left == 0) goto inf_leave;
+ copy = out - left;
+ if (state->offset > copy) { /* copy from window */
+ copy = state->offset - copy;
+ if (copy > state->whave) {
+ if (state->sane) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+ Trace((stderr, "inflate.c too far\n"));
+ copy -= state->whave;
+ if (copy > state->length) copy = state->length;
+ if (copy > left) copy = left;
+ left -= copy;
+ state->length -= copy;
+ do {
+ *put++ = 0;
+ } while (--copy);
+ if (state->length == 0) state->mode = LEN;
+ break;
+#endif
+ }
+ if (copy > state->wnext) {
+ copy -= state->wnext;
+ from = state->window + (state->wsize - copy);
+ }
+ else
+ from = state->window + (state->wnext - copy);
+ if (copy > state->length) copy = state->length;
+ }
+ else { /* copy from output */
+ from = put - state->offset;
+ copy = state->length;
+ }
+ if (copy > left) copy = left;
+ left -= copy;
+ state->length -= copy;
+ do {
+ *put++ = *from++;
+ } while (--copy);
+ if (state->length == 0) state->mode = LEN;
+ break;
+ case LIT:
+ if (left == 0) goto inf_leave;
+ *put++ = (unsigned char)(state->length);
+ left--;
+ state->mode = LEN;
+ break;
+ case CHECK:
+ if (state->wrap) {
+ NEEDBITS(32);
+ out -= left;
+ strm->total_out += out;
+ state->total += out;
+ if (out)
+ strm->adler = state->check =
+ UPDATE(state->check, put - out, out);
+ out = left;
+ if ((
+#ifdef GUNZIP
+ state->flags ? hold :
+#endif
+ REVERSE(hold)) != state->check) {
+ strm->msg = (char *)"incorrect data check";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ Tracev((stderr, "inflate: check matches trailer\n"));
+ }
+#ifdef GUNZIP
+ state->mode = LENGTH;
+ case LENGTH:
+ if (state->wrap && state->flags) {
+ NEEDBITS(32);
+ if (hold != (state->total & 0xffffffffUL)) {
+ strm->msg = (char *)"incorrect length check";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ Tracev((stderr, "inflate: length matches trailer\n"));
+ }
+#endif
+ state->mode = DONE;
+ case DONE:
+ ret = Z_STREAM_END;
+ goto inf_leave;
+ case BAD:
+ ret = Z_DATA_ERROR;
+ goto inf_leave;
+ case MEM:
+ return Z_MEM_ERROR;
+ case SYNC:
+ default:
+ return Z_STREAM_ERROR;
+ }
+
+ /*
+ Return from inflate(), updating the total counts and the check value.
+ If there was no progress during the inflate() call, return a buffer
+ error. Call updatewindow() to create and/or update the window state.
+ Note: a memory error from inflate() is non-recoverable.
+ */
+ inf_leave:
+ RESTORE();
+ if (state->wsize || (state->mode < CHECK && out != strm->avail_out))
+ if (updatewindow(strm, out)) {
+ state->mode = MEM;
+ return Z_MEM_ERROR;
+ }
+ in -= strm->avail_in;
+ out -= strm->avail_out;
+ strm->total_in += in;
+ strm->total_out += out;
+ state->total += out;
+ if (state->wrap && out)
+ strm->adler = state->check =
+ UPDATE(state->check, strm->next_out - out, out);
+ strm->data_type = state->bits + (state->last ? 64 : 0) +
+ (state->mode == TYPE ? 128 : 0) +
+ (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0);
+ if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
+ ret = Z_BUF_ERROR;
+ return ret;
+}
+
+int ZEXPORT inflateEnd(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->window != Z_NULL) ZFREE(strm, state->window);
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+ Tracev((stderr, "inflate: end\n"));
+ return Z_OK;
+}
+
+int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
+z_streamp strm;
+const Bytef *dictionary;
+uInt dictLength;
+{
+ struct inflate_state FAR *state;
+ unsigned long id;
+
+ /* check state */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->wrap != 0 && state->mode != DICT)
+ return Z_STREAM_ERROR;
+
+ /* check for correct dictionary id */
+ if (state->mode == DICT) {
+ id = adler32(0L, Z_NULL, 0);
+ id = adler32(id, dictionary, dictLength);
+ if (id != state->check)
+ return Z_DATA_ERROR;
+ }
+
+ /* copy dictionary to window */
+ if (updatewindow(strm, strm->avail_out)) {
+ state->mode = MEM;
+ return Z_MEM_ERROR;
+ }
+ if (dictLength > state->wsize) {
+ zmemcpy(state->window, dictionary + dictLength - state->wsize,
+ state->wsize);
+ state->whave = state->wsize;
+ }
+ else {
+ zmemcpy(state->window + state->wsize - dictLength, dictionary,
+ dictLength);
+ state->whave = dictLength;
+ }
+ state->havedict = 1;
+ Tracev((stderr, "inflate: dictionary set\n"));
+ return Z_OK;
+}
+
+int ZEXPORT inflateGetHeader(strm, head)
+z_streamp strm;
+gz_headerp head;
+{
+ struct inflate_state FAR *state;
+
+ /* check state */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if ((state->wrap & 2) == 0) return Z_STREAM_ERROR;
+
+ /* save header structure */
+ state->head = head;
+ head->done = 0;
+ return Z_OK;
+}
+
+/*
+ Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found
+ or when out of input. When called, *have is the number of pattern bytes
+ found in order so far, in 0..3. On return *have is updated to the new
+ state. If on return *have equals four, then the pattern was found and the
+ return value is how many bytes were read including the last byte of the
+ pattern. If *have is less than four, then the pattern has not been found
+ yet and the return value is len. In the latter case, syncsearch() can be
+ called again with more data and the *have state. *have is initialized to
+ zero for the first call.
+ */
+local unsigned syncsearch(have, buf, len)
+unsigned FAR *have;
+unsigned char FAR *buf;
+unsigned len;
+{
+ unsigned got;
+ unsigned next;
+
+ got = *have;
+ next = 0;
+ while (next < len && got < 4) {
+ if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))
+ got++;
+ else if (buf[next])
+ got = 0;
+ else
+ got = 4 - got;
+ next++;
+ }
+ *have = got;
+ return next;
+}
+
+int ZEXPORT inflateSync(strm)
+z_streamp strm;
+{
+ unsigned len; /* number of bytes to look at or looked at */
+ unsigned long in, out; /* temporary to save total_in and total_out */
+ unsigned char buf[4]; /* to restore bit buffer to byte string */
+ struct inflate_state FAR *state;
+
+ /* check parameters */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
+
+ /* if first time, start search in bit buffer */
+ if (state->mode != SYNC) {
+ state->mode = SYNC;
+ state->hold <<= state->bits & 7;
+ state->bits -= state->bits & 7;
+ len = 0;
+ while (state->bits >= 8) {
+ buf[len++] = (unsigned char)(state->hold);
+ state->hold >>= 8;
+ state->bits -= 8;
+ }
+ state->have = 0;
+ syncsearch(&(state->have), buf, len);
+ }
+
+ /* search available input */
+ len = syncsearch(&(state->have), strm->next_in, strm->avail_in);
+ strm->avail_in -= len;
+ strm->next_in += len;
+ strm->total_in += len;
+
+ /* return no joy or set up to restart inflate() on a new block */
+ if (state->have != 4) return Z_DATA_ERROR;
+ in = strm->total_in; out = strm->total_out;
+ inflateReset(strm);
+ strm->total_in = in; strm->total_out = out;
+ state->mode = TYPE;
+ return Z_OK;
+}
+
+/*
+ Returns true if inflate is currently at the end of a block generated by
+ Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+ implementation to provide an additional safety check. PPP uses
+ Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
+ block. When decompressing, PPP checks that at the end of input packet,
+ inflate is waiting for these length bytes.
+ */
+int ZEXPORT inflateSyncPoint(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ return state->mode == STORED && state->bits == 0;
+}
+
+int ZEXPORT inflateCopy(dest, source)
+z_streamp dest;
+z_streamp source;
+{
+ struct inflate_state FAR *state;
+ struct inflate_state FAR *copy;
+ unsigned char FAR *window;
+ unsigned wsize;
+
+ /* check input */
+ if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL ||
+ source->zalloc == (alloc_func)0 || source->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)source->state;
+
+ /* allocate space */
+ copy = (struct inflate_state FAR *)
+ ZALLOC(source, 1, sizeof(struct inflate_state));
+ if (copy == Z_NULL) return Z_MEM_ERROR;
+ window = Z_NULL;
+ if (state->window != Z_NULL) {
+ window = (unsigned char FAR *)
+ ZALLOC(source, 1U << state->wbits, sizeof(unsigned char));
+ if (window == Z_NULL) {
+ ZFREE(source, copy);
+ return Z_MEM_ERROR;
+ }
+ }
+
+ /* copy state */
+ zmemcpy(dest, source, sizeof(z_stream));
+ zmemcpy(copy, state, sizeof(struct inflate_state));
+ if (state->lencode >= state->codes &&
+ state->lencode <= state->codes + ENOUGH - 1) {
+ copy->lencode = copy->codes + (state->lencode - state->codes);
+ copy->distcode = copy->codes + (state->distcode - state->codes);
+ }
+ copy->next = copy->codes + (state->next - state->codes);
+ if (window != Z_NULL) {
+ wsize = 1U << state->wbits;
+ zmemcpy(window, state->window, wsize);
+ }
+ copy->window = window;
+ dest->state = (struct internal_state FAR *)copy;
+ return Z_OK;
+}
+
+int ZEXPORT inflateUndermine(strm, subvert)
+z_streamp strm;
+int subvert;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ state->sane = !subvert;
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+ return Z_OK;
+#else
+ state->sane = 1;
+ return Z_DATA_ERROR;
+#endif
+}
+
+long ZEXPORT inflateMark(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16;
+ state = (struct inflate_state FAR *)strm->state;
+ return ((long)(state->back) << 16) +
+ (state->mode == COPY ? state->length :
+ (state->mode == MATCH ? state->was - state->length : 0));
+}
diff --git a/src/3rdparty/assimp/contrib/zlib/inflate.h b/src/3rdparty/assimp/contrib/zlib/inflate.h
new file mode 100644
index 000000000..95f4986d4
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/inflate.h
@@ -0,0 +1,122 @@
+/* inflate.h -- internal inflate state definition
+ * Copyright (C) 1995-2009 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+ trailer decoding by inflate(). NO_GZIP would be used to avoid linking in
+ the crc code when it is not needed. For shared libraries, gzip decoding
+ should be left enabled. */
+#ifndef NO_GZIP
+# define GUNZIP
+#endif
+
+/* Possible inflate modes between inflate() calls */
+typedef enum {
+ HEAD, /* i: waiting for magic header */
+ FLAGS, /* i: waiting for method and flags (gzip) */
+ TIME, /* i: waiting for modification time (gzip) */
+ OS, /* i: waiting for extra flags and operating system (gzip) */
+ EXLEN, /* i: waiting for extra length (gzip) */
+ EXTRA, /* i: waiting for extra bytes (gzip) */
+ NAME, /* i: waiting for end of file name (gzip) */
+ COMMENT, /* i: waiting for end of comment (gzip) */
+ HCRC, /* i: waiting for header crc (gzip) */
+ DICTID, /* i: waiting for dictionary check value */
+ DICT, /* waiting for inflateSetDictionary() call */
+ TYPE, /* i: waiting for type bits, including last-flag bit */
+ TYPEDO, /* i: same, but skip check to exit inflate on new block */
+ STORED, /* i: waiting for stored size (length and complement) */
+ COPY_, /* i/o: same as COPY below, but only first time in */
+ COPY, /* i/o: waiting for input or output to copy stored block */
+ TABLE, /* i: waiting for dynamic block table lengths */
+ LENLENS, /* i: waiting for code length code lengths */
+ CODELENS, /* i: waiting for length/lit and distance code lengths */
+ LEN_, /* i: same as LEN below, but only first time in */
+ LEN, /* i: waiting for length/lit/eob code */
+ LENEXT, /* i: waiting for length extra bits */
+ DIST, /* i: waiting for distance code */
+ DISTEXT, /* i: waiting for distance extra bits */
+ MATCH, /* o: waiting for output space to copy string */
+ LIT, /* o: waiting for output space to write literal */
+ CHECK, /* i: waiting for 32-bit check value */
+ LENGTH, /* i: waiting for 32-bit length (gzip) */
+ DONE, /* finished check, done -- remain here until reset */
+ BAD, /* got a data error -- remain here until reset */
+ MEM, /* got an inflate() memory error -- remain here until reset */
+ SYNC /* looking for synchronization bytes to restart inflate() */
+} inflate_mode;
+
+/*
+ State transitions between above modes -
+
+ (most modes can go to BAD or MEM on error -- not shown for clarity)
+
+ Process header:
+ HEAD -> (gzip) or (zlib) or (raw)
+ (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT ->
+ HCRC -> TYPE
+ (zlib) -> DICTID or TYPE
+ DICTID -> DICT -> TYPE
+ (raw) -> TYPEDO
+ Read deflate blocks:
+ TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK
+ STORED -> COPY_ -> COPY -> TYPE
+ TABLE -> LENLENS -> CODELENS -> LEN_
+ LEN_ -> LEN
+ Read deflate codes in fixed or dynamic block:
+ LEN -> LENEXT or LIT or TYPE
+ LENEXT -> DIST -> DISTEXT -> MATCH -> LEN
+ LIT -> LEN
+ Process trailer:
+ CHECK -> LENGTH -> DONE
+ */
+
+/* state maintained between inflate() calls. Approximately 10K bytes. */
+struct inflate_state {
+ inflate_mode mode; /* current inflate mode */
+ int last; /* true if processing last block */
+ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
+ int havedict; /* true if dictionary provided */
+ int flags; /* gzip header method and flags (0 if zlib) */
+ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */
+ unsigned long check; /* protected copy of check value */
+ unsigned long total; /* protected copy of output count */
+ gz_headerp head; /* where to save gzip header information */
+ /* sliding window */
+ unsigned wbits; /* log base 2 of requested window size */
+ unsigned wsize; /* window size or zero if not using window */
+ unsigned whave; /* valid bytes in the window */
+ unsigned wnext; /* window write index */
+ unsigned char FAR *window; /* allocated sliding window, if needed */
+ /* bit accumulator */
+ unsigned long hold; /* input bit accumulator */
+ unsigned bits; /* number of bits in "in" */
+ /* for string and stored block copying */
+ unsigned length; /* literal or length of data to copy */
+ unsigned offset; /* distance back to copy string from */
+ /* for table and code decoding */
+ unsigned extra; /* extra bits needed */
+ /* fixed and dynamic code tables */
+ code const FAR *lencode; /* starting table for length/literal codes */
+ code const FAR *distcode; /* starting table for distance codes */
+ unsigned lenbits; /* index bits for lencode */
+ unsigned distbits; /* index bits for distcode */
+ /* dynamic table building */
+ unsigned ncode; /* number of code length code lengths */
+ unsigned nlen; /* number of length code lengths */
+ unsigned ndist; /* number of distance code lengths */
+ unsigned have; /* number of code lengths in lens[] */
+ code FAR *next; /* next available space in codes[] */
+ unsigned short lens[320]; /* temporary storage for code lengths */
+ unsigned short work[288]; /* work area for code table building */
+ code codes[ENOUGH]; /* space for code tables */
+ int sane; /* if false, allow invalid distance too far */
+ int back; /* bits back of last unprocessed length/lit */
+ unsigned was; /* initial length of match */
+};
diff --git a/src/3rdparty/assimp/contrib/zlib/inftrees.c b/src/3rdparty/assimp/contrib/zlib/inftrees.c
new file mode 100644
index 000000000..11e9c52ac
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/inftrees.c
@@ -0,0 +1,330 @@
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+
+#define MAXBITS 15
+
+const char inflate_copyright[] =
+ " inflate 1.2.5 Copyright 1995-2010 Mark Adler ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/*
+ Build a set of tables to decode the provided canonical Huffman code.
+ The code lengths are lens[0..codes-1]. The result starts at *table,
+ whose indices are 0..2^bits-1. work is a writable array of at least
+ lens shorts, which is used as a work area. type is the type of code
+ to be generated, CODES, LENS, or DISTS. On return, zero is success,
+ -1 is an invalid code, and +1 means that ENOUGH isn't enough. table
+ on return points to the next available entry's address. bits is the
+ requested root table index bits, and on return it is the actual root
+ table index bits. It will differ if the request is greater than the
+ longest code or if it is less than the shortest code.
+ */
+int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work)
+codetype type;
+unsigned short FAR *lens;
+unsigned codes;
+code FAR * FAR *table;
+unsigned FAR *bits;
+unsigned short FAR *work;
+{
+ unsigned len; /* a code's length in bits */
+ unsigned sym; /* index of code symbols */
+ unsigned min, max; /* minimum and maximum code lengths */
+ unsigned root; /* number of index bits for root table */
+ unsigned curr; /* number of index bits for current table */
+ unsigned drop; /* code bits to drop for sub-table */
+ int left; /* number of prefix codes available */
+ unsigned used; /* code entries in table used */
+ unsigned huff; /* Huffman code */
+ unsigned incr; /* for incrementing code, index */
+ unsigned fill; /* index for replicating entries */
+ unsigned low; /* low bits for current root entry */
+ unsigned mask; /* mask for low root bits */
+ code here; /* table entry for duplication */
+ code FAR *next; /* next available space in table */
+ const unsigned short FAR *base; /* base value table to use */
+ const unsigned short FAR *extra; /* extra bits table to use */
+ int end; /* use base and extra for symbol > end */
+ unsigned short count[MAXBITS+1]; /* number of codes of each length */
+ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */
+ static const unsigned short lbase[31] = { /* Length codes 257..285 base */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+ static const unsigned short lext[31] = { /* Length codes 257..285 extra */
+ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
+ 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 73, 195};
+ static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577, 0, 0};
+ static const unsigned short dext[32] = { /* Distance codes 0..29 extra */
+ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+ 28, 28, 29, 29, 64, 64};
+
+ /*
+ Process a set of code lengths to create a canonical Huffman code. The
+ code lengths are lens[0..codes-1]. Each length corresponds to the
+ symbols 0..codes-1. The Huffman code is generated by first sorting the
+ symbols by length from short to long, and retaining the symbol order
+ for codes with equal lengths. Then the code starts with all zero bits
+ for the first code of the shortest length, and the codes are integer
+ increments for the same length, and zeros are appended as the length
+ increases. For the deflate format, these bits are stored backwards
+ from their more natural integer increment ordering, and so when the
+ decoding tables are built in the large loop below, the integer codes
+ are incremented backwards.
+
+ This routine assumes, but does not check, that all of the entries in
+ lens[] are in the range 0..MAXBITS. The caller must assure this.
+ 1..MAXBITS is interpreted as that code length. zero means that that
+ symbol does not occur in this code.
+
+ The codes are sorted by computing a count of codes for each length,
+ creating from that a table of starting indices for each length in the
+ sorted table, and then entering the symbols in order in the sorted
+ table. The sorted table is work[], with that space being provided by
+ the caller.
+
+ The length counts are used for other purposes as well, i.e. finding
+ the minimum and maximum length codes, determining if there are any
+ codes at all, checking for a valid set of lengths, and looking ahead
+ at length counts to determine sub-table sizes when building the
+ decoding tables.
+ */
+
+ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
+ for (len = 0; len <= MAXBITS; len++)
+ count[len] = 0;
+ for (sym = 0; sym < codes; sym++)
+ count[lens[sym]]++;
+
+ /* bound code lengths, force root to be within code lengths */
+ root = *bits;
+ for (max = MAXBITS; max >= 1; max--)
+ if (count[max] != 0) break;
+ if (root > max) root = max;
+ if (max == 0) { /* no symbols to code at all */
+ here.op = (unsigned char)64; /* invalid code marker */
+ here.bits = (unsigned char)1;
+ here.val = (unsigned short)0;
+ *(*table)++ = here; /* make a table to force an error */
+ *(*table)++ = here;
+ *bits = 1;
+ return 0; /* no symbols, but wait for decoding to report error */
+ }
+ for (min = 1; min < max; min++)
+ if (count[min] != 0) break;
+ if (root < min) root = min;
+
+ /* check for an over-subscribed or incomplete set of lengths */
+ left = 1;
+ for (len = 1; len <= MAXBITS; len++) {
+ left <<= 1;
+ left -= count[len];
+ if (left < 0) return -1; /* over-subscribed */
+ }
+ if (left > 0 && (type == CODES || max != 1))
+ return -1; /* incomplete set */
+
+ /* generate offsets into symbol table for each length for sorting */
+ offs[1] = 0;
+ for (len = 1; len < MAXBITS; len++)
+ offs[len + 1] = offs[len] + count[len];
+
+ /* sort symbols by length, by symbol order within each length */
+ for (sym = 0; sym < codes; sym++)
+ if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;
+
+ /*
+ Create and fill in decoding tables. In this loop, the table being
+ filled is at next and has curr index bits. The code being used is huff
+ with length len. That code is converted to an index by dropping drop
+ bits off of the bottom. For codes where len is less than drop + curr,
+ those top drop + curr - len bits are incremented through all values to
+ fill the table with replicated entries.
+
+ root is the number of index bits for the root table. When len exceeds
+ root, sub-tables are created pointed to by the root entry with an index
+ of the low root bits of huff. This is saved in low to check for when a
+ new sub-table should be started. drop is zero when the root table is
+ being filled, and drop is root when sub-tables are being filled.
+
+ When a new sub-table is needed, it is necessary to look ahead in the
+ code lengths to determine what size sub-table is needed. The length
+ counts are used for this, and so count[] is decremented as codes are
+ entered in the tables.
+
+ used keeps track of how many table entries have been allocated from the
+ provided *table space. It is checked for LENS and DIST tables against
+ the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
+ the initial root table size constants. See the comments in inftrees.h
+ for more information.
+
+ sym increments through all symbols, and the loop terminates when
+ all codes of length max, i.e. all codes, have been processed. This
+ routine permits incomplete codes, so another loop after this one fills
+ in the rest of the decoding tables with invalid code markers.
+ */
+
+ /* set up for code type */
+ switch (type) {
+ case CODES:
+ base = extra = work; /* dummy value--not used */
+ end = 19;
+ break;
+ case LENS:
+ base = lbase;
+ base -= 257;
+ extra = lext;
+ extra -= 257;
+ end = 256;
+ break;
+ default: /* DISTS */
+ base = dbase;
+ extra = dext;
+ end = -1;
+ }
+
+ /* initialize state for loop */
+ huff = 0; /* starting code */
+ sym = 0; /* starting code symbol */
+ len = min; /* starting code length */
+ next = *table; /* current table to fill in */
+ curr = root; /* current table index bits */
+ drop = 0; /* current bits to drop from code for index */
+ low = (unsigned)(-1); /* trigger new sub-table when len > root */
+ used = 1U << root; /* use root table entries */
+ mask = used - 1; /* mask for comparing low */
+
+ /* check available table space */
+ if ((type == LENS && used >= ENOUGH_LENS) ||
+ (type == DISTS && used >= ENOUGH_DISTS))
+ return 1;
+
+ /* process all codes and make table entries */
+ for (;;) {
+ /* create table entry */
+ here.bits = (unsigned char)(len - drop);
+ if ((int)(work[sym]) < end) {
+ here.op = (unsigned char)0;
+ here.val = work[sym];
+ }
+ else if ((int)(work[sym]) > end) {
+ here.op = (unsigned char)(extra[work[sym]]);
+ here.val = base[work[sym]];
+ }
+ else {
+ here.op = (unsigned char)(32 + 64); /* end of block */
+ here.val = 0;
+ }
+
+ /* replicate for those indices with low len bits equal to huff */
+ incr = 1U << (len - drop);
+ fill = 1U << curr;
+ min = fill; /* save offset to next table */
+ do {
+ fill -= incr;
+ next[(huff >> drop) + fill] = here;
+ } while (fill != 0);
+
+ /* backwards increment the len-bit code huff */
+ incr = 1U << (len - 1);
+ while (huff & incr)
+ incr >>= 1;
+ if (incr != 0) {
+ huff &= incr - 1;
+ huff += incr;
+ }
+ else
+ huff = 0;
+
+ /* go to next symbol, update count, len */
+ sym++;
+ if (--(count[len]) == 0) {
+ if (len == max) break;
+ len = lens[work[sym]];
+ }
+
+ /* create new sub-table if needed */
+ if (len > root && (huff & mask) != low) {
+ /* if first time, transition to sub-tables */
+ if (drop == 0)
+ drop = root;
+
+ /* increment past last table */
+ next += min; /* here min is 1 << curr */
+
+ /* determine length of next table */
+ curr = len - drop;
+ left = (int)(1 << curr);
+ while (curr + drop < max) {
+ left -= count[curr + drop];
+ if (left <= 0) break;
+ curr++;
+ left <<= 1;
+ }
+
+ /* check for enough space */
+ used += 1U << curr;
+ if ((type == LENS && used >= ENOUGH_LENS) ||
+ (type == DISTS && used >= ENOUGH_DISTS))
+ return 1;
+
+ /* point entry in root table to sub-table */
+ low = huff & mask;
+ (*table)[low].op = (unsigned char)curr;
+ (*table)[low].bits = (unsigned char)root;
+ (*table)[low].val = (unsigned short)(next - *table);
+ }
+ }
+
+ /*
+ Fill in rest of table for incomplete codes. This loop is similar to the
+ loop above in incrementing huff for table indices. It is assumed that
+ len is equal to curr + drop, so there is no loop needed to increment
+ through high index bits. When the current sub-table is filled, the loop
+ drops back to the root table to fill in any remaining entries there.
+ */
+ here.op = (unsigned char)64; /* invalid code marker */
+ here.bits = (unsigned char)(len - drop);
+ here.val = (unsigned short)0;
+ while (huff != 0) {
+ /* when done with sub-table, drop back to root table */
+ if (drop != 0 && (huff & mask) != low) {
+ drop = 0;
+ len = root;
+ next = *table;
+ here.bits = (unsigned char)len;
+ }
+
+ /* put invalid code marker in table */
+ next[huff >> drop] = here;
+
+ /* backwards increment the len-bit code huff */
+ incr = 1U << (len - 1);
+ while (huff & incr)
+ incr >>= 1;
+ if (incr != 0) {
+ huff &= incr - 1;
+ huff += incr;
+ }
+ else
+ huff = 0;
+ }
+
+ /* set return parameters */
+ *table += used;
+ *bits = root;
+ return 0;
+}
diff --git a/src/3rdparty/assimp/contrib/zlib/inftrees.h b/src/3rdparty/assimp/contrib/zlib/inftrees.h
new file mode 100644
index 000000000..baa53a0b1
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/inftrees.h
@@ -0,0 +1,62 @@
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-2005, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* Structure for decoding tables. Each entry provides either the
+ information needed to do the operation requested by the code that
+ indexed that table entry, or it provides a pointer to another
+ table that indexes more bits of the code. op indicates whether
+ the entry is a pointer to another table, a literal, a length or
+ distance, an end-of-block, or an invalid code. For a table
+ pointer, the low four bits of op is the number of index bits of
+ that table. For a length or distance, the low four bits of op
+ is the number of extra bits to get after the code. bits is
+ the number of bits in this code or part of the code to drop off
+ of the bit buffer. val is the actual byte to output in the case
+ of a literal, the base length or distance, or the offset from
+ the current table to the next table. Each entry is four bytes. */
+typedef struct {
+ unsigned char op; /* operation, extra bits, table bits */
+ unsigned char bits; /* bits in this part of the code */
+ unsigned short val; /* offset in table or code value */
+} code;
+
+/* op values as set by inflate_table():
+ 00000000 - literal
+ 0000tttt - table link, tttt != 0 is the number of table index bits
+ 0001eeee - length or distance, eeee is the number of extra bits
+ 01100000 - end of block
+ 01000000 - invalid code
+ */
+
+/* Maximum size of the dynamic table. The maximum number of code structures is
+ 1444, which is the sum of 852 for literal/length codes and 592 for distance
+ codes. These values were found by exhaustive searches using the program
+ examples/enough.c found in the zlib distribtution. The arguments to that
+ program are the number of symbols, the initial root table size, and the
+ maximum bit length of a code. "enough 286 9 15" for literal/length codes
+ returns returns 852, and "enough 30 6 15" for distance codes returns 592.
+ The initial root table size (9 or 6) is found in the fifth argument of the
+ inflate_table() calls in inflate.c and infback.c. If the root table size is
+ changed, then these maximum sizes would be need to be recalculated and
+ updated. */
+#define ENOUGH_LENS 852
+#define ENOUGH_DISTS 592
+#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS)
+
+/* Type of code to build for inflate_table() */
+typedef enum {
+ CODES,
+ LENS,
+ DISTS
+} codetype;
+
+int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens,
+ unsigned codes, code FAR * FAR *table,
+ unsigned FAR *bits, unsigned short FAR *work));
diff --git a/src/3rdparty/assimp/contrib/zlib/trees.c b/src/3rdparty/assimp/contrib/zlib/trees.c
new file mode 100644
index 000000000..56e9bb1c1
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/trees.c
@@ -0,0 +1,1244 @@
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995-2010 Jean-loup Gailly
+ * detect_data_type() function provided freely by Cosmin Truta, 2006
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process uses several Huffman trees. The more
+ * common source values are represented by shorter bit sequences.
+ *
+ * Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values). The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ * Storer, James A.
+ * Data Compression: Methods and Theory, pp. 49-50.
+ * Computer Science Press, 1988. ISBN 0-7167-8156-5.
+ *
+ * Sedgewick, R.
+ * Algorithms, p290.
+ * Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* @(#) $Id$ */
+
+/* #define GEN_TREES_H */
+
+#include "deflate.h"
+
+#ifdef DEBUG
+# include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6 16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10 17
+/* repeat a zero length 3-10 times (3 bits of repeat count) */
+
+#define REPZ_11_138 18
+/* repeat a zero length 11-138 times (7 bits of repeat count) */
+
+local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+local const int extra_dbits[D_CODES] /* extra bits for each distance code */
+ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local const uch bl_order[BL_CODES]
+ = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+#define Buf_size (8 * 2*sizeof(char))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ */
+
+#define DIST_CODE_LEN 512 /* see definition of array dist_code below */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+/* non ANSI compilers may not accept trees.h */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+uch _dist_code[DIST_CODE_LEN];
+/* Distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+uch _length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+#else
+# include "trees.h"
+#endif /* GEN_TREES_H */
+
+struct static_tree_desc_s {
+ const ct_data *static_tree; /* static tree or NULL */
+ const intf *extra_bits; /* extra bits for each code or NULL */
+ int extra_base; /* base index for extra_bits */
+ int elems; /* max number of elements in the tree */
+ int max_length; /* max bit length for the codes */
+};
+
+local static_tree_desc static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local static_tree_desc static_d_desc =
+{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
+
+local static_tree_desc static_bl_desc =
+{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void tr_static_init OF((void));
+local void init_block OF((deflate_state *s));
+local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
+local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree OF((deflate_state *s, tree_desc *desc));
+local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local int build_bl_tree OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+ int blcodes));
+local void compress_block OF((deflate_state *s, ct_data *ltree,
+ ct_data *dtree));
+local int detect_data_type OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup OF((deflate_state *s));
+local void bi_flush OF((deflate_state *s));
+local void copy_block OF((deflate_state *s, charf *buf, unsigned len,
+ int header));
+
+#ifdef GEN_TREES_H
+local void gen_trees_header OF((void));
+#endif
+
+#ifndef DEBUG
+# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+ /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* DEBUG */
+# define send_code(s, c, tree) \
+ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
+ send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+ put_byte(s, (uch)((w) & 0xff)); \
+ put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef DEBUG
+local void send_bits OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+ deflate_state *s;
+ int value; /* value to send */
+ int length; /* number of bits */
+{
+ Tracevv((stderr," l %2d v %4x ", length, value));
+ Assert(length > 0 && length <= 15, "invalid length");
+ s->bits_sent += (ulg)length;
+
+ /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+ * unused bits in value.
+ */
+ if (s->bi_valid > (int)Buf_size - length) {
+ s->bi_buf |= (ush)value << s->bi_valid;
+ put_short(s, s->bi_buf);
+ s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+ s->bi_valid += length - Buf_size;
+ } else {
+ s->bi_buf |= (ush)value << s->bi_valid;
+ s->bi_valid += length;
+ }
+}
+#else /* !DEBUG */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+ if (s->bi_valid > (int)Buf_size - len) {\
+ int val = value;\
+ s->bi_buf |= (ush)val << s->bi_valid;\
+ put_short(s, s->bi_buf);\
+ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+ s->bi_valid += len - Buf_size;\
+ } else {\
+ s->bi_buf |= (ush)(value) << s->bi_valid;\
+ s->bi_valid += len;\
+ }\
+}
+#endif /* DEBUG */
+
+
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables.
+ */
+local void tr_static_init()
+{
+#if defined(GEN_TREES_H) || !defined(STDC)
+ static int static_init_done = 0;
+ int n; /* iterates over tree elements */
+ int bits; /* bit counter */
+ int length; /* length value */
+ int code; /* code value */
+ int dist; /* distance index */
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ if (static_init_done) return;
+
+ /* For some embedded targets, global variables are not initialized: */
+#ifdef NO_INIT_GLOBAL_POINTERS
+ static_l_desc.static_tree = static_ltree;
+ static_l_desc.extra_bits = extra_lbits;
+ static_d_desc.static_tree = static_dtree;
+ static_d_desc.extra_bits = extra_dbits;
+ static_bl_desc.extra_bits = extra_blbits;
+#endif
+
+ /* Initialize the mapping length (0..255) -> length code (0..28) */
+ length = 0;
+ for (code = 0; code < LENGTH_CODES-1; code++) {
+ base_length[code] = length;
+ for (n = 0; n < (1<<extra_lbits[code]); n++) {
+ _length_code[length++] = (uch)code;
+ }
+ }
+ Assert (length == 256, "tr_static_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ _length_code[length-1] = (uch)code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0 ; code < 16; code++) {
+ base_dist[code] = dist;
+ for (n = 0; n < (1<<extra_dbits[code]); n++) {
+ _dist_code[dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: dist != 256");
+ dist >>= 7; /* from now on, all distances are divided by 128 */
+ for ( ; code < D_CODES; code++) {
+ base_dist[code] = dist << 7;
+ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+ _dist_code[256 + dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: 256+dist != 512");
+
+ /* Construct the codes of the static literal tree */
+ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+ n = 0;
+ while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+ while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+ while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+ while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+ /* Codes 286 and 287 do not exist, but we must include them in the
+ * tree construction to get a canonical Huffman tree (longest code
+ * all ones)
+ */
+ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+ /* The static distance tree is trivial: */
+ for (n = 0; n < D_CODES; n++) {
+ static_dtree[n].Len = 5;
+ static_dtree[n].Code = bi_reverse((unsigned)n, 5);
+ }
+ static_init_done = 1;
+
+# ifdef GEN_TREES_H
+ gen_trees_header();
+# endif
+#endif /* defined(GEN_TREES_H) || !defined(STDC) */
+}
+
+/* ===========================================================================
+ * Genererate the file trees.h describing the static trees.
+ */
+#ifdef GEN_TREES_H
+# ifndef DEBUG
+# include <stdio.h>
+# endif
+
+# define SEPARATOR(i, last, width) \
+ ((i) == (last)? "\n};\n\n" : \
+ ((i) % (width) == (width)-1 ? ",\n" : ", "))
+
+void gen_trees_header()
+{
+ FILE *header = fopen("trees.h", "w");
+ int i;
+
+ Assert (header != NULL, "Can't open trees.h");
+ fprintf(header,
+ "/* header created automatically with -DGEN_TREES_H */\n\n");
+
+ fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
+ for (i = 0; i < L_CODES+2; i++) {
+ fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
+ static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
+ }
+
+ fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
+ for (i = 0; i < D_CODES; i++) {
+ fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
+ static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
+ }
+
+ fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n");
+ for (i = 0; i < DIST_CODE_LEN; i++) {
+ fprintf(header, "%2u%s", _dist_code[i],
+ SEPARATOR(i, DIST_CODE_LEN-1, 20));
+ }
+
+ fprintf(header,
+ "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
+ for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
+ fprintf(header, "%2u%s", _length_code[i],
+ SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
+ }
+
+ fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
+ for (i = 0; i < LENGTH_CODES; i++) {
+ fprintf(header, "%1u%s", base_length[i],
+ SEPARATOR(i, LENGTH_CODES-1, 20));
+ }
+
+ fprintf(header, "local const int base_dist[D_CODES] = {\n");
+ for (i = 0; i < D_CODES; i++) {
+ fprintf(header, "%5u%s", base_dist[i],
+ SEPARATOR(i, D_CODES-1, 10));
+ }
+
+ fclose(header);
+}
+#endif /* GEN_TREES_H */
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+void ZLIB_INTERNAL _tr_init(s)
+ deflate_state *s;
+{
+ tr_static_init();
+
+ s->l_desc.dyn_tree = s->dyn_ltree;
+ s->l_desc.stat_desc = &static_l_desc;
+
+ s->d_desc.dyn_tree = s->dyn_dtree;
+ s->d_desc.stat_desc = &static_d_desc;
+
+ s->bl_desc.dyn_tree = s->bl_tree;
+ s->bl_desc.stat_desc = &static_bl_desc;
+
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+#ifdef DEBUG
+ s->compressed_len = 0L;
+ s->bits_sent = 0L;
+#endif
+
+ /* Initialize the first block of the first file: */
+ init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+ deflate_state *s;
+{
+ int n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
+ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
+ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+ s->dyn_ltree[END_BLOCK].Freq = 1;
+ s->opt_len = s->static_len = 0L;
+ s->last_lit = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+ top = s->heap[SMALLEST]; \
+ s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+ pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+ (tree[n].Freq < tree[m].Freq || \
+ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+ deflate_state *s;
+ ct_data *tree; /* the tree to restore */
+ int k; /* node to move down */
+{
+ int v = s->heap[k];
+ int j = k << 1; /* left son of k */
+ while (j <= s->heap_len) {
+ /* Set j to the smallest of the two sons: */
+ if (j < s->heap_len &&
+ smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+ j++;
+ }
+ /* Exit if v is smaller than both sons */
+ if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+ /* Exchange v with the smallest son */
+ s->heap[k] = s->heap[j]; k = j;
+
+ /* And continue down the tree, setting j to the left son of k */
+ j <<= 1;
+ }
+ s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ * above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ * array bl_count contains the frequencies for each bit length.
+ * The length opt_len is updated; static_len is also updated if stree is
+ * not null.
+ */
+local void gen_bitlen(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ int max_code = desc->max_code;
+ const ct_data *stree = desc->stat_desc->static_tree;
+ const intf *extra = desc->stat_desc->extra_bits;
+ int base = desc->stat_desc->extra_base;
+ int max_length = desc->stat_desc->max_length;
+ int h; /* heap index */
+ int n, m; /* iterate over the tree elements */
+ int bits; /* bit length */
+ int xbits; /* extra bits */
+ ush f; /* frequency */
+ int overflow = 0; /* number of elements with bit length too large */
+
+ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+ for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+ n = s->heap[h];
+ bits = tree[tree[n].Dad].Len + 1;
+ if (bits > max_length) bits = max_length, overflow++;
+ tree[n].Len = (ush)bits;
+ /* We overwrite tree[n].Dad which is no longer needed */
+
+ if (n > max_code) continue; /* not a leaf node */
+
+ s->bl_count[bits]++;
+ xbits = 0;
+ if (n >= base) xbits = extra[n-base];
+ f = tree[n].Freq;
+ s->opt_len += (ulg)f * (bits + xbits);
+ if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
+ }
+ if (overflow == 0) return;
+
+ Trace((stderr,"\nbit length overflow\n"));
+ /* This happens for example on obj2 and pic of the Calgary corpus */
+
+ /* Find the first bit length which could increase: */
+ do {
+ bits = max_length-1;
+ while (s->bl_count[bits] == 0) bits--;
+ s->bl_count[bits]--; /* move one leaf down the tree */
+ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+ s->bl_count[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ } while (overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for (bits = max_length; bits != 0; bits--) {
+ n = s->bl_count[bits];
+ while (n != 0) {
+ m = s->heap[--h];
+ if (m > max_code) continue;
+ if ((unsigned) tree[m].Len != (unsigned) bits) {
+ Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+ s->opt_len += ((long)bits - (long)tree[m].Len)
+ *(long)tree[m].Freq;
+ tree[m].Len = (ush)bits;
+ }
+ n--;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+ ct_data *tree; /* the tree to decorate */
+ int max_code; /* largest code with non zero frequency */
+ ushf *bl_count; /* number of codes at each bit length */
+{
+ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+ ush code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ next_code[bits] = code = (code + bl_count[bits-1]) << 1;
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+ "inconsistent bit counts");
+ Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+ for (n = 0; n <= max_code; n++) {
+ int len = tree[n].Len;
+ if (len == 0) continue;
+ /* Now reverse the bits */
+ tree[n].Code = bi_reverse(next_code[len]++, len);
+
+ Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+ n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+ }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ * and corresponding code. The length opt_len is updated; static_len is
+ * also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ const ct_data *stree = desc->stat_desc->static_tree;
+ int elems = desc->stat_desc->elems;
+ int n, m; /* iterate over heap elements */
+ int max_code = -1; /* largest code with non zero frequency */
+ int node; /* new node being created */
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+ for (n = 0; n < elems; n++) {
+ if (tree[n].Freq != 0) {
+ s->heap[++(s->heap_len)] = max_code = n;
+ s->depth[n] = 0;
+ } else {
+ tree[n].Len = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while (s->heap_len < 2) {
+ node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+ tree[node].Freq = 1;
+ s->depth[node] = 0;
+ s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+ /* node is 0 or 1 so it does not have extra bits */
+ }
+ desc->max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ node = elems; /* next internal node of the tree */
+ do {
+ pqremove(s, tree, n); /* n = node of least frequency */
+ m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+ s->heap[--(s->heap_max)] = m;
+
+ /* Create a new node father of n and m */
+ tree[node].Freq = tree[n].Freq + tree[m].Freq;
+ s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ?
+ s->depth[n] : s->depth[m]) + 1);
+ tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+ if (tree == s->bl_tree) {
+ fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+ }
+#endif
+ /* and insert the new node in the heap */
+ s->heap[SMALLEST] = node++;
+ pqdownheap(s, tree, SMALLEST);
+
+ } while (s->heap_len >= 2);
+
+ s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen(s, (tree_desc *)desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ if (nextlen == 0) max_count = 138, min_count = 3;
+ tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ s->bl_tree[curlen].Freq += count;
+ } else if (curlen != 0) {
+ if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+ s->bl_tree[REP_3_6].Freq++;
+ } else if (count <= 10) {
+ s->bl_tree[REPZ_3_10].Freq++;
+ } else {
+ s->bl_tree[REPZ_11_138].Freq++;
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ /* tree[max_code+1].Len = -1; */ /* guard already set */
+ if (nextlen == 0) max_count = 138, min_count = 3;
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+ } else if (curlen != 0) {
+ if (curlen != prevlen) {
+ send_code(s, curlen, s->bl_tree); count--;
+ }
+ Assert(count >= 3 && count <= 6, " 3_6?");
+ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+ } else if (count <= 10) {
+ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+ } else {
+ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+ deflate_state *s;
+{
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ /* Determine the bit length frequencies for literal and distance trees */
+ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+ scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+ /* Build the bit length tree: */
+ build_tree(s, (tree_desc *)(&(s->bl_desc)));
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+ if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+ }
+ /* Update opt_len to include the bit length tree and counts */
+ s->opt_len += 3*(max_blindex+1) + 5+5+4;
+ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+ s->opt_len, s->static_len));
+
+ return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+ deflate_state *s;
+ int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+ int rank; /* index in bl_order */
+
+ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+ Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+ "too many codes");
+ Tracev((stderr, "\nbl counts: "));
+ send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+ send_bits(s, dcodes-1, 5);
+ send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
+ for (rank = 0; rank < blcodes; rank++) {
+ Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+ send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+ }
+ Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last)
+ deflate_state *s;
+ charf *buf; /* input block */
+ ulg stored_len; /* length of input block */
+ int last; /* one if this is the last block for a file */
+{
+ send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */
+#ifdef DEBUG
+ s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
+ s->compressed_len += (stored_len + 4) << 3;
+#endif
+ copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
+}
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ * The current inflate code requires 9 bits of lookahead. If the
+ * last two codes for the previous block (real code plus EOB) were coded
+ * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
+ * the last real code. In this case we send two empty static blocks instead
+ * of one. (There are no problems if the previous block is stored or fixed.)
+ * To simplify the code, we assume the worst case of last real code encoded
+ * on one bit only.
+ */
+void ZLIB_INTERNAL _tr_align(s)
+ deflate_state *s;
+{
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+#endif
+ bi_flush(s);
+ /* Of the 10 bits for the empty block, we have already sent
+ * (10 - bi_valid) bits. The lookahead for the last real code (before
+ * the EOB of the previous block) was thus at least one plus the length
+ * of the EOB plus what we have just sent of the empty static block.
+ */
+ if (1 + s->last_eob_len + 10 - s->bi_valid < 9) {
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+ s->compressed_len += 10L;
+#endif
+ bi_flush(s);
+ }
+ s->last_eob_len = 7;
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file.
+ */
+void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
+ deflate_state *s;
+ charf *buf; /* input block, or NULL if too old */
+ ulg stored_len; /* length of input block */
+ int last; /* one if this is the last block for a file */
+{
+ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+ int max_blindex = 0; /* index of last bit length code of non zero freq */
+
+ /* Build the Huffman trees unless a stored block is forced */
+ if (s->level > 0) {
+
+ /* Check if the file is binary or text */
+ if (s->strm->data_type == Z_UNKNOWN)
+ s->strm->data_type = detect_data_type(s);
+
+ /* Construct the literal and distance trees */
+ build_tree(s, (tree_desc *)(&(s->l_desc)));
+ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+
+ build_tree(s, (tree_desc *)(&(s->d_desc)));
+ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree(s);
+
+ /* Determine the best encoding. Compute the block lengths in bytes. */
+ opt_lenb = (s->opt_len+3+7)>>3;
+ static_lenb = (s->static_len+3+7)>>3;
+
+ Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+ s->last_lit));
+
+ if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+ } else {
+ Assert(buf != (char*)0, "lost buf");
+ opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
+ }
+
+#ifdef FORCE_STORED
+ if (buf != (char*)0) { /* force stored block */
+#else
+ if (stored_len+4 <= opt_lenb && buf != (char*)0) {
+ /* 4: two words for the lengths */
+#endif
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ _tr_stored_block(s, buf, stored_len, last);
+
+#ifdef FORCE_STATIC
+ } else if (static_lenb >= 0) { /* force static trees */
+#else
+ } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) {
+#endif
+ send_bits(s, (STATIC_TREES<<1)+last, 3);
+ compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
+#ifdef DEBUG
+ s->compressed_len += 3 + s->static_len;
+#endif
+ } else {
+ send_bits(s, (DYN_TREES<<1)+last, 3);
+ send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+ max_blindex+1);
+ compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
+#ifdef DEBUG
+ s->compressed_len += 3 + s->opt_len;
+#endif
+ }
+ Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+ /* The above check is made mod 2^32, for files larger than 512 MB
+ * and uLong implemented on 32 bits.
+ */
+ init_block(s);
+
+ if (last) {
+ bi_windup(s);
+#ifdef DEBUG
+ s->compressed_len += 7; /* align on byte boundary */
+#endif
+ }
+ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+ s->compressed_len-7*last));
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+int ZLIB_INTERNAL _tr_tally (s, dist, lc)
+ deflate_state *s;
+ unsigned dist; /* distance of matched string */
+ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+ s->d_buf[s->last_lit] = (ush)dist;
+ s->l_buf[s->last_lit++] = (uch)lc;
+ if (dist == 0) {
+ /* lc is the unmatched char */
+ s->dyn_ltree[lc].Freq++;
+ } else {
+ s->matches++;
+ /* Here, lc is the match length - MIN_MATCH */
+ dist--; /* dist = match distance - 1 */
+ Assert((ush)dist < (ush)MAX_DIST(s) &&
+ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+ (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
+
+ s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
+ s->dyn_dtree[d_code(dist)].Freq++;
+ }
+
+#ifdef TRUNCATE_BLOCK
+ /* Try to guess if it is profitable to stop the current block here */
+ if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
+ /* Compute an upper bound for the compressed length */
+ ulg out_length = (ulg)s->last_lit*8L;
+ ulg in_length = (ulg)((long)s->strstart - s->block_start);
+ int dcode;
+ for (dcode = 0; dcode < D_CODES; dcode++) {
+ out_length += (ulg)s->dyn_dtree[dcode].Freq *
+ (5L+extra_dbits[dcode]);
+ }
+ out_length >>= 3;
+ Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+ s->last_lit, in_length, out_length,
+ 100L - out_length*100L/in_length));
+ if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
+ }
+#endif
+ return (s->last_lit == s->lit_bufsize-1);
+ /* We avoid equality with lit_bufsize because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+ deflate_state *s;
+ ct_data *ltree; /* literal tree */
+ ct_data *dtree; /* distance tree */
+{
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned lx = 0; /* running index in l_buf */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if (s->last_lit != 0) do {
+ dist = s->d_buf[lx];
+ lc = s->l_buf[lx++];
+ if (dist == 0) {
+ send_code(s, lc, ltree); /* send a literal byte */
+ Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = _length_code[lc];
+ send_code(s, code+LITERALS+1, ltree); /* send the length code */
+ extra = extra_lbits[code];
+ if (extra != 0) {
+ lc -= base_length[code];
+ send_bits(s, lc, extra); /* send the extra length bits */
+ }
+ dist--; /* dist is now the match distance - 1 */
+ code = d_code(dist);
+ Assert (code < D_CODES, "bad d_code");
+
+ send_code(s, code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra != 0) {
+ dist -= base_dist[code];
+ send_bits(s, dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+
+ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+ Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
+ "pendingBuf overflow");
+
+ } while (lx < s->last_lit);
+
+ send_code(s, END_BLOCK, ltree);
+ s->last_eob_len = ltree[END_BLOCK].Len;
+}
+
+/* ===========================================================================
+ * Check if the data type is TEXT or BINARY, using the following algorithm:
+ * - TEXT if the two conditions below are satisfied:
+ * a) There are no non-portable control characters belonging to the
+ * "black list" (0..6, 14..25, 28..31).
+ * b) There is at least one printable character belonging to the
+ * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
+ * - BINARY otherwise.
+ * - The following partially-portable control characters form a
+ * "gray list" that is ignored in this detection algorithm:
+ * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
+ * IN assertion: the fields Freq of dyn_ltree are set.
+ */
+local int detect_data_type(s)
+ deflate_state *s;
+{
+ /* black_mask is the bit mask of black-listed bytes
+ * set bits 0..6, 14..25, and 28..31
+ * 0xf3ffc07f = binary 11110011111111111100000001111111
+ */
+ unsigned long black_mask = 0xf3ffc07fUL;
+ int n;
+
+ /* Check for non-textual ("black-listed") bytes. */
+ for (n = 0; n <= 31; n++, black_mask >>= 1)
+ if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0))
+ return Z_BINARY;
+
+ /* Check for textual ("white-listed") bytes. */
+ if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0
+ || s->dyn_ltree[13].Freq != 0)
+ return Z_TEXT;
+ for (n = 32; n < LITERALS; n++)
+ if (s->dyn_ltree[n].Freq != 0)
+ return Z_TEXT;
+
+ /* There are no "black-listed" or "white-listed" bytes:
+ * this stream either is empty or has tolerated ("gray-listed") bytes only.
+ */
+ return Z_BINARY;
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+ unsigned code; /* the value to invert */
+ int len; /* its bit length */
+{
+ register unsigned res = 0;
+ do {
+ res |= code & 1;
+ code >>= 1, res <<= 1;
+ } while (--len > 0);
+ return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+ deflate_state *s;
+{
+ if (s->bi_valid == 16) {
+ put_short(s, s->bi_buf);
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ } else if (s->bi_valid >= 8) {
+ put_byte(s, (Byte)s->bi_buf);
+ s->bi_buf >>= 8;
+ s->bi_valid -= 8;
+ }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+ deflate_state *s;
+{
+ if (s->bi_valid > 8) {
+ put_short(s, s->bi_buf);
+ } else if (s->bi_valid > 0) {
+ put_byte(s, (Byte)s->bi_buf);
+ }
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+#ifdef DEBUG
+ s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+local void copy_block(s, buf, len, header)
+ deflate_state *s;
+ charf *buf; /* the input data */
+ unsigned len; /* its length */
+ int header; /* true if block header must be written */
+{
+ bi_windup(s); /* align on byte boundary */
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+
+ if (header) {
+ put_short(s, (ush)len);
+ put_short(s, (ush)~len);
+#ifdef DEBUG
+ s->bits_sent += 2*16;
+#endif
+ }
+#ifdef DEBUG
+ s->bits_sent += (ulg)len<<3;
+#endif
+ while (len--) {
+ put_byte(s, *buf++);
+ }
+}
diff --git a/src/3rdparty/assimp/contrib/zlib/trees.h b/src/3rdparty/assimp/contrib/zlib/trees.h
new file mode 100644
index 000000000..d35639d82
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/trees.h
@@ -0,0 +1,128 @@
+/* header created automatically with -DGEN_TREES_H */
+
+local const ct_data static_ltree[L_CODES+2] = {
+{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}},
+{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}},
+{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}},
+{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}},
+{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}},
+{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}},
+{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}},
+{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}},
+{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}},
+{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}},
+{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}},
+{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}},
+{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}},
+{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}},
+{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}},
+{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}},
+{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}},
+{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}},
+{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}},
+{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}},
+{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}},
+{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}},
+{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}},
+{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}},
+{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}},
+{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}},
+{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}},
+{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}},
+{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}},
+{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}},
+{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}},
+{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}},
+{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}},
+{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}},
+{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}},
+{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}},
+{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}},
+{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}},
+{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}},
+{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}},
+{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}},
+{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}},
+{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}},
+{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}},
+{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}},
+{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}},
+{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}},
+{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}},
+{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}},
+{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}},
+{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}},
+{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}},
+{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}},
+{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}},
+{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}},
+{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}},
+{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}},
+{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}}
+};
+
+local const ct_data static_dtree[D_CODES] = {
+{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
+{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
+{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
+{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
+{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
+{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
+};
+
+const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {
+ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
+ 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
+10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17,
+18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
+};
+
+const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12,
+13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
+17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
+19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
+22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
+};
+
+local const int base_length[LENGTH_CODES] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
+64, 80, 96, 112, 128, 160, 192, 224, 0
+};
+
+local const int base_dist[D_CODES] = {
+ 0, 1, 2, 3, 4, 6, 8, 12, 16, 24,
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
+ 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
+};
+
diff --git a/src/3rdparty/assimp/contrib/zlib/zconf.h b/src/3rdparty/assimp/contrib/zlib/zconf.h
new file mode 100644
index 000000000..02ce56c43
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/zconf.h
@@ -0,0 +1,428 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2010 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ * Even better than compiling with -DZ_PREFIX would be to use configure to set
+ * this permanently in zconf.h using "./configure --zprefix".
+ */
+#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */
+
+/* all linked symbols */
+# define _dist_code z__dist_code
+# define _length_code z__length_code
+# define _tr_align z__tr_align
+# define _tr_flush_block z__tr_flush_block
+# define _tr_init z__tr_init
+# define _tr_stored_block z__tr_stored_block
+# define _tr_tally z__tr_tally
+# define adler32 z_adler32
+# define adler32_combine z_adler32_combine
+# define adler32_combine64 z_adler32_combine64
+# define compress z_compress
+# define compress2 z_compress2
+# define compressBound z_compressBound
+# define crc32 z_crc32
+# define crc32_combine z_crc32_combine
+# define crc32_combine64 z_crc32_combine64
+# define deflate z_deflate
+# define deflateBound z_deflateBound
+# define deflateCopy z_deflateCopy
+# define deflateEnd z_deflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateInit_ z_deflateInit_
+# define deflateParams z_deflateParams
+# define deflatePrime z_deflatePrime
+# define deflateReset z_deflateReset
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateSetHeader z_deflateSetHeader
+# define deflateTune z_deflateTune
+# define deflate_copyright z_deflate_copyright
+# define get_crc_table z_get_crc_table
+# define gz_error z_gz_error
+# define gz_intmax z_gz_intmax
+# define gz_strwinerror z_gz_strwinerror
+# define gzbuffer z_gzbuffer
+# define gzclearerr z_gzclearerr
+# define gzclose z_gzclose
+# define gzclose_r z_gzclose_r
+# define gzclose_w z_gzclose_w
+# define gzdirect z_gzdirect
+# define gzdopen z_gzdopen
+# define gzeof z_gzeof
+# define gzerror z_gzerror
+# define gzflush z_gzflush
+# define gzgetc z_gzgetc
+# define gzgets z_gzgets
+# define gzoffset z_gzoffset
+# define gzoffset64 z_gzoffset64
+# define gzopen z_gzopen
+# define gzopen64 z_gzopen64
+# define gzprintf z_gzprintf
+# define gzputc z_gzputc
+# define gzputs z_gzputs
+# define gzread z_gzread
+# define gzrewind z_gzrewind
+# define gzseek z_gzseek
+# define gzseek64 z_gzseek64
+# define gzsetparams z_gzsetparams
+# define gztell z_gztell
+# define gztell64 z_gztell64
+# define gzungetc z_gzungetc
+# define gzwrite z_gzwrite
+# define inflate z_inflate
+# define inflateBack z_inflateBack
+# define inflateBackEnd z_inflateBackEnd
+# define inflateBackInit_ z_inflateBackInit_
+# define inflateCopy z_inflateCopy
+# define inflateEnd z_inflateEnd
+# define inflateGetHeader z_inflateGetHeader
+# define inflateInit2_ z_inflateInit2_
+# define inflateInit_ z_inflateInit_
+# define inflateMark z_inflateMark
+# define inflatePrime z_inflatePrime
+# define inflateReset z_inflateReset
+# define inflateReset2 z_inflateReset2
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateSync z_inflateSync
+# define inflateSyncPoint z_inflateSyncPoint
+# define inflateUndermine z_inflateUndermine
+# define inflate_copyright z_inflate_copyright
+# define inflate_fast z_inflate_fast
+# define inflate_table z_inflate_table
+# define uncompress z_uncompress
+# define zError z_zError
+# define zcalloc z_zcalloc
+# define zcfree z_zcfree
+# define zlibCompileFlags z_zlibCompileFlags
+# define zlibVersion z_zlibVersion
+
+/* all zlib typedefs in zlib.h and zconf.h */
+# define Byte z_Byte
+# define Bytef z_Bytef
+# define alloc_func z_alloc_func
+# define charf z_charf
+# define free_func z_free_func
+# define gzFile z_gzFile
+# define gz_header z_gz_header
+# define gz_headerp z_gz_headerp
+# define in_func z_in_func
+# define intf z_intf
+# define out_func z_out_func
+# define uInt z_uInt
+# define uIntf z_uIntf
+# define uLong z_uLong
+# define uLongf z_uLongf
+# define voidp z_voidp
+# define voidpc z_voidpc
+# define voidpf z_voidpf
+
+/* all zlib structs in zlib.h and zconf.h */
+# define gz_header_s z_gz_header_s
+# define internal_state z_internal_state
+
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+# define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+# define WINDOWS
+#endif
+#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
+# ifndef WIN32
+# define WIN32
+# endif
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+# ifndef SYS16BIT
+# define SYS16BIT
+# endif
+# endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+# ifndef STDC
+# define STDC
+# endif
+# if __STDC_VERSION__ >= 199901L
+# ifndef STDC99
+# define STDC99
+# endif
+# endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+# define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
+# define STDC
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const /* note: need a more gentle solution here */
+# endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+# if defined(M_I86SM) || defined(M_I86MM)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+# if (defined(__SMALL__) || defined(__MEDIUM__))
+ /* Turbo C small or medium model */
+# define SMALL_MEDIUM
+# ifdef __BORLANDC__
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+ /* If building or using zlib as a DLL, define ZLIB_DLL.
+ * This is not mandatory, but it offers a little performance increase.
+ */
+# ifdef ZLIB_DLL
+# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+# ifdef ZLIB_INTERNAL
+# define ZEXTERN extern __declspec(dllexport)
+# else
+# define ZEXTERN extern __declspec(dllimport)
+# endif
+# endif
+# endif /* ZLIB_DLL */
+ /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+ * define ZLIB_WINAPI.
+ * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+ */
+# ifdef ZLIB_WINAPI
+# ifdef FAR
+# undef FAR
+# endif
+# include <windows.h>
+ /* No need for _export, use ZLIB.DEF instead. */
+ /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+# define ZEXPORT WINAPI
+# ifdef WIN32
+# define ZEXPORTVA WINAPIV
+# else
+# define ZEXPORTVA FAR CDECL
+# endif
+# endif
+#endif
+
+#if defined (__BEOS__)
+# ifdef ZLIB_DLL
+# ifdef ZLIB_INTERNAL
+# define ZEXPORT __declspec(dllexport)
+# define ZEXPORTVA __declspec(dllexport)
+# else
+# define ZEXPORT __declspec(dllimport)
+# define ZEXPORTVA __declspec(dllimport)
+# endif
+# endif
+#endif
+
+#ifndef ZEXTERN
+# define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+# define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char Byte; /* 8 bits */
+#endif
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void const *voidpc;
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte const *voidpc;
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */
+# define Z_HAVE_UNISTD_H
+#endif
+
+#ifdef STDC
+# include <sys/types.h> /* for off_t */
+#endif
+
+/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and
+ * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even
+ * though the former does not conform to the LFS document), but considering
+ * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as
+ * equivalently requesting no 64-bit operations
+ */
+#if -_LARGEFILE64_SOURCE - -1 == 1
+# undef _LARGEFILE64_SOURCE
+#endif
+
+#if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE)
+# include <unistd.h> /* for SEEK_* and off_t */
+# ifdef VMS
+# include <unixio.h> /* for off_t */
+# endif
+# ifndef z_off_t
+# define z_off_t off_t
+# endif
+#endif
+
+#ifndef SEEK_SET
+# define SEEK_SET 0 /* Seek from beginning of file. */
+# define SEEK_CUR 1 /* Seek from current position. */
+# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
+#endif
+
+#ifndef z_off_t
+# define z_off_t long
+#endif
+
+#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0
+# define z_off64_t off64_t
+#else
+# define z_off64_t z_off_t
+#endif
+
+#if defined(__OS400__)
+# define NO_vsnprintf
+#endif
+
+#if defined(__MVS__)
+# define NO_vsnprintf
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+ #pragma map(deflateInit_,"DEIN")
+ #pragma map(deflateInit2_,"DEIN2")
+ #pragma map(deflateEnd,"DEEND")
+ #pragma map(deflateBound,"DEBND")
+ #pragma map(inflateInit_,"ININ")
+ #pragma map(inflateInit2_,"ININ2")
+ #pragma map(inflateEnd,"INEND")
+ #pragma map(inflateSync,"INSY")
+ #pragma map(inflateSetDictionary,"INSEDI")
+ #pragma map(compressBound,"CMBND")
+ #pragma map(inflate_table,"INTABL")
+ #pragma map(inflate_fast,"INFA")
+ #pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/src/3rdparty/assimp/contrib/zlib/zconf.in.h b/src/3rdparty/assimp/contrib/zlib/zconf.in.h
new file mode 100644
index 000000000..03a9431c8
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/zconf.in.h
@@ -0,0 +1,332 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+# define deflateInit_ z_deflateInit_
+# define deflate z_deflate
+# define deflateEnd z_deflateEnd
+# define inflateInit_ z_inflateInit_
+# define inflate z_inflate
+# define inflateEnd z_inflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateCopy z_deflateCopy
+# define deflateReset z_deflateReset
+# define deflateParams z_deflateParams
+# define deflateBound z_deflateBound
+# define deflatePrime z_deflatePrime
+# define inflateInit2_ z_inflateInit2_
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateSync z_inflateSync
+# define inflateSyncPoint z_inflateSyncPoint
+# define inflateCopy z_inflateCopy
+# define inflateReset z_inflateReset
+# define inflateBack z_inflateBack
+# define inflateBackEnd z_inflateBackEnd
+# define compress z_compress
+# define compress2 z_compress2
+# define compressBound z_compressBound
+# define uncompress z_uncompress
+# define adler32 z_adler32
+# define crc32 z_crc32
+# define get_crc_table z_get_crc_table
+# define zError z_zError
+
+# define alloc_func z_alloc_func
+# define free_func z_free_func
+# define in_func z_in_func
+# define out_func z_out_func
+# define Byte z_Byte
+# define uInt z_uInt
+# define uLong z_uLong
+# define Bytef z_Bytef
+# define charf z_charf
+# define intf z_intf
+# define uIntf z_uIntf
+# define uLongf z_uLongf
+# define voidpf z_voidpf
+# define voidp z_voidp
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+# define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+# define WINDOWS
+#endif
+#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
+# ifndef WIN32
+# define WIN32
+# endif
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+# ifndef SYS16BIT
+# define SYS16BIT
+# endif
+# endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+# ifndef STDC
+# define STDC
+# endif
+# if __STDC_VERSION__ >= 199901L
+# ifndef STDC99
+# define STDC99
+# endif
+# endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+# define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
+# define STDC
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const /* note: need a more gentle solution here */
+# endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+# if defined(M_I86SM) || defined(M_I86MM)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+# if (defined(__SMALL__) || defined(__MEDIUM__))
+ /* Turbo C small or medium model */
+# define SMALL_MEDIUM
+# ifdef __BORLANDC__
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+ /* If building or using zlib as a DLL, define ZLIB_DLL.
+ * This is not mandatory, but it offers a little performance increase.
+ */
+# ifdef ZLIB_DLL
+# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+# ifdef ZLIB_INTERNAL
+# define ZEXTERN extern __declspec(dllexport)
+# else
+# define ZEXTERN extern __declspec(dllimport)
+# endif
+# endif
+# endif /* ZLIB_DLL */
+ /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+ * define ZLIB_WINAPI.
+ * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+ */
+# ifdef ZLIB_WINAPI
+# ifdef FAR
+# undef FAR
+# endif
+# include <windows.h>
+ /* No need for _export, use ZLIB.DEF instead. */
+ /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+# define ZEXPORT WINAPI
+# ifdef WIN32
+# define ZEXPORTVA WINAPIV
+# else
+# define ZEXPORTVA FAR CDECL
+# endif
+# endif
+#endif
+
+#if defined (__BEOS__)
+# ifdef ZLIB_DLL
+# ifdef ZLIB_INTERNAL
+# define ZEXPORT __declspec(dllexport)
+# define ZEXPORTVA __declspec(dllexport)
+# else
+# define ZEXPORT __declspec(dllimport)
+# define ZEXPORTVA __declspec(dllimport)
+# endif
+# endif
+#endif
+
+#ifndef ZEXTERN
+# define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+# define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char Byte; /* 8 bits */
+#endif
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void const *voidpc;
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte const *voidpc;
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */
+# include <sys/types.h> /* for off_t */
+# include <unistd.h> /* for SEEK_* and off_t */
+# ifdef VMS
+# include <unixio.h> /* for off_t */
+# endif
+# define z_off_t off_t
+#endif
+#ifndef SEEK_SET
+# define SEEK_SET 0 /* Seek from beginning of file. */
+# define SEEK_CUR 1 /* Seek from current position. */
+# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+# define z_off_t long
+#endif
+
+#if defined(__OS400__)
+# define NO_vsnprintf
+#endif
+
+#if defined(__MVS__)
+# define NO_vsnprintf
+# ifdef FAR
+# undef FAR
+# endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+# pragma map(deflateInit_,"DEIN")
+# pragma map(deflateInit2_,"DEIN2")
+# pragma map(deflateEnd,"DEEND")
+# pragma map(deflateBound,"DEBND")
+# pragma map(inflateInit_,"ININ")
+# pragma map(inflateInit2_,"ININ2")
+# pragma map(inflateEnd,"INEND")
+# pragma map(inflateSync,"INSY")
+# pragma map(inflateSetDictionary,"INSEDI")
+# pragma map(compressBound,"CMBND")
+# pragma map(inflate_table,"INTABL")
+# pragma map(inflate_fast,"INFA")
+# pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/src/3rdparty/assimp/contrib/zlib/zlib.h b/src/3rdparty/assimp/contrib/zlib/zlib.h
new file mode 100644
index 000000000..bfbba83e8
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/zlib.h
@@ -0,0 +1,1613 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.2.5, April 19th, 2010
+
+ Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
+ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef ZLIB_H
+#define ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.2.5"
+#define ZLIB_VERNUM 0x1250
+#define ZLIB_VER_MAJOR 1
+#define ZLIB_VER_MINOR 2
+#define ZLIB_VER_REVISION 5
+#define ZLIB_VER_SUBREVISION 0
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed data.
+ This version of the library supports only one compression method (deflation)
+ but other algorithms will be added later and will have the same stream
+ interface.
+
+ Compression can be done in a single step if the buffers are large enough,
+ or can be done by repeated calls of the compression function. In the latter
+ case, the application must provide more input and/or consume the output
+ (providing more output space) before each call.
+
+ The compressed data format used by default by the in-memory functions is
+ the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
+ around a deflate stream, which is itself documented in RFC 1951.
+
+ The library also supports reading and writing files in gzip (.gz) format
+ with an interface similar to that of stdio using the functions that start
+ with "gz". The gzip format is different from the zlib format. gzip is a
+ gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
+
+ This library can optionally read and write gzip streams in memory as well.
+
+ The zlib format was designed to be compact and fast for use in memory
+ and on communications channels. The gzip format was designed for single-
+ file compression on file systems, has a larger header than zlib to maintain
+ directory information, and uses a different, slower check method than zlib.
+
+ The library does not install any signal handler. The decoder checks
+ the consistency of the compressed data, so the library should never crash
+ even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total nb of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total nb of bytes output so far */
+
+ char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func zalloc; /* used to allocate the internal state */
+ free_func zfree; /* used to free the internal state */
+ voidpf opaque; /* private data object passed to zalloc and zfree */
+
+ int data_type; /* best guess about the data type: binary or text */
+ uLong adler; /* adler32 value of the uncompressed data */
+ uLong reserved; /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+ gzip header information passed to and from zlib routines. See RFC 1952
+ for more details on the meanings of these fields.
+*/
+typedef struct gz_header_s {
+ int text; /* true if compressed data believed to be text */
+ uLong time; /* modification time */
+ int xflags; /* extra flags (not used when writing a gzip file) */
+ int os; /* operating system */
+ Bytef *extra; /* pointer to extra field or Z_NULL if none */
+ uInt extra_len; /* extra field length (valid if extra != Z_NULL) */
+ uInt extra_max; /* space at extra (only when reading header) */
+ Bytef *name; /* pointer to zero-terminated file name or Z_NULL */
+ uInt name_max; /* space at name (only when reading header) */
+ Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */
+ uInt comm_max; /* space at comment (only when reading header) */
+ int hcrc; /* true if there was or will be a header crc */
+ int done; /* true when done reading gzip header (not used
+ when writing a gzip file) */
+} gz_header;
+
+typedef gz_header FAR *gz_headerp;
+
+/*
+ The application must update next_in and avail_in when avail_in has dropped
+ to zero. It must update next_out and avail_out when avail_out has dropped
+ to zero. The application must initialize zalloc, zfree and opaque before
+ calling the init function. All other fields are set by the compression
+ library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ If zlib is used in a multi-threaded application, zalloc and zfree must be
+ thread safe.
+
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this if
+ the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers
+ returned by zalloc for objects of exactly 65536 bytes *must* have their
+ offset normalized to zero. The default allocation function provided by this
+ library ensures this (see zutil.c). To reduce memory requirements and avoid
+ any allocation of 64K objects, at the expense of compression ratio, compile
+ the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or progress
+ reports. After compression, total_in holds the total size of the
+ uncompressed data and may be saved for use in the decompressor (particularly
+ if the decompressor wants to decompress everything in a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1
+#define Z_SYNC_FLUSH 2
+#define Z_FULL_FLUSH 3
+#define Z_FINISH 4
+#define Z_BLOCK 5
+#define Z_TREES 6
+/* Allowed flush values; see deflate() and inflate() below for details */
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_NEED_DICT 2
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative values
+ * are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION 0
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_RLE 3
+#define Z_FIXED 4
+#define Z_DEFAULT_STRATEGY 0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY 0
+#define Z_TEXT 1
+#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */
+#define Z_UNKNOWN 2
+/* Possible values of the data_type field (though see inflate()) */
+
+#define Z_DEFLATED 8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+
+ /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is not
+ compatible with the zlib.h header file used by the application. This check
+ is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller. If
+ zalloc and zfree are set to Z_NULL, deflateInit updates them to use default
+ allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+ 1 gives best speed, 9 gives best compression, 0 gives no compression at all
+ (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION
+ requests a default compromise between speed and compression (currently
+ equivalent to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if level is not a valid compression level, or
+ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+ with the version assumed by the caller (ZLIB_VERSION). msg is set to null
+ if there is no error message. deflateInit does not perform any compression:
+ this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+ deflate compresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce
+ some output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. deflate performs one or both of the
+ following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications). Some
+ output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming more
+ output, and updating avail_in or avail_out accordingly; avail_out should
+ never be zero before the call. The application can consume the compressed
+ output when it wants, for example when the output buffer is full (avail_out
+ == 0), or after each call of deflate(). If deflate returns Z_OK and with
+ zero avail_out, it must be called again after making room in the output
+ buffer because there might be more output pending.
+
+ Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
+ decide how much data to accumulate before producing output, in order to
+ maximize compression.
+
+ If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+ flushed to the output buffer and the output is aligned on a byte boundary, so
+ that the decompressor can get all input data available so far. (In
+ particular avail_in is zero after the call if enough output space has been
+ provided before the call.) Flushing may degrade compression for some
+ compression algorithms and so it should be used only when necessary. This
+ completes the current deflate block and follows it with an empty stored block
+ that is three bits plus filler bits to the next byte, followed by four bytes
+ (00 00 ff ff).
+
+ If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the
+ output buffer, but the output is not aligned to a byte boundary. All of the
+ input data so far will be available to the decompressor, as for Z_SYNC_FLUSH.
+ This completes the current deflate block and follows it with an empty fixed
+ codes block that is 10 bits long. This assures that enough bytes are output
+ in order for the decompressor to finish the block before the empty fixed code
+ block.
+
+ If flush is set to Z_BLOCK, a deflate block is completed and emitted, as
+ for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to
+ seven bits of the current block are held to be written as the next byte after
+ the next deflate block is completed. In this case, the decompressor may not
+ be provided enough bits at this point in order to complete decompression of
+ the data provided so far to the compressor. It may need to wait for the next
+ block to be emitted. This is for advanced applications that need to control
+ the emission of deflate blocks.
+
+ If flush is set to Z_FULL_FLUSH, all output is flushed as with
+ Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+ restart from this point if previous compressed data has been damaged or if
+ random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+ compression.
+
+ If deflate returns with avail_out == 0, this function must be called again
+ with the same value of the flush parameter and more output space (updated
+ avail_out), until the flush is complete (deflate returns with non-zero
+ avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
+ avail_out is greater than six to avoid repeated flush markers due to
+ avail_out == 0 on return.
+
+ If the parameter flush is set to Z_FINISH, pending input is processed,
+ pending output is flushed and deflate returns with Z_STREAM_END if there was
+ enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the stream
+ are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least the
+ value returned by deflateBound (see below). If deflate does not return
+ Z_STREAM_END, then it must be called again as described above.
+
+ deflate() sets strm->adler to the adler32 checksum of all input read
+ so far (that is, total_in bytes).
+
+ deflate() may update strm->data_type if it can make a good guess about
+ the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect the
+ compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible
+ (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not
+ fatal, and deflate() can be called again with more input and more output
+ space to continue compressing.
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any pending
+ output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+ prematurely (some input or output was discarded). In the error case, msg
+ may be set but then points to a static string (which must not be
+ deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+ Initializes the internal stream state for decompression. The fields
+ next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+ the caller. If next_in is not Z_NULL and avail_in is large enough (the
+ exact value depends on the compression method), inflateInit determines the
+ compression method from the zlib header and allocates all data structures
+ accordingly; otherwise the allocation will be deferred to the first call of
+ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+ use default allocation functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+ version assumed by the caller, or Z_STREAM_ERROR if the parameters are
+ invalid, such as a null pointer to the structure. msg is set to null if
+ there is no error message. inflateInit does not perform any decompression
+ apart from possibly reading the zlib header if present: actual decompression
+ will be done by inflate(). (So next_in and avail_in may be modified, but
+ next_out and avail_out are unused and unchanged.) The current implementation
+ of inflateInit() does not process any header information -- that is deferred
+ until inflate() is called.
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+ inflate decompresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce
+ some output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. inflate performs one or both of the
+ following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing will
+ resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() provides as much output as possible, until there is
+ no more input data or no more space in the output buffer (see below about
+ the flush parameter).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming more
+ output, and updating the next_* and avail_* values accordingly. The
+ application can consume the uncompressed output when it wants, for example
+ when the output buffer is full (avail_out == 0), or after each call of
+ inflate(). If inflate returns Z_OK and with zero avail_out, it must be
+ called again after making room in the output buffer because there might be
+ more output pending.
+
+ The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH,
+ Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much
+ output as possible to the output buffer. Z_BLOCK requests that inflate()
+ stop if and when it gets to the next deflate block boundary. When decoding
+ the zlib or gzip format, this will cause inflate() to return immediately
+ after the header and before the first block. When doing a raw inflate,
+ inflate() will go ahead and process the first block, and will return when it
+ gets to the end of that block, or when it runs out of data.
+
+ The Z_BLOCK option assists in appending to or combining deflate streams.
+ Also to assist in this, on return inflate() will set strm->data_type to the
+ number of unused bits in the last byte taken from strm->next_in, plus 64 if
+ inflate() is currently decoding the last block in the deflate stream, plus
+ 128 if inflate() returned immediately after decoding an end-of-block code or
+ decoding the complete header up to just before the first byte of the deflate
+ stream. The end-of-block will not be indicated until all of the uncompressed
+ data from that block has been written to strm->next_out. The number of
+ unused bits may in general be greater than seven, except when bit 7 of
+ data_type is set, in which case the number of unused bits will be less than
+ eight. data_type is set as noted here every time inflate() returns for all
+ flush options, and so can be used to determine the amount of currently
+ consumed input in bits.
+
+ The Z_TREES option behaves as Z_BLOCK does, but it also returns when the
+ end of each deflate block header is reached, before any actual data in that
+ block is decoded. This allows the caller to determine the length of the
+ deflate block header for later use in random access within a deflate block.
+ 256 is added to the value of strm->data_type when inflate() returns
+ immediately after reaching the end of the deflate block header.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step (a
+ single call of inflate), the parameter flush should be set to Z_FINISH. In
+ this case all pending input is processed and all pending output is flushed;
+ avail_out must be large enough to hold all the uncompressed data. (The size
+ of the uncompressed data may have been saved by the compressor for this
+ purpose.) The next operation on this stream must be inflateEnd to deallocate
+ the decompression state. The use of Z_FINISH is never required, but can be
+ used to inform inflate that a faster approach may be used for the single
+ inflate() call.
+
+ In this implementation, inflate() always flushes as much output as
+ possible to the output buffer, and always uses the faster approach on the
+ first call. So the only effect of the flush parameter in this implementation
+ is on the return value of inflate(), as noted below, or when it returns early
+ because Z_BLOCK or Z_TREES is used.
+
+ If a preset dictionary is needed after this call (see inflateSetDictionary
+ below), inflate sets strm->adler to the adler32 checksum of the dictionary
+ chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
+ strm->adler to the adler32 checksum of all output produced so far (that is,
+ total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
+ below. At the end of the stream, inflate() checks that its computed adler32
+ checksum is equal to that saved by the compressor and returns Z_STREAM_END
+ only if the checksum is correct.
+
+ inflate() can decompress and check either zlib-wrapped or gzip-wrapped
+ deflate data. The header type is detected automatically, if requested when
+ initializing with inflateInit2(). Any information contained in the gzip
+ header is not retained, so applications that need that information should
+ instead use raw inflate, see inflateInit2() below, or inflateBack() and
+ perform their own processing of the gzip header and trailer.
+
+ inflate() returns Z_OK if some progress has been made (more input processed
+ or more output produced), Z_STREAM_END if the end of the compressed data has
+ been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+ corrupted (input stream not conforming to the zlib format or incorrect check
+ value), Z_STREAM_ERROR if the stream structure was inconsistent (for example
+ next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory,
+ Z_BUF_ERROR if no progress is possible or if there was not enough room in the
+ output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and
+ inflate() can be called again with more input and more output space to
+ continue decompressing. If Z_DATA_ERROR is returned, the application may
+ then call inflateSync() to look for a good compression block if a partial
+ recovery of the data is desired.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any pending
+ output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+
+ /* Advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy));
+
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc, zfree and opaque must be initialized before by the
+ caller.
+
+ The method parameter is the compression method. It must be Z_DEFLATED in
+ this version of the library.
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library. Larger values of this parameter result in better
+ compression at the expense of memory usage. The default value is 15 if
+ deflateInit is used instead.
+
+ windowBits can also be -8..-15 for raw deflate. In this case, -windowBits
+ determines the window size. deflate() will then generate raw deflate data
+ with no zlib header or trailer, and will not compute an adler32 check value.
+
+ windowBits can also be greater than 15 for optional gzip encoding. Add
+ 16 to windowBits to write a simple gzip header and trailer around the
+ compressed data instead of a zlib wrapper. The gzip header will have no
+ file name, no extra data, no comment, no modification time (set to zero), no
+ header crc, and the operating system will be set to 255 (unknown). If a
+ gzip stream is being written, strm->adler is a crc32 instead of an adler32.
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but is
+ slow and reduces compression ratio; memLevel=9 uses maximum memory for
+ optimal speed. The default value is 8. See zconf.h for total memory usage
+ as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use the
+ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+ filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
+ string match), or Z_RLE to limit match distances to one (run-length
+ encoding). Filtered data consists mostly of small values with a somewhat
+ random distribution. In this case, the compression algorithm is tuned to
+ compress them better. The effect of Z_FILTERED is to force more Huffman
+ coding and less string matching; it is somewhat intermediate between
+ Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as
+ fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The
+ strategy parameter only affects the compression ratio but not the
+ correctness of the compressed output even if it is not set appropriately.
+ Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler
+ decoder for special applications.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid
+ method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is
+ incompatible with the version assumed by the caller (ZLIB_VERSION). msg is
+ set to null if there is no error message. deflateInit2 does not perform any
+ compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the compression dictionary from the given byte sequence
+ without producing any compressed output. This function must be called
+ immediately after deflateInit, deflateInit2 or deflateReset, before any call
+ of deflate. The compressor and decompressor must use exactly the same
+ dictionary (see inflateSetDictionary).
+
+ The dictionary should consist of strings (byte sequences) that are likely
+ to be encountered later in the data to be compressed, with the most commonly
+ used strings preferably put towards the end of the dictionary. Using a
+ dictionary is most useful when the data to be compressed is short and can be
+ predicted with good accuracy; the data can then be compressed better than
+ with the default empty dictionary.
+
+ Depending on the size of the compression data structures selected by
+ deflateInit or deflateInit2, a part of the dictionary may in effect be
+ discarded, for example if the dictionary is larger than the window size
+ provided in deflateInit or deflateInit2. Thus the strings most likely to be
+ useful should be put at the end of the dictionary, not at the front. In
+ addition, the current implementation of deflate will use at most the window
+ size minus 262 bytes of the provided dictionary.
+
+ Upon return of this function, strm->adler is set to the adler32 value
+ of the dictionary; the decompressor may later use this value to determine
+ which dictionary has been used by the compressor. (The adler32 value
+ applies to the whole dictionary even if only a subset of the dictionary is
+ actually used by the compressor.) If a raw deflate was requested, then the
+ adler32 value is not computed and strm->adler is not set.
+
+ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+ parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is
+ inconsistent (for example if deflate has already been called for this stream
+ or if the compression method is bsort). deflateSetDictionary does not
+ perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and can
+ consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being Z_NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state. The
+ stream will keep the same compression level and any other attributes that
+ may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being Z_NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+ int level,
+ int strategy));
+/*
+ Dynamically update the compression level and compression strategy. The
+ interpretation of level and strategy is as in deflateInit2. This can be
+ used to switch between compression and straight copy of the input data, or
+ to switch to a different kind of input data requiring a different strategy.
+ If the compression level is changed, the input available so far is
+ compressed with the old level (and may be flushed); the new level will take
+ effect only at the next call of deflate().
+
+ Before the call of deflateParams, the stream state must be set as for
+ a call of deflate(), since the currently available input may have to be
+ compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if
+ strm->avail_out was zero.
+*/
+
+ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
+ int good_length,
+ int max_lazy,
+ int nice_length,
+ int max_chain));
+/*
+ Fine tune deflate's internal compression parameters. This should only be
+ used by someone who understands the algorithm used by zlib's deflate for
+ searching for the best matching string, and even then only by the most
+ fanatic optimizer trying to squeeze out the last compressed bit for their
+ specific input data. Read the deflate.c source code for the meaning of the
+ max_lazy, good_length, nice_length, and max_chain parameters.
+
+ deflateTune() can be called after deflateInit() or deflateInit2(), and
+ returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
+ */
+
+ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
+ uLong sourceLen));
+/*
+ deflateBound() returns an upper bound on the compressed size after
+ deflation of sourceLen bytes. It must be called after deflateInit() or
+ deflateInit2(), and after deflateSetHeader(), if used. This would be used
+ to allocate an output buffer for deflation in a single pass, and so would be
+ called before deflate().
+*/
+
+ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
+ int bits,
+ int value));
+/*
+ deflatePrime() inserts bits in the deflate output stream. The intent
+ is that this function is used to start off the deflate output with the bits
+ leftover from a previous deflate stream when appending to it. As such, this
+ function can only be used for raw deflate, and must be used before the first
+ deflate() call after a deflateInit2() or deflateReset(). bits must be less
+ than or equal to 16, and that many of the least significant bits of value
+ will be inserted in the output.
+
+ deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
+ gz_headerp head));
+/*
+ deflateSetHeader() provides gzip header information for when a gzip
+ stream is requested by deflateInit2(). deflateSetHeader() may be called
+ after deflateInit2() or deflateReset() and before the first call of
+ deflate(). The text, time, os, extra field, name, and comment information
+ in the provided gz_header structure are written to the gzip header (xflag is
+ ignored -- the extra flags are set according to the compression level). The
+ caller must assure that, if not Z_NULL, name and comment are terminated with
+ a zero byte, and that if extra is not Z_NULL, that extra_len bytes are
+ available there. If hcrc is true, a gzip header crc is included. Note that
+ the current versions of the command-line version of gzip (up through version
+ 1.3.x) do not support header crc's, and will report that it is a "multi-part
+ gzip file" and give up.
+
+ If deflateSetHeader is not used, the default gzip header has text false,
+ the time set to zero, and os set to 255, with no extra, name, or comment
+ fields. The gzip header is returned to the default state by deflateReset().
+
+ deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+ int windowBits));
+
+ This is another version of inflateInit with an extra parameter. The
+ fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+ before by the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library. The default value is 15 if inflateInit is used
+ instead. windowBits must be greater than or equal to the windowBits value
+ provided to deflateInit2() while compressing, or it must be equal to 15 if
+ deflateInit2() was not used. If a compressed stream with a larger window
+ size is given as input, inflate() will return with the error code
+ Z_DATA_ERROR instead of trying to allocate a larger window.
+
+ windowBits can also be zero to request that inflate use the window size in
+ the zlib header of the compressed stream.
+
+ windowBits can also be -8..-15 for raw inflate. In this case, -windowBits
+ determines the window size. inflate() will then process raw deflate data,
+ not looking for a zlib or gzip header, not generating a check value, and not
+ looking for any check values for comparison at the end of the stream. This
+ is for use with other formats that use the deflate compressed data format
+ such as zip. Those formats provide their own check values. If a custom
+ format is developed using the raw deflate format for compressed data, it is
+ recommended that a check value such as an adler32 or a crc32 be applied to
+ the uncompressed data as is done in the zlib, gzip, and zip formats. For
+ most applications, the zlib format should be used as is. Note that comments
+ above on the use in deflateInit2() applies to the magnitude of windowBits.
+
+ windowBits can also be greater than 15 for optional gzip decoding. Add
+ 32 to windowBits to enable zlib and gzip decoding with automatic header
+ detection, or add 16 to decode only the gzip format (the zlib format will
+ return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a
+ crc32 instead of an adler32.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+ version assumed by the caller, or Z_STREAM_ERROR if the parameters are
+ invalid, such as a null pointer to the structure. msg is set to null if
+ there is no error message. inflateInit2 does not perform any decompression
+ apart from possibly reading the zlib header if present: actual decompression
+ will be done by inflate(). (So next_in and avail_in may be modified, but
+ next_out and avail_out are unused and unchanged.) The current implementation
+ of inflateInit2() does not process any header information -- that is
+ deferred until inflate() is called.
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the decompression dictionary from the given uncompressed byte
+ sequence. This function must be called immediately after a call of inflate,
+ if that call returned Z_NEED_DICT. The dictionary chosen by the compressor
+ can be determined from the adler32 value returned by that call of inflate.
+ The compressor and decompressor must use exactly the same dictionary (see
+ deflateSetDictionary). For raw inflate, this function can be called
+ immediately after inflateInit2() or inflateReset() and before any call of
+ inflate() to set the dictionary. The application must insure that the
+ dictionary that was used for compression is provided.
+
+ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+ parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is
+ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+ expected one (incorrect adler32 value). inflateSetDictionary does not
+ perform any decompression: this will be done by subsequent calls of
+ inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/*
+ Skips invalid compressed data until a full flush point (see above the
+ description of deflate with Z_FULL_FLUSH) can be found, or until all
+ available input is skipped. No output is provided.
+
+ inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
+ if no more input was provided, Z_DATA_ERROR if no flush point has been
+ found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the
+ success case, the application may save the current current value of total_in
+ which indicates where valid compressed data was found. In the error case,
+ the application may repeatedly call inflateSync, providing more input each
+ time, until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when randomly accessing a large stream. The
+ first pass through the stream can periodically record the inflate state,
+ allowing restarting inflate at those points when randomly accessing the
+ stream.
+
+ inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being Z_NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state. The
+ stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being Z_NULL).
+*/
+
+ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm,
+ int windowBits));
+/*
+ This function is the same as inflateReset, but it also permits changing
+ the wrap and window size requests. The windowBits parameter is interpreted
+ the same as it is for inflateInit2.
+
+ inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being Z_NULL), or if
+ the windowBits parameter is invalid.
+*/
+
+ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
+ int bits,
+ int value));
+/*
+ This function inserts bits in the inflate input stream. The intent is
+ that this function is used to start inflating at a bit position in the
+ middle of a byte. The provided bits will be used before any bytes are used
+ from next_in. This function should only be used with raw inflate, and
+ should be used before the first inflate() call after inflateInit2() or
+ inflateReset(). bits must be less than or equal to 16, and that many of the
+ least significant bits of value will be inserted in the input.
+
+ If bits is negative, then the input stream bit buffer is emptied. Then
+ inflatePrime() can be called again to put bits in the buffer. This is used
+ to clear out bits leftover after feeding inflate a block description prior
+ to feeding inflate codes.
+
+ inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm));
+/*
+ This function returns two values, one in the lower 16 bits of the return
+ value, and the other in the remaining upper bits, obtained by shifting the
+ return value down 16 bits. If the upper value is -1 and the lower value is
+ zero, then inflate() is currently decoding information outside of a block.
+ If the upper value is -1 and the lower value is non-zero, then inflate is in
+ the middle of a stored block, with the lower value equaling the number of
+ bytes from the input remaining to copy. If the upper value is not -1, then
+ it is the number of bits back from the current bit position in the input of
+ the code (literal or length/distance pair) currently being processed. In
+ that case the lower value is the number of bytes already emitted for that
+ code.
+
+ A code is being processed if inflate is waiting for more input to complete
+ decoding of the code, or if it has completed decoding but is waiting for
+ more output space to write the literal or match data.
+
+ inflateMark() is used to mark locations in the input data for random
+ access, which may be at bit positions, and to note those cases where the
+ output of a code may span boundaries of random access blocks. The current
+ location in the input stream can be determined from avail_in and data_type
+ as noted in the description for the Z_BLOCK flush parameter for inflate.
+
+ inflateMark returns the value noted above or -1 << 16 if the provided
+ source stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
+ gz_headerp head));
+/*
+ inflateGetHeader() requests that gzip header information be stored in the
+ provided gz_header structure. inflateGetHeader() may be called after
+ inflateInit2() or inflateReset(), and before the first call of inflate().
+ As inflate() processes the gzip stream, head->done is zero until the header
+ is completed, at which time head->done is set to one. If a zlib stream is
+ being decoded, then head->done is set to -1 to indicate that there will be
+ no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be
+ used to force inflate() to return immediately after header processing is
+ complete and before any actual data is decompressed.
+
+ The text, time, xflags, and os fields are filled in with the gzip header
+ contents. hcrc is set to true if there is a header CRC. (The header CRC
+ was valid if done is set to one.) If extra is not Z_NULL, then extra_max
+ contains the maximum number of bytes to write to extra. Once done is true,
+ extra_len contains the actual extra field length, and extra contains the
+ extra field, or that field truncated if extra_max is less than extra_len.
+ If name is not Z_NULL, then up to name_max characters are written there,
+ terminated with a zero unless the length is greater than name_max. If
+ comment is not Z_NULL, then up to comm_max characters are written there,
+ terminated with a zero unless the length is greater than comm_max. When any
+ of extra, name, or comment are not Z_NULL and the respective field is not
+ present in the header, then that field is set to Z_NULL to signal its
+ absence. This allows the use of deflateSetHeader() with the returned
+ structure to duplicate the header. However if those fields are set to
+ allocated memory, then the application will need to save those pointers
+ elsewhere so that they can be eventually freed.
+
+ If inflateGetHeader is not used, then the header information is simply
+ discarded. The header is always checked for validity, including the header
+ CRC if present. inflateReset() will reset the process to discard the header
+ information. The application would need to call inflateGetHeader() again to
+ retrieve the header from the next gzip stream.
+
+ inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
+ unsigned char FAR *window));
+
+ Initialize the internal stream state for decompression using inflateBack()
+ calls. The fields zalloc, zfree and opaque in strm must be initialized
+ before the call. If zalloc and zfree are Z_NULL, then the default library-
+ derived memory allocation routines are used. windowBits is the base two
+ logarithm of the window size, in the range 8..15. window is a caller
+ supplied buffer of that size. Except for special applications where it is
+ assured that deflate was used with small window sizes, windowBits must be 15
+ and a 32K byte window must be supplied to be able to decompress general
+ deflate streams.
+
+ See inflateBack() for the usage of these routines.
+
+ inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
+ the paramaters are invalid, Z_MEM_ERROR if the internal state could not be
+ allocated, or Z_VERSION_ERROR if the version of the library does not match
+ the version of the header file.
+*/
+
+typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *));
+typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
+
+ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
+ in_func in, void FAR *in_desc,
+ out_func out, void FAR *out_desc));
+/*
+ inflateBack() does a raw inflate with a single call using a call-back
+ interface for input and output. This is more efficient than inflate() for
+ file i/o applications in that it avoids copying between the output and the
+ sliding window by simply making the window itself the output buffer. This
+ function trusts the application to not change the output buffer passed by
+ the output function, at least until inflateBack() returns.
+
+ inflateBackInit() must be called first to allocate the internal state
+ and to initialize the state with the user-provided window buffer.
+ inflateBack() may then be used multiple times to inflate a complete, raw
+ deflate stream with each call. inflateBackEnd() is then called to free the
+ allocated state.
+
+ A raw deflate stream is one with no zlib or gzip header or trailer.
+ This routine would normally be used in a utility that reads zip or gzip
+ files and writes out uncompressed files. The utility would decode the
+ header and process the trailer on its own, hence this routine expects only
+ the raw deflate stream to decompress. This is different from the normal
+ behavior of inflate(), which expects either a zlib or gzip header and
+ trailer around the deflate stream.
+
+ inflateBack() uses two subroutines supplied by the caller that are then
+ called by inflateBack() for input and output. inflateBack() calls those
+ routines until it reads a complete deflate stream and writes out all of the
+ uncompressed data, or until it encounters an error. The function's
+ parameters and return types are defined above in the in_func and out_func
+ typedefs. inflateBack() will call in(in_desc, &buf) which should return the
+ number of bytes of provided input, and a pointer to that input in buf. If
+ there is no input available, in() must return zero--buf is ignored in that
+ case--and inflateBack() will return a buffer error. inflateBack() will call
+ out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out()
+ should return zero on success, or non-zero on failure. If out() returns
+ non-zero, inflateBack() will return with an error. Neither in() nor out()
+ are permitted to change the contents of the window provided to
+ inflateBackInit(), which is also the buffer that out() uses to write from.
+ The length written by out() will be at most the window size. Any non-zero
+ amount of input may be provided by in().
+
+ For convenience, inflateBack() can be provided input on the first call by
+ setting strm->next_in and strm->avail_in. If that input is exhausted, then
+ in() will be called. Therefore strm->next_in must be initialized before
+ calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called
+ immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in
+ must also be initialized, and then if strm->avail_in is not zero, input will
+ initially be taken from strm->next_in[0 .. strm->avail_in - 1].
+
+ The in_desc and out_desc parameters of inflateBack() is passed as the
+ first parameter of in() and out() respectively when they are called. These
+ descriptors can be optionally used to pass any information that the caller-
+ supplied in() and out() functions need to do their job.
+
+ On return, inflateBack() will set strm->next_in and strm->avail_in to
+ pass back any unused input that was provided by the last in() call. The
+ return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
+ if in() or out() returned an error, Z_DATA_ERROR if there was a format error
+ in the deflate stream (in which case strm->msg is set to indicate the nature
+ of the error), or Z_STREAM_ERROR if the stream was not properly initialized.
+ In the case of Z_BUF_ERROR, an input or output error can be distinguished
+ using strm->next_in which will be Z_NULL only if in() returned an error. If
+ strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning
+ non-zero. (in() will always be called before out(), so strm->next_in is
+ assured to be defined if out() returns non-zero.) Note that inflateBack()
+ cannot return Z_OK.
+*/
+
+ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
+/*
+ All memory allocated by inflateBackInit() is freed.
+
+ inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
+ state was inconsistent.
+*/
+
+ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
+/* Return flags indicating compile-time options.
+
+ Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
+ 1.0: size of uInt
+ 3.2: size of uLong
+ 5.4: size of voidpf (pointer)
+ 7.6: size of z_off_t
+
+ Compiler, assembler, and debug options:
+ 8: DEBUG
+ 9: ASMV or ASMINF -- use ASM code
+ 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
+ 11: 0 (reserved)
+
+ One-time table building (smaller code, but not thread-safe if true):
+ 12: BUILDFIXED -- build static block decoding tables when needed
+ 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
+ 14,15: 0 (reserved)
+
+ Library content (indicates missing functionality):
+ 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
+ deflate code when not needed)
+ 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
+ and decode gzip streams (to avoid linking crc code)
+ 18-19: 0 (reserved)
+
+ Operation variations (changes in library functionality):
+ 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
+ 21: FASTEST -- deflate algorithm with only one, lowest compression level
+ 22,23: 0 (reserved)
+
+ The sprintf variant used by gzprintf (zero is best):
+ 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
+ 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
+ 26: 0 = returns value, 1 = void -- 1 means inferred string length returned
+
+ Remainder:
+ 27-31: 0 (reserved)
+ */
+
+
+ /* utility functions */
+
+/*
+ The following utility functions are implemented on top of the basic
+ stream-oriented functions. To simplify the interface, some default options
+ are assumed (compression level and memory usage, standard memory allocation
+ functions). The source code of these utility functions can be modified if
+ you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total size
+ of the destination buffer, which must be at least the value returned by
+ compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+
+ compress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen,
+ int level));
+/*
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least the value returned by
+ compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
+/*
+ compressBound() returns an upper bound on the compressed size after
+ compress() or compress2() on sourceLen bytes. It would be used before a
+ compress() or compress2() call to allocate the destination buffer.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total size
+ of the destination buffer, which must be large enough to hold the entire
+ uncompressed data. (The size of the uncompressed data must have been saved
+ previously by the compressor and transmitted to the decompressor by some
+ mechanism outside the scope of this compression library.) Upon exit, destLen
+ is the actual size of the uncompressed buffer.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.
+*/
+
+
+ /* gzip file access functions */
+
+/*
+ This library supports reading and writing files in gzip (.gz) format with
+ an interface similar to that of stdio, using the functions that start with
+ "gz". The gzip format is different from the zlib format. gzip is a gzip
+ wrapper, documented in RFC 1952, wrapped around a deflate stream.
+*/
+
+typedef voidp gzFile; /* opaque gzip file descriptor */
+
+/*
+ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
+
+ Opens a gzip (.gz) file for reading or writing. The mode parameter is as
+ in fopen ("rb" or "wb") but can also include a compression level ("wb9") or
+ a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only
+ compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F'
+ for fixed code compression as in "wb9F". (See the description of
+ deflateInit2 for more information about the strategy parameter.) Also "a"
+ can be used instead of "w" to request that the gzip stream that will be
+ written be appended to the file. "+" will result in an error, since reading
+ and writing to the same gzip file is not supported.
+
+ gzopen can be used to read a file which is not in gzip format; in this
+ case gzread will directly read from the file without decompression.
+
+ gzopen returns NULL if the file could not be opened, if there was
+ insufficient memory to allocate the gzFile state, or if an invalid mode was
+ specified (an 'r', 'w', or 'a' was not provided, or '+' was provided).
+ errno can be checked to determine if the reason gzopen failed was that the
+ file could not be opened.
+*/
+
+ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
+/*
+ gzdopen associates a gzFile with the file descriptor fd. File descriptors
+ are obtained from calls like open, dup, creat, pipe or fileno (if the file
+ has been previously opened with fopen). The mode parameter is as in gzopen.
+
+ The next call of gzclose on the returned gzFile will also close the file
+ descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor
+ fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd,
+ mode);. The duplicated descriptor should be saved to avoid a leak, since
+ gzdopen does not close fd if it fails.
+
+ gzdopen returns NULL if there was insufficient memory to allocate the
+ gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not
+ provided, or '+' was provided), or if fd is -1. The file descriptor is not
+ used until the next gz* read, write, seek, or close operation, so gzdopen
+ will not detect if fd is invalid (unless fd is -1).
+*/
+
+ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
+/*
+ Set the internal buffer size used by this library's functions. The
+ default buffer size is 8192 bytes. This function must be called after
+ gzopen() or gzdopen(), and before any other calls that read or write the
+ file. The buffer memory allocation is always deferred to the first read or
+ write. Two buffers are allocated, either both of the specified size when
+ writing, or one of the specified size and the other twice that size when
+ reading. A larger buffer size of, for example, 64K or 128K bytes will
+ noticeably increase the speed of decompression (reading).
+
+ The new buffer size also affects the maximum length for gzprintf().
+
+ gzbuffer() returns 0 on success, or -1 on failure, such as being called
+ too late.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+ Dynamically update the compression level or strategy. See the description
+ of deflateInit2 for the meaning of these parameters.
+
+ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+ opened for writing.
+*/
+
+ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+/*
+ Reads the given number of uncompressed bytes from the compressed file. If
+ the input file was not in gzip format, gzread copies the given number of
+ bytes into the buffer.
+
+ After reaching the end of a gzip stream in the input, gzread will continue
+ to read, looking for another gzip stream, or failing that, reading the rest
+ of the input file directly without decompression. The entire input file
+ will be read if gzread is called until it returns less than the requested
+ len.
+
+ gzread returns the number of uncompressed bytes actually read, less than
+ len for end of file, or -1 for error.
+*/
+
+ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
+ voidpc buf, unsigned len));
+/*
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of uncompressed bytes written or 0 in case of
+ error.
+*/
+
+ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...));
+/*
+ Converts, formats, and writes the arguments to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written, or 0 in case of error. The number of
+ uncompressed bytes written is limited to 8191, or one less than the buffer
+ size given to gzbuffer(). The caller should assure that this limit is not
+ exceeded. If it is exceeded, then gzprintf() will return an error (0) with
+ nothing written. In this case, there may also be a buffer overflow with
+ unpredictable consequences, which is possible only if zlib was compiled with
+ the insecure functions sprintf() or vsprintf() because the secure snprintf()
+ or vsnprintf() functions were not available. This can be determined using
+ zlibCompileFlags().
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+ Reads bytes from the compressed file until len-1 characters are read, or a
+ newline character is read and transferred to buf, or an end-of-file
+ condition is encountered. If any characters are read or if len == 1, the
+ string is terminated with a null character. If no characters are read due
+ to an end-of-file or len < 1, then the buffer is left untouched.
+
+ gzgets returns buf which is a null-terminated string, or it returns NULL
+ for end-of-file or in case of error. If there was an error, the contents at
+ buf are indeterminate.
+*/
+
+ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
+/*
+ Writes c, converted to an unsigned char, into the compressed file. gzputc
+ returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
+/*
+ Reads one byte from the compressed file. gzgetc returns this byte or -1
+ in case of end of file or error.
+*/
+
+ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
+/*
+ Push one character back onto the stream to be read as the first character
+ on the next read. At least one character of push-back is allowed.
+ gzungetc() returns the character pushed, or -1 on failure. gzungetc() will
+ fail if c is -1, and may fail if a character has been pushed but not read
+ yet. If gzungetc is used immediately after gzopen or gzdopen, at least the
+ output buffer size of pushed characters is allowed. (See gzbuffer above.)
+ The pushed character will be discarded if the stream is repositioned with
+ gzseek() or gzrewind().
+*/
+
+ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
+/*
+ Flushes all pending output into the compressed file. The parameter flush
+ is as in the deflate() function. The return value is the zlib error number
+ (see function gzerror below). gzflush is only permitted when writing.
+
+ If the flush parameter is Z_FINISH, the remaining data is written and the
+ gzip stream is completed in the output. If gzwrite() is called again, a new
+ gzip stream will be started in the output. gzread() is able to read such
+ concatented gzip streams.
+
+ gzflush should be called only when strictly necessary because it will
+ degrade compression if called too often.
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
+ z_off_t offset, int whence));
+
+ Sets the starting position for the next gzread or gzwrite on the given
+ compressed file. The offset represents a number of bytes in the
+ uncompressed data stream. The whence parameter is defined as in lseek(2);
+ the value SEEK_END is not supported.
+
+ If the file is opened for reading, this function is emulated but can be
+ extremely slow. If the file is opened for writing, only forward seeks are
+ supported; gzseek then compresses a sequence of zeroes up to the new
+ starting position.
+
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error, in
+ particular if the file is opened for writing and the new starting position
+ would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
+/*
+ Rewinds the given file. This function is supported only for reading.
+
+ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
+
+ Returns the starting position for the next gzread or gzwrite on the given
+ compressed file. This position represents a number of bytes in the
+ uncompressed data stream, and is zero when starting, even if appending or
+ reading a gzip stream from the middle of a file using gzdopen().
+
+ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));
+
+ Returns the current offset in the file being read or written. This offset
+ includes the count of bytes that precede the gzip stream, for example when
+ appending or when using gzdopen() for reading. When reading, the offset
+ does not include as yet unused buffered input. This information can be used
+ for a progress indicator. On error, gzoffset() returns -1.
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+ Returns true (1) if the end-of-file indicator has been set while reading,
+ false (0) otherwise. Note that the end-of-file indicator is set only if the
+ read tried to go past the end of the input, but came up short. Therefore,
+ just like feof(), gzeof() may return false even if there is no more data to
+ read, in the event that the last read request was for the exact number of
+ bytes remaining in the input file. This will happen if the input file size
+ is an exact multiple of the buffer size.
+
+ If gzeof() returns true, then the read functions will return no more data,
+ unless the end-of-file indicator is reset by gzclearerr() and the input file
+ has grown since the previous end of file was detected.
+*/
+
+ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
+/*
+ Returns true (1) if file is being copied directly while reading, or false
+ (0) if file is a gzip stream being decompressed. This state can change from
+ false to true while reading the input file if the end of a gzip stream is
+ reached, but is followed by data that is not another gzip stream.
+
+ If the input file is empty, gzdirect() will return true, since the input
+ does not contain a gzip stream.
+
+ If gzdirect() is used immediately after gzopen() or gzdopen() it will
+ cause buffers to be allocated to allow reading the file to determine if it
+ is a gzip file. Therefore if gzbuffer() is used, it should be called before
+ gzdirect().
+*/
+
+ZEXTERN int ZEXPORT gzclose OF((gzFile file));
+/*
+ Flushes all pending output if necessary, closes the compressed file and
+ deallocates the (de)compression state. Note that once file is closed, you
+ cannot call gzerror with file, since its structures have been deallocated.
+ gzclose must not be called more than once on the same file, just as free
+ must not be called more than once on the same allocation.
+
+ gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a
+ file operation error, or Z_OK on success.
+*/
+
+ZEXTERN int ZEXPORT gzclose_r OF((gzFile file));
+ZEXTERN int ZEXPORT gzclose_w OF((gzFile file));
+/*
+ Same as gzclose(), but gzclose_r() is only for use when reading, and
+ gzclose_w() is only for use when writing or appending. The advantage to
+ using these instead of gzclose() is that they avoid linking in zlib
+ compression or decompression code that is not used when only reading or only
+ writing respectively. If gzclose() is used, then both compression and
+ decompression code will be included the application when linking to a static
+ zlib library.
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+ Returns the error message for the last error which occurred on the given
+ compressed file. errnum is set to zlib error number. If an error occurred
+ in the file system and not in the compression library, errnum is set to
+ Z_ERRNO and the application may consult errno to get the exact error code.
+
+ The application must not modify the returned string. Future calls to
+ this function may invalidate the previously returned string. If file is
+ closed, then the string previously returned by gzerror will no longer be
+ available.
+
+ gzerror() should be used to distinguish errors from end-of-file for those
+ functions above that do not distinguish those cases in their return values.
+*/
+
+ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
+/*
+ Clears the error and end-of-file flags for file. This is analogous to the
+ clearerr() function in stdio. This is useful for continuing to read a gzip
+ file that is being written concurrently.
+*/
+
+
+ /* checksum functions */
+
+/*
+ These functions are not related to compression but are exported
+ anyway because they might be useful in applications using the compression
+ library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is Z_NULL, this function returns the
+ required initial value for the checksum.
+
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster.
+
+ Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+/*
+ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
+ z_off_t len2));
+
+ Combine two Adler-32 checksums into one. For two sequences of bytes, seq1
+ and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
+ each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of
+ seq1 and seq2 concatenated, requiring only adler1, adler2, and len2.
+*/
+
+ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
+/*
+ Update a running CRC-32 with the bytes buf[0..len-1] and return the
+ updated CRC-32. If buf is Z_NULL, this function returns the required
+ initial value for the for the crc. Pre- and post-conditioning (one's
+ complement) is performed within this function so it shouldn't be done by the
+ application.
+
+ Usage example:
+
+ uLong crc = crc32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ crc = crc32(crc, buffer, length);
+ }
+ if (crc != original_crc) error();
+*/
+
+/*
+ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
+
+ Combine two CRC-32 check values into one. For two sequences of bytes,
+ seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
+ calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32
+ check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
+ len2.
+*/
+
+
+ /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
+ int windowBits, int memLevel,
+ int strategy, const char *version,
+ int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
+ unsigned char FAR *window,
+ const char *version,
+ int stream_size));
+#define deflateInit(strm, level) \
+ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+ (strategy), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+#define inflateBackInit(strm, windowBits, window) \
+ inflateBackInit_((strm), (windowBits), (window), \
+ ZLIB_VERSION, sizeof(z_stream))
+
+/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or
+ * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if
+ * both are true, the application gets the *64 functions, and the regular
+ * functions are changed to 64 bits) -- in case these are set on systems
+ * without large file support, _LFS64_LARGEFILE must also be true
+ */
+#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0
+ ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
+ ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
+ ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
+ ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
+ ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t));
+ ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t));
+#endif
+
+#if !defined(ZLIB_INTERNAL) && _FILE_OFFSET_BITS-0 == 64 && _LFS64_LARGEFILE-0
+# define gzopen gzopen64
+# define gzseek gzseek64
+# define gztell gztell64
+# define gzoffset gzoffset64
+# define adler32_combine adler32_combine64
+# define crc32_combine crc32_combine64
+# ifdef _LARGEFILE64_SOURCE
+ ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
+ ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int));
+ ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile));
+ ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile));
+ ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
+ ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
+# endif
+#else
+ ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *));
+ ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int));
+ ZEXTERN z_off_t ZEXPORT gztell OF((gzFile));
+ ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile));
+ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
+ ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
+#endif
+
+/* hack for buggy compilers */
+#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL)
+ struct internal_state {int dummy;};
+#endif
+
+/* undocumented functions */
+ZEXTERN const char * ZEXPORT zError OF((int));
+ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp));
+ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void));
+ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZLIB_H */
diff --git a/src/3rdparty/assimp/contrib/zlib/zutil.c b/src/3rdparty/assimp/contrib/zlib/zutil.c
new file mode 100644
index 000000000..898ed345b
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/zutil.c
@@ -0,0 +1,318 @@
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995-2005, 2010 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#include "zutil.h"
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+const char * const z_errmsg[10] = {
+"need dictionary", /* Z_NEED_DICT 2 */
+"stream end", /* Z_STREAM_END 1 */
+"", /* Z_OK 0 */
+"file error", /* Z_ERRNO (-1) */
+"stream error", /* Z_STREAM_ERROR (-2) */
+"data error", /* Z_DATA_ERROR (-3) */
+"insufficient memory", /* Z_MEM_ERROR (-4) */
+"buffer error", /* Z_BUF_ERROR (-5) */
+"incompatible version",/* Z_VERSION_ERROR (-6) */
+""};
+
+
+const char * ZEXPORT zlibVersion()
+{
+ return ZLIB_VERSION;
+}
+
+uLong ZEXPORT zlibCompileFlags()
+{
+ uLong flags;
+
+ flags = 0;
+ switch ((int)(sizeof(uInt))) {
+ case 2: break;
+ case 4: flags += 1; break;
+ case 8: flags += 2; break;
+ default: flags += 3;
+ }
+ switch ((int)(sizeof(uLong))) {
+ case 2: break;
+ case 4: flags += 1 << 2; break;
+ case 8: flags += 2 << 2; break;
+ default: flags += 3 << 2;
+ }
+ switch ((int)(sizeof(voidpf))) {
+ case 2: break;
+ case 4: flags += 1 << 4; break;
+ case 8: flags += 2 << 4; break;
+ default: flags += 3 << 4;
+ }
+ switch ((int)(sizeof(z_off_t))) {
+ case 2: break;
+ case 4: flags += 1 << 6; break;
+ case 8: flags += 2 << 6; break;
+ default: flags += 3 << 6;
+ }
+#ifdef DEBUG
+ flags += 1 << 8;
+#endif
+#if defined(ASMV) || defined(ASMINF)
+ flags += 1 << 9;
+#endif
+#ifdef ZLIB_WINAPI
+ flags += 1 << 10;
+#endif
+#ifdef BUILDFIXED
+ flags += 1 << 12;
+#endif
+#ifdef DYNAMIC_CRC_TABLE
+ flags += 1 << 13;
+#endif
+#ifdef NO_GZCOMPRESS
+ flags += 1L << 16;
+#endif
+#ifdef NO_GZIP
+ flags += 1L << 17;
+#endif
+#ifdef PKZIP_BUG_WORKAROUND
+ flags += 1L << 20;
+#endif
+#ifdef FASTEST
+ flags += 1L << 21;
+#endif
+#ifdef STDC
+# ifdef NO_vsnprintf
+ flags += 1L << 25;
+# ifdef HAS_vsprintf_void
+ flags += 1L << 26;
+# endif
+# else
+# ifdef HAS_vsnprintf_void
+ flags += 1L << 26;
+# endif
+# endif
+#else
+ flags += 1L << 24;
+# ifdef NO_snprintf
+ flags += 1L << 25;
+# ifdef HAS_sprintf_void
+ flags += 1L << 26;
+# endif
+# else
+# ifdef HAS_snprintf_void
+ flags += 1L << 26;
+# endif
+# endif
+#endif
+ return flags;
+}
+
+#ifdef DEBUG
+
+# ifndef verbose
+# define verbose 0
+# endif
+int ZLIB_INTERNAL z_verbose = verbose;
+
+void ZLIB_INTERNAL z_error (m)
+ char *m;
+{
+ fprintf(stderr, "%s\n", m);
+ exit(1);
+}
+#endif
+
+/* exported to allow conversion of error code to string for compress() and
+ * uncompress()
+ */
+const char * ZEXPORT zError(err)
+ int err;
+{
+ return ERR_MSG(err);
+}
+
+#if defined(_WIN32_WCE)
+ /* The Microsoft C Run-Time Library for Windows CE doesn't have
+ * errno. We define it as a global variable to simplify porting.
+ * Its value is always 0 and should not be used.
+ */
+ int errno = 0;
+#endif
+
+#ifndef HAVE_MEMCPY
+
+void ZLIB_INTERNAL zmemcpy(dest, source, len)
+ Bytef* dest;
+ const Bytef* source;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = *source++; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+
+int ZLIB_INTERNAL zmemcmp(s1, s2, len)
+ const Bytef* s1;
+ const Bytef* s2;
+ uInt len;
+{
+ uInt j;
+
+ for (j = 0; j < len; j++) {
+ if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+ }
+ return 0;
+}
+
+void ZLIB_INTERNAL zmemzero(dest, len)
+ Bytef* dest;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = 0; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+#endif
+
+
+#ifdef SYS16BIT
+
+#ifdef __TURBOC__
+/* Turbo C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
+ * and farmalloc(64K) returns a pointer with an offset of 8, so we
+ * must fix the pointer. Warning: the pointer must be put back to its
+ * original form in order to free it, use zcfree().
+ */
+
+#define MAX_PTR 10
+/* 10*64K = 640K */
+
+local int next_ptr = 0;
+
+typedef struct ptr_table_s {
+ voidpf org_ptr;
+ voidpf new_ptr;
+} ptr_table;
+
+local ptr_table table[MAX_PTR];
+/* This table is used to remember the original form of pointers
+ * to large buffers (64K). Such pointers are normalized with a zero offset.
+ * Since MSDOS is not a preemptive multitasking OS, this table is not
+ * protected from concurrent access. This hack doesn't work anyway on
+ * a protected system like OS/2. Use Microsoft C instead.
+ */
+
+voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ voidpf buf = opaque; /* just to make some compilers happy */
+ ulg bsize = (ulg)items*size;
+
+ /* If we allocate less than 65520 bytes, we assume that farmalloc
+ * will return a usable pointer which doesn't have to be normalized.
+ */
+ if (bsize < 65520L) {
+ buf = farmalloc(bsize);
+ if (*(ush*)&buf != 0) return buf;
+ } else {
+ buf = farmalloc(bsize + 16L);
+ }
+ if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
+ table[next_ptr].org_ptr = buf;
+
+ /* Normalize the pointer to seg:0 */
+ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
+ *(ush*)&buf = 0;
+ table[next_ptr++].new_ptr = buf;
+ return buf;
+}
+
+void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
+{
+ int n;
+ if (*(ush*)&ptr != 0) { /* object < 64K */
+ farfree(ptr);
+ return;
+ }
+ /* Find the original pointer */
+ for (n = 0; n < next_ptr; n++) {
+ if (ptr != table[n].new_ptr) continue;
+
+ farfree(table[n].org_ptr);
+ while (++n < next_ptr) {
+ table[n-1] = table[n];
+ }
+ next_ptr--;
+ return;
+ }
+ ptr = opaque; /* just to make some compilers happy */
+ Assert(0, "zcfree: ptr not found");
+}
+
+#endif /* __TURBOC__ */
+
+
+#ifdef M_I86
+/* Microsoft C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
+# define _halloc halloc
+# define _hfree hfree
+#endif
+
+voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ return _halloc((long)items, size);
+}
+
+void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ _hfree(ptr);
+}
+
+#endif /* M_I86 */
+
+#endif /* SYS16BIT */
+
+
+#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+
+#ifndef STDC
+extern voidp malloc OF((uInt size));
+extern voidp calloc OF((uInt items, uInt size));
+extern void free OF((voidpf ptr));
+#endif
+
+voidpf ZLIB_INTERNAL zcalloc (opaque, items, size)
+ voidpf opaque;
+ unsigned items;
+ unsigned size;
+{
+ if (opaque) items += size - size; /* make compiler happy */
+ return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
+ (voidpf)calloc(items, size);
+}
+
+void ZLIB_INTERNAL zcfree (opaque, ptr)
+ voidpf opaque;
+ voidpf ptr;
+{
+ free(ptr);
+ if (opaque) return; /* make compiler happy */
+}
+
+#endif /* MY_ZCALLOC */
diff --git a/src/3rdparty/assimp/contrib/zlib/zutil.h b/src/3rdparty/assimp/contrib/zlib/zutil.h
new file mode 100644
index 000000000..439d41c93
--- /dev/null
+++ b/src/3rdparty/assimp/contrib/zlib/zutil.h
@@ -0,0 +1,274 @@
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-2010 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZUTIL_H
+#define ZUTIL_H
+
+#if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33) && !defined(NO_VIZ)
+# define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
+#else
+# define ZLIB_INTERNAL
+#endif
+
+#include "zlib.h"
+
+#ifdef STDC
+# if !(defined(_WIN32_WCE) && defined(_MSC_VER))
+# include <stddef.h>
+# endif
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long ulg;
+
+extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+ return (strm->msg = (char*)ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+ /* common constants */
+
+#ifndef DEF_WBITS
+# define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+/* The three kinds of block type */
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+ /* target dependencies */
+
+#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))
+# define OS_CODE 0x00
+# if defined(__TURBOC__) || defined(__BORLANDC__)
+# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
+ /* Allow compilation with ANSI keywords only enabled */
+ void _Cdecl farfree( void *block );
+ void *_Cdecl farmalloc( unsigned long nbytes );
+# else
+# include <alloc.h>
+# endif
+# else /* MSC or DJGPP */
+# include <malloc.h>
+# endif
+#endif
+
+#ifdef AMIGA
+# define OS_CODE 0x01
+#endif
+
+#if defined(VAXC) || defined(VMS)
+# define OS_CODE 0x02
+# define F_OPEN(name, mode) \
+ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#if defined(ATARI) || defined(atarist)
+# define OS_CODE 0x05
+#endif
+
+#ifdef OS2
+# define OS_CODE 0x06
+# ifdef M_I86
+# include <malloc.h>
+# endif
+#endif
+
+#if defined(MACOS) || defined(TARGET_OS_MAC)
+# define OS_CODE 0x07
+# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+# include <unix.h> /* for fdopen */
+# else
+# ifndef fdopen
+# define fdopen(fd,mode) NULL /* No fdopen() */
+# endif
+# endif
+#endif
+
+#ifdef TOPS20
+# define OS_CODE 0x0a
+#endif
+
+#ifdef WIN32
+# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */
+# define OS_CODE 0x0b
+# endif
+#endif
+
+#ifdef __50SERIES /* Prime/PRIMOS */
+# define OS_CODE 0x0f
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX
+# if defined(_WIN32_WCE)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+# ifndef _PTRDIFF_T_DEFINED
+ typedef int ptrdiff_t;
+# define _PTRDIFF_T_DEFINED
+# endif
+# else
+# define fdopen(fd,type) _fdopen(fd,type)
+# endif
+#endif
+
+#if defined(__BORLANDC__)
+ #pragma warn -8004
+ #pragma warn -8008
+ #pragma warn -8066
+#endif
+
+/* provide prototypes for these when building zlib without LFS */
+#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0
+ ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
+ ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
+#endif
+
+ /* common defaults */
+
+#ifndef OS_CODE
+# define OS_CODE 0x03 /* assume Unix */
+#endif
+
+#ifndef F_OPEN
+# define F_OPEN(name, mode) fopen((name), (mode))
+#endif
+
+ /* functions */
+
+#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)
+# ifndef HAVE_VSNPRINTF
+# define HAVE_VSNPRINTF
+# endif
+#endif
+#if defined(__CYGWIN__)
+# ifndef HAVE_VSNPRINTF
+# define HAVE_VSNPRINTF
+# endif
+#endif
+#ifndef HAVE_VSNPRINTF
+# ifdef MSDOS
+ /* vsnprintf may exist on some MS-DOS compilers (DJGPP?),
+ but for now we just assume it doesn't. */
+# define NO_vsnprintf
+# endif
+# ifdef __TURBOC__
+# define NO_vsnprintf
+# endif
+# ifdef WIN32
+ /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
+# if !defined(vsnprintf) && !defined(NO_vsnprintf)
+# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 )
+# define vsnprintf _vsnprintf
+# endif
+# endif
+# endif
+# ifdef __SASC
+# define NO_vsnprintf
+# endif
+#endif
+#ifdef VMS
+# define NO_vsnprintf
+#endif
+
+#if defined(pyr)
+# define NO_MEMCPY
+#endif
+#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+ * You may have to use the same strategy for Borland C (untested).
+ * The __SC__ check is for Symantec.
+ */
+# define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+# define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+# ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+# define zmemcpy _fmemcpy
+# define zmemcmp _fmemcmp
+# define zmemzero(dest, len) _fmemset(dest, 0, len)
+# else
+# define zmemcpy memcpy
+# define zmemcmp memcmp
+# define zmemzero(dest, len) memset(dest, 0, len)
+# endif
+#else
+ void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
+ int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
+ void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG
+# include <stdio.h>
+ extern int ZLIB_INTERNAL z_verbose;
+ extern void ZLIB_INTERNAL z_error OF((char *m));
+# define Assert(cond,msg) {if (!(cond)) z_error(msg);}
+# define Trace(x) {if (z_verbose>=0) fprintf x ;}
+# define Tracev(x) {if (z_verbose>0) fprintf x ;}
+# define Tracevv(x) {if (z_verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+
+voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items,
+ unsigned size));
+void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr));
+
+#define ZALLOC(strm, items, size) \
+ (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+#endif /* ZUTIL_H */
diff --git a/src/3rdparty/assimp/include/Compiler/poppack1.h b/src/3rdparty/assimp/include/Compiler/poppack1.h
new file mode 100644
index 000000000..8944b2b60
--- /dev/null
+++ b/src/3rdparty/assimp/include/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/src/3rdparty/assimp/include/Compiler/pushpack1.h b/src/3rdparty/assimp/include/Compiler/pushpack1.h
new file mode 100644
index 000000000..b89a8ff71
--- /dev/null
+++ b/src/3rdparty/assimp/include/Compiler/pushpack1.h
@@ -0,0 +1,42 @@
+
+
+// ===============================================================================
+// 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)
+//
+//
+// 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__ )
+# define PACK_STRUCT __attribute__((packed))
+#else
+# define PACK_STRUCT
+//# error Compiler not supported
+#endif
+
+#if defined(_MSC_VER)
+
+// C4103: Packing was changed after the inclusion of the header, propably missing #pragma pop
+# pragma warning (disable : 4103)
+#endif
+
+#define AI_PUSHPACK_IS_DEFINED
+
+
diff --git a/src/3rdparty/assimp/include/DefaultLogger.h b/src/3rdparty/assimp/include/DefaultLogger.h
new file mode 100644
index 000000000..6d939d6a8
--- /dev/null
+++ b/src/3rdparty/assimp/include/DefaultLogger.h
@@ -0,0 +1,190 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/** @file DefaultLogger.h
+*/
+
+#ifndef INCLUDED_AI_DEFAULTLOGGER
+#define INCLUDED_AI_DEFAULTLOGGER
+
+#include "Logger.h"
+#include "LogStream.h"
+#include "NullLogger.h"
+#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 */
+ DefaultLogger(LogSeverity severity);
+
+ // ----------------------------------------------------------------------
+ /** @briefDestructor */
+ ~DefaultLogger();
+
+private:
+
+ /** @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/src/3rdparty/assimp/include/IOStream.h b/src/3rdparty/assimp/include/IOStream.h
new file mode 100644
index 000000000..22fdbde95
--- /dev/null
+++ b/src/3rdparty/assimp/include/IOStream.h
@@ -0,0 +1,135 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file IOStream.h
+ * @brief File I/O wrappers for C++.
+ */
+
+#ifndef AI_IOSTREAM_H_INC
+#define AI_IOSTREAM_H_INC
+
+#include "aiTypes.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 : public Intern::AllocateFromAssimpHeap
+{
+protected:
+ /** Constructor protected, use IOSystem::Open() to create an instance. */
+ IOStream(void);
+
+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()
+{
+ // empty
+}
+
+// ----------------------------------------------------------------------------------
+inline IOStream::~IOStream()
+{
+ // empty
+}
+// ----------------------------------------------------------------------------------
+} //!namespace Assimp
+
+#endif //!!AI_IOSTREAM_H_INC
diff --git a/src/3rdparty/assimp/include/IOSystem.h b/src/3rdparty/assimp/include/IOSystem.h
new file mode 100644
index 000000000..f41dda903
--- /dev/null
+++ b/src/3rdparty/assimp/include/IOSystem.h
@@ -0,0 +1,222 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file IOSystem.h
+ * @brief File system wrapper for C++. Inherit this class to supply
+ * custom file handling logic to the Import library.
+*/
+
+#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 "aiTypes.h"
+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 : public Intern::AllocateFromAssimpHeap
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** @brief Default constructor.
+ *
+ * Create an instance of your derived class and assign it to an
+ * #Assimp::Importer instance by calling Importer::SetIOHandler().
+ */
+ IOSystem();
+
+ // -------------------------------------------------------------------
+ /** @brief Virtual destructor.
+ *
+ * It is safe to be called from within DLL Assimp, we're constructed
+ * on Assimp's heap.
+ */
+ virtual ~IOSystem();
+
+
+public:
+
+ // -------------------------------------------------------------------
+ /** @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 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;
+};
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE IOSystem::IOSystem()
+{
+ // 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());
+}
+
+// ----------------------------------------------------------------------------
+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());
+}
+
+// ----------------------------------------------------------------------------
+} //!ns Assimp
+
+#endif //AI_IOSYSTEM_H_INC
diff --git a/src/3rdparty/assimp/include/LogStream.h b/src/3rdparty/assimp/include/LogStream.h
new file mode 100644
index 000000000..760a9f655
--- /dev/null
+++ b/src/3rdparty/assimp/include/LogStream.h
@@ -0,0 +1,93 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file LogStream.h
+ * @brief Abstract base class 'LogStream', representing an output log stream.
+ */
+#ifndef INCLUDED_AI_LOGSTREAM_H
+#define INCLUDED_AI_LOGSTREAM_H
+#include "aiTypes.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
+ : public Intern::AllocateFromAssimpHeap {
+protected:
+ /** @brief Default constructor */
+ LogStream() {
+ }
+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 = NULL);
+
+}; // !class LogStream
+// ------------------------------------------------------------------------------------
+} // Namespace Assimp
+
+#endif
diff --git a/src/3rdparty/assimp/include/Logger.h b/src/3rdparty/assimp/include/Logger.h
new file mode 100644
index 000000000..94a421418
--- /dev/null
+++ b/src/3rdparty/assimp/include/Logger.h
@@ -0,0 +1,234 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Logger.h
+ * @brief Abstract base class 'Logger', base of the logging system.
+ */
+#ifndef INCLUDED_AI_LOGGER_H
+#define INCLUDED_AI_LOGGER_H
+
+#include "aiTypes.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
+ * behaviour and is not of interest for you. Instead, take a look at #DefaultLogger. */
+class ASSIMP_API Logger
+ : public Intern::AllocateFromAssimpHeap {
+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 std::string &message);
+
+ // ----------------------------------------------------------------------
+ /** @brief Writes a info message
+ * @param message Info message*/
+ void info(const std::string &message);
+
+ // ----------------------------------------------------------------------
+ /** @brief Writes a warning message
+ * @param message Warn message*/
+ void warn(const std::string &message);
+
+ // ----------------------------------------------------------------------
+ /** @brief Writes an error message
+ * @param message Error 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 logstream
+ *
+ * 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 Logstream 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 Logstream 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 dettached, false otherwise.*/
+ virtual bool detatchStream(LogStream *pStream,
+ unsigned int severity = DEBUGGING | ERR | WARN | INFO) = 0;
+
+protected:
+
+ /** Default constructor */
+ Logger();
+
+ /** Construction with a given log severity */
+ 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 (exluding 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 (exluding 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:
+
+ //! Logger severity
+ LogSeverity m_Severity;
+};
+
+// ----------------------------------------------------------------------------------
+// Default constructor
+inline Logger::Logger() {
+ setLogSeverity(NORMAL);
+}
+
+// ----------------------------------------------------------------------------------
+// Virtual destructor
+inline Logger::~Logger()
+{
+}
+
+// ----------------------------------------------------------------------------------
+// Construction with given logging severity
+inline Logger::Logger(LogSeverity severity) {
+ setLogSeverity(severity);
+}
+
+// ----------------------------------------------------------------------------------
+// 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;
+}
+
+// ----------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! INCLUDED_AI_LOGGER_H
diff --git a/src/3rdparty/assimp/include/NullLogger.h b/src/3rdparty/assimp/include/NullLogger.h
new file mode 100644
index 000000000..152abafaf
--- /dev/null
+++ b/src/3rdparty/assimp/include/NullLogger.h
@@ -0,0 +1,95 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file NullLogger.h
+ * @brief Dummy logger
+*/
+
+#ifndef INCLUDED_AI_NULLLOGGER_H
+#define INCLUDED_AI_NULLLOGGER_H
+
+#include "Logger.h"
+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/src/3rdparty/assimp/include/ProgressHandler.h b/src/3rdparty/assimp/include/ProgressHandler.h
new file mode 100644
index 000000000..24c393d38
--- /dev/null
+++ b/src/3rdparty/assimp/include/ProgressHandler.h
@@ -0,0 +1,93 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file ProgressHandler.h
+ * @brief Abstract base class 'ProgressHandler'.
+ */
+#ifndef INCLUDED_AI_PROGRESSHANDLER_H
+#define INCLUDED_AI_PROGRESSHANDLER_H
+#include "aiTypes.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
+ : public Intern::AllocateFromAssimpHeap {
+protected:
+ /** @brief Default constructor */
+ ProgressHandler () {
+ }
+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.
+ *
+ * @note Currently, percentage is always -1.f because there is
+ * no reliable way to compute it.
+ * */
+ virtual bool Update(float percentage = -1.f) = 0;
+
+
+
+}; // !class ProgressHandler
+// ------------------------------------------------------------------------------------
+} // Namespace Assimp
+
+#endif
diff --git a/src/3rdparty/assimp/include/aiAnim.h b/src/3rdparty/assimp/include/aiAnim.h
new file mode 100644
index 000000000..52552de30
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiAnim.h
@@ -0,0 +1,484 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiAnim.h
+ * @brief Defines the data structures in which the imported animations
+ * are returned.
+ */
+#ifndef AI_ANIM_H_INC
+#define AI_ANIM_H_INC
+
+#include "aiTypes.h"
+#include "aiQuaternion.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
+
+ //! Default constructor
+ aiVectorKey() : mTime(0.0) {}
+
+ //! Construction from a given time and key value
+ aiVectorKey(double time, const aiVector3D& value)
+ : mTime (time)
+ , mValue (value)
+ {}
+
+
+ typedef aiVector3D elem_type;
+
+ // Comparison operators. For use with std::find();
+ bool operator == (const aiVectorKey& o) const {
+ return o.mValue == this->mValue;
+ }
+ bool operator != (const aiVectorKey& o) const {
+ return o.mValue != this->mValue;
+ }
+
+ // Relational operators. For use with std::sort();
+ bool operator < (const aiVectorKey& o) const {
+ return mTime < o.mTime;
+ }
+ bool operator > (const aiVectorKey& o) const {
+ return mTime > o.mTime;
+ }
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** 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() : mTime(0.0) {
+ }
+
+ /** 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& o) const {
+ return o.mValue == this->mValue;
+ }
+ bool operator != (const aiQuatKey& o) const {
+ return o.mValue != this->mValue;
+ }
+
+ // Relational operators. For use with std::sort();
+ bool operator < (const aiQuatKey& o) const {
+ return mTime < o.mTime;
+ }
+ bool operator > (const aiQuatKey& o) const {
+ return mTime > o.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 coresponding 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() {
+ }
+
+ /** 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
+};
+
+// ---------------------------------------------------------------------------
+/** 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 = 0x8fffffff
+#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()
+ {
+ mNumPositionKeys = 0; mPositionKeys = NULL;
+ mNumRotationKeys = 0; mRotationKeys = NULL;
+ mNumScalingKeys = 0; mScalingKeys = NULL;
+
+ mPreState = mPostState = aiAnimBehaviour_DEFAULT;
+ }
+
+ ~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 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 aiMeshKey* mKeys;
+
+#ifdef __cplusplus
+
+ aiMeshAnim()
+ : mNumKeys()
+ , mKeys()
+ {}
+
+ ~aiMeshAnim()
+ {
+ delete[] mKeys;
+ }
+
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** An animation consists of keyframe 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;
+
+#ifdef __cplusplus
+ aiAnimation()
+ : mDuration(-1.)
+ , mTicksPerSecond()
+ , mNumChannels()
+ , mChannels()
+ , mNumMeshChannels()
+ , mMeshChannels()
+ {
+ }
+
+ ~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;
+ }
+ }
+#endif // __cplusplus
+};
+
+#ifdef __cplusplus
+}
+
+
+// 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 choosen 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, float 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, float 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, float d) const
+ {
+ out = d>0.5f ? b : a;
+ }
+}; // ! Interpolator <aiQuaternion>
+
+template <>
+struct Interpolator <aiVectorKey> {
+ void operator () (aiVector3D& out,const aiVectorKey& a,
+ const aiVectorKey& b, float 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, float 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, float 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/src/3rdparty/assimp/include/aiAssert.h b/src/3rdparty/assimp/include/aiAssert.h
new file mode 100644
index 000000000..5e00d8466
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiAssert.h
@@ -0,0 +1,30 @@
+/** @file aiAssert.h
+ */
+#ifndef AI_DEBUG_H_INC
+#define AI_DEBUG_H_INC
+
+#include <string>
+
+#ifndef __cplusplus
+#error This header requires C++ to be used.
+#endif
+
+namespace Assimp {
+
+//! \brief ASSIMP specific assertion test, only works in debug mode
+//! \param uiLine Line in file
+//! \param file Source file
+AI_WONT_RETURN void aiAssert(const std::string &message, unsigned int uiLine, const std::string &file);
+
+
+//! \def ai_assert
+//! \brief ASSIMP specific assertion test
+#ifdef DEBUG
+# define ai_assert(expression) if ( !(expression)) Assimp::aiAssert( #expression, __LINE__, __FILE__);
+#else
+# define ai_assert(expression)
+#endif
+
+} // Namespace Assimp
+
+#endif
diff --git a/src/3rdparty/assimp/include/aiCamera.h b/src/3rdparty/assimp/include/aiCamera.h
new file mode 100644
index 000000000..8f3d0a714
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiCamera.h
@@ -0,0 +1,223 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiCamera.h
+ * @brief Defines the aiCamera data structure
+ */
+
+#ifndef AI_CAMERA_H_INC
+#define AI_CAMERA_H_INC
+
+#include "aiTypes.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()
+ : 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/src/3rdparty/assimp/include/aiColor4D.h b/src/3rdparty/assimp/include/aiColor4D.h
new file mode 100644
index 000000000..ce7e9dfe1
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiColor4D.h
@@ -0,0 +1,94 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file aiColor4D.h
+ * @brief RGBA color structure, including operators when compiling in C++
+ */
+#ifndef AI_COLOR4D_H_INC
+#define AI_COLOR4D_H_INC
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "./Compiler/pushpack1.h"
+// ----------------------------------------------------------------------------------
+/** Represents a color in Red-Green-Blue space including an
+* alpha component. Color values range from 0 to 1. */
+// ----------------------------------------------------------------------------------
+struct aiColor4D
+{
+#ifdef __cplusplus
+ aiColor4D () : r(0.0f), g(0.0f), b(0.0f), a(0.0f) {}
+ aiColor4D (float _r, float _g, float _b, float _a)
+ : r(_r), g(_g), b(_b), a(_a) {}
+ aiColor4D (float _r) : r(_r), g(_r), b(_r), a(_r) {}
+ aiColor4D (const aiColor4D& o)
+ : r(o.r), g(o.g), b(o.b), a(o.a) {}
+
+ // combined operators
+ const aiColor4D& operator += (const aiColor4D& o);
+ const aiColor4D& operator -= (const aiColor4D& o);
+ const aiColor4D& operator *= (float f);
+ const aiColor4D& operator /= (float f);
+
+ // comparison
+ bool operator == (const aiColor4D& other) const;
+ bool operator != (const aiColor4D& other) const;
+
+ // color tuple access, rgba order
+ inline float operator[](unsigned int i) const;
+ inline float& operator[](unsigned int i);
+
+ /** check whether a color is (close to) black */
+ inline bool IsBlack() const;
+
+#endif // !__cplusplus
+
+ // Red, green, blue and alpha color values
+ float r, g, b, a;
+} PACK_STRUCT; // !struct aiColor4D
+
+#include "./Compiler/poppack1.h"
+#ifdef __cplusplus
+} // end extern "C"
+
+#endif // __cplusplus
+#endif // AI_VECTOR3D_H_INC
diff --git a/src/3rdparty/assimp/include/aiColor4D.inl b/src/3rdparty/assimp/include/aiColor4D.inl
new file mode 100644
index 000000000..a4985ead5
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiColor4D.inl
@@ -0,0 +1,140 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiColor4D.inl
+ * @brief Inline implementation of aiColor4D operators
+ */
+#ifndef AI_COLOR4D_INL_INC
+#define AI_COLOR4D_INL_INC
+
+#include "aiColor4D.h"
+#ifdef __cplusplus
+
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE const aiColor4D& aiColor4D::operator += (const aiColor4D& o) {
+ r += o.r; g += o.g; b += o.b; a += o.a; return *this;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE const aiColor4D& aiColor4D::operator -= (const aiColor4D& o) {
+ r -= o.r; g -= o.g; b -= o.b; a -= o.a; return *this;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE const aiColor4D& aiColor4D::operator *= (float f) {
+ r *= f; g *= f; b *= f; a *= f; return *this;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE const aiColor4D& aiColor4D::operator /= (float f) {
+ r /= f; g /= f; b /= f; a /= f; return *this;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE float aiColor4D::operator[](unsigned int i) const {
+ return *(&r + i);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE float& aiColor4D::operator[](unsigned int i) {
+ return *(&r + i);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE bool aiColor4D::operator== (const aiColor4D& other) const {
+ return r == other.r && g == other.g && b == other.b && a == other.a;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE bool aiColor4D::operator!= (const aiColor4D& other) const {
+ return r != other.r || g != other.g || b != other.b || a != other.a;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiColor4D operator + (const aiColor4D& v1, const aiColor4D& v2) {
+ return aiColor4D( v1.r + v2.r, v1.g + v2.g, v1.b + v2.b, v1.a + v2.a);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiColor4D operator - (const aiColor4D& v1, const aiColor4D& v2) {
+ return aiColor4D( v1.r - v2.r, v1.g - v2.g, v1.b - v2.b, v1.a - v2.a);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiColor4D operator * (const aiColor4D& v1, const aiColor4D& v2) {
+ return aiColor4D( v1.r * v2.r, v1.g * v2.g, v1.b * v2.b, v1.a * v2.a);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiColor4D operator / (const aiColor4D& v1, const aiColor4D& v2) {
+ return aiColor4D( v1.r / v2.r, v1.g / v2.g, v1.b / v2.b, v1.a / v2.a);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiColor4D operator * ( float f, const aiColor4D& v) {
+ return aiColor4D( f*v.r, f*v.g, f*v.b, f*v.a);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiColor4D operator * ( const aiColor4D& v, float f) {
+ return aiColor4D( f*v.r, f*v.g, f*v.b, f*v.a);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiColor4D operator / ( const aiColor4D& v, float f) {
+ return v * (1/f);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiColor4D operator / ( float f,const aiColor4D& v) {
+ return aiColor4D(f,f,f,f)/v;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiColor4D operator + ( const aiColor4D& v, float f) {
+ return aiColor4D( f+v.r, f+v.g, f+v.b, f+v.a);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiColor4D operator - ( const aiColor4D& v, float f) {
+ return aiColor4D( v.r-f, v.g-f, v.b-f, v.a-f);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiColor4D operator + ( float f, const aiColor4D& v) {
+ return aiColor4D( f+v.r, f+v.g, f+v.b, f+v.a);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiColor4D operator - ( float f, const aiColor4D& v) {
+ return aiColor4D( f-v.r, f-v.g, f-v.b, f-v.a);
+}
+
+// ------------------------------------------------------------------------------------------------
+inline bool aiColor4D :: IsBlack() const {
+ // The alpha component doesn't care here. black is black.
+ static const float epsilon = 10e-3f;
+ return fabs( r ) < epsilon && fabs( g ) < epsilon && fabs( b ) < epsilon;
+}
+
+#endif // __cplusplus
+#endif // AI_VECTOR3D_INL_INC
diff --git a/src/3rdparty/assimp/include/aiConfig.h b/src/3rdparty/assimp/include/aiConfig.h
new file mode 100644
index 000000000..e53a2870f
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiConfig.h
@@ -0,0 +1,634 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiConfig.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
+ */
+#ifndef INCLUDED_AI_CONFIG_H
+#define INCLUDED_AI_CONFIG_H
+
+
+// ###########################################################################
+// 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"
+
+# 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 Specifies the maximum angle that may be between two vertex tangents
+ * that their tangents and bitangents are smoothed.
+ *
+ * This applies to the CalcTangentSpace-Step. The angle is specified
+ * in degrees, so 180 is PI. The default value is
+ * 45 degrees. The maximum value is 175.
+ * Property type: float.
+ */
+#define AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE \
+ "PP_CT_MAX_SMOOTHING_ANGLE"
+
+// ---------------------------------------------------------------------------
+/** @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_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_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_LBW_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 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 #aiPrpcess_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 light sources (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_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 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"
+
+
+// ---------------------------------------------------------------------------
+/** @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><dir>/<model_name>.shader</tt> first, <tt><dir>/<file_name>.shader</tt>
+ * is the fallback file. Note that <dir> 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"
+
+
+// ---------------------------------------------------------------------------
+/** Ogre Importer will try to load this Materialfile
+ * Ogre Mehs contain only the MaterialName, not the MaterialFile. If there
+ * is no material file with the same name as the material, Ogre Importer will
+ * try to load this file and search the material in it.
+ */
+#define AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE "IMPORT_OGRE_MATERIAL_FILE"
+
+
+#endif // !! AI_CONFIG_H_INC
diff --git a/src/3rdparty/assimp/include/aiDefines.h b/src/3rdparty/assimp/include/aiDefines.h
new file mode 100644
index 000000000..a1fcda702
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiDefines.h
@@ -0,0 +1,255 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiDefines.h
+ * @brief Assimp build configuration setup. See the notes in the comment
+ * blocks to find out how to customize _your_ Assimp build.
+ */
+
+#ifndef INCLUDED_AI_DEFINES_H
+#define INCLUDED_AI_DEFINES_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
+ * ASSIMP_BUILD_NO_COMPRESSED_BLEND
+ * - Disable support for compressed Blender files*/
+ //////////////////////////////////////////////////////////////////////////
+
+#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
+
+ //////////////////////////////////////////////////////////////////////////
+ /* 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
+ * GENFACENORMALS
+ * GENVERTEXNORMALS
+ * REMOVEVC
+ * SPLITLARGEMESHES
+ * PRETRANSFORMVERTICES
+ * LIMITBONEWEIGHTS
+ * VALIDATEDS
+ * IMPROVECACHELOCALITY
+ * FIXINFACINGNORMALS
+ * REMOVE_REDUNDANTMATERIALS
+ * OPTIMIZEGRAPH
+ * SORTBYPTYPE
+ * FINDINVALIDDATA
+ * TRANSFORMTEXCOORDS
+ * GENUVCOORDS
+ * ENTITYMESHBUILDER
+ * 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)
+# 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)
+# else
+# define ASSIMP_API
+# 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)
+#else
+
+# define AI_WONT_RETURN
+
+# define ASSIMP_API
+# define AI_FORCE_INLINE inline
+#endif // (defined _MSC_VER)
+
+#ifdef __cplusplus
+ /* No explicit 'struct' and 'enum' tags for C++, we don't want to
+ * confuse the _AI_ of our IDE.
+ */
+# 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 Packgre der Struktur gendert"
+
+#endif
+ //////////////////////////////////////////////////////////////////////////
+ /* Define 'ASSIMP_BUILD_BOOST_WORKAROUND' to compile assimp
+ * without boost. This is done by using a few workaround
+ * classes and brings some limitations (e.g. some logging won't be done,
+ * the library won't utilize threads or be threadsafe at all).
+ * This implies the 'ASSIMP_BUILD_SINGLETHREADED' setting. */
+ //////////////////////////////////////////////////////////////////////////
+#ifdef ASSIMP_BUILD_BOOST_WORKAROUND
+
+ // threading support requires boost
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+# define ASSIMP_BUILD_SINGLETHREADED
+#endif
+
+#endif // !! ASSIMP_BUILD_BOOST_WORKAROUND
+
+ //////////////////////////////////////////////////////////////////////////
+ /* Define ASSIMP_BUILD_SINGLETHREADED to compile assimp
+ * without threading support. The library doesn't utilize
+ * threads then and is itself not threadsafe.
+ * If this flag is specified boost::threads is *not* required. */
+ //////////////////////////////////////////////////////////////////////////
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+# define ASSIMP_BUILD_SINGLETHREADED
+#endif
+
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+# define AI_C_THREADSAFE
+#endif // !! ASSIMP_BUILD_SINGLETHREADED
+
+#if (defined _DEBUG || defined DEBUG) // one of the two should be defined ..
+# define ASSIMP_BUILD_DEBUG
+#endif
+
+ //////////////////////////////////////////////////////////////////////////
+ /* 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*0.0174532925f)
+#define AI_RAD_TO_DEG(x) (x*57.2957795f)
+
+/* 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
+
+#endif // !! INCLUDED_AI_DEFINES_H
diff --git a/src/3rdparty/assimp/include/aiFileIO.h b/src/3rdparty/assimp/include/aiFileIO.h
new file mode 100644
index 000000000..bc012fde8
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiFileIO.h
@@ -0,0 +1,135 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiFileIO.h
+ * @brief Defines generic C routines to access memory-mapped files
+ */
+#ifndef AI_FILEIO_H_INC
+#define AI_FILEIO_H_INC
+
+#include "aiTypes.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 aiReturn (*aiFileSeek)(C_STRUCT aiFile*, size_t, aiOrigin);
+
+// aiFileIO callbacks
+typedef 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/src/3rdparty/assimp/include/aiLight.h b/src/3rdparty/assimp/include/aiLight.h
new file mode 100644
index 000000000..9d1ab10e3
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiLight.h
@@ -0,0 +1,233 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiLight.h
+ * @brief Defines the aiLight data structure
+ */
+
+#ifndef __AI_LIGHT_H_INC__
+#define __AI_LIGHT_H_INC__
+
+#include "aiTypes.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,
+
+
+ /** 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 = 0x9fffffff
+#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;
+
+ /** 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;
+
+#ifdef __cplusplus
+
+ aiLight()
+ : mType (aiLightSource_UNDEFINED)
+ , mAttenuationConstant (0.f)
+ , mAttenuationLinear (1.f)
+ , mAttenuationQuadratic (0.f)
+ , mAngleInnerCone ((float)AI_MATH_TWO_PI)
+ , mAngleOuterCone ((float)AI_MATH_TWO_PI)
+ {
+ }
+
+#endif
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // !! __AI_LIGHT_H_INC__
diff --git a/src/3rdparty/assimp/include/aiMaterial.h b/src/3rdparty/assimp/include/aiMaterial.h
new file mode 100644
index 000000000..df33f5abf
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiMaterial.h
@@ -0,0 +1,1423 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiMaterial.h
+ * @brief Defines the material system of the library
+ */
+
+#ifndef AI_MATERIAL_H_INC
+#define AI_MATERIAL_H_INC
+
+#include "aiTypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Name for default materials (2nd is used if meshes have UV coords)
+#define AI_DEFAULT_MATERIAL_NAME "DefaultMaterial"
+#define AI_DEFAULT_TEXTURED_MATERIAL_NAME "TexturedDefaultMaterial"
+
+// ---------------------------------------------------------------------------
+/** @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,
+
+
+ /** @cond never
+ * This value is not used. It forces the compiler to use at least
+ * 32 Bit integers to represent this enum.
+ */
+#ifndef SWIG
+ _aiTextureOp_Force32Bit = 0x9fffffff
+#endif
+ //! @endcond
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Defines how UV coordinates outside the [0...1] range are handled.
+ *
+ * Commonly refered 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,
+
+ /** @cond never
+ * This value is not used. It forces the compiler to use at least
+ * 32 Bit integers to represent this enum.
+ */
+#ifndef SWIG
+ _aiTextureMapMode_Force32Bit = 0x9fffffff
+#endif
+ //! @endcond
+};
+
+// ---------------------------------------------------------------------------
+/** @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,
+
+
+ /** @cond never
+ * This value is not used. It forces the compiler to use at least
+ * 32 Bit integers to represent this enum.
+ */
+#ifndef SWIG
+ _aiTextureMapping_Force32Bit = 0x9fffffff
+#endif
+ //! @endcond
+};
+
+// ---------------------------------------------------------------------------
+/** @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 postprocessing.
+ */
+ aiTextureType_UNKNOWN = 0xC,
+
+
+ /** @cond never
+ * This value is not used. It forces the compiler to use at least
+ * 32 Bit integers to represent this enum.
+ */
+#ifndef SWIG
+ _aiTextureType_Force32Bit = 0x9fffffff
+#endif
+ //! @endcond
+};
+
+#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,
+
+
+ /** @cond never
+ * This value is not used. It forces the compiler to use at least
+ * 32 Bit integers to represent this enum.
+ */
+#ifndef SWIG
+ _aiShadingMode_Force32Bit = 0x9fffffff
+#endif
+ //! @endcond
+};
+
+
+// ---------------------------------------------------------------------------
+/** @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 (componentwise 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,
+
+ /** @cond never
+ * This value is not used. It forces the compiler to use at least
+ * 32 Bit integers to represent this enum.
+ */
+#ifndef SWIG
+ _aiTextureFlags_Force32Bit = 0x9fffffff
+#endif
+ //! @endcond
+};
+
+
+// ---------------------------------------------------------------------------
+/** @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 colro 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 ...
+
+ /** @cond never
+ * This value is not used. It forces the compiler to use at least
+ * 32 Bit integers to represent this enum.
+ */
+#ifndef SWIG
+ _aiBlendMode_Force32Bit = 0x9fffffff
+#endif
+ //! @endcond
+};
+
+
+#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.
+ */
+ float mRotation;
+
+
+#ifdef __cplusplus
+ aiUVTransform()
+ : mScaling (1.f,1.f)
+ , mRotation (0.f)
+ {
+ // nothing to be done here ...
+ }
+#endif
+
+} PACK_STRUCT;
+
+#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,
+
+ /** 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 = 0x9fffffff
+#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() {
+ mData = NULL;
+ mDataLength = 0;
+ mType = aiPTI_Float;
+ mIndex = mSemantic = 0;
+ }
+
+ ~aiMaterialProperty() {
+ delete[] mData;
+ }
+
+#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).
+*/
+struct ASSIMP_API aiMaterial
+{
+
+#ifdef __cplusplus
+
+ aiMaterial() : mProperties(0), mNumProperties(0), mNumAllocated(0) {}
+
+public:
+
+ ~aiMaterial();
+
+ // -------------------------------------------------------------------
+ /** @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;
+
+ // -------------------------------------------------------------------
+ /** @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;
+
+ // -------------------------------------------------------------------
+ /** 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.
+ * 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,
+ float* blend = NULL,
+ aiTextureOp* op = NULL,
+ aiTextureMapMode* mapmode = NULL) const;
+
+#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_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
+
+// ---------------------------------------------------------------------------
+// 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)
+
+// ---------------------------------------------------------------------------
+/** @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,
+ float* 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,
+ float* 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 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
+ * 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).
+ * @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,
+ float* 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*/,
+ float* blend /*= NULL*/,
+ C_ENUM aiTextureOp* op /*= NULL*/,
+ C_ENUM aiTextureMapMode* mapmode /*= NULL*/,
+ unsigned int* flags /*= NULL*/);
+#endif // !#ifdef __cplusplus
+
+#ifdef __cplusplus
+}
+
+#include "aiMaterial.inl"
+
+#endif //!__cplusplus
+#endif //!!AI_MATERIAL_H_INC
diff --git a/src/3rdparty/assimp/include/aiMaterial.inl b/src/3rdparty/assimp/include/aiMaterial.inl
new file mode 100644
index 000000000..6be16ff63
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiMaterial.inl
@@ -0,0 +1,191 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiMaterial.inl
+ * @brief Defines the C++ getters for the material system
+ */
+
+#ifndef AI_MATERIAL_INL_INC
+#define AI_MATERIAL_INL_INC
+
+//! @cond never
+
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::GetTexture( aiTextureType type,
+ unsigned int index,
+ C_STRUCT aiString* path,
+ aiTextureMapping* mapping /*= NULL*/,
+ unsigned int* uvindex /*= NULL*/,
+ float* 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 (::strcmp(prop->mData,(char*)aiPTI_Buffer)!=0)
+ // 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 (strcmp(prop->mData,(char*)aiPTI_Buffer)!=0) {
+ return AI_FAILURE;
+ }
+
+ memcpy(&pOut,prop->mData,sizeof(Type));
+ */
+
+ if (prop->mType == aiPTI_Buffer) {
+ return AI_FAILURE;
+ } else {
+ if (prop->mDataLength < sizeof(Type)) {
+ return AI_FAILURE;
+ } else {
+ memcpy(&pOut,prop->mData,sizeof(Type));
+ }
+ }
+ }
+ return ret;
+}
+// ---------------------------------------------------------------------------
+template <>
+inline aiReturn aiMaterial::Get<float>(const char* pKey,unsigned int type,
+ unsigned int idx,float* pOut,
+ unsigned int* pMax) const
+{
+ return ::aiGetMaterialFloatArray(this,pKey,type,idx,pOut,pMax);
+}
+// ---------------------------------------------------------------------------
+template <>
+inline aiReturn aiMaterial::Get<int>(const char* pKey,unsigned int type,
+ unsigned int idx,int* pOut,
+ unsigned int* pMax) const
+{
+ return ::aiGetMaterialIntegerArray(this,pKey,type,idx,pOut,pMax);
+}
+
+// ---------------------------------------------------------------------------
+template <>
+inline aiReturn aiMaterial::Get<float>(const char* pKey,unsigned int type,
+ unsigned int idx,float& pOut) const
+{
+ return aiGetMaterialFloat(this,pKey,type,idx,&pOut);
+}
+// ---------------------------------------------------------------------------
+template <>
+inline aiReturn aiMaterial::Get<int>(const char* pKey,unsigned int type,
+ unsigned int idx,int& pOut) const
+{
+ return aiGetMaterialInteger(this,pKey,type,idx,&pOut);
+}
+// ---------------------------------------------------------------------------
+template <>
+inline aiReturn aiMaterial::Get<aiColor4D>(const char* pKey,unsigned int type,
+ unsigned int idx,aiColor4D& pOut) const
+{
+ return aiGetMaterialColor(this,pKey,type,idx,&pOut);
+}
+// ---------------------------------------------------------------------------
+template <>
+inline aiReturn aiMaterial::Get<aiColor3D>(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;
+}
+// ---------------------------------------------------------------------------
+template <>
+inline aiReturn aiMaterial::Get<aiString>(const char* pKey,unsigned int type,
+ unsigned int idx,aiString& pOut) const
+{
+ return aiGetMaterialString(this,pKey,type,idx,&pOut);
+}
+
+//! @endcond
+
+#endif //! AI_MATERIAL_INL_INC
diff --git a/src/3rdparty/assimp/include/aiMatrix3x3.h b/src/3rdparty/assimp/include/aiMatrix3x3.h
new file mode 100644
index 000000000..8dcc4ce46
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiMatrix3x3.h
@@ -0,0 +1,166 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiMatrix3x3.h
+ * @brief Definition of a 3x3 matrix, including operators when compiling in C++
+ */
+#ifndef AI_MATRIX3x3_H_INC
+#define AI_MATRIX3x3_H_INC
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct aiMatrix4x4;
+struct aiVector2D;
+
+// ---------------------------------------------------------------------------
+/** @brief Represents a row-major 3x3 matrix
+ *
+ * There's much confusion about matrix layouts (colum vs. row order).
+ * This is *always* a row-major matrix. Even with the
+ * aiProcess_ConvertToLeftHanded flag.
+ */
+struct aiMatrix3x3
+{
+#ifdef __cplusplus
+
+ aiMatrix3x3 () :
+ a1(1.0f), a2(0.0f), a3(0.0f),
+ b1(0.0f), b2(1.0f), b3(0.0f),
+ c1(0.0f), c2(0.0f), c3(1.0f) {}
+
+ aiMatrix3x3 ( float _a1, float _a2, float _a3,
+ float _b1, float _b2, float _b3,
+ float _c1, float _c2, float _c3) :
+ a1(_a1), a2(_a2), a3(_a3),
+ b1(_b1), b2(_b2), b3(_b3),
+ c1(_c1), c2(_c2), c3(_c3)
+ {}
+
+public:
+
+ // matrix multiplication. beware, not commutative
+ aiMatrix3x3& operator *= (const aiMatrix3x3& m);
+ aiMatrix3x3 operator * (const aiMatrix3x3& m) const;
+
+ // array access operators
+ float* operator[] (unsigned int p_iIndex);
+ const float* operator[] (unsigned int p_iIndex) const;
+
+ // comparison operators
+ bool operator== (const aiMatrix4x4 m) const;
+ bool operator!= (const aiMatrix4x4 m) const;
+
+public:
+
+ // -------------------------------------------------------------------
+ /** @brief Construction from a 4x4 matrix. The remaining parts
+ * of the matrix are ignored.
+ */
+ explicit aiMatrix3x3( const aiMatrix4x4& pMatrix);
+
+ // -------------------------------------------------------------------
+ /** @brief Transpose the matrix
+ */
+ aiMatrix3x3& 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 float f is qnan.
+ */
+ aiMatrix3x3& Inverse();
+ float 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 aiMatrix3x3& RotationZ(float a, aiMatrix3x3& 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 aiMatrix3x3& Rotation( float a,
+ const aiVector3D& axis, aiMatrix3x3& out);
+
+ // -------------------------------------------------------------------
+ /** @brief Returns a translation matrix
+ * @param v Translation vector
+ * @param out Receives the output matrix
+ * @return Reference to the output matrix
+ */
+ static aiMatrix3x3& Translation( const aiVector2D& v, aiMatrix3x3& 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 colum-major form
+ * Authors: Tomas Mller, John Hughes
+ * "Efficiently Building a Matrix to Rotate One Vector to Another"
+ * Journal of Graphics Tools, 4(4):1-4, 1999
+ */
+ static aiMatrix3x3& FromToMatrix(const aiVector3D& from,
+ const aiVector3D& to, aiMatrix3x3& out);
+
+#endif // __cplusplus
+
+
+ float a1, a2, a3;
+ float b1, b2, b3;
+ float c1, c2, c3;
+};
+
+#ifdef __cplusplus
+} // end of extern C
+#endif
+
+#endif // AI_MATRIX3x3_H_INC
diff --git a/src/3rdparty/assimp/include/aiMatrix3x3.inl b/src/3rdparty/assimp/include/aiMatrix3x3.inl
new file mode 100644
index 000000000..0b5d7a917
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiMatrix3x3.inl
@@ -0,0 +1,250 @@
+/** @file aiMatrix3x3.inl
+ * @brief Inline implementation of the 3x3 matrix operators
+ */
+#ifndef AI_MATRIX3x3_INL_INC
+#define AI_MATRIX3x3_INL_INC
+
+#include "aiMatrix3x3.h"
+
+#ifdef __cplusplus
+#include "aiMatrix4x4.h"
+#include <algorithm>
+#include <limits>
+
+// ------------------------------------------------------------------------------------------------
+// Construction from a 4x4 matrix. The remaining parts of the matrix are ignored.
+inline aiMatrix3x3::aiMatrix3x3( const aiMatrix4x4& 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;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline aiMatrix3x3& aiMatrix3x3::operator *= (const aiMatrix3x3& m)
+{
+ *this = aiMatrix3x3(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;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline aiMatrix3x3 aiMatrix3x3::operator* (const aiMatrix3x3& m) const
+{
+ aiMatrix3x3 temp( *this);
+ temp *= m;
+ return temp;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline float* aiMatrix3x3::operator[] (unsigned int p_iIndex)
+{
+ return &this->a1 + p_iIndex * 3;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline const float* aiMatrix3x3::operator[] (unsigned int p_iIndex) const
+{
+ return &this->a1 + p_iIndex * 3;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline bool aiMatrix3x3::operator== (const aiMatrix4x4 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;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline bool aiMatrix3x3::operator!= (const aiMatrix4x4 m) const
+{
+ return !(*this == m);
+}
+
+// ------------------------------------------------------------------------------------------------
+inline aiMatrix3x3& aiMatrix3x3::Transpose()
+{
+ // (float&) don't remove, GCC complains cause of packed fields
+ std::swap( (float&)a2, (float&)b1);
+ std::swap( (float&)a3, (float&)c1);
+ std::swap( (float&)b3, (float&)c2);
+ return *this;
+}
+
+// ----------------------------------------------------------------------------------------
+inline float aiMatrix3x3::Determinant() const
+{
+ return a1*b2*c3 - a1*b3*c2 + a2*b3*c1 - a2*b1*c3 + a3*b1*c2 - a3*b2*c1;
+}
+
+// ----------------------------------------------------------------------------------------
+inline aiMatrix3x3& aiMatrix3x3::Inverse()
+{
+ // Compute the reciprocal determinant
+ float det = Determinant();
+ if(det == 0.0f)
+ {
+ // 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 float nan = std::numeric_limits<float>::quiet_NaN();
+ *this = aiMatrix3x3( nan,nan,nan,nan,nan,nan,nan,nan,nan);
+
+ return *this;
+ }
+
+ float invdet = 1.0f / det;
+
+ aiMatrix3x3 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;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline aiMatrix3x3& aiMatrix3x3::RotationZ(float a, aiMatrix3x3& out)
+{
+ out.a1 = out.b2 = ::cos(a);
+ out.b1 = ::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.
+inline aiMatrix3x3& aiMatrix3x3::Rotation( float a, const aiVector3D& axis, aiMatrix3x3& out)
+{
+ float c = cos( a), s = sin( a), t = 1 - c;
+ float 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;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline aiMatrix3x3& aiMatrix3x3::Translation( const aiVector2D& v, aiMatrix3x3& out)
+{
+ out = aiMatrix3x3();
+ 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 Mller, John Hughes
+ * "Efficiently Building a Matrix to Rotate One Vector to Another"
+ * Journal of Graphics Tools, 4(4):1-4, 1999
+ */
+// ----------------------------------------------------------------------------------------
+inline aiMatrix3x3& aiMatrix3x3::FromToMatrix(const aiVector3D& from,
+ const aiVector3D& to, aiMatrix3x3& mtx)
+{
+ const float e = from * to;
+ const float f = (e < 0)? -e:e;
+
+ if (f > 1.0 - 0.00001f) /* "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 = 1.0; x.y = x.z = 0.0;
+ }
+ else
+ {
+ x.z = 1.0; x.y = x.z = 0.0;
+ }
+ }
+ else
+ {
+ if (x.y < x.z)
+ {
+ x.y = 1.0; x.x = x.z = 0.0;
+ }
+ else
+ {
+ x.z = 1.0; x.x = x.y = 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 float c1 = 2.0f / (u * u);
+ const float c2 = 2.0f / (v * v);
+ const float 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] += 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 float h = 1.0f/(1.0f + e); /* optimization by Gottfried Chen */
+ const float hvx = h * v.x;
+ const float hvz = h * v.z;
+ const float hvxy = hvx * v.y;
+ const float hvxz = hvx * v.z;
+ const float 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/src/3rdparty/assimp/include/aiMatrix4x4.h b/src/3rdparty/assimp/include/aiMatrix4x4.h
new file mode 100644
index 000000000..406a615d5
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiMatrix4x4.h
@@ -0,0 +1,240 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file aiMatrix4x4.h
+ * @brief 4x4 matrix structure, including operators when compiling in C++
+ */
+#ifndef AI_MATRIX4X4_H_INC
+#define AI_MATRIX4X4_H_INC
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct aiMatrix3x3;
+struct aiQuaternion;
+
+#include "./Compiler/pushpack1.h"
+
+// ---------------------------------------------------------------------------
+/** @brief Represents a row-major 4x4 matrix, use this for homogeneous
+ * coordinates.
+ *
+ * There's much confusion about matrix layouts (colum vs. row order).
+ * This is *always* a row-major matrix. Even with the
+ * aiProcess_ConvertToLeftHanded flag.
+ */
+struct aiMatrix4x4
+{
+#ifdef __cplusplus
+
+ // default c'tor, init to zero
+ aiMatrix4x4 () :
+ a1(1.0f), a2(0.0f), a3(0.0f), a4(0.0f),
+ b1(0.0f), b2(1.0f), b3(0.0f), b4(0.0f),
+ c1(0.0f), c2(0.0f), c3(1.0f), c4(0.0f),
+ d1(0.0f), d2(0.0f), d3(0.0f), d4(1.0f)
+ {}
+
+ // from single values
+ aiMatrix4x4 ( float _a1, float _a2, float _a3, float _a4,
+ float _b1, float _b2, float _b3, float _b4,
+ float _c1, float _c2, float _c3, float _c4,
+ float _d1, float _d2, float _d3, float _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)
+ {}
+
+
+ // -------------------------------------------------------------------
+ /** @brief Constructor from 3x3 matrix.
+ * The remaining elements are set to identity.
+ */
+ explicit aiMatrix4x4( const aiMatrix3x3& m);
+
+public:
+
+ // array access operators
+ float* operator[] (unsigned int p_iIndex);
+ const float* operator[] (unsigned int p_iIndex) const;
+
+ // comparison operators
+ bool operator== (const aiMatrix4x4 m) const;
+ bool operator!= (const aiMatrix4x4 m) const;
+
+ // Matrix multiplication. Not commutative.
+ aiMatrix4x4& operator *= (const aiMatrix4x4& m);
+ aiMatrix4x4 operator * (const aiMatrix4x4& m) const;
+
+public:
+
+ // -------------------------------------------------------------------
+ /** @brief Transpose the matrix
+ */
+ aiMatrix4x4& 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 float f is qnan.
+ */
+ aiMatrix4x4& Inverse();
+ float 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 (aiVector3D& scaling, aiQuaternion& rotation,
+ aiVector3D& position) 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 (aiQuaternion& rotation,
+ aiVector3D& 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
+ */
+ aiMatrix4x4& FromEulerAnglesXYZ(float x, float y, float z);
+ aiMatrix4x4& FromEulerAnglesXYZ(const aiVector3D& 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 aiMatrix4x4& RotationX(float a, aiMatrix4x4& 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 aiMatrix4x4& RotationY(float a, aiMatrix4x4& 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 aiMatrix4x4& RotationZ(float a, aiMatrix4x4& 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 aiMatrix4x4& Rotation(float a, const aiVector3D& axis,
+ aiMatrix4x4& out);
+
+ // -------------------------------------------------------------------
+ /** @brief Returns a translation matrix
+ * @param v Translation vector
+ * @param out Receives the output matrix
+ * @return Reference to the output matrix
+ */
+ static aiMatrix4x4& Translation( const aiVector3D& v, aiMatrix4x4& out);
+
+ // -------------------------------------------------------------------
+ /** @brief Returns a scaling matrix
+ * @param v Scaling vector
+ * @param out Receives the output matrix
+ * @return Reference to the output matrix
+ */
+ static aiMatrix4x4& Scaling( const aiVector3D& v, aiMatrix4x4& 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 colum-major form
+ * Authors: Tomas Mller, John Hughes
+ * "Efficiently Building a Matrix to Rotate One Vector to Another"
+ * Journal of Graphics Tools, 4(4):1-4, 1999
+ */
+ static aiMatrix4x4& FromToMatrix(const aiVector3D& from,
+ const aiVector3D& to, aiMatrix4x4& out);
+
+#endif // __cplusplus
+
+ float a1, a2, a3, a4;
+ float b1, b2, b3, b4;
+ float c1, c2, c3, c4;
+ float d1, d2, d3, d4;
+
+} PACK_STRUCT; // !class aiMatrix4x4
+
+
+#include "./Compiler/poppack1.h"
+
+#ifdef __cplusplus
+} // end extern "C"
+
+
+#endif // __cplusplus
+#endif // AI_MATRIX4X4_H_INC
diff --git a/src/3rdparty/assimp/include/aiMatrix4x4.inl b/src/3rdparty/assimp/include/aiMatrix4x4.inl
new file mode 100644
index 000000000..3dc182eb9
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiMatrix4x4.inl
@@ -0,0 +1,419 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiMatrix4x4.inl
+ * @brief Inline implementation of the 4x4 matrix operators
+ */
+#ifndef AI_MATRIX4x4_INL_INC
+#define AI_MATRIX4x4_INL_INC
+
+#include "aiMatrix4x4.h"
+
+#ifdef __cplusplus
+#include "aiMatrix3x3.h"
+
+#include <algorithm>
+#include <limits>
+#include <math.h>
+
+#include "aiAssert.h"
+#include "aiQuaternion.h"
+
+// ----------------------------------------------------------------------------------------
+inline aiMatrix4x4::aiMatrix4x4( const aiMatrix3x3& m)
+{
+ a1 = m.a1; a2 = m.a2; a3 = m.a3; a4 = 0.0f;
+ b1 = m.b1; b2 = m.b2; b3 = m.b3; b4 = 0.0f;
+ c1 = m.c1; c2 = m.c2; c3 = m.c3; c4 = 0.0f;
+ d1 = 0.0f; d2 = 0.0f; d3 = 0.0f; d4 = 1.0f;
+}
+
+// ----------------------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::operator *= (const aiMatrix4x4& m)
+{
+ *this = aiMatrix4x4(
+ 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;
+}
+
+// ----------------------------------------------------------------------------------------
+inline aiMatrix4x4 aiMatrix4x4::operator* (const aiMatrix4x4& m) const
+{
+ aiMatrix4x4 temp( *this);
+ temp *= m;
+ return temp;
+}
+
+
+// ----------------------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::Transpose()
+{
+ // (float&) don't remove, GCC complains cause of packed fields
+ std::swap( (float&)b1, (float&)a2);
+ std::swap( (float&)c1, (float&)a3);
+ std::swap( (float&)c2, (float&)b3);
+ std::swap( (float&)d1, (float&)a4);
+ std::swap( (float&)d2, (float&)b4);
+ std::swap( (float&)d3, (float&)c4);
+ return *this;
+}
+
+
+// ----------------------------------------------------------------------------------------
+inline float aiMatrix4x4::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;
+}
+
+// ----------------------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::Inverse()
+{
+ // Compute the reciprocal determinant
+ float det = Determinant();
+ if(det == 0.0f)
+ {
+ // 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 float nan = std::numeric_limits<float>::quiet_NaN();
+ *this = aiMatrix4x4(
+ nan,nan,nan,nan,
+ nan,nan,nan,nan,
+ nan,nan,nan,nan,
+ nan,nan,nan,nan);
+
+ return *this;
+ }
+
+ float invdet = 1.0f / det;
+
+ aiMatrix4x4 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;
+}
+
+// ----------------------------------------------------------------------------------------
+inline float* aiMatrix4x4::operator[](unsigned int p_iIndex)
+{
+ return &this->a1 + p_iIndex * 4;
+}
+
+// ----------------------------------------------------------------------------------------
+inline const float* aiMatrix4x4::operator[](unsigned int p_iIndex) const
+{
+ return &this->a1 + p_iIndex * 4;
+}
+
+// ----------------------------------------------------------------------------------------
+inline bool aiMatrix4x4::operator== (const aiMatrix4x4 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);
+}
+
+// ----------------------------------------------------------------------------------------
+inline bool aiMatrix4x4::operator!= (const aiMatrix4x4 m) const
+{
+ return !(*this == m);
+}
+
+// ----------------------------------------------------------------------------------------
+inline void aiMatrix4x4::Decompose (aiVector3D& scaling, aiQuaternion& rotation,
+ aiVector3D& position) const
+{
+ const aiMatrix4x4& _this = *this;
+
+ // extract translation
+ position.x = _this[0][3];
+ position.y = _this[1][3];
+ position.z = _this[2][3];
+
+ // extract the rows of the matrix
+ aiVector3D vRows[3] = {
+ aiVector3D(_this[0][0],_this[1][0],_this[2][0]),
+ aiVector3D(_this[0][1],_this[1][1],_this[2][1]),
+ aiVector3D(_this[0][2],_this[1][2],_this[2][2])
+ };
+
+ // extract the scaling factors
+ scaling.x = vRows[0].Length();
+ scaling.y = vRows[1].Length();
+ scaling.z = vRows[2].Length();
+
+ // and remove all scaling from the matrix
+ if(scaling.x)
+ {
+ vRows[0] /= scaling.x;
+ }
+ if(scaling.y)
+ {
+ vRows[1] /= scaling.y;
+ }
+ if(scaling.z)
+ {
+ vRows[2] /= scaling.z;
+ }
+
+ // build a 3x3 rotation matrix
+ aiMatrix3x3 m(vRows[0].x,vRows[1].x,vRows[2].x,
+ vRows[0].y,vRows[1].y,vRows[2].y,
+ vRows[0].z,vRows[1].z,vRows[2].z);
+
+ // and generate the rotation quaternion from it
+ rotation = aiQuaternion(m);
+}
+
+// ----------------------------------------------------------------------------------------
+inline void aiMatrix4x4::DecomposeNoScaling (aiQuaternion& rotation,
+ aiVector3D& position) const
+{
+ const aiMatrix4x4& _this = *this;
+
+ // extract translation
+ position.x = _this[0][3];
+ position.y = _this[1][3];
+ position.z = _this[2][3];
+
+ // extract rotation
+ rotation = aiQuaternion((aiMatrix3x3)_this);
+}
+
+// ----------------------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::FromEulerAnglesXYZ(const aiVector3D& blubb)
+{
+ return FromEulerAnglesXYZ(blubb.x,blubb.y,blubb.z);
+}
+
+// ----------------------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::FromEulerAnglesXYZ(float x, float y, float z)
+{
+ aiMatrix4x4& _this = *this;
+
+ float cr = cos( x );
+ float sr = sin( x );
+ float cp = cos( y );
+ float sp = sin( y );
+ float cy = cos( z );
+ float sy = sin( z );
+
+ _this.a1 = cp*cy ;
+ _this.a2 = cp*sy;
+ _this.a3 = -sp ;
+
+ float srsp = sr*sp;
+ float crsp = cr*sp;
+
+ _this.b1 = srsp*cy-cr*sy ;
+ _this.b2 = srsp*sy+cr*cy ;
+ _this.b3 = sr*cp ;
+
+ _this.c1 = crsp*cy+sr*sy ;
+ _this.c2 = crsp*sy-sr*cy ;
+ _this.c3 = cr*cp ;
+
+ return *this;
+}
+
+// ----------------------------------------------------------------------------------------
+inline bool aiMatrix4x4::IsIdentity() const
+{
+ // Use a small epsilon to solve floating-point inaccuracies
+ const static float 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);
+}
+
+// ----------------------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::RotationX(float a, aiMatrix4x4& out)
+{
+ /*
+ | 1 0 0 0 |
+ M = | 0 cos(A) -sin(A) 0 |
+ | 0 sin(A) cos(A) 0 |
+ | 0 0 0 1 | */
+ out = aiMatrix4x4();
+ out.b2 = out.c3 = cos(a);
+ out.b3 = -(out.c2 = sin(a));
+ return out;
+}
+
+// ----------------------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::RotationY(float a, aiMatrix4x4& out)
+{
+ /*
+ | cos(A) 0 sin(A) 0 |
+ M = | 0 1 0 0 |
+ | -sin(A) 0 cos(A) 0 |
+ | 0 0 0 1 |
+ */
+ out = aiMatrix4x4();
+ out.a1 = out.c3 = cos(a);
+ out.c1 = -(out.a3 = sin(a));
+ return out;
+}
+
+// ----------------------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::RotationZ(float a, aiMatrix4x4& out)
+{
+ /*
+ | cos(A) -sin(A) 0 0 |
+ M = | sin(A) cos(A) 0 0 |
+ | 0 0 1 0 |
+ | 0 0 0 1 | */
+ out = aiMatrix4x4();
+ out.a1 = out.b2 = cos(a);
+ out.a2 = -(out.b1 = sin(a));
+ return out;
+}
+
+// ----------------------------------------------------------------------------------------
+// Returns a rotation matrix for a rotation around an arbitrary axis.
+inline aiMatrix4x4& aiMatrix4x4::Rotation( float a, const aiVector3D& axis, aiMatrix4x4& out)
+{
+ float c = cos( a), s = sin( a), t = 1 - c;
+ float 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 = 0.0f;
+ out.d1 = out.d2 = out.d3 = 0.0f;
+ out.d4 = 1.0f;
+
+ return out;
+}
+
+// ----------------------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::Translation( const aiVector3D& v, aiMatrix4x4& out)
+{
+ out = aiMatrix4x4();
+ out.a4 = v.x;
+ out.b4 = v.y;
+ out.c4 = v.z;
+ return out;
+}
+
+// ----------------------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::Scaling( const aiVector3D& v, aiMatrix4x4& out)
+{
+ out = aiMatrix4x4();
+ 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 Mller, John Hughes
+ * "Efficiently Building a Matrix to Rotate One Vector to Another"
+ * Journal of Graphics Tools, 4(4):1-4, 1999
+ */
+// ----------------------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::FromToMatrix(const aiVector3D& from,
+ const aiVector3D& to, aiMatrix4x4& mtx)
+{
+ aiMatrix3x3 m3;
+ aiMatrix3x3::FromToMatrix(from,to,m3);
+ mtx = aiMatrix4x4(m3);
+ return mtx;
+}
+
+#endif // __cplusplus
+#endif // AI_MATRIX4x4_INL_INC
diff --git a/src/3rdparty/assimp/include/aiMesh.h b/src/3rdparty/assimp/include/aiMesh.h
new file mode 100644
index 000000000..6708a7d1b
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiMesh.h
@@ -0,0 +1,733 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiMesh.h
+ * @brief Declares the data structures in which the imported geometry is
+ returned by ASSIMP: aiMesh, aiFace and aiBone data structures.
+ */
+#ifndef INCLUDED_AI_MESH_H
+#define INCLUDED_AI_MESH_H
+
+#include "aiTypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------------
+// Limits. These values are required to match the settings Assimp was
+// compiled against. Therfore, 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 0x4
+#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 0x4
+#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()
+ {
+ mNumIndices = 0; mIndices = NULL;
+ }
+
+ //! Default destructor. Delete the index array
+ ~aiFace()
+ {
+ delete [] mIndices;
+ }
+
+ //! Copy constructor. Copy the index array
+ aiFace( const aiFace& o)
+ {
+ mIndices = NULL;
+ *this = o;
+ }
+
+ //! Assignment operator. Copy the index array
+ const aiFace& operator = ( const aiFace& o)
+ {
+ if (&o == this)
+ return *this;
+
+ delete[] mIndices;
+ mNumIndices = o.mNumIndices;
+ mIndices = new unsigned int[mNumIndices];
+ ::memcpy( mIndices, o.mIndices, mNumIndices * sizeof( unsigned int));
+ 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;
+ else if (mIndices && mNumIndices == o.mNumIndices)
+ {
+ for (unsigned int i = 0;i < this->mNumIndices;++i)
+ if (mIndices[i] != o.mIndices[i])return false;
+ return true;
+ }
+ return false;
+ }
+
+ //! 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() : mVertexId(0), mWeight(0.0f) { }
+
+ //! Initialisation 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)
+ { /* nothing to do here */ }
+
+#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.
+ */
+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 vertices affected by this bone
+ C_STRUCT aiVertexWeight* mWeights;
+
+ //! Matrix that transforms from mesh space to bone space in bind pose
+ C_STRUCT aiMatrix4x4 mOffsetMatrix;
+
+#ifdef __cplusplus
+
+ //! Default constructor
+ aiBone()
+ {
+ mNumWeights = 0; mWeights = NULL;
+ }
+
+ //! Copy constructor
+ aiBone(const aiBone& other)
+ {
+ mNumWeights = other.mNumWeights;
+ mOffsetMatrix = other.mOffsetMatrix;
+ mName = other.mName;
+
+ if (other.mWeights && other.mNumWeights)
+ {
+ mWeights = new aiVertexWeight[mNumWeights];
+ ::memcpy(mWeights,other.mWeights,mNumWeights * sizeof(aiVertexWeight));
+ }
+ }
+
+ //! 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 = 0x9fffffff
+#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 NOT CURRENTLY IN USE. 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
+{
+ /** 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;
+
+#ifdef __cplusplus
+
+ aiAnimMesh()
+ : mVertices()
+ , mNormals()
+ , mTangents()
+ , mBitangents()
+ {
+ // 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] = NULL;
+ }
+ for ( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) {
+ mColors[a] = NULL;
+ }
+ }
+
+ ~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 != NULL;
+ }
+
+ /** Check whether the anim mesh overrides the vertex normals
+ * of its host mesh*/
+ bool HasNormals() const {
+ return mNormals != NULL;
+ }
+
+ /** 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 != NULL;
+ }
+
+ /** 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] != NULL;
+ }
+
+ /** 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] != NULL;
+ }
+
+#endif
+};
+
+
+// ---------------------------------------------------------------------------
+/** @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 detailled discussion of qNaNs.
+ * @note If the mesh contains tangents, it automatically also
+ * contains bitangents (the bitangent is just the cross product of
+ * tangent and normal vectors).
+ */
+ 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 does use 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;
+
+
+ /** NOT CURRENTLY IN USE. The number of attachment meshes */
+ unsigned int mNumAnimMeshes;
+
+ /** NOT CURRENTLY IN USE. 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). */
+ C_STRUCT aiAnimMesh** mAnimMeshes;
+
+
+#ifdef __cplusplus
+
+ //! Default constructor. Initializes all members to 0
+ aiMesh()
+ {
+ mNumVertices = 0;
+ mNumFaces = 0;
+
+ mNumAnimMeshes = 0;
+
+ mPrimitiveTypes = 0;
+ mVertices = NULL; mFaces = NULL;
+ mNormals = NULL; mTangents = NULL;
+ mBitangents = NULL;
+ mAnimMeshes = NULL;
+
+ for ( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
+ {
+ mNumUVComponents[a] = 0;
+ mTextureCoords[a] = NULL;
+ }
+ for ( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
+ mColors[a] = NULL;
+ mNumBones = 0; mBones = NULL;
+ mMaterialIndex = 0;
+ }
+
+ //! 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 (such as #AI_SCENE_FLAGS_ANIM_SKELETON_ONLY),
+ //! this will always be true
+ bool HasPositions() const
+ { return mVertices != NULL && 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 != NULL && mNumFaces > 0; }
+
+ //! Check whether the mesh contains normal vectors
+ bool HasNormals() const
+ { return mNormals != NULL && 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 != NULL && mBitangents != NULL && 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] != NULL && 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] != NULL && 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
+ inline bool HasBones() const
+ { return mBones != NULL && mNumBones > 0; }
+
+#endif // __cplusplus
+};
+
+
+#ifdef __cplusplus
+}
+#endif //! extern "C"
+#endif // __AI_MESH_H_INC
+
diff --git a/src/3rdparty/assimp/include/aiPostProcess.h b/src/3rdparty/assimp/include/aiPostProcess.h
new file mode 100644
index 000000000..a6dc9e305
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiPostProcess.h
@@ -0,0 +1,604 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file aiPostProcess.h
+ * @brief Definitions for import post processing steps
+ */
+#ifndef AI_POSTPROCESS_H_INC
+#define AI_POSTPROCESS_H_INC
+
+#include "aiTypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// -----------------------------------------------------------------------------------
+/** @enum aiPostProcessSteps
+ * @brief Defines the flags for all possible post processing steps.
+ *
+ * @see Importer::ReadFile
+ * @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 a config setting,
+ * <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. Thanks.
+ */
+ aiProcess_CalcTangentSpace = 0x1,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Identifies and joins identical vertex data sets within all
+ * imported meshes.
+ *
+ * After this step is run each mesh does contain only unique vertices anymore,
+ * so a vertex is possibly 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
+ * for example OpenGL prefers. In this space, +X points to the right,
+ * +Z points towards the viewer and 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 all higher faces to 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
+ * configuration option, <tt>#AI_CONFIG_PP_RVC_FLAGS</tt>. This is quite useful
+ * if you don't need all parts of the output structure. Especially vertex
+ * colors are rarely used today ... . Calling this step to remove unrequired
+ * stuff from the pipeline as early as possible results in an increased
+ * performance and a better 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, it has per-face vertex colors. Vertex positions can't be
+ * shared, thus the #aiProcess_JoinIdenticalVertices step fails to
+ * optimize the data. Just because 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 optimzations.
+ */
+ aiProcess_RemoveComponent = 0x10,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Generates normals for all faces of all meshes.
+ *
+ * This is ignored if normals are already there at the time where 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 in
+ * other words, enforces 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 where this flag
+ * is evaluated. Model importers try to load them from the source file, so
+ * they're usually already there.
+ *
+ * This flag may (of course) not be specified together with
+ * #aiProcess_GenNormals. There's a configuration option,
+ * <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 a 'hard' seam between two faces.
+ * Using a decent angle here (e.g. 80) results in very good visual
+ * appearance.
+ */
+ aiProcess_GenSmoothNormals = 0x40,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Splits large meshes into smaller submeshes
+ *
+ * This is quite useful for realtime rendering where the number of triangles
+ * which can be maximally processed in a single draw-call is usually 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>
+ * settings. 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 not if there's
+ * nothing 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 does still contain nodes, however, there is only a
+ * root node with children, each one referencing only one mesh,
+ * 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 that 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
+ * aiConfig.h), but you can use the <tt>#AI_CONFIG_PP_LBW_MAX_WEIGHTS</tt> setting 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 for 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 to capture Assimp's log output if you use this flag,
+ * so you can easily find ot what's actually wrong if a file fails the
+ * validation. The validator is quite rude and will find *all*
+ * inconsistencies in the data structure ... plugin developers are
+ * recommended to 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. It's 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 for you. The <tt>#AI_CONFIG_PP_ICL_PTCACHE_SIZE</tt>config
+ * setting 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 always 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 ... the material name is
+ * one of them. 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> setting.
+ */
+ aiProcess_RemoveRedundantMaterials = 0x1000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>This step tries to determine which meshes have normal vectors
+ * that are facing inwards. 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 submeshes.
+ *
+ * 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> option 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 degenerated primitives and
+ * converts them to proper lines or points.
+ *
+ * A face is 'degenerated' if one or more of its points are identical.
+ * To have the degenerated stuff not only detected and collapsed but
+ * also removed, try one of the following procedures:
+ * <br><b>1.</b> (if you support lines&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> option to 1. This will
+ * cause the step to remove degenerated 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&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> option to
+ * @code aiPrimitiveType_POINTS | aiPrimitiveType_LINES
+ * @endcode to cause SortByPType to reject point
+ * and line meshes from the scene.
+ * </li>
+ * </ul>
+ * @note Degenerated 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 ... some exporters bypass the
+ * format specification and write them as degenerated triangle 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 tha 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
+ * father app compute the UV channels, at least 3ds max, maja, blender,
+ * lightwave, modo, ... are able to achieve this.
+ *
+ * @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 to stand-alone vtexture coordinate channelss.
+ *
+ * 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 new (pretransformed) UV channel
+ * which replace 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 realtime 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 duplicates
+ * with references to the first mesh.
+ *
+ * This step takes a while, don't use it if you have no time.
+ * Its main purpose is to workaround the limitation 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 currently not support per-node material
+ * assignment to meshes, which means that identical meshes with
+ * differnent 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.
+ *
+ * In fact, it will reduce the number of drawcalls.
+ *
+ * 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 with no 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> setting 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, complex hierarchies are usually completely lost. That's not
+ * the right choice 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.
+ * <br><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.
+ * <br><b>Output face order:</b>
+ * @code
+ * x2
+ *
+ * x0
+ * x1
+ * @endcode
+ */
+ aiProcess_FlipWindingOrder = 0x1000000
+
+ // aiProcess_GenEntityMeshes = 0x100000,
+ // aiProcess_OptimizeAnimations = 0x200000
+ // aiProcess_FixTexturePaths = 0x200000
+};
+
+
+// ---------------------------------------------------------------------------------------
+/** @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_TargetRealtimeUse_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 doc to the steps enabled by this preset.
+ * Some of them offer further configurable properties, 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 doc for the steps enabled by this preset.
+ * Some of them offer further configurable properties, 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 doc for the steps enabled by this preset.
+ * Some of them offer further configurable properties, 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/src/3rdparty/assimp/include/aiQuaternion.h b/src/3rdparty/assimp/include/aiQuaternion.h
new file mode 100644
index 000000000..790cd0602
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiQuaternion.h
@@ -0,0 +1,311 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file aiQuaternion.h
+ * @brief Quaternion structure, including operators when compiling in C++
+ */
+#ifndef AI_QUATERNION_H_INC
+#define AI_QUATERNION_H_INC
+
+#include <math.h>
+#include "aiTypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------------
+/** Represents a quaternion in a 4D vector. */
+struct aiQuaternion
+{
+#ifdef __cplusplus
+ aiQuaternion() : w(0.0f), x(0.0f), y(0.0f), z(0.0f) {}
+ aiQuaternion(float _w, float _x, float _y, float _z) : w(_w), x(_x), y(_y), z(_z) {}
+
+ /** Construct from rotation matrix. Result is undefined if the matrix is not orthonormal. */
+ aiQuaternion( const aiMatrix3x3& pRotMatrix);
+
+ /** Construct from euler angles */
+ aiQuaternion( float rotx, float roty, float rotz);
+
+ /** Construct from an axis-angle pair */
+ aiQuaternion( aiVector3D axis, float angle);
+
+ /** Construct from a normalized quaternion stored in a vec3 */
+ aiQuaternion( aiVector3D normalized);
+
+ /** Returns a matrix representation of the quaternion */
+ aiMatrix3x3 GetMatrix() const;
+
+
+ bool operator== (const aiQuaternion& o) const
+ {return x == o.x && y == o.y && z == o.z && w == o.w;}
+
+ bool operator!= (const aiQuaternion& o) const
+ {return !(*this == o);}
+
+ /** Normalize the quaternion */
+ aiQuaternion& Normalize();
+
+ /** Compute quaternion conjugate */
+ aiQuaternion& Conjugate ();
+
+ /** Rotate a point by this quaternion */
+ aiVector3D Rotate (const aiVector3D& in);
+
+ /** Multiply two quaternions */
+ aiQuaternion operator* (const aiQuaternion& two) const;
+
+ /** 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( aiQuaternion& pOut, const aiQuaternion& pStart, const aiQuaternion& pEnd, float pFactor);
+
+#endif // __cplusplus
+
+ //! w,x,y,z components of the quaternion
+ float w, x, y, z;
+} ;
+
+
+#ifdef __cplusplus
+
+// ---------------------------------------------------------------------------
+// Constructs a quaternion from a rotation matrix
+inline aiQuaternion::aiQuaternion( const aiMatrix3x3 &pRotMatrix)
+{
+ float t = 1 + pRotMatrix.a1 + pRotMatrix.b2 + pRotMatrix.c3;
+
+ // large enough
+ if ( t > 0.001f)
+ {
+ float s = sqrt( t) * 2.0f;
+ x = (pRotMatrix.c2 - pRotMatrix.b3) / s;
+ y = (pRotMatrix.a3 - pRotMatrix.c1) / s;
+ z = (pRotMatrix.b1 - pRotMatrix.a2) / s;
+ w = 0.25f * s;
+ } // else we have to check several cases
+ else if ( pRotMatrix.a1 > pRotMatrix.b2 && pRotMatrix.a1 > pRotMatrix.c3 )
+ {
+ // Column 0:
+ float s = sqrt( 1.0f + pRotMatrix.a1 - pRotMatrix.b2 - pRotMatrix.c3) * 2.0f;
+ x = 0.25f * 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:
+ float s = sqrt( 1.0f + pRotMatrix.b2 - pRotMatrix.a1 - pRotMatrix.c3) * 2.0f;
+ x = (pRotMatrix.b1 + pRotMatrix.a2) / s;
+ y = 0.25f * s;
+ z = (pRotMatrix.c2 + pRotMatrix.b3) / s;
+ w = (pRotMatrix.a3 - pRotMatrix.c1) / s;
+ } else
+ {
+ // Column 2:
+ float s = sqrt( 1.0f + pRotMatrix.c3 - pRotMatrix.a1 - pRotMatrix.b2) * 2.0f;
+ x = (pRotMatrix.a3 + pRotMatrix.c1) / s;
+ y = (pRotMatrix.c2 + pRotMatrix.b3) / s;
+ z = 0.25f * s;
+ w = (pRotMatrix.b1 - pRotMatrix.a2) / s;
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Construction from euler angles
+inline aiQuaternion::aiQuaternion( float fPitch, float fYaw, float fRoll )
+{
+ const float fSinPitch(sin(fPitch*0.5F));
+ const float fCosPitch(cos(fPitch*0.5F));
+ const float fSinYaw(sin(fYaw*0.5F));
+ const float fCosYaw(cos(fYaw*0.5F));
+ const float fSinRoll(sin(fRoll*0.5F));
+ const float fCosRoll(cos(fRoll*0.5F));
+ const float fCosPitchCosYaw(fCosPitch*fCosYaw);
+ const float 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
+inline aiMatrix3x3 aiQuaternion::GetMatrix() const
+{
+ aiMatrix3x3 resMatrix;
+ resMatrix.a1 = 1.0f - 2.0f * (y * y + z * z);
+ resMatrix.a2 = 2.0f * (x * y - z * w);
+ resMatrix.a3 = 2.0f * (x * z + y * w);
+ resMatrix.b1 = 2.0f * (x * y + z * w);
+ resMatrix.b2 = 1.0f - 2.0f * (x * x + z * z);
+ resMatrix.b3 = 2.0f * (y * z - x * w);
+ resMatrix.c1 = 2.0f * (x * z - y * w);
+ resMatrix.c2 = 2.0f * (y * z + x * w);
+ resMatrix.c3 = 1.0f - 2.0f * (x * x + y * y);
+
+ return resMatrix;
+}
+
+// ---------------------------------------------------------------------------
+// Construction from an axis-angle pair
+inline aiQuaternion::aiQuaternion( aiVector3D axis, float angle)
+{
+ axis.Normalize();
+
+ const float sin_a = sin( angle / 2 );
+ const float cos_a = 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
+inline aiQuaternion::aiQuaternion( aiVector3D normalized)
+{
+ x = normalized.x;
+ y = normalized.y;
+ z = normalized.z;
+
+ const float t = 1.0f - (x*x) - (y*y) - (z*z);
+
+ if (t < 0.0f)
+ w = 0.0f;
+ else w = 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!
+inline void aiQuaternion::Interpolate( aiQuaternion& pOut, const aiQuaternion& pStart, const aiQuaternion& pEnd, float pFactor)
+{
+ // calc cosine theta
+ float cosom = pStart.x * pEnd.x + pStart.y * pEnd.y + pStart.z * pEnd.z + pStart.w * pEnd.w;
+
+ // adjust signs (if necessary)
+ aiQuaternion end = pEnd;
+ if ( cosom < 0.0f)
+ {
+ cosom = -cosom;
+ end.x = -end.x; // Reverse all signs
+ end.y = -end.y;
+ end.z = -end.z;
+ end.w = -end.w;
+ }
+
+ // Calculate coefficients
+ float sclp, sclq;
+ if ( (1.0f - cosom) > 0.0001f) // 0.0001 -> some epsillon
+ {
+ // Standard case (slerp)
+ float omega, sinom;
+ omega = acos( cosom); // extract theta from dot product's cos theta
+ sinom = sin( omega);
+ sclp = sin( (1.0f - pFactor) * omega) / sinom;
+ sclq = sin( pFactor * omega) / sinom;
+ } else
+ {
+ // Very close, do linear interp (because it's faster)
+ sclp = 1.0f - 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;
+}
+
+// ---------------------------------------------------------------------------
+inline aiQuaternion& aiQuaternion::Normalize()
+{
+ // compute the magnitude and divide through it
+ const float mag = x*x+y*y+z*z+w*w;
+ if (mag)
+ {
+ x /= mag;
+ y /= mag;
+ z /= mag;
+ w /= mag;
+ }
+ return *this;
+}
+
+// ---------------------------------------------------------------------------
+inline aiQuaternion aiQuaternion::operator* (const aiQuaternion& t) const
+{
+ return aiQuaternion(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);
+}
+
+// ---------------------------------------------------------------------------
+inline aiQuaternion& aiQuaternion::Conjugate ()
+{
+ x = -x;
+ y = -y;
+ z = -z;
+ return *this;
+}
+
+// ---------------------------------------------------------------------------
+inline aiVector3D aiQuaternion::Rotate (const aiVector3D& v)
+{
+ aiQuaternion q2(0.f,v.x,v.y,v.z), q = *this, qinv = q;
+ q.Conjugate();
+
+ q = q*q2*qinv;
+ return aiVector3D(q.x,q.y,q.z);
+
+}
+
+} // end extern "C"
+
+#endif // __cplusplus
+
+#endif // AI_QUATERNION_H_INC
diff --git a/src/3rdparty/assimp/include/aiScene.h b/src/3rdparty/assimp/include/aiScene.h
new file mode 100644
index 000000000..6c4b49009
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiScene.h
@@ -0,0 +1,417 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiScene.h
+ * @brief Defines the data structures in which the imported scene is returned.
+ */
+#ifndef __AI_SCENE_H_INC__
+#define __AI_SCENE_H_INC__
+
+#include "aiTypes.h"
+#include "aiTexture.h"
+#include "aiMesh.h"
+#include "aiLight.h"
+#include "aiCamera.h"
+#include "aiMaterial.h"
+#include "aiAnim.h"
+
+#ifdef __cplusplus
+extern "C" {
+#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 aiNode
+{
+ /** The name of the node.
+ *
+ * The name might be empty (length of zero) but all nodes which
+ * need to be accessed afterwards by bones or anims are usually named.
+ * Multiple nodes may have the same name, but nodes which are accessed
+ * by bones (see #aiBone and #aiMesh::mBones) *must* be unique.
+ *
+ * Cameras and lights are assigned to a specific node name - if there
+ * are multiple nodes with this name, they're assigned to each of them.
+ * <br>
+ * There are no limitations regarding the characters contained in
+ * this text. You should be able to handle stuff like whitespace, tabs,
+ * linefeeds, quotation marks, ampersands, ... .
+ */
+ 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 */
+ unsigned int* mMeshes;
+
+#ifdef __cplusplus
+ /** Constructor */
+ aiNode()
+ {
+ // set all members to zero by default
+ mParent = NULL;
+ mNumChildren = 0; mChildren = NULL;
+ mNumMeshes = 0; mMeshes = NULL;
+ }
+
+ /** Construction from a specific name */
+ aiNode(const std::string& name)
+ {
+ // set all members to zero by default
+ mParent = NULL;
+ mNumChildren = 0; mChildren = NULL;
+ mNumMeshes = 0; mMeshes = NULL;
+ mName = name;
+ }
+
+ /** Destructor */
+ ~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;
+ }
+
+ /** 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 aiNode* FindNode(const aiString& name)
+ {
+ return FindNode(name.data);
+ }
+
+ /** @override
+ */
+ inline aiNode* FindNode(const char* name)
+ {
+ if (!::strcmp( mName.data,name))return this;
+ for (unsigned int i = 0; i < mNumChildren;++i)
+ {
+ aiNode* p = mChildren[i]->FindNode(name);
+ if (p)return p;
+ }
+ // there is definitely no sub node with this name
+ return NULL;
+ }
+
+#endif // __cplusplus
+};
+
+
+// -------------------------------------------------------------------------------
+/** @def AI_SCENE_FLAGS_INCOMPLETE
+ * 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
+
+/** @def AI_SCENE_FLAGS_VALIDATED
+ * 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
+
+/** @def AI_SCENE_FLAGS_VALIDATION_WARNING
+ * 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
+
+/** @def AI_SCENE_FLAGS_NON_VERBOSE_FORMAT
+ * 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
+
+ /** @def AI_SCENE_FLAGS_TERRAIN
+ * 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
+
+
+// -------------------------------------------------------------------------------
+/** 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;
+
+#ifdef __cplusplus
+
+ //! Default constructor
+ aiScene()
+ {
+ // set all members to zero by default
+ mRootNode = NULL;
+ mNumMeshes = 0; mMeshes = NULL;
+ mNumMaterials = 0; mMaterials = NULL;
+ mNumAnimations = 0; mAnimations = NULL;
+ mNumTextures = 0; mTextures = NULL;
+ mNumCameras = 0; mCameras = NULL;
+ mNumLights = 0; mLights = NULL;
+ mFlags = 0;
+ }
+
+ //! Destructor
+ ~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;
+ }
+
+ //! 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; }
+
+#endif // __cplusplus
+};
+
+#ifdef __cplusplus
+} //! namespace Assimp
+#endif
+
+#endif // __AI_SCENE_H_INC__
diff --git a/src/3rdparty/assimp/include/aiTexture.h b/src/3rdparty/assimp/include/aiTexture.h
new file mode 100644
index 000000000..e64f0205d
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiTexture.h
@@ -0,0 +1,198 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiTexture.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.
+ */
+
+#ifndef AI_TEXTURE_H_INC
+#define AI_TEXTURE_H_INC
+
+#include "aiTypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#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_) "*" # _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"
+
+// --------------------------------------------------------------------------------
+/** 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.
+ */
+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 compressed textures.
+ *
+ * If mHeight != 0 this member is undefined. Otherwise it
+ * 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[4];
+
+ /** 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;
+
+#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
+ {
+ ai_assert(s && !mHeight);
+ return (0 == ::strncmp(achFormatHint,s,3));
+ }
+
+ // Construction
+ aiTexture ()
+ : mWidth (0)
+ , mHeight (0)
+ , pcData (NULL)
+ {
+ 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/src/3rdparty/assimp/include/aiTypes.h b/src/3rdparty/assimp/include/aiTypes.h
new file mode 100644
index 000000000..f56cac074
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiTypes.h
@@ -0,0 +1,493 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiTypes.h
+ * Basic data types and primitives, such as vectors or colors.
+ */
+#ifndef AI_TYPES_H_INC
+#define AI_TYPES_H_INC
+
+// Some runtime headers
+#include <sys/types.h>
+#include <memory.h>
+#include <math.h>
+#include <stddef.h>
+#include <string.h>
+
+// Our compile configuration
+#include "aiDefines.h"
+
+// Some types moved to separate header due to size of operators
+#include "aiVector3D.h"
+#include "aiVector2D.h"
+#include "aiColor4D.h"
+#include "aiMatrix3x3.h"
+#include "aiMatrix4x4.h"
+#include "aiQuaternion.h"
+
+#ifdef __cplusplus
+#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
+const size_t MAXLEN = 1024;
+#else
+# define MAXLEN 1024
+#endif
+
+#include "./Compiler/pushpack1.h"
+
+// ----------------------------------------------------------------------------------
+/** Represents a plane in a three-dimensional, euclidean space
+*/
+struct aiPlane
+{
+#ifdef __cplusplus
+ aiPlane () : a(0.f), b(0.f), c(0.f), d(0.f) {}
+ aiPlane (float _a, float _b, float _c, float _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
+ float a,b,c,d;
+} PACK_STRUCT; // !struct aiPlane
+
+// ----------------------------------------------------------------------------------
+/** Represents a ray
+*/
+struct aiRay
+{
+#ifdef __cplusplus
+ aiRay () {}
+ 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;
+} PACK_STRUCT; // !struct aiRay
+
+// ----------------------------------------------------------------------------------
+/** Represents a color in Red-Green-Blue space.
+*/
+struct aiColor3D
+{
+#ifdef __cplusplus
+ aiColor3D () : r(0.0f), g(0.0f), b(0.0f) {}
+ aiColor3D (float _r, float _g, float _b) : r(_r), g(_g), b(_b) {}
+ aiColor3D (float _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 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*(float f) const {
+ return aiColor3D(r*f,g*f,b*f);
+ }
+
+ /** Access a specific color component */
+ float operator[](unsigned int i) const {
+ return *(&r + i);
+ }
+
+ /** Access a specific color component */
+ float& operator[](unsigned int i) {
+ return *(&r + i);
+ }
+
+ /** Check whether a color is black */
+ bool IsBlack() const {
+ static const float epsilon = 10e-3f;
+ return fabs( r ) < epsilon && fabs( g ) < epsilon && fabs( b ) < epsilon;
+ }
+
+#endif // !__cplusplus
+
+ //! Red, green and blue color values
+ float r, g, b;
+} PACK_STRUCT; // !struct aiColor3D
+#include "./Compiler/poppack1.h"
+
+// ----------------------------------------------------------------------------------
+/** 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 the ASCII characters, thus the character set needed to be 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() :
+ length(0)
+ {
+ data[0] = '\0';
+
+#ifdef _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 */
+ 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;
+ }
+
+ /** 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 == strcmp(this->data,other.data));
+ }
+
+ /** Inverse comparison operator */
+ bool operator!=(const aiString& other) const {
+ return (length != other.length || 0 != ::strcmp(this->data,other.data));
+ }
+
+ /** 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 _DEBUG
+ // Debug build: overwrite the string on its full length with ESC (27)
+ memset(data+1,27,MAXLEN-1);
+#endif
+ }
+
+#endif // !__cplusplus
+
+ /** Binary length of the string excluding the terminal 0. This is NOT the
+ * logical length of strings containing UTF-8 multibyte 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.
+ */
+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
+}; // !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
+}; // !enum aiOrigin
+
+// ----------------------------------------------------------------------------------
+/** @brief Enumerates predefined log streaming destinations.
+ * Logging to these streams can be enabled with a single call to
+ * #LogStream::createDefaultStream or #aiAttachPredefinedLogStream(),
+ * respectively.
+ */
+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
+}; // !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()
+ : 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 implementations
+#include "aiVector3D.inl"
+#include "aiColor4D.inl"
+#include "aiMatrix3x3.inl"
+#include "aiMatrix4x4.inl"
+#endif
diff --git a/src/3rdparty/assimp/include/aiVector2D.h b/src/3rdparty/assimp/include/aiVector2D.h
new file mode 100644
index 000000000..c9533aa72
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiVector2D.h
@@ -0,0 +1,187 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file aiVector2D.h
+ * @brief 2D vector structure, including operators when compiling in C++
+ */
+#ifndef AI_VECTOR2D_H_INC
+#define AI_VECTOR2D_H_INC
+
+#include <math.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "./Compiler/pushpack1.h"
+
+// ----------------------------------------------------------------------------------
+/** Represents a two-dimensional vector.
+ */
+struct aiVector2D
+{
+#ifdef __cplusplus
+ aiVector2D () : x(0.0f), y(0.0f) {}
+ aiVector2D (float _x, float _y) : x(_x), y(_y) {}
+ aiVector2D (float _xyz) : x(_xyz), y(_xyz) {}
+ aiVector2D (const aiVector2D& o) : x(o.x), y(o.y) {}
+
+ void Set( float pX, float pY) {
+ x = pX; y = pY;
+ }
+
+ float SquareLength() const {
+ return x*x + y*y;
+ }
+
+ float Length() const {
+ return ::sqrt( SquareLength());
+ }
+
+ aiVector2D& Normalize() {
+ *this /= Length(); return *this;
+ }
+
+ const aiVector2D& operator += (const aiVector2D& o) {
+ x += o.x; y += o.y; return *this;
+ }
+ const aiVector2D& operator -= (const aiVector2D& o) {
+ x -= o.x; y -= o.y; return *this;
+ }
+ const aiVector2D& operator *= (float f) {
+ x *= f; y *= f; return *this;
+ }
+ const aiVector2D& operator /= (float f) {
+ x /= f; y /= f; return *this;
+ }
+
+ float operator[](unsigned int i) const {
+ return *(&x + i);
+ }
+
+ float& operator[](unsigned int i) {
+ return *(&x + i);
+ }
+
+ bool operator== (const aiVector2D& other) const {
+ return x == other.x && y == other.y;
+ }
+
+ bool operator!= (const aiVector2D& other) const {
+ return x != other.x || y != other.y;
+ }
+
+ aiVector2D& operator= (float f) {
+ x = y = f;return *this;
+ }
+
+ const aiVector2D SymMul(const aiVector2D& o) {
+ return aiVector2D(x*o.x,y*o.y);
+ }
+
+#endif // __cplusplus
+
+ float x, y;
+} PACK_STRUCT;
+
+#include "./Compiler/poppack1.h"
+
+#ifdef __cplusplus
+} // end extern "C"
+
+// ----------------------------------------------------------------------------------
+// symmetric addition
+inline aiVector2D operator + (const aiVector2D& v1, const aiVector2D& v2)
+{
+ return aiVector2D( v1.x + v2.x, v1.y + v2.y);
+}
+
+// ----------------------------------------------------------------------------------
+// symmetric subtraction
+inline aiVector2D operator - (const aiVector2D& v1, const aiVector2D& v2)
+{
+ return aiVector2D( v1.x - v2.x, v1.y - v2.y);
+}
+
+// ----------------------------------------------------------------------------------
+// scalar product
+inline float operator * (const aiVector2D& v1, const aiVector2D& v2)
+{
+ return v1.x*v2.x + v1.y*v2.y;
+}
+
+// ----------------------------------------------------------------------------------
+// scalar multiplication
+inline aiVector2D operator * ( float f, const aiVector2D& v)
+{
+ return aiVector2D( f*v.x, f*v.y);
+}
+
+// ----------------------------------------------------------------------------------
+// and the other way around
+inline aiVector2D operator * ( const aiVector2D& v, float f)
+{
+ return aiVector2D( f*v.x, f*v.y);
+}
+
+// ----------------------------------------------------------------------------------
+// scalar division
+inline aiVector2D operator / ( const aiVector2D& v, float f)
+{
+
+ return v * (1/f);
+}
+
+// ----------------------------------------------------------------------------------
+// vector division
+inline aiVector2D operator / ( const aiVector2D& v, const aiVector2D& v2)
+{
+ return aiVector2D(v.x / v2.x,v.y / v2.y);
+}
+
+// ----------------------------------------------------------------------------------
+// vector inversion
+inline aiVector2D operator - ( const aiVector2D& v)
+{
+ return aiVector2D( -v.x, -v.y);
+}
+
+#endif // __cplusplus
+#endif // AI_VECTOR2D_H_INC
diff --git a/src/3rdparty/assimp/include/aiVector3D.h b/src/3rdparty/assimp/include/aiVector3D.h
new file mode 100644
index 000000000..3a6a6ff3b
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiVector3D.h
@@ -0,0 +1,129 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file aiVector3D.h
+ * @brief 3D vector structure, including operators when compiling in C++
+ */
+#ifndef AI_VECTOR3D_H_INC
+#define AI_VECTOR3D_H_INC
+
+#include <math.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "./Compiler/pushpack1.h"
+
+struct aiMatrix3x3;
+struct aiMatrix4x4;
+
+// ---------------------------------------------------------------------------
+/** Represents a three-dimensional vector. */
+struct aiVector3D
+{
+#ifdef __cplusplus
+ aiVector3D () : x(0.0f), y(0.0f), z(0.0f) {}
+ aiVector3D (float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
+ aiVector3D (float _xyz) : x(_xyz), y(_xyz), z(_xyz) {}
+ aiVector3D (const aiVector3D& o) : x(o.x), y(o.y), z(o.z) {}
+
+public:
+
+ // combined operators
+ const aiVector3D& operator += (const aiVector3D& o);
+ const aiVector3D& operator -= (const aiVector3D& o);
+ const aiVector3D& operator *= (float f);
+ const aiVector3D& operator /= (float f);
+
+ // transform vector by matrix
+ aiVector3D& operator *= (const aiMatrix3x3& mat);
+ aiVector3D& operator *= (const aiMatrix4x4& mat);
+
+ // access a single element
+ float operator[](unsigned int i) const;
+ float& operator[](unsigned int i);
+
+ // comparison
+ bool operator== (const aiVector3D& other) const;
+ bool operator!= (const aiVector3D& other) const;
+
+public:
+
+ /** @brief Set the components of a vector
+ * @param pX X component
+ * @param pY Y component
+ * @param pZ Z component */
+ void Set( float pX, float pY, float pZ = 0.f);
+
+ /** @brief Get the squared length of the vector
+ * @return Square length */
+ float SquareLength() const;
+
+
+ /** @brief Get the length of the vector
+ * @return length */
+ float Length() const;
+
+
+ /** @brief Normalize the vector */
+ aiVector3D& Normalize();
+
+
+ /** @brief Componentwise multiplication of two vectors
+ *
+ * Note that vec*vec yields the dot product.
+ * @param o Second factor */
+ const aiVector3D SymMul(const aiVector3D& o);
+
+#endif // __cplusplus
+
+ float x, y, z;
+} PACK_STRUCT;
+
+#include "./Compiler/poppack1.h"
+
+#ifdef __cplusplus
+} // end extern "C"
+
+
+#endif // __cplusplus
+
+#endif // AI_VECTOR3D_H_INC
diff --git a/src/3rdparty/assimp/include/aiVector3D.inl b/src/3rdparty/assimp/include/aiVector3D.inl
new file mode 100644
index 000000000..c14b347bc
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiVector3D.inl
@@ -0,0 +1,180 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiVector3D.inl
+ * @brief Inline implementation of aiVector3D operators
+ */
+#ifndef AI_VECTOR3D_INL_INC
+#define AI_VECTOR3D_INL_INC
+
+#include "aiVector3D.h"
+#ifdef __cplusplus
+
+// ------------------------------------------------------------------------------------------------
+/** Transformation of a vector by a 3x3 matrix */
+inline aiVector3D operator * (const aiMatrix3x3& pMatrix, const aiVector3D& pVector)
+{
+ aiVector3D 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 */
+inline aiVector3D operator * (const aiMatrix4x4& pMatrix, const aiVector3D& pVector)
+{
+ aiVector3D 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;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE void aiVector3D::Set( float pX, float pY, float pZ) {
+ x = pX; y = pY; z = pZ;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE float aiVector3D::SquareLength() const {
+ return x*x + y*y + z*z;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE float aiVector3D::Length() const {
+ return sqrt( SquareLength());
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiVector3D& aiVector3D::Normalize() {
+ *this /= Length(); return *this;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE const aiVector3D& aiVector3D::operator += (const aiVector3D& o) {
+ x += o.x; y += o.y; z += o.z; return *this;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE const aiVector3D& aiVector3D::operator -= (const aiVector3D& o) {
+ x -= o.x; y -= o.y; z -= o.z; return *this;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE const aiVector3D& aiVector3D::operator *= (float f) {
+ x *= f; y *= f; z *= f; return *this;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE const aiVector3D& aiVector3D::operator /= (float f) {
+ x /= f; y /= f; z /= f; return *this;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiVector3D& aiVector3D::operator *= (const aiMatrix3x3& mat){
+ return(*this = mat * (*this));
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE aiVector3D& aiVector3D::operator *= (const aiMatrix4x4& mat){
+ return(*this = mat * (*this));
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE float aiVector3D::operator[](unsigned int i) const {
+ return *(&x + i);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE float& aiVector3D::operator[](unsigned int i) {
+ return *(&x + i);
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE bool aiVector3D::operator== (const aiVector3D& other) const {
+ return x == other.x && y == other.y && z == other.z;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE bool aiVector3D::operator!= (const aiVector3D& other) const {
+ return x != other.x || y != other.y || z != other.z;
+}
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE const aiVector3D aiVector3D::SymMul(const aiVector3D& o) {
+ return aiVector3D(x*o.x,y*o.y,z*o.z);
+}
+// ------------------------------------------------------------------------------------------------
+// symmetric addition
+AI_FORCE_INLINE aiVector3D operator + (const aiVector3D& v1, const aiVector3D& v2) {
+ return aiVector3D( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
+}
+// ------------------------------------------------------------------------------------------------
+// symmetric subtraction
+AI_FORCE_INLINE aiVector3D operator - (const aiVector3D& v1, const aiVector3D& v2) {
+ return aiVector3D( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
+}
+// ------------------------------------------------------------------------------------------------
+// scalar product
+AI_FORCE_INLINE float operator * (const aiVector3D& v1, const aiVector3D& v2) {
+ return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
+}
+// ------------------------------------------------------------------------------------------------
+// scalar multiplication
+AI_FORCE_INLINE aiVector3D operator * ( float f, const aiVector3D& v) {
+ return aiVector3D( f*v.x, f*v.y, f*v.z);
+}
+// ------------------------------------------------------------------------------------------------
+// and the other way around
+AI_FORCE_INLINE aiVector3D operator * ( const aiVector3D& v, float f) {
+ return aiVector3D( f*v.x, f*v.y, f*v.z);
+}
+// ------------------------------------------------------------------------------------------------
+// scalar division
+AI_FORCE_INLINE aiVector3D operator / ( const aiVector3D& v, float f) {
+ return v * (1/f);
+}
+// ------------------------------------------------------------------------------------------------
+// vector division
+AI_FORCE_INLINE aiVector3D operator / ( const aiVector3D& v, const aiVector3D& v2) {
+ return aiVector3D(v.x / v2.x,v.y / v2.y,v.z / v2.z);
+}
+// ------------------------------------------------------------------------------------------------
+// cross product
+AI_FORCE_INLINE aiVector3D operator ^ ( const aiVector3D& v1, const aiVector3D& v2) {
+ return aiVector3D( 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 inversion
+AI_FORCE_INLINE aiVector3D operator - ( const aiVector3D& v) {
+ return aiVector3D( -v.x, -v.y, -v.z);
+}
+
+
+#endif // __cplusplus
+#endif // AI_VECTOR3D_INL_INC
diff --git a/src/3rdparty/assimp/include/aiVersion.h b/src/3rdparty/assimp/include/aiVersion.h
new file mode 100644
index 000000000..98e95b7dd
--- /dev/null
+++ b/src/3rdparty/assimp/include/aiVersion.h
@@ -0,0 +1,104 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiVersion.h
+ * @brief Functions to query the version of the Assimp runtime, check
+ * compile flags, ...
+ */
+#ifndef INCLUDED_AI_VERSION_H
+#define INCLUDED_AI_VERSION_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);
+
+
+//! 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 INCLUDED_AI_VERSION_H
diff --git a/src/3rdparty/assimp/include/assimp.h b/src/3rdparty/assimp/include/assimp.h
new file mode 100644
index 000000000..bc1a6cdd9
--- /dev/null
+++ b/src/3rdparty/assimp/include/assimp.h
@@ -0,0 +1,434 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file assimp.h
+ * @brief Defines the C-API to the Open Asset Import Library.
+ */
+#ifndef AI_ASSIMP_H_INC
+#define AI_ASSIMP_H_INC
+#include "aiTypes.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;
+};
+
+/** 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.
+ * @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);
+
+// --------------------------------------------------------------------------------
+/** 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 spreading their data across multiple files or even
+ * directories. Examples include OBJ or MD3, which outsource parts of their material
+ * stuff into external scripts. If you need the full functionality, provide a custom
+ * IOSystem to make Assimp find these files.
+ */
+ASSIMP_API const C_STRUCT aiScene* aiImportFileFromMemory(
+ const char* pBuffer,
+ unsigned int pLength,
+ unsigned int pFlags,
+ const char* pHint);
+
+// --------------------------------------------------------------------------------
+/** 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_ValidateDS 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 proepr 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 #aiAttachPredefinedLogStream. 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();
+
+// --------------------------------------------------------------------------------
+/** 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 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);
+
+// --------------------------------------------------------------------------------
+/** 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 szName Name of the configuration property to be set. All supported
+ * public properties are defined in the aiConfig.h header file (#AI_CONFIG_XXX).
+ * @param value New value for the property
+ */
+ASSIMP_API void aiSetImportPropertyInteger(
+ 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 szName Name of the configuration property to be set. All supported
+ * public properties are defined in the aiConfig.h header file (#AI_CONFIG_XXX).
+ * @param value New value for the property
+ */
+ASSIMP_API void aiSetImportPropertyFloat(
+ const char* szName,
+ float 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 szName Name of the configuration property to be set. All supported
+ * public properties are defined in the aiConfig.h header file (#AI_CONFIG_XXX).
+ * @param value New value for the property
+ */
+ASSIMP_API void aiSetImportPropertyString(
+ const char* szName,
+ const C_STRUCT aiString* st);
+
+// --------------------------------------------------------------------------------
+/** 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);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // AI_ASSIMP_H_INC
diff --git a/src/3rdparty/assimp/include/assimp.hpp b/src/3rdparty/assimp/include/assimp.hpp
new file mode 100644
index 000000000..945e7a8fd
--- /dev/null
+++ b/src/3rdparty/assimp/include/assimp.hpp
@@ -0,0 +1,609 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file assimp.hpp
+ * @brief Defines the C++-API to the Open Asset Import Library.
+ */
+#ifndef INCLUDED_AI_ASSIMP_HPP
+#define INCLUDED_AI_ASSIMP_HPP
+
+#ifndef __cplusplus
+# error This header requires C++ to be used. Use assimp.h for plain C.
+#endif
+
+// Public ASSIMP data structures
+#include "aiTypes.h"
+#include "aiConfig.h"
+#include "aiAssert.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;
+struct aiFileIO;
+extern "C" ASSIMP_API const aiScene* aiImportFileEx( const char*, unsigned int, aiFileIO*);
+extern "C" ASSIMP_API const aiScene* aiImportFileFromMemory( const char*,
+ unsigned int,unsigned int,const char*);
+
+/** @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 {
+
+ // for internal use
+ friend class BaseProcess;
+ friend class BatchLoader;
+ friend const aiScene* ::aiImportFileEx( const char*, unsigned int, aiFileIO*);
+ friend const aiScene* ::aiImportFileFromMemory( const char*,
+ unsigned int,unsigned int,const char*);
+
+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);
+
+ // -------------------------------------------------------------------
+ /** 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
+ * @param bWasExisting Optional pointer to receive 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.
+ */
+ void SetPropertyInteger(const char* szName, int iValue,
+ bool* bWasExisting = NULL);
+
+ // -------------------------------------------------------------------
+ /** 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()
+ */
+ void SetPropertyBool(const char* szName, bool value, bool* /* bWasExisting */ = NULL) {
+ SetPropertyInteger(szName,value);
+ }
+
+ // -------------------------------------------------------------------
+ /** Set a floating-point configuration property.
+ * @see SetPropertyInteger()
+ */
+ void SetPropertyFloat(const char* szName, float fValue,
+ bool* bWasExisting = NULL);
+
+ // -------------------------------------------------------------------
+ /** Set a string configuration property.
+ * @see SetPropertyInteger()
+ */
+ void SetPropertyString(const char* szName, const std::string& sValue,
+ bool* bWasExisting = NULL);
+
+ // -------------------------------------------------------------------
+ /** 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()
+ */
+ float GetPropertyFloat(const char* szName,
+ float 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;
+
+ // -------------------------------------------------------------------
+ /** Supplies a custom IO handler to the importer to use to open and
+ * access files. If you need the importer to use custion 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 custion 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 a #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 postprocessing 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 spreading their
+ * data across multiple files or even directories. Examples include
+ * OBJ or MD3, which outsource parts of their material stuff into
+ * external scripts. If you need the full functionality, provide
+ * a custom IOSystem to make Assimp find these files.
+ */
+ 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);
+
+ // -------------------------------------------------------------------
+ /** @brief Reads the given file and returns its contents if successful.
+ *
+ * This function is provided for backward compatibility.
+ * See the const char* version for detailled 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 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;
+
+
+ // -------------------------------------------------------------------
+ /** Find the loader 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 recgnized (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 there is no loader for the extension.*/
+ BaseImporter* FindLoader (const char* szExtension) 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 when 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 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);
+
+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 // INCLUDED_AI_ASSIMP_HPP
diff --git a/src/3rdparty/assimp/revision.h b/src/3rdparty/assimp/revision.h
new file mode 100644
index 000000000..d24da62ff
--- /dev/null
+++ b/src/3rdparty/assimp/revision.h
@@ -0,0 +1 @@
+#define SVNRevision 863
diff --git a/src/core/core.pro b/src/core/core.pro
index dfa1d9824..4b1d05a51 100644
--- a/src/core/core.pro
+++ b/src/core/core.pro
@@ -20,6 +20,7 @@ gcov {
win32-g++*:QMAKE_CXXFLAGS_CXX11 = -std=gnu++0x
include(../3rdparty/threadweaver/src/threadweaver.pri)
+include(../3rdparty/assimp/assimp.pri)
include(core.pri)
include(render/render.pri)
PUBLIC_HEADERS = $$HEADERS