From 243f400ee201aa59f23ec073983b8557d641d01a Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Mon, 29 Jul 2019 23:54:00 +0100 Subject: Updated assimp to commit 1d565b0 with iFire Signed-off-by: RevoluPowered Signed-off-by: K. S. Ernest (iFIre) Lee --- thirdparty/assimp/assimp/config.h | 16 + thirdparty/assimp/code/BaseImporter.cpp | 616 ---- thirdparty/assimp/code/BaseProcess.cpp | 107 - thirdparty/assimp/code/BaseProcess.h | 290 -- thirdparty/assimp/code/Bitmap.cpp | 155 - thirdparty/assimp/code/CApi/AssimpCExport.cpp | 156 + .../assimp/code/CApi/CInterfaceIOWrapper.cpp | 136 + thirdparty/assimp/code/CApi/CInterfaceIOWrapper.h | 99 + thirdparty/assimp/code/CInterfaceIOWrapper.cpp | 136 - thirdparty/assimp/code/CInterfaceIOWrapper.h | 99 - thirdparty/assimp/code/CalcTangentsProcess.cpp | 319 -- thirdparty/assimp/code/CalcTangentsProcess.h | 117 - thirdparty/assimp/code/Common/Assimp.cpp | 695 ++++ thirdparty/assimp/code/Common/BaseImporter.cpp | 620 ++++ thirdparty/assimp/code/Common/BaseProcess.cpp | 107 + thirdparty/assimp/code/Common/BaseProcess.h | 290 ++ thirdparty/assimp/code/Common/Bitmap.cpp | 155 + thirdparty/assimp/code/Common/CreateAnimMesh.cpp | 88 + thirdparty/assimp/code/Common/DefaultIOStream.cpp | 154 + thirdparty/assimp/code/Common/DefaultIOSystem.cpp | 257 ++ thirdparty/assimp/code/Common/DefaultLogger.cpp | 418 +++ .../assimp/code/Common/DefaultProgressHandler.h | 65 + thirdparty/assimp/code/Common/Exporter.cpp | 654 ++++ thirdparty/assimp/code/Common/FileLogStream.h | 107 + thirdparty/assimp/code/Common/FileSystemFilter.h | 345 ++ thirdparty/assimp/code/Common/IFF.h | 102 + thirdparty/assimp/code/Common/Importer.cpp | 1174 +++++++ thirdparty/assimp/code/Common/Importer.h | 247 ++ thirdparty/assimp/code/Common/ImporterRegistry.cpp | 371 ++ thirdparty/assimp/code/Common/PolyTools.h | 229 ++ thirdparty/assimp/code/Common/PostStepRegistry.cpp | 258 ++ thirdparty/assimp/code/Common/RemoveComments.cpp | 113 + thirdparty/assimp/code/Common/SGSpatialSort.cpp | 168 + thirdparty/assimp/code/Common/SceneCombiner.cpp | 1300 +++++++ .../assimp/code/Common/ScenePreprocessor.cpp | 261 ++ thirdparty/assimp/code/Common/ScenePreprocessor.h | 125 + thirdparty/assimp/code/Common/ScenePrivate.h | 105 + .../assimp/code/Common/SkeletonMeshBuilder.cpp | 270 ++ thirdparty/assimp/code/Common/SpatialSort.cpp | 342 ++ .../assimp/code/Common/SplitByBoneCountProcess.cpp | 407 +++ .../assimp/code/Common/SplitByBoneCountProcess.h | 111 + thirdparty/assimp/code/Common/StandardShapes.cpp | 507 +++ .../assimp/code/Common/StdOStreamLogStream.h | 101 + thirdparty/assimp/code/Common/Subdivision.cpp | 589 ++++ thirdparty/assimp/code/Common/TargetAnimation.cpp | 248 ++ thirdparty/assimp/code/Common/TargetAnimation.h | 183 + thirdparty/assimp/code/Common/Version.cpp | 185 + .../assimp/code/Common/VertexTriangleAdjacency.cpp | 134 + .../assimp/code/Common/VertexTriangleAdjacency.h | 117 + .../assimp/code/Common/Win32DebugLogStream.h | 95 + thirdparty/assimp/code/Common/assbin_chunks.h | 196 ++ thirdparty/assimp/code/Common/scene.cpp | 140 + thirdparty/assimp/code/Common/simd.cpp | 79 + thirdparty/assimp/code/Common/simd.h | 53 + thirdparty/assimp/code/ComputeUVMappingProcess.cpp | 506 --- thirdparty/assimp/code/ComputeUVMappingProcess.h | 148 - thirdparty/assimp/code/ConvertToLHProcess.cpp | 414 --- thirdparty/assimp/code/ConvertToLHProcess.h | 170 - thirdparty/assimp/code/CreateAnimMesh.cpp | 92 - thirdparty/assimp/code/DeboneProcess.cpp | 465 --- thirdparty/assimp/code/DeboneProcess.h | 134 - thirdparty/assimp/code/DefaultIOStream.cpp | 154 - thirdparty/assimp/code/DefaultIOSystem.cpp | 257 -- thirdparty/assimp/code/DefaultLogger.cpp | 418 --- thirdparty/assimp/code/DefaultProgressHandler.h | 65 - thirdparty/assimp/code/DropFaceNormalsProcess.cpp | 109 - thirdparty/assimp/code/DropFaceNormalsProcess.h | 86 - thirdparty/assimp/code/EmbedTexturesProcess.cpp | 152 - thirdparty/assimp/code/EmbedTexturesProcess.h | 85 - thirdparty/assimp/code/Exporter.cpp | 648 ---- thirdparty/assimp/code/FBX/FBXAnimation.cpp | 305 ++ thirdparty/assimp/code/FBX/FBXBinaryTokenizer.cpp | 466 +++ thirdparty/assimp/code/FBX/FBXCommon.h | 86 + thirdparty/assimp/code/FBX/FBXCompileConfig.h | 70 + thirdparty/assimp/code/FBX/FBXConverter.cpp | 3636 ++++++++++++++++++++ thirdparty/assimp/code/FBX/FBXConverter.h | 482 +++ thirdparty/assimp/code/FBX/FBXDeformer.cpp | 213 ++ thirdparty/assimp/code/FBX/FBXDocument.cpp | 726 ++++ thirdparty/assimp/code/FBX/FBXDocument.h | 1180 +++++++ thirdparty/assimp/code/FBX/FBXDocumentUtil.cpp | 135 + thirdparty/assimp/code/FBX/FBXDocumentUtil.h | 120 + thirdparty/assimp/code/FBX/FBXExportNode.cpp | 571 +++ thirdparty/assimp/code/FBX/FBXExportNode.h | 271 ++ thirdparty/assimp/code/FBX/FBXExportProperty.cpp | 389 +++ thirdparty/assimp/code/FBX/FBXExportProperty.h | 129 + thirdparty/assimp/code/FBX/FBXExporter.cpp | 2543 ++++++++++++++ thirdparty/assimp/code/FBX/FBXExporter.h | 178 + thirdparty/assimp/code/FBX/FBXImportSettings.h | 164 + thirdparty/assimp/code/FBX/FBXImporter.cpp | 203 ++ thirdparty/assimp/code/FBX/FBXImporter.h | 100 + thirdparty/assimp/code/FBX/FBXMaterial.cpp | 405 +++ thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp | 712 ++++ thirdparty/assimp/code/FBX/FBXMeshGeometry.h | 235 ++ thirdparty/assimp/code/FBX/FBXModel.cpp | 153 + thirdparty/assimp/code/FBX/FBXNodeAttribute.cpp | 170 + thirdparty/assimp/code/FBX/FBXParser.cpp | 1309 +++++++ thirdparty/assimp/code/FBX/FBXParser.h | 235 ++ thirdparty/assimp/code/FBX/FBXProperties.cpp | 235 ++ thirdparty/assimp/code/FBX/FBXProperties.h | 185 + thirdparty/assimp/code/FBX/FBXTokenizer.cpp | 248 ++ thirdparty/assimp/code/FBX/FBXTokenizer.h | 187 + thirdparty/assimp/code/FBX/FBXUtil.cpp | 243 ++ thirdparty/assimp/code/FBX/FBXUtil.h | 137 + thirdparty/assimp/code/FBXAnimation.cpp | 305 -- thirdparty/assimp/code/FBXBinaryTokenizer.cpp | 466 --- thirdparty/assimp/code/FBXCommon.h | 86 - thirdparty/assimp/code/FBXCompileConfig.h | 70 - thirdparty/assimp/code/FBXConverter.cpp | 3536 ------------------- thirdparty/assimp/code/FBXConverter.h | 461 --- thirdparty/assimp/code/FBXDeformer.cpp | 213 -- thirdparty/assimp/code/FBXDocument.cpp | 726 ---- thirdparty/assimp/code/FBXDocument.h | 1180 ------- thirdparty/assimp/code/FBXDocumentUtil.cpp | 135 - thirdparty/assimp/code/FBXDocumentUtil.h | 120 - thirdparty/assimp/code/FBXExportNode.cpp | 568 --- thirdparty/assimp/code/FBXExportNode.h | 271 -- thirdparty/assimp/code/FBXExportProperty.cpp | 364 -- thirdparty/assimp/code/FBXExportProperty.h | 129 - thirdparty/assimp/code/FBXExporter.cpp | 2480 ------------- thirdparty/assimp/code/FBXExporter.h | 178 - thirdparty/assimp/code/FBXImportSettings.h | 153 - thirdparty/assimp/code/FBXImporter.cpp | 197 -- thirdparty/assimp/code/FBXImporter.h | 100 - thirdparty/assimp/code/FBXMaterial.cpp | 381 -- thirdparty/assimp/code/FBXMeshGeometry.cpp | 711 ---- thirdparty/assimp/code/FBXMeshGeometry.h | 235 -- thirdparty/assimp/code/FBXModel.cpp | 153 - thirdparty/assimp/code/FBXNodeAttribute.cpp | 170 - thirdparty/assimp/code/FBXParser.cpp | 1313 ------- thirdparty/assimp/code/FBXParser.h | 235 -- thirdparty/assimp/code/FBXProperties.cpp | 235 -- thirdparty/assimp/code/FBXProperties.h | 185 - thirdparty/assimp/code/FBXTokenizer.cpp | 248 -- thirdparty/assimp/code/FBXTokenizer.h | 187 - thirdparty/assimp/code/FBXUtil.cpp | 164 - thirdparty/assimp/code/FBXUtil.h | 120 - thirdparty/assimp/code/FIReader.cpp | 1834 ---------- thirdparty/assimp/code/FileLogStream.h | 107 - thirdparty/assimp/code/FileSystemFilter.h | 345 -- thirdparty/assimp/code/FindDegenerates.cpp | 300 -- thirdparty/assimp/code/FindDegenerates.h | 129 - thirdparty/assimp/code/FindInstancesProcess.cpp | 277 -- thirdparty/assimp/code/FindInstancesProcess.h | 137 - thirdparty/assimp/code/FindInvalidDataProcess.cpp | 424 --- thirdparty/assimp/code/FindInvalidDataProcess.h | 105 - thirdparty/assimp/code/FixNormalsStep.cpp | 184 - thirdparty/assimp/code/FixNormalsStep.h | 91 - thirdparty/assimp/code/GenFaceNormalsProcess.cpp | 146 - thirdparty/assimp/code/GenFaceNormalsProcess.h | 87 - thirdparty/assimp/code/GenVertexNormalsProcess.cpp | 239 -- thirdparty/assimp/code/GenVertexNormalsProcess.h | 115 - thirdparty/assimp/code/Importer.cpp | 1171 ------- thirdparty/assimp/code/Importer.h | 247 -- thirdparty/assimp/code/ImporterRegistry.cpp | 371 -- thirdparty/assimp/code/ImproveCacheLocality.cpp | 386 --- thirdparty/assimp/code/ImproveCacheLocality.h | 100 - thirdparty/assimp/code/JoinVerticesProcess.cpp | 463 --- thirdparty/assimp/code/JoinVerticesProcess.h | 99 - thirdparty/assimp/code/LimitBoneWeightsProcess.cpp | 201 -- thirdparty/assimp/code/LimitBoneWeightsProcess.h | 148 - thirdparty/assimp/code/MMD/MMDCpp14.h | 83 + thirdparty/assimp/code/MMD/MMDImporter.cpp | 372 ++ thirdparty/assimp/code/MMD/MMDImporter.h | 96 + thirdparty/assimp/code/MMD/MMDPmdParser.h | 597 ++++ thirdparty/assimp/code/MMD/MMDPmxParser.cpp | 608 ++++ thirdparty/assimp/code/MMD/MMDPmxParser.h | 782 +++++ thirdparty/assimp/code/MMD/MMDVmdParser.h | 376 ++ thirdparty/assimp/code/MMDCpp14.h | 83 - thirdparty/assimp/code/MMDImporter.cpp | 370 -- thirdparty/assimp/code/MMDImporter.h | 96 - thirdparty/assimp/code/MMDPmdParser.h | 597 ---- thirdparty/assimp/code/MMDPmxParser.cpp | 604 ---- thirdparty/assimp/code/MMDPmxParser.h | 782 ----- thirdparty/assimp/code/MMDVmdParser.h | 376 -- thirdparty/assimp/code/MakeVerboseFormat.cpp | 226 -- thirdparty/assimp/code/MakeVerboseFormat.h | 105 - thirdparty/assimp/code/Material/MaterialSystem.cpp | 649 ++++ thirdparty/assimp/code/Material/MaterialSystem.h | 72 + thirdparty/assimp/code/MaterialSystem.cpp | 647 ---- thirdparty/assimp/code/MaterialSystem.h | 72 - thirdparty/assimp/code/OptimizeGraph.cpp | 353 -- thirdparty/assimp/code/OptimizeGraph.h | 145 - thirdparty/assimp/code/OptimizeMeshes.cpp | 256 -- thirdparty/assimp/code/OptimizeMeshes.h | 186 - thirdparty/assimp/code/PolyTools.h | 229 -- .../code/PostProcessing/CalcTangentsProcess.cpp | 319 ++ .../code/PostProcessing/CalcTangentsProcess.h | 117 + .../PostProcessing/ComputeUVMappingProcess.cpp | 506 +++ .../code/PostProcessing/ComputeUVMappingProcess.h | 149 + .../code/PostProcessing/ConvertToLHProcess.cpp | 414 +++ .../code/PostProcessing/ConvertToLHProcess.h | 171 + .../assimp/code/PostProcessing/DeboneProcess.cpp | 465 +++ .../assimp/code/PostProcessing/DeboneProcess.h | 131 + .../code/PostProcessing/DropFaceNormalsProcess.cpp | 109 + .../code/PostProcessing/DropFaceNormalsProcess.h | 83 + .../code/PostProcessing/EmbedTexturesProcess.cpp | 152 + .../code/PostProcessing/EmbedTexturesProcess.h | 85 + .../assimp/code/PostProcessing/FindDegenerates.cpp | 301 ++ .../assimp/code/PostProcessing/FindDegenerates.h | 130 + .../code/PostProcessing/FindInstancesProcess.cpp | 277 ++ .../code/PostProcessing/FindInstancesProcess.h | 137 + .../code/PostProcessing/FindInvalidDataProcess.cpp | 424 +++ .../code/PostProcessing/FindInvalidDataProcess.h | 106 + .../assimp/code/PostProcessing/FixNormalsStep.cpp | 184 + .../assimp/code/PostProcessing/FixNormalsStep.h | 91 + .../PostProcessing/GenBoundingBoxesProcess.cpp | 115 + .../code/PostProcessing/GenBoundingBoxesProcess.h | 76 + .../code/PostProcessing/GenFaceNormalsProcess.cpp | 146 + .../code/PostProcessing/GenFaceNormalsProcess.h | 87 + .../PostProcessing/GenVertexNormalsProcess.cpp | 239 ++ .../code/PostProcessing/GenVertexNormalsProcess.h | 111 + .../code/PostProcessing/ImproveCacheLocality.cpp | 379 ++ .../code/PostProcessing/ImproveCacheLocality.h | 101 + .../code/PostProcessing/JoinVerticesProcess.cpp | 463 +++ .../code/PostProcessing/JoinVerticesProcess.h | 95 + .../PostProcessing/LimitBoneWeightsProcess.cpp | 201 ++ .../code/PostProcessing/LimitBoneWeightsProcess.h | 138 + .../code/PostProcessing/MakeVerboseFormat.cpp | 226 ++ .../assimp/code/PostProcessing/MakeVerboseFormat.h | 106 + .../assimp/code/PostProcessing/OptimizeGraph.cpp | 351 ++ .../assimp/code/PostProcessing/OptimizeGraph.h | 140 + .../assimp/code/PostProcessing/OptimizeMeshes.cpp | 256 ++ .../assimp/code/PostProcessing/OptimizeMeshes.h | 186 + .../code/PostProcessing/PretransformVertices.cpp | 728 ++++ .../code/PostProcessing/PretransformVertices.h | 166 + .../assimp/code/PostProcessing/ProcessHelper.cpp | 443 +++ .../assimp/code/PostProcessing/ProcessHelper.h | 386 +++ .../PostProcessing/RemoveRedundantMaterials.cpp | 221 ++ .../code/PostProcessing/RemoveRedundantMaterials.h | 103 + .../assimp/code/PostProcessing/RemoveVCProcess.cpp | 337 ++ .../assimp/code/PostProcessing/RemoveVCProcess.h | 124 + .../assimp/code/PostProcessing/ScaleProcess.cpp | 103 + .../assimp/code/PostProcessing/ScaleProcess.h | 88 + .../code/PostProcessing/SortByPTypeProcess.cpp | 403 +++ .../code/PostProcessing/SortByPTypeProcess.h | 82 + .../code/PostProcessing/SplitLargeMeshes.cpp | 623 ++++ .../assimp/code/PostProcessing/SplitLargeMeshes.h | 209 ++ .../code/PostProcessing/TextureTransform.cpp | 566 +++ .../assimp/code/PostProcessing/TextureTransform.h | 232 ++ .../code/PostProcessing/TriangulateProcess.cpp | 530 +++ .../code/PostProcessing/TriangulateProcess.h | 91 + .../code/PostProcessing/ValidateDataStructure.cpp | 979 ++++++ .../code/PostProcessing/ValidateDataStructure.h | 189 + thirdparty/assimp/code/PostStepRegistry.cpp | 251 -- thirdparty/assimp/code/PretransformVertices.cpp | 728 ---- thirdparty/assimp/code/PretransformVertices.h | 163 - thirdparty/assimp/code/ProcessHelper.cpp | 443 --- thirdparty/assimp/code/ProcessHelper.h | 386 --- thirdparty/assimp/code/RawLoader.cpp | 331 -- thirdparty/assimp/code/RemoveComments.cpp | 113 - .../assimp/code/RemoveRedundantMaterials.cpp | 221 -- thirdparty/assimp/code/RemoveRedundantMaterials.h | 107 - thirdparty/assimp/code/RemoveVCProcess.cpp | 337 -- thirdparty/assimp/code/RemoveVCProcess.h | 123 - thirdparty/assimp/code/SGSpatialSort.cpp | 168 - thirdparty/assimp/code/ScaleProcess.cpp | 103 - thirdparty/assimp/code/ScaleProcess.h | 88 - thirdparty/assimp/code/SceneCombiner.cpp | 1300 ------- thirdparty/assimp/code/ScenePreprocessor.cpp | 261 -- thirdparty/assimp/code/ScenePreprocessor.h | 125 - thirdparty/assimp/code/ScenePrivate.h | 105 - thirdparty/assimp/code/SkeletonMeshBuilder.cpp | 270 -- thirdparty/assimp/code/SortByPTypeProcess.cpp | 403 --- thirdparty/assimp/code/SortByPTypeProcess.h | 85 - thirdparty/assimp/code/SpatialSort.cpp | 342 -- thirdparty/assimp/code/SplitByBoneCountProcess.cpp | 407 --- thirdparty/assimp/code/SplitByBoneCountProcess.h | 111 - thirdparty/assimp/code/SplitLargeMeshes.cpp | 623 ---- thirdparty/assimp/code/SplitLargeMeshes.h | 210 -- thirdparty/assimp/code/StandardShapes.cpp | 507 --- thirdparty/assimp/code/StdOStreamLogStream.h | 101 - thirdparty/assimp/code/Subdivision.cpp | 588 ---- thirdparty/assimp/code/TargetAnimation.cpp | 248 -- thirdparty/assimp/code/TargetAnimation.h | 183 - thirdparty/assimp/code/TextureTransform.cpp | 566 --- thirdparty/assimp/code/TextureTransform.h | 232 -- thirdparty/assimp/code/TriangulateProcess.cpp | 530 --- thirdparty/assimp/code/TriangulateProcess.h | 95 - thirdparty/assimp/code/ValidateDataStructure.cpp | 986 ------ thirdparty/assimp/code/ValidateDataStructure.h | 188 - thirdparty/assimp/code/Version.cpp | 185 - thirdparty/assimp/code/VertexTriangleAdjacency.cpp | 134 - thirdparty/assimp/code/VertexTriangleAdjacency.h | 117 - thirdparty/assimp/code/Win32DebugLogStream.h | 95 - thirdparty/assimp/code/scene.cpp | 140 - thirdparty/assimp/code/simd.cpp | 79 - thirdparty/assimp/code/simd.h | 53 - thirdparty/assimp/include/assimp/Exporter.hpp | 2 +- thirdparty/assimp/include/assimp/ParsingUtils.h | 3 +- thirdparty/assimp/include/assimp/aabb.h | 76 + thirdparty/assimp/include/assimp/camera.h | 5 +- thirdparty/assimp/include/assimp/color4.inl | 4 + thirdparty/assimp/include/assimp/config.h.in | 35 +- thirdparty/assimp/include/assimp/defs.h | 21 +- thirdparty/assimp/include/assimp/mesh.h | 11 +- thirdparty/assimp/include/assimp/postprocess.h | 13 +- thirdparty/assimp/include/assimp/scene.h | 8 + 297 files changed, 47148 insertions(+), 47509 deletions(-) delete mode 100644 thirdparty/assimp/code/BaseImporter.cpp delete mode 100644 thirdparty/assimp/code/BaseProcess.cpp delete mode 100644 thirdparty/assimp/code/BaseProcess.h delete mode 100644 thirdparty/assimp/code/Bitmap.cpp create mode 100644 thirdparty/assimp/code/CApi/AssimpCExport.cpp create mode 100644 thirdparty/assimp/code/CApi/CInterfaceIOWrapper.cpp create mode 100644 thirdparty/assimp/code/CApi/CInterfaceIOWrapper.h delete mode 100644 thirdparty/assimp/code/CInterfaceIOWrapper.cpp delete mode 100644 thirdparty/assimp/code/CInterfaceIOWrapper.h delete mode 100644 thirdparty/assimp/code/CalcTangentsProcess.cpp delete mode 100644 thirdparty/assimp/code/CalcTangentsProcess.h create mode 100644 thirdparty/assimp/code/Common/Assimp.cpp create mode 100644 thirdparty/assimp/code/Common/BaseImporter.cpp create mode 100644 thirdparty/assimp/code/Common/BaseProcess.cpp create mode 100644 thirdparty/assimp/code/Common/BaseProcess.h create mode 100644 thirdparty/assimp/code/Common/Bitmap.cpp create mode 100644 thirdparty/assimp/code/Common/CreateAnimMesh.cpp create mode 100644 thirdparty/assimp/code/Common/DefaultIOStream.cpp create mode 100644 thirdparty/assimp/code/Common/DefaultIOSystem.cpp create mode 100644 thirdparty/assimp/code/Common/DefaultLogger.cpp create mode 100644 thirdparty/assimp/code/Common/DefaultProgressHandler.h create mode 100644 thirdparty/assimp/code/Common/Exporter.cpp create mode 100644 thirdparty/assimp/code/Common/FileLogStream.h create mode 100644 thirdparty/assimp/code/Common/FileSystemFilter.h create mode 100644 thirdparty/assimp/code/Common/IFF.h create mode 100644 thirdparty/assimp/code/Common/Importer.cpp create mode 100644 thirdparty/assimp/code/Common/Importer.h create mode 100644 thirdparty/assimp/code/Common/ImporterRegistry.cpp create mode 100644 thirdparty/assimp/code/Common/PolyTools.h create mode 100644 thirdparty/assimp/code/Common/PostStepRegistry.cpp create mode 100644 thirdparty/assimp/code/Common/RemoveComments.cpp create mode 100644 thirdparty/assimp/code/Common/SGSpatialSort.cpp create mode 100644 thirdparty/assimp/code/Common/SceneCombiner.cpp create mode 100644 thirdparty/assimp/code/Common/ScenePreprocessor.cpp create mode 100644 thirdparty/assimp/code/Common/ScenePreprocessor.h create mode 100644 thirdparty/assimp/code/Common/ScenePrivate.h create mode 100644 thirdparty/assimp/code/Common/SkeletonMeshBuilder.cpp create mode 100644 thirdparty/assimp/code/Common/SpatialSort.cpp create mode 100644 thirdparty/assimp/code/Common/SplitByBoneCountProcess.cpp create mode 100644 thirdparty/assimp/code/Common/SplitByBoneCountProcess.h create mode 100644 thirdparty/assimp/code/Common/StandardShapes.cpp create mode 100644 thirdparty/assimp/code/Common/StdOStreamLogStream.h create mode 100644 thirdparty/assimp/code/Common/Subdivision.cpp create mode 100644 thirdparty/assimp/code/Common/TargetAnimation.cpp create mode 100644 thirdparty/assimp/code/Common/TargetAnimation.h create mode 100644 thirdparty/assimp/code/Common/Version.cpp create mode 100644 thirdparty/assimp/code/Common/VertexTriangleAdjacency.cpp create mode 100644 thirdparty/assimp/code/Common/VertexTriangleAdjacency.h create mode 100644 thirdparty/assimp/code/Common/Win32DebugLogStream.h create mode 100644 thirdparty/assimp/code/Common/assbin_chunks.h create mode 100644 thirdparty/assimp/code/Common/scene.cpp create mode 100644 thirdparty/assimp/code/Common/simd.cpp create mode 100644 thirdparty/assimp/code/Common/simd.h delete mode 100644 thirdparty/assimp/code/ComputeUVMappingProcess.cpp delete mode 100644 thirdparty/assimp/code/ComputeUVMappingProcess.h delete mode 100644 thirdparty/assimp/code/ConvertToLHProcess.cpp delete mode 100644 thirdparty/assimp/code/ConvertToLHProcess.h delete mode 100644 thirdparty/assimp/code/CreateAnimMesh.cpp delete mode 100644 thirdparty/assimp/code/DeboneProcess.cpp delete mode 100644 thirdparty/assimp/code/DeboneProcess.h delete mode 100644 thirdparty/assimp/code/DefaultIOStream.cpp delete mode 100644 thirdparty/assimp/code/DefaultIOSystem.cpp delete mode 100644 thirdparty/assimp/code/DefaultLogger.cpp delete mode 100644 thirdparty/assimp/code/DefaultProgressHandler.h delete mode 100644 thirdparty/assimp/code/DropFaceNormalsProcess.cpp delete mode 100644 thirdparty/assimp/code/DropFaceNormalsProcess.h delete mode 100644 thirdparty/assimp/code/EmbedTexturesProcess.cpp delete mode 100644 thirdparty/assimp/code/EmbedTexturesProcess.h delete mode 100644 thirdparty/assimp/code/Exporter.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXAnimation.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXBinaryTokenizer.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXCommon.h create mode 100644 thirdparty/assimp/code/FBX/FBXCompileConfig.h create mode 100644 thirdparty/assimp/code/FBX/FBXConverter.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXConverter.h create mode 100644 thirdparty/assimp/code/FBX/FBXDeformer.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXDocument.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXDocument.h create mode 100644 thirdparty/assimp/code/FBX/FBXDocumentUtil.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXDocumentUtil.h create mode 100644 thirdparty/assimp/code/FBX/FBXExportNode.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXExportNode.h create mode 100644 thirdparty/assimp/code/FBX/FBXExportProperty.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXExportProperty.h create mode 100644 thirdparty/assimp/code/FBX/FBXExporter.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXExporter.h create mode 100644 thirdparty/assimp/code/FBX/FBXImportSettings.h create mode 100644 thirdparty/assimp/code/FBX/FBXImporter.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXImporter.h create mode 100644 thirdparty/assimp/code/FBX/FBXMaterial.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXMeshGeometry.h create mode 100644 thirdparty/assimp/code/FBX/FBXModel.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXNodeAttribute.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXParser.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXParser.h create mode 100644 thirdparty/assimp/code/FBX/FBXProperties.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXProperties.h create mode 100644 thirdparty/assimp/code/FBX/FBXTokenizer.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXTokenizer.h create mode 100644 thirdparty/assimp/code/FBX/FBXUtil.cpp create mode 100644 thirdparty/assimp/code/FBX/FBXUtil.h delete mode 100644 thirdparty/assimp/code/FBXAnimation.cpp delete mode 100644 thirdparty/assimp/code/FBXBinaryTokenizer.cpp delete mode 100644 thirdparty/assimp/code/FBXCommon.h delete mode 100644 thirdparty/assimp/code/FBXCompileConfig.h delete mode 100644 thirdparty/assimp/code/FBXConverter.cpp delete mode 100644 thirdparty/assimp/code/FBXConverter.h delete mode 100644 thirdparty/assimp/code/FBXDeformer.cpp delete mode 100644 thirdparty/assimp/code/FBXDocument.cpp delete mode 100644 thirdparty/assimp/code/FBXDocument.h delete mode 100644 thirdparty/assimp/code/FBXDocumentUtil.cpp delete mode 100644 thirdparty/assimp/code/FBXDocumentUtil.h delete mode 100644 thirdparty/assimp/code/FBXExportNode.cpp delete mode 100644 thirdparty/assimp/code/FBXExportNode.h delete mode 100644 thirdparty/assimp/code/FBXExportProperty.cpp delete mode 100644 thirdparty/assimp/code/FBXExportProperty.h delete mode 100644 thirdparty/assimp/code/FBXExporter.cpp delete mode 100644 thirdparty/assimp/code/FBXExporter.h delete mode 100644 thirdparty/assimp/code/FBXImportSettings.h delete mode 100644 thirdparty/assimp/code/FBXImporter.cpp delete mode 100644 thirdparty/assimp/code/FBXImporter.h delete mode 100644 thirdparty/assimp/code/FBXMaterial.cpp delete mode 100644 thirdparty/assimp/code/FBXMeshGeometry.cpp delete mode 100644 thirdparty/assimp/code/FBXMeshGeometry.h delete mode 100644 thirdparty/assimp/code/FBXModel.cpp delete mode 100644 thirdparty/assimp/code/FBXNodeAttribute.cpp delete mode 100644 thirdparty/assimp/code/FBXParser.cpp delete mode 100644 thirdparty/assimp/code/FBXParser.h delete mode 100644 thirdparty/assimp/code/FBXProperties.cpp delete mode 100644 thirdparty/assimp/code/FBXProperties.h delete mode 100644 thirdparty/assimp/code/FBXTokenizer.cpp delete mode 100644 thirdparty/assimp/code/FBXTokenizer.h delete mode 100644 thirdparty/assimp/code/FBXUtil.cpp delete mode 100644 thirdparty/assimp/code/FBXUtil.h delete mode 100644 thirdparty/assimp/code/FIReader.cpp delete mode 100644 thirdparty/assimp/code/FileLogStream.h delete mode 100644 thirdparty/assimp/code/FileSystemFilter.h delete mode 100644 thirdparty/assimp/code/FindDegenerates.cpp delete mode 100644 thirdparty/assimp/code/FindDegenerates.h delete mode 100644 thirdparty/assimp/code/FindInstancesProcess.cpp delete mode 100644 thirdparty/assimp/code/FindInstancesProcess.h delete mode 100644 thirdparty/assimp/code/FindInvalidDataProcess.cpp delete mode 100644 thirdparty/assimp/code/FindInvalidDataProcess.h delete mode 100644 thirdparty/assimp/code/FixNormalsStep.cpp delete mode 100644 thirdparty/assimp/code/FixNormalsStep.h delete mode 100644 thirdparty/assimp/code/GenFaceNormalsProcess.cpp delete mode 100644 thirdparty/assimp/code/GenFaceNormalsProcess.h delete mode 100644 thirdparty/assimp/code/GenVertexNormalsProcess.cpp delete mode 100644 thirdparty/assimp/code/GenVertexNormalsProcess.h delete mode 100644 thirdparty/assimp/code/Importer.cpp delete mode 100644 thirdparty/assimp/code/Importer.h delete mode 100644 thirdparty/assimp/code/ImporterRegistry.cpp delete mode 100644 thirdparty/assimp/code/ImproveCacheLocality.cpp delete mode 100644 thirdparty/assimp/code/ImproveCacheLocality.h delete mode 100644 thirdparty/assimp/code/JoinVerticesProcess.cpp delete mode 100644 thirdparty/assimp/code/JoinVerticesProcess.h delete mode 100644 thirdparty/assimp/code/LimitBoneWeightsProcess.cpp delete mode 100644 thirdparty/assimp/code/LimitBoneWeightsProcess.h create mode 100644 thirdparty/assimp/code/MMD/MMDCpp14.h create mode 100644 thirdparty/assimp/code/MMD/MMDImporter.cpp create mode 100644 thirdparty/assimp/code/MMD/MMDImporter.h create mode 100644 thirdparty/assimp/code/MMD/MMDPmdParser.h create mode 100644 thirdparty/assimp/code/MMD/MMDPmxParser.cpp create mode 100644 thirdparty/assimp/code/MMD/MMDPmxParser.h create mode 100644 thirdparty/assimp/code/MMD/MMDVmdParser.h delete mode 100644 thirdparty/assimp/code/MMDCpp14.h delete mode 100644 thirdparty/assimp/code/MMDImporter.cpp delete mode 100644 thirdparty/assimp/code/MMDImporter.h delete mode 100644 thirdparty/assimp/code/MMDPmdParser.h delete mode 100644 thirdparty/assimp/code/MMDPmxParser.cpp delete mode 100644 thirdparty/assimp/code/MMDPmxParser.h delete mode 100644 thirdparty/assimp/code/MMDVmdParser.h delete mode 100644 thirdparty/assimp/code/MakeVerboseFormat.cpp delete mode 100644 thirdparty/assimp/code/MakeVerboseFormat.h create mode 100644 thirdparty/assimp/code/Material/MaterialSystem.cpp create mode 100644 thirdparty/assimp/code/Material/MaterialSystem.h delete mode 100644 thirdparty/assimp/code/MaterialSystem.cpp delete mode 100644 thirdparty/assimp/code/MaterialSystem.h delete mode 100644 thirdparty/assimp/code/OptimizeGraph.cpp delete mode 100644 thirdparty/assimp/code/OptimizeGraph.h delete mode 100644 thirdparty/assimp/code/OptimizeMeshes.cpp delete mode 100644 thirdparty/assimp/code/OptimizeMeshes.h delete mode 100644 thirdparty/assimp/code/PolyTools.h create mode 100644 thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/DeboneProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/DeboneProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/FindDegenerates.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/FindDegenerates.h create mode 100644 thirdparty/assimp/code/PostProcessing/FindInstancesProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/FindInstancesProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/FixNormalsStep.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/FixNormalsStep.h create mode 100644 thirdparty/assimp/code/PostProcessing/GenBoundingBoxesProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/GenBoundingBoxesProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.h create mode 100644 thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h create mode 100644 thirdparty/assimp/code/PostProcessing/OptimizeGraph.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/OptimizeGraph.h create mode 100644 thirdparty/assimp/code/PostProcessing/OptimizeMeshes.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/OptimizeMeshes.h create mode 100644 thirdparty/assimp/code/PostProcessing/PretransformVertices.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/PretransformVertices.h create mode 100644 thirdparty/assimp/code/PostProcessing/ProcessHelper.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/ProcessHelper.h create mode 100644 thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.h create mode 100644 thirdparty/assimp/code/PostProcessing/RemoveVCProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/RemoveVCProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/ScaleProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/ScaleProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.h create mode 100644 thirdparty/assimp/code/PostProcessing/TextureTransform.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/TextureTransform.h create mode 100644 thirdparty/assimp/code/PostProcessing/TriangulateProcess.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/TriangulateProcess.h create mode 100644 thirdparty/assimp/code/PostProcessing/ValidateDataStructure.cpp create mode 100644 thirdparty/assimp/code/PostProcessing/ValidateDataStructure.h delete mode 100644 thirdparty/assimp/code/PostStepRegistry.cpp delete mode 100644 thirdparty/assimp/code/PretransformVertices.cpp delete mode 100644 thirdparty/assimp/code/PretransformVertices.h delete mode 100644 thirdparty/assimp/code/ProcessHelper.cpp delete mode 100644 thirdparty/assimp/code/ProcessHelper.h delete mode 100644 thirdparty/assimp/code/RawLoader.cpp delete mode 100644 thirdparty/assimp/code/RemoveComments.cpp delete mode 100644 thirdparty/assimp/code/RemoveRedundantMaterials.cpp delete mode 100644 thirdparty/assimp/code/RemoveRedundantMaterials.h delete mode 100644 thirdparty/assimp/code/RemoveVCProcess.cpp delete mode 100644 thirdparty/assimp/code/RemoveVCProcess.h delete mode 100644 thirdparty/assimp/code/SGSpatialSort.cpp delete mode 100644 thirdparty/assimp/code/ScaleProcess.cpp delete mode 100644 thirdparty/assimp/code/ScaleProcess.h delete mode 100644 thirdparty/assimp/code/SceneCombiner.cpp delete mode 100644 thirdparty/assimp/code/ScenePreprocessor.cpp delete mode 100644 thirdparty/assimp/code/ScenePreprocessor.h delete mode 100644 thirdparty/assimp/code/ScenePrivate.h delete mode 100644 thirdparty/assimp/code/SkeletonMeshBuilder.cpp delete mode 100644 thirdparty/assimp/code/SortByPTypeProcess.cpp delete mode 100644 thirdparty/assimp/code/SortByPTypeProcess.h delete mode 100644 thirdparty/assimp/code/SpatialSort.cpp delete mode 100644 thirdparty/assimp/code/SplitByBoneCountProcess.cpp delete mode 100644 thirdparty/assimp/code/SplitByBoneCountProcess.h delete mode 100644 thirdparty/assimp/code/SplitLargeMeshes.cpp delete mode 100644 thirdparty/assimp/code/SplitLargeMeshes.h delete mode 100644 thirdparty/assimp/code/StandardShapes.cpp delete mode 100644 thirdparty/assimp/code/StdOStreamLogStream.h delete mode 100644 thirdparty/assimp/code/Subdivision.cpp delete mode 100644 thirdparty/assimp/code/TargetAnimation.cpp delete mode 100644 thirdparty/assimp/code/TargetAnimation.h delete mode 100644 thirdparty/assimp/code/TextureTransform.cpp delete mode 100644 thirdparty/assimp/code/TextureTransform.h delete mode 100644 thirdparty/assimp/code/TriangulateProcess.cpp delete mode 100644 thirdparty/assimp/code/TriangulateProcess.h delete mode 100644 thirdparty/assimp/code/ValidateDataStructure.cpp delete mode 100644 thirdparty/assimp/code/ValidateDataStructure.h delete mode 100644 thirdparty/assimp/code/Version.cpp delete mode 100644 thirdparty/assimp/code/VertexTriangleAdjacency.cpp delete mode 100644 thirdparty/assimp/code/VertexTriangleAdjacency.h delete mode 100644 thirdparty/assimp/code/Win32DebugLogStream.h delete mode 100644 thirdparty/assimp/code/scene.cpp delete mode 100644 thirdparty/assimp/code/simd.cpp delete mode 100644 thirdparty/assimp/code/simd.h create mode 100644 thirdparty/assimp/include/assimp/aabb.h (limited to 'thirdparty/assimp') diff --git a/thirdparty/assimp/assimp/config.h b/thirdparty/assimp/assimp/config.h index 8b0634d28b..382a698268 100644 --- a/thirdparty/assimp/assimp/config.h +++ b/thirdparty/assimp/assimp/config.h @@ -646,6 +646,21 @@ enum aiComponent { #define AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING \ "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING" +// --------------------------------------------------------------------------- +/** @brief Set wether the FBX importer shall not remove empty bones. + * + * + * Empty bone are often used to define connections for other models. + */ +#define AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES \ + "AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES" + +// --------------------------------------------------------------------------- +/** @brief Set wether the FBX importer shall convert the unit from cm to m. + */ +#define AI_CONFIG_FBX_CONVERT_TO_M \ + "AI_CONFIG_FBX_CONVERT_TO_M" + // --------------------------------------------------------------------------- /** @brief Set the vertex animation keyframe to be imported * @@ -978,3 +993,4 @@ enum aiComponent { /* #cmakedefine ASSIMP_DOUBLE_PRECISION 1 */ #endif // !! AI_CONFIG_H_INC + diff --git a/thirdparty/assimp/code/BaseImporter.cpp b/thirdparty/assimp/code/BaseImporter.cpp deleted file mode 100644 index 4803c6d6f2..0000000000 --- a/thirdparty/assimp/code/BaseImporter.cpp +++ /dev/null @@ -1,616 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file BaseImporter.cpp - * @brief Implementation of BaseImporter - */ - -#include -#include -#include "FileSystemFilter.h" -#include "Importer.h" -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -BaseImporter::BaseImporter() AI_NO_EXCEPT -: m_progress() { - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -BaseImporter::~BaseImporter() { - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Imports the given file and returns the imported data. -aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) { - m_progress = pImp->GetProgressHandler(); - if (nullptr == m_progress) { - return nullptr; - } - - ai_assert(m_progress); - - // Gather configuration properties for this run - SetupProperties( pImp ); - - // Construct a file system filter to improve our success ratio at reading external files - FileSystemFilter filter(pFile,pIOHandler); - - // create a scene object to hold the data - std::unique_ptr sc(new aiScene()); - - // dispatch importing - try - { - InternReadFile( pFile, sc.get(), &filter); - - } catch( const std::exception& err ) { - // extract error description - m_ErrorText = err.what(); - ASSIMP_LOG_ERROR(m_ErrorText); - return nullptr; - } - - // return what we gathered from the import. - return sc.release(); -} - -// ------------------------------------------------------------------------------------------------ -void BaseImporter::SetupProperties(const Importer* /*pImp*/) -{ - // the default implementation does nothing -} - -// ------------------------------------------------------------------------------------------------ -void BaseImporter::GetExtensionList(std::set& extensions) { - const aiImporterDesc* desc = GetInfo(); - ai_assert(desc != nullptr); - - const char* ext = desc->mFileExtensions; - ai_assert(ext != nullptr ); - - const char* last = ext; - do { - if (!*ext || *ext == ' ') { - extensions.insert(std::string(last,ext-last)); - ai_assert(ext-last > 0); - last = ext; - while(*last == ' ') { - ++last; - } - } - } - while(*ext++); -} - -// ------------------------------------------------------------------------------------------------ -/*static*/ bool BaseImporter::SearchFileHeaderForToken( IOSystem* pIOHandler, - const std::string& pFile, - const char** tokens, - unsigned int numTokens, - unsigned int searchBytes /* = 200 */, - bool tokensSol /* false */, - bool noAlphaBeforeTokens /* false */) -{ - ai_assert( nullptr != tokens ); - ai_assert( 0 != numTokens ); - ai_assert( 0 != searchBytes); - - if ( nullptr == pIOHandler ) { - return false; - } - - std::unique_ptr pStream (pIOHandler->Open(pFile)); - if (pStream.get() ) { - // read 200 characters from the file - std::unique_ptr _buffer (new char[searchBytes+1 /* for the '\0' */]); - char *buffer( _buffer.get() ); - const size_t read( pStream->Read(buffer,1,searchBytes) ); - if( 0 == read ) { - return false; - } - - for( size_t i = 0; i < read; ++i ) { - buffer[ i ] = static_cast( ::tolower( buffer[ i ] ) ); - } - - // It is not a proper handling of unicode files here ... - // ehm ... but it works in most cases. - char* cur = buffer,*cur2 = buffer,*end = &buffer[read]; - while (cur != end) { - if( *cur ) { - *cur2++ = *cur; - } - ++cur; - } - *cur2 = '\0'; - - std::string token; - for (unsigned int i = 0; i < numTokens; ++i ) { - ai_assert( nullptr != tokens[i] ); - const size_t len( strlen( tokens[ i ] ) ); - token.clear(); - const char *ptr( tokens[ i ] ); - for ( size_t tokIdx = 0; tokIdx < len; ++tokIdx ) { - token.push_back( static_cast( tolower( *ptr ) ) ); - ++ptr; - } - const char* r = strstr( buffer, token.c_str() ); - if( !r ) { - continue; - } - // We need to make sure that we didn't accidentially identify the end of another token as our token, - // e.g. in a previous version the "gltf " present in some gltf files was detected as "f " - if (noAlphaBeforeTokens && (r != buffer && isalpha(r[-1]))) { - continue; - } - // We got a match, either we don't care where it is, or it happens to - // be in the beginning of the file / line - if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') { - ASSIMP_LOG_DEBUG_F( "Found positive match for header keyword: ", tokens[i] ); - return true; - } - } - } - - return false; -} - -// ------------------------------------------------------------------------------------------------ -// Simple check for file extension -/*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile, - const char* ext0, - const char* ext1, - const char* ext2) -{ - std::string::size_type pos = pFile.find_last_of('.'); - - // no file extension - can't read - if( pos == std::string::npos) - return false; - - const char* ext_real = & pFile[ pos+1 ]; - if( !ASSIMP_stricmp(ext_real,ext0) ) - return true; - - // check for other, optional, file extensions - if (ext1 && !ASSIMP_stricmp(ext_real,ext1)) - return true; - - if (ext2 && !ASSIMP_stricmp(ext_real,ext2)) - return true; - - return false; -} - -// ------------------------------------------------------------------------------------------------ -// Get file extension from path -std::string BaseImporter::GetExtension( const std::string& file ) { - std::string::size_type pos = file.find_last_of('.'); - - // no file extension at all - if (pos == std::string::npos) { - return ""; - } - - - // thanks to Andy Maloney for the hint - std::string ret = file.substr( pos + 1 ); - std::transform( ret.begin(), ret.end(), ret.begin(), ToLower); - - return ret; -} - -// ------------------------------------------------------------------------------------------------ -// Check for magic bytes at the beginning of the file. -/* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile, - const void* _magic, unsigned int num, unsigned int offset, unsigned int size) -{ - ai_assert( size <= 16 ); - ai_assert( _magic ); - - if (!pIOHandler) { - return false; - } - union { - const char* magic; - const uint16_t* magic_u16; - const uint32_t* magic_u32; - }; - magic = reinterpret_cast(_magic); - std::unique_ptr pStream (pIOHandler->Open(pFile)); - if (pStream.get() ) { - - // skip to offset - pStream->Seek(offset,aiOrigin_SET); - - // read 'size' characters from the file - union { - char data[16]; - uint16_t data_u16[8]; - uint32_t data_u32[4]; - }; - if(size != pStream->Read(data,1,size)) { - return false; - } - - for (unsigned int i = 0; i < num; ++i) { - // also check against big endian versions of tokens with size 2,4 - // that's just for convenience, the chance that we cause conflicts - // is quite low and it can save some lines and prevent nasty bugs - if (2 == size) { - uint16_t rev = *magic_u16; - ByteSwap::Swap(&rev); - if (data_u16[0] == *magic_u16 || data_u16[0] == rev) { - return true; - } - } - else if (4 == size) { - uint32_t rev = *magic_u32; - ByteSwap::Swap(&rev); - if (data_u32[0] == *magic_u32 || data_u32[0] == rev) { - return true; - } - } - else { - // any length ... just compare - if(!memcmp(magic,data,size)) { - return true; - } - } - magic += size; - } - } - return false; -} - -#include "../contrib/utf8cpp/source/utf8.h" - -// ------------------------------------------------------------------------------------------------ -// Convert to UTF8 data -void BaseImporter::ConvertToUTF8(std::vector& data) -{ - //ConversionResult result; - if(data.size() < 8) { - throw DeadlyImportError("File is too small"); - } - - // UTF 8 with BOM - if((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) { - ASSIMP_LOG_DEBUG("Found UTF-8 BOM ..."); - - std::copy(data.begin()+3,data.end(),data.begin()); - data.resize(data.size()-3); - return; - } - - - // UTF 32 BE with BOM - if(*((uint32_t*)&data.front()) == 0xFFFE0000) { - - // swap the endianness .. - for(uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) { - AI_SWAP4P(p); - } - } - - // UTF 32 LE with BOM - if(*((uint32_t*)&data.front()) == 0x0000FFFE) { - ASSIMP_LOG_DEBUG("Found UTF-32 BOM ..."); - - std::vector output; - int *ptr = (int*)&data[ 0 ]; - int *end = ptr + ( data.size() / sizeof(int) ) +1; - utf8::utf32to8( ptr, end, back_inserter(output)); - return; - } - - // UTF 16 BE with BOM - if(*((uint16_t*)&data.front()) == 0xFFFE) { - - // swap the endianness .. - for(uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) { - ByteSwap::Swap2(p); - } - } - - // UTF 16 LE with BOM - if(*((uint16_t*)&data.front()) == 0xFEFF) { - ASSIMP_LOG_DEBUG("Found UTF-16 BOM ..."); - - std::vector output; - utf8::utf16to8(data.begin(), data.end(), back_inserter(output)); - return; - } -} - -// ------------------------------------------------------------------------------------------------ -// Convert to UTF8 data to ISO-8859-1 -void BaseImporter::ConvertUTF8toISO8859_1(std::string& data) -{ - size_t size = data.size(); - size_t i = 0, j = 0; - - while(i < size) { - if ((unsigned char) data[i] < (size_t) 0x80) { - data[j] = data[i]; - } else if(i < size - 1) { - if((unsigned char) data[i] == 0xC2) { - data[j] = data[++i]; - } else if((unsigned char) data[i] == 0xC3) { - data[j] = ((unsigned char) data[++i] + 0x40); - } else { - std::stringstream stream; - stream << "UTF8 code " << std::hex << data[i] << data[i + 1] << " can not be converted into ISA-8859-1."; - ASSIMP_LOG_ERROR( stream.str() ); - - data[j++] = data[i++]; - data[j] = data[i]; - } - } else { - ASSIMP_LOG_ERROR("UTF8 code but only one character remaining"); - - data[j] = data[i]; - } - - i++; j++; - } - - data.resize(j); -} - -// ------------------------------------------------------------------------------------------------ -void BaseImporter::TextFileToBuffer(IOStream* stream, - std::vector& data, - TextFileMode mode) -{ - ai_assert(nullptr != stream); - - const size_t fileSize = stream->FileSize(); - if (mode == FORBID_EMPTY) { - if(!fileSize) { - throw DeadlyImportError("File is empty"); - } - } - - data.reserve(fileSize+1); - data.resize(fileSize); - if(fileSize > 0) { - if(fileSize != stream->Read( &data[0], 1, fileSize)) { - throw DeadlyImportError("File read error"); - } - - ConvertToUTF8(data); - } - - // append a binary zero to simplify string parsing - data.push_back(0); -} - -// ------------------------------------------------------------------------------------------------ -namespace Assimp { - // Represents an import request - struct LoadRequest { - LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id) - : file(_file) - , flags(_flags) - , refCnt(1) - , scene(NULL) - , loaded(false) - , id(_id) { - if ( _map ) { - map = *_map; - } - } - - bool operator== ( const std::string& f ) const { - return file == f; - } - - const std::string file; - unsigned int flags; - unsigned int refCnt; - aiScene *scene; - bool loaded; - BatchLoader::PropertyMap map; - unsigned int id; - }; -} - -// ------------------------------------------------------------------------------------------------ -// BatchLoader::pimpl data structure -struct Assimp::BatchData { - BatchData( IOSystem* pIO, bool validate ) - : pIOSystem( pIO ) - , pImporter( nullptr ) - , next_id(0xffff) - , validate( validate ) { - ai_assert( nullptr != pIO ); - - pImporter = new Importer(); - pImporter->SetIOHandler( pIO ); - } - - ~BatchData() { - pImporter->SetIOHandler( nullptr ); /* get pointer back into our possession */ - delete pImporter; - } - - // IO system to be used for all imports - IOSystem* pIOSystem; - - // Importer used to load all meshes - Importer* pImporter; - - // List of all imports - std::list requests; - - // Base path - std::string pathBase; - - // Id for next item - unsigned int next_id; - - // Validation enabled state - bool validate; -}; - -typedef std::list::iterator LoadReqIt; - -// ------------------------------------------------------------------------------------------------ -BatchLoader::BatchLoader(IOSystem* pIO, bool validate ) { - ai_assert(nullptr != pIO); - - m_data = new BatchData( pIO, validate ); -} - -// ------------------------------------------------------------------------------------------------ -BatchLoader::~BatchLoader() -{ - // delete all scenes what have not been polled by the user - for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { - delete (*it).scene; - } - delete m_data; -} - -// ------------------------------------------------------------------------------------------------ -void BatchLoader::setValidation( bool enabled ) { - m_data->validate = enabled; -} - -// ------------------------------------------------------------------------------------------------ -bool BatchLoader::getValidation() const { - return m_data->validate; -} - -// ------------------------------------------------------------------------------------------------ -unsigned int BatchLoader::AddLoadRequest(const std::string& file, - unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/) -{ - ai_assert(!file.empty()); - - // check whether we have this loading request already - for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { - // Call IOSystem's path comparison function here - if ( m_data->pIOSystem->ComparePaths((*it).file,file)) { - if (map) { - if ( !( ( *it ).map == *map ) ) { - continue; - } - } - else if ( !( *it ).map.empty() ) { - continue; - } - - (*it).refCnt++; - return (*it).id; - } - } - - // no, we don't have it. So add it to the queue ... - m_data->requests.push_back(LoadRequest(file,steps,map, m_data->next_id)); - return m_data->next_id++; -} - -// ------------------------------------------------------------------------------------------------ -aiScene* BatchLoader::GetImport( unsigned int which ) -{ - for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { - if ((*it).id == which && (*it).loaded) { - aiScene* sc = (*it).scene; - if (!(--(*it).refCnt)) { - m_data->requests.erase(it); - } - return sc; - } - } - return nullptr; -} - -// ------------------------------------------------------------------------------------------------ -void BatchLoader::LoadAll() -{ - // no threaded implementation for the moment - for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { - // force validation in debug builds - unsigned int pp = (*it).flags; - if ( m_data->validate ) { - pp |= aiProcess_ValidateDataStructure; - } - - // setup config properties if necessary - ImporterPimpl* pimpl = m_data->pImporter->Pimpl(); - pimpl->mFloatProperties = (*it).map.floats; - pimpl->mIntProperties = (*it).map.ints; - pimpl->mStringProperties = (*it).map.strings; - pimpl->mMatrixProperties = (*it).map.matrices; - - if (!DefaultLogger::isNullLogger()) - { - ASSIMP_LOG_INFO("%%% BEGIN EXTERNAL FILE %%%"); - ASSIMP_LOG_INFO_F("File: ", (*it).file); - } - m_data->pImporter->ReadFile((*it).file,pp); - (*it).scene = m_data->pImporter->GetOrphanedScene(); - (*it).loaded = true; - - ASSIMP_LOG_INFO("%%% END EXTERNAL FILE %%%"); - } -} diff --git a/thirdparty/assimp/code/BaseProcess.cpp b/thirdparty/assimp/code/BaseProcess.cpp deleted file mode 100644 index 18872c3693..0000000000 --- a/thirdparty/assimp/code/BaseProcess.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of BaseProcess */ - -#include -#include "BaseProcess.h" -#include -#include -#include "Importer.h" - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -BaseProcess::BaseProcess() AI_NO_EXCEPT -: shared() -, progress() -{ -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -BaseProcess::~BaseProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -void BaseProcess::ExecuteOnScene( Importer* pImp) -{ - ai_assert(NULL != pImp && NULL != pImp->Pimpl()->mScene); - - progress = pImp->GetProgressHandler(); - ai_assert(progress); - - SetupProperties( pImp ); - - // catch exceptions thrown inside the PostProcess-Step - try - { - Execute(pImp->Pimpl()->mScene); - - } catch( const std::exception& err ) { - - // extract error description - pImp->Pimpl()->mErrorString = err.what(); - ASSIMP_LOG_ERROR(pImp->Pimpl()->mErrorString); - - // and kill the partially imported data - delete pImp->Pimpl()->mScene; - pImp->Pimpl()->mScene = NULL; - } -} - -// ------------------------------------------------------------------------------------------------ -void BaseProcess::SetupProperties(const Importer* /*pImp*/) -{ - // the default implementation does nothing -} - -// ------------------------------------------------------------------------------------------------ -bool BaseProcess::RequireVerboseFormat() const -{ - return true; -} - diff --git a/thirdparty/assimp/code/BaseProcess.h b/thirdparty/assimp/code/BaseProcess.h deleted file mode 100644 index 4d5c7a76be..0000000000 --- a/thirdparty/assimp/code/BaseProcess.h +++ /dev/null @@ -1,290 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Base class of all import post processing steps */ -#ifndef INCLUDED_AI_BASEPROCESS_H -#define INCLUDED_AI_BASEPROCESS_H - -#include -#include - -struct aiScene; - -namespace Assimp { - -class Importer; - -// --------------------------------------------------------------------------- -/** Helper class to allow post-processing steps to interact with each other. - * - * The class maintains a simple property list that can be used by pp-steps - * to provide additional information to other steps. This is primarily - * intended for cross-step optimizations. - */ -class SharedPostProcessInfo -{ -public: - - struct Base - { - virtual ~Base() - {} - }; - - //! Represents data that is allocated on the heap, thus needs to be deleted - template - struct THeapData : public Base - { - explicit THeapData(T* in) - : data (in) - {} - - ~THeapData() - { - delete data; - } - T* data; - }; - - //! Represents static, by-value data not allocated on the heap - template - struct TStaticData : public Base - { - explicit TStaticData(T in) - : data (in) - {} - - ~TStaticData() - {} - - T data; - }; - - // some typedefs for cleaner code - typedef unsigned int KeyType; - typedef std::map 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 - void AddProperty( const char* name, T* in ){ - AddProperty(name,(Base*)new THeapData(in)); - } - - //! Add a static by-value property to the list - template - void AddProperty( const char* name, T in ){ - AddProperty(name,(Base*)new TStaticData(in)); - } - - - //! Get a heap property - template - bool GetProperty( const char* name, T*& out ) const - { - THeapData* t = (THeapData*)GetPropertyInternal(name); - if(!t) - { - out = NULL; - return false; - } - out = t->data; - return true; - } - - //! Get a static, by-value property - template - bool GetProperty( const char* name, T& out ) const - { - TStaticData* t = (TStaticData*)GetPropertyInternal(name); - if(!t)return false; - out = t->data; - return true; - } - - //! Remove a property of a specific type - void RemoveProperty( const char* name) { - SetGenericPropertyPtr(pmap,name,NULL); - } - -private: - - void AddProperty( const char* name, Base* data) { - SetGenericPropertyPtr(pmap,name,data); - } - - Base* GetPropertyInternal( const char* name) const { - return GetGenericProperty(pmap,name,NULL); - } - -private: - - //! Map of all stored properties - PropertyMap pmap; -}; - -#if 0 - -// --------------------------------------------------------------------------- -/** @brief Represents a dependency table for a postprocessing steps. - * - * For future use. - */ - struct PPDependencyTable - { - unsigned int execute_me_before_these; - unsigned int execute_me_after_these; - unsigned int only_if_these_are_not_specified; - unsigned int mutually_exclusive_with; - }; - -#endif - - -#define AI_SPP_SPATIAL_SORT "$Spat" - -// --------------------------------------------------------------------------- -/** The BaseProcess defines a common interface for all post processing steps. - * A post processing step is run after a successful import if the caller - * specified the corresponding flag when calling ReadFile(). - * Enum #aiPostProcessSteps defines which flags are available. - * After a successful import the Importer iterates over its internal array - * of processes and calls IsActive() on each process to evaluate if the step - * should be executed. If the function returns true, the class' Execute() - * function is called subsequently. - */ -class ASSIMP_API_WINONLY BaseProcess { - friend class Importer; - -public: - /** Constructor to be privately used by Importer */ - BaseProcess() AI_NO_EXCEPT; - - /** Destructor, private as well */ - virtual ~BaseProcess(); - - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag. - * @param pFlags The processing flags the importer was called with. A - * bitwise combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, - * false if not. - */ - virtual bool IsActive( unsigned int pFlags) const = 0; - - // ------------------------------------------------------------------- - /** Check whether this step expects its input vertex data to be - * in verbose format. */ - virtual bool RequireVerboseFormat() const; - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * The function deletes the scene if the postprocess step fails ( - * the object pointer will be set to NULL). - * @param pImp Importer instance (pImp->mScene must be valid) - */ - void ExecuteOnScene( Importer* pImp); - - // ------------------------------------------------------------------- - /** Called prior to ExecuteOnScene(). - * The function is a request to the process to update its configuration - * basing on the Importer's configuration property list. - */ - virtual void SetupProperties(const Importer* pImp); - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * A process should throw an ImportErrorException* if it fails. - * This method must be implemented by deriving classes. - * @param pScene The imported data to work at. - */ - virtual void Execute( aiScene* pScene) = 0; - - - // ------------------------------------------------------------------- - /** Assign a new SharedPostProcessInfo to the step. This object - * allows multiple postprocess steps to share data. - * @param sh May be NULL - */ - inline void SetSharedData(SharedPostProcessInfo* sh) { - shared = sh; - } - - // ------------------------------------------------------------------- - /** Get the shared data that is assigned to the step. - */ - inline SharedPostProcessInfo* GetSharedData() { - return shared; - } - -protected: - - /** See the doc of #SharedPostProcessInfo for more details */ - SharedPostProcessInfo* shared; - - /** Currently active progress handler */ - ProgressHandler* progress; -}; - - -} // end of namespace Assimp - -#endif // AI_BASEPROCESS_H_INC diff --git a/thirdparty/assimp/code/Bitmap.cpp b/thirdparty/assimp/code/Bitmap.cpp deleted file mode 100644 index b22b71ea9e..0000000000 --- a/thirdparty/assimp/code/Bitmap.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Bitmap.cpp - * @brief Defines bitmap format helper for textures - * - * Used for file formats which embed their textures into the model file. - */ - - -#include -#include -#include -#include - -namespace Assimp { - - void Bitmap::Save(aiTexture* texture, IOStream* file) { - if(file != NULL) { - Header header; - DIB dib; - - dib.size = DIB::dib_size; - dib.width = texture->mWidth; - dib.height = texture->mHeight; - dib.planes = 1; - dib.bits_per_pixel = 8 * mBytesPerPixel; - dib.compression = 0; - dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height; - dib.x_resolution = 0; - dib.y_resolution = 0; - dib.nb_colors = 0; - dib.nb_important_colors = 0; - - header.type = 0x4D42; // 'BM' - header.offset = Header::header_size + DIB::dib_size; - header.size = header.offset + dib.image_size; - header.reserved1 = 0; - header.reserved2 = 0; - - WriteHeader(header, file); - WriteDIB(dib, file); - WriteData(texture, file); - } - } - - template - inline - std::size_t Copy(uint8_t* data, const T &field) { -#ifdef AI_BUILD_BIG_ENDIAN - T field_swapped=AI_BE(field); - std::memcpy(data, &field_swapped, sizeof(field)); return sizeof(field); -#else - std::memcpy(data, &AI_BE(field), sizeof(field)); return sizeof(field); -#endif - } - - void Bitmap::WriteHeader(Header& header, IOStream* file) { - uint8_t data[Header::header_size]; - - std::size_t offset = 0; - - offset += Copy(&data[offset], header.type); - offset += Copy(&data[offset], header.size); - offset += Copy(&data[offset], header.reserved1); - offset += Copy(&data[offset], header.reserved2); - Copy(&data[offset], header.offset); - - file->Write(data, Header::header_size, 1); - } - - void Bitmap::WriteDIB(DIB& dib, IOStream* file) { - uint8_t data[DIB::dib_size]; - - std::size_t offset = 0; - - offset += Copy(&data[offset], dib.size); - offset += Copy(&data[offset], dib.width); - offset += Copy(&data[offset], dib.height); - offset += Copy(&data[offset], dib.planes); - offset += Copy(&data[offset], dib.bits_per_pixel); - offset += Copy(&data[offset], dib.compression); - offset += Copy(&data[offset], dib.image_size); - offset += Copy(&data[offset], dib.x_resolution); - offset += Copy(&data[offset], dib.y_resolution); - offset += Copy(&data[offset], dib.nb_colors); - Copy(&data[offset], dib.nb_important_colors); - - file->Write(data, DIB::dib_size, 1); - } - - void Bitmap::WriteData(aiTexture* texture, IOStream* file) { - static const std::size_t padding_offset = 4; - static const uint8_t padding_data[padding_offset] = {0x0, 0x0, 0x0, 0x0}; - - unsigned int padding = (padding_offset - ((mBytesPerPixel * texture->mWidth) % padding_offset)) % padding_offset; - uint8_t pixel[mBytesPerPixel]; - - for(std::size_t i = 0; i < texture->mHeight; ++i) { - for(std::size_t j = 0; j < texture->mWidth; ++j) { - const aiTexel& texel = texture->pcData[(texture->mHeight - i - 1) * texture->mWidth + j]; // Bitmap files are stored in bottom-up format - - pixel[0] = texel.r; - pixel[1] = texel.g; - pixel[2] = texel.b; - pixel[3] = texel.a; - - file->Write(pixel, mBytesPerPixel, 1); - } - - file->Write(padding_data, padding, 1); - } - } - -} diff --git a/thirdparty/assimp/code/CApi/AssimpCExport.cpp b/thirdparty/assimp/code/CApi/AssimpCExport.cpp new file mode 100644 index 0000000000..7557edcfc6 --- /dev/null +++ b/thirdparty/assimp/code/CApi/AssimpCExport.cpp @@ -0,0 +1,156 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file AssimpCExport.cpp +Assimp C export interface. See Exporter.cpp for some notes. +*/ + +#ifndef ASSIMP_BUILD_NO_EXPORT + +#include "CInterfaceIOWrapper.h" +#include +#include "Common/ScenePrivate.h" +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API size_t aiGetExportFormatCount(void) +{ + return Exporter().GetExportFormatCount(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const aiExportFormatDesc* aiGetExportFormatDescription( size_t index) +{ + // Note: this is valid as the index always pertains to a built-in exporter, + // for which the returned structure is guaranteed to be of static storage duration. + Exporter exporter; + const aiExportFormatDesc* orig( exporter.GetExportFormatDescription( index ) ); + if (NULL == orig) { + return NULL; + } + + aiExportFormatDesc *desc = new aiExportFormatDesc; + desc->description = new char[ strlen( orig->description ) + 1 ](); + ::strncpy( (char*) desc->description, orig->description, strlen( orig->description ) ); + desc->fileExtension = new char[ strlen( orig->fileExtension ) + 1 ](); + ::strncpy( ( char* ) desc->fileExtension, orig->fileExtension, strlen( orig->fileExtension ) ); + desc->id = new char[ strlen( orig->id ) + 1 ](); + ::strncpy( ( char* ) desc->id, orig->id, strlen( orig->id ) ); + + return desc; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiReleaseExportFormatDescription( const aiExportFormatDesc *desc ) { + if (NULL == desc) { + return; + } + + delete [] desc->description; + delete [] desc->fileExtension; + delete [] desc->id; + delete desc; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiCopyScene(const aiScene* pIn, aiScene** pOut) +{ + if (!pOut || !pIn) { + return; + } + + SceneCombiner::CopyScene(pOut,pIn,true); + ScenePriv(*pOut)->mIsCopy = true; +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiFreeScene(const C_STRUCT aiScene* pIn) +{ + // note: aiReleaseImport() is also able to delete scene copies, but in addition + // it also handles scenes with import metadata. + delete pIn; +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiReturn aiExportScene( const aiScene* pScene, const char* pFormatId, const char* pFileName, unsigned int pPreprocessing ) +{ + return ::aiExportSceneEx(pScene,pFormatId,pFileName,NULL,pPreprocessing); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiReturn aiExportSceneEx( const aiScene* pScene, const char* pFormatId, const char* pFileName, aiFileIO* pIO, unsigned int pPreprocessing ) +{ + Exporter exp; + + if (pIO) { + exp.SetIOHandler(new CIOSystemWrapper(pIO)); + } + return exp.Export(pScene,pFormatId,pFileName,pPreprocessing); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing ) +{ + Exporter exp; + if (!exp.ExportToBlob(pScene,pFormatId,pPreprocessing)) { + return NULL; + } + const aiExportDataBlob* blob = exp.GetOrphanedBlob(); + ai_assert(blob); + + return blob; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API C_STRUCT void aiReleaseExportBlob( const aiExportDataBlob* pData ) +{ + delete pData; +} + +#endif // !ASSIMP_BUILD_NO_EXPORT diff --git a/thirdparty/assimp/code/CApi/CInterfaceIOWrapper.cpp b/thirdparty/assimp/code/CApi/CInterfaceIOWrapper.cpp new file mode 100644 index 0000000000..5a3a49565a --- /dev/null +++ b/thirdparty/assimp/code/CApi/CInterfaceIOWrapper.cpp @@ -0,0 +1,136 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file aiFileIO -> IOSystem wrapper*/ + +#include "CInterfaceIOWrapper.h" + +namespace Assimp { + +CIOStreamWrapper::~CIOStreamWrapper(void) +{ + /* Various places depend on this destructor to close the file */ + if (mFile) { + mIO->mFileSystem->CloseProc(mIO->mFileSystem, mFile); + mFile = nullptr; + } +} + +// ................................................................... +size_t CIOStreamWrapper::Read(void* pvBuffer, + size_t pSize, + size_t pCount +){ + // need to typecast here as C has no void* + return mFile->ReadProc(mFile,(char*)pvBuffer,pSize,pCount); +} + +// ................................................................... +size_t CIOStreamWrapper::Write(const void* pvBuffer, + size_t pSize, + size_t pCount +){ + // need to typecast here as C has no void* + return mFile->WriteProc(mFile,(const char*)pvBuffer,pSize,pCount); +} + +// ................................................................... +aiReturn CIOStreamWrapper::Seek(size_t pOffset, + aiOrigin pOrigin +){ + return mFile->SeekProc(mFile,pOffset,pOrigin); +} + +// ................................................................... +size_t CIOStreamWrapper::Tell(void) const { + return mFile->TellProc(mFile); +} + +// ................................................................... +size_t CIOStreamWrapper::FileSize() const { + return mFile->FileSizeProc(mFile); +} + +// ................................................................... +void CIOStreamWrapper::Flush () { + return mFile->FlushProc(mFile); +} + +// ------------------------------------------------------------------------------------------------ +// Custom IOStream implementation for the C-API +bool CIOSystemWrapper::Exists( const char* pFile) const { + aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,"rb"); + if (p){ + mFileSystem->CloseProc(mFileSystem,p); + return true; + } + return false; +} + +// ................................................................... +char CIOSystemWrapper::getOsSeparator() const { +#ifndef _WIN32 + return '/'; +#else + return '\\'; +#endif +} + +// ................................................................... +IOStream* CIOSystemWrapper::Open(const char* pFile,const char* pMode) { + aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,pMode); + if (!p) { + return NULL; + } + return new CIOStreamWrapper(p, this); +} + +// ................................................................... +void CIOSystemWrapper::Close( IOStream* pFile) { + if (!pFile) { + return; + } + delete pFile; +} + +} diff --git a/thirdparty/assimp/code/CApi/CInterfaceIOWrapper.h b/thirdparty/assimp/code/CApi/CInterfaceIOWrapper.h new file mode 100644 index 0000000000..2162320302 --- /dev/null +++ b/thirdparty/assimp/code/CApi/CInterfaceIOWrapper.h @@ -0,0 +1,99 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file aiFileIO -> IOSystem wrapper*/ + +#ifndef AI_CIOSYSTEM_H_INCLUDED +#define AI_CIOSYSTEM_H_INCLUDED + +#include +#include +#include + +namespace Assimp { + +class CIOSystemWrapper; + +// ------------------------------------------------------------------------------------------------ +// Custom IOStream implementation for the C-API +class CIOStreamWrapper : public IOStream +{ +public: + explicit CIOStreamWrapper(aiFile* pFile, CIOSystemWrapper* io) + : mFile(pFile), + mIO(io) + {} + ~CIOStreamWrapper(void); + + size_t Read(void* pvBuffer, size_t pSize, size_t pCount); + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount); + aiReturn Seek(size_t pOffset, aiOrigin pOrigin); + size_t Tell(void) const; + size_t FileSize() const; + void Flush(); + +private: + aiFile* mFile; + CIOSystemWrapper* mIO; +}; + +class CIOSystemWrapper : public IOSystem +{ + friend class CIOStreamWrapper; +public: + explicit CIOSystemWrapper(aiFileIO* pFile) + : mFileSystem(pFile) + {} + + bool Exists( const char* pFile) const; + char getOsSeparator() const; + IOStream* Open(const char* pFile,const char* pMode = "rb"); + void Close( IOStream* pFile); +private: + aiFileIO* mFileSystem; +}; + +} + +#endif + diff --git a/thirdparty/assimp/code/CInterfaceIOWrapper.cpp b/thirdparty/assimp/code/CInterfaceIOWrapper.cpp deleted file mode 100644 index 5a3a49565a..0000000000 --- a/thirdparty/assimp/code/CInterfaceIOWrapper.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file aiFileIO -> IOSystem wrapper*/ - -#include "CInterfaceIOWrapper.h" - -namespace Assimp { - -CIOStreamWrapper::~CIOStreamWrapper(void) -{ - /* Various places depend on this destructor to close the file */ - if (mFile) { - mIO->mFileSystem->CloseProc(mIO->mFileSystem, mFile); - mFile = nullptr; - } -} - -// ................................................................... -size_t CIOStreamWrapper::Read(void* pvBuffer, - size_t pSize, - size_t pCount -){ - // need to typecast here as C has no void* - return mFile->ReadProc(mFile,(char*)pvBuffer,pSize,pCount); -} - -// ................................................................... -size_t CIOStreamWrapper::Write(const void* pvBuffer, - size_t pSize, - size_t pCount -){ - // need to typecast here as C has no void* - return mFile->WriteProc(mFile,(const char*)pvBuffer,pSize,pCount); -} - -// ................................................................... -aiReturn CIOStreamWrapper::Seek(size_t pOffset, - aiOrigin pOrigin -){ - return mFile->SeekProc(mFile,pOffset,pOrigin); -} - -// ................................................................... -size_t CIOStreamWrapper::Tell(void) const { - return mFile->TellProc(mFile); -} - -// ................................................................... -size_t CIOStreamWrapper::FileSize() const { - return mFile->FileSizeProc(mFile); -} - -// ................................................................... -void CIOStreamWrapper::Flush () { - return mFile->FlushProc(mFile); -} - -// ------------------------------------------------------------------------------------------------ -// Custom IOStream implementation for the C-API -bool CIOSystemWrapper::Exists( const char* pFile) const { - aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,"rb"); - if (p){ - mFileSystem->CloseProc(mFileSystem,p); - return true; - } - return false; -} - -// ................................................................... -char CIOSystemWrapper::getOsSeparator() const { -#ifndef _WIN32 - return '/'; -#else - return '\\'; -#endif -} - -// ................................................................... -IOStream* CIOSystemWrapper::Open(const char* pFile,const char* pMode) { - aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,pMode); - if (!p) { - return NULL; - } - return new CIOStreamWrapper(p, this); -} - -// ................................................................... -void CIOSystemWrapper::Close( IOStream* pFile) { - if (!pFile) { - return; - } - delete pFile; -} - -} diff --git a/thirdparty/assimp/code/CInterfaceIOWrapper.h b/thirdparty/assimp/code/CInterfaceIOWrapper.h deleted file mode 100644 index 2162320302..0000000000 --- a/thirdparty/assimp/code/CInterfaceIOWrapper.h +++ /dev/null @@ -1,99 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file aiFileIO -> IOSystem wrapper*/ - -#ifndef AI_CIOSYSTEM_H_INCLUDED -#define AI_CIOSYSTEM_H_INCLUDED - -#include -#include -#include - -namespace Assimp { - -class CIOSystemWrapper; - -// ------------------------------------------------------------------------------------------------ -// Custom IOStream implementation for the C-API -class CIOStreamWrapper : public IOStream -{ -public: - explicit CIOStreamWrapper(aiFile* pFile, CIOSystemWrapper* io) - : mFile(pFile), - mIO(io) - {} - ~CIOStreamWrapper(void); - - size_t Read(void* pvBuffer, size_t pSize, size_t pCount); - size_t Write(const void* pvBuffer, size_t pSize, size_t pCount); - aiReturn Seek(size_t pOffset, aiOrigin pOrigin); - size_t Tell(void) const; - size_t FileSize() const; - void Flush(); - -private: - aiFile* mFile; - CIOSystemWrapper* mIO; -}; - -class CIOSystemWrapper : public IOSystem -{ - friend class CIOStreamWrapper; -public: - explicit CIOSystemWrapper(aiFileIO* pFile) - : mFileSystem(pFile) - {} - - bool Exists( const char* pFile) const; - char getOsSeparator() const; - IOStream* Open(const char* pFile,const char* pMode = "rb"); - void Close( IOStream* pFile); -private: - aiFileIO* mFileSystem; -}; - -} - -#endif - diff --git a/thirdparty/assimp/code/CalcTangentsProcess.cpp b/thirdparty/assimp/code/CalcTangentsProcess.cpp deleted file mode 100644 index b30f39c274..0000000000 --- a/thirdparty/assimp/code/CalcTangentsProcess.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of the post processing step to calculate - * tangents and bitangents for all imported meshes - */ - -// internal headers -#include "CalcTangentsProcess.h" -#include "ProcessHelper.h" -#include -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -CalcTangentsProcess::CalcTangentsProcess() -: configMaxAngle( AI_DEG_TO_RAD(45.f) ) -, configSourceUV( 0 ) { - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -CalcTangentsProcess::~CalcTangentsProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool CalcTangentsProcess::IsActive( unsigned int pFlags) const -{ - return (pFlags & aiProcess_CalcTangentSpace) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void CalcTangentsProcess::SetupProperties(const Importer* pImp) -{ - ai_assert( NULL != pImp ); - - // get the current value of the property - configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f); - configMaxAngle = std::max(std::min(configMaxAngle,45.0f),0.0f); - configMaxAngle = AI_DEG_TO_RAD(configMaxAngle); - - configSourceUV = pImp->GetPropertyInteger(AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX,0); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void CalcTangentsProcess::Execute( aiScene* pScene) -{ - ai_assert( NULL != pScene ); - - ASSIMP_LOG_DEBUG("CalcTangentsProcess begin"); - - bool bHas = false; - for ( unsigned int a = 0; a < pScene->mNumMeshes; a++ ) { - if(ProcessMesh( pScene->mMeshes[a],a))bHas = true; - } - - if ( bHas ) { - ASSIMP_LOG_INFO("CalcTangentsProcess finished. Tangents have been calculated"); - } else { - ASSIMP_LOG_DEBUG("CalcTangentsProcess finished"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Calculates tangents and bi-tangents for the given mesh -bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) -{ - // we assume that the mesh is still in the verbose vertex format where each face has its own set - // of vertices and no vertices are shared between faces. Sadly I don't know any quick test to - // assert() it here. - // assert( must be verbose, dammit); - - if (pMesh->mTangents) // this implies that mBitangents is also there - return false; - - // If the mesh consists of lines and/or points but not of - // triangles or higher-order polygons the normal vectors - // are undefined. - if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) - { - ASSIMP_LOG_INFO("Tangents are undefined for line and point meshes"); - return false; - } - - // what we can check, though, is if the mesh has normals and texture coordinates. That's a requirement - if( pMesh->mNormals == NULL) - { - ASSIMP_LOG_ERROR("Failed to compute tangents; need normals"); - return false; - } - if( configSourceUV >= AI_MAX_NUMBER_OF_TEXTURECOORDS || !pMesh->mTextureCoords[configSourceUV] ) - { - ASSIMP_LOG_ERROR((Formatter::format("Failed to compute tangents; need UV data in channel"),configSourceUV)); - return false; - } - - const float angleEpsilon = 0.9999f; - - std::vector vertexDone( pMesh->mNumVertices, false); - const float qnan = get_qnan(); - - // create space for the tangents and bitangents - pMesh->mTangents = new aiVector3D[pMesh->mNumVertices]; - pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices]; - - const aiVector3D* meshPos = pMesh->mVertices; - const aiVector3D* meshNorm = pMesh->mNormals; - const aiVector3D* meshTex = pMesh->mTextureCoords[configSourceUV]; - aiVector3D* meshTang = pMesh->mTangents; - aiVector3D* meshBitang = pMesh->mBitangents; - - // calculate the tangent and bitangent for every face - for( unsigned int a = 0; a < pMesh->mNumFaces; a++) - { - const aiFace& face = pMesh->mFaces[a]; - if (face.mNumIndices < 3) - { - // There are less than three indices, thus the tangent vector - // is not defined. We are finished with these vertices now, - // their tangent vectors are set to qnan. - for (unsigned int i = 0; i < face.mNumIndices;++i) - { - unsigned int idx = face.mIndices[i]; - vertexDone [idx] = true; - meshTang [idx] = aiVector3D(qnan); - meshBitang [idx] = aiVector3D(qnan); - } - - continue; - } - - // triangle or polygon... we always use only the first three indices. A polygon - // is supposed to be planar anyways.... - // FIXME: (thom) create correct calculation for multi-vertex polygons maybe? - const unsigned int p0 = face.mIndices[0], p1 = face.mIndices[1], p2 = face.mIndices[2]; - - // position differences p1->p2 and p1->p3 - aiVector3D v = meshPos[p1] - meshPos[p0], w = meshPos[p2] - meshPos[p0]; - - // texture offset p1->p2 and p1->p3 - float sx = meshTex[p1].x - meshTex[p0].x, sy = meshTex[p1].y - meshTex[p0].y; - float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y; - float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f; - // when t1, t2, t3 in same position in UV space, just use default UV direction. - if ( sx * ty == sy * tx ) { - sx = 0.0; sy = 1.0; - tx = 1.0; ty = 0.0; - } - - // tangent points in the direction where to positive X axis of the texture coord's would point in model space - // bitangent's points along the positive Y axis of the texture coord's, respectively - aiVector3D tangent, bitangent; - tangent.x = (w.x * sy - v.x * ty) * dirCorrection; - tangent.y = (w.y * sy - v.y * ty) * dirCorrection; - tangent.z = (w.z * sy - v.z * ty) * dirCorrection; - bitangent.x = (w.x * sx - v.x * tx) * dirCorrection; - bitangent.y = (w.y * sx - v.y * tx) * dirCorrection; - bitangent.z = (w.z * sx - v.z * tx) * dirCorrection; - - // store for every vertex of that face - for( unsigned int b = 0; b < face.mNumIndices; ++b ) { - unsigned int p = face.mIndices[b]; - - // project tangent and bitangent into the plane formed by the vertex' normal - aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]); - aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]); - localTangent.Normalize(); localBitangent.Normalize(); - - // reconstruct tangent/bitangent according to normal and bitangent/tangent when it's infinite or NaN. - bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z); - bool invalid_bitangent = is_special_float(localBitangent.x) || is_special_float(localBitangent.y) || is_special_float(localBitangent.z); - if (invalid_tangent != invalid_bitangent) { - if (invalid_tangent) { - localTangent = meshNorm[p] ^ localBitangent; - localTangent.Normalize(); - } else { - localBitangent = localTangent ^ meshNorm[p]; - localBitangent.Normalize(); - } - } - - // and write it into the mesh. - meshTang[ p ] = localTangent; - meshBitang[ p ] = localBitangent; - } - } - - - // create a helper to quickly find locally close vertices among the vertex array - // FIX: check whether we can reuse the SpatialSort of a previous step - SpatialSort* vertexFinder = NULL; - SpatialSort _vertexFinder; - float posEpsilon; - if (shared) - { - std::vector >* avf; - shared->GetProperty(AI_SPP_SPATIAL_SORT,avf); - if (avf) - { - std::pair& 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 verticesFound; - - const float fLimit = std::cos(configMaxAngle); - std::vector closeVertices; - - // in the second pass we now smooth out all tangents and bitangents at the same local position - // if they are not too far off. - for( unsigned int a = 0; a < pMesh->mNumVertices; a++) - { - if( vertexDone[a]) - continue; - - const aiVector3D& origPos = pMesh->mVertices[a]; - const aiVector3D& origNorm = pMesh->mNormals[a]; - const aiVector3D& origTang = pMesh->mTangents[a]; - const aiVector3D& origBitang = pMesh->mBitangents[a]; - closeVertices.resize( 0 ); - - // find all vertices close to that position - vertexFinder->FindPositions( origPos, posEpsilon, verticesFound); - - closeVertices.reserve (verticesFound.size()+5); - closeVertices.push_back( a); - - // look among them for other vertices sharing the same normal and a close-enough tangent/bitangent - for( unsigned int b = 0; b < verticesFound.size(); b++) - { - unsigned int idx = verticesFound[b]; - if( vertexDone[idx]) - continue; - if( meshNorm[idx] * origNorm < angleEpsilon) - continue; - if( meshTang[idx] * origTang < fLimit) - continue; - if( meshBitang[idx] * origBitang < fLimit) - continue; - - // it's similar enough -> add it to the smoothing group - closeVertices.push_back( idx); - vertexDone[idx] = true; - } - - // smooth the tangents and bitangents of all vertices that were found to be close enough - aiVector3D smoothTangent( 0, 0, 0), smoothBitangent( 0, 0, 0); - for( unsigned int b = 0; b < closeVertices.size(); ++b) - { - smoothTangent += meshTang[ closeVertices[b] ]; - smoothBitangent += meshBitang[ closeVertices[b] ]; - } - smoothTangent.Normalize(); - smoothBitangent.Normalize(); - - // and write it back into all affected tangents - for( unsigned int b = 0; b < closeVertices.size(); ++b) - { - meshTang[ closeVertices[b] ] = smoothTangent; - meshBitang[ closeVertices[b] ] = smoothBitangent; - } - } - return true; -} diff --git a/thirdparty/assimp/code/CalcTangentsProcess.h b/thirdparty/assimp/code/CalcTangentsProcess.h deleted file mode 100644 index 18775abcc7..0000000000 --- a/thirdparty/assimp/code/CalcTangentsProcess.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - - -/** @file Defines a post processing step to calculate tangents and - bitangents on all imported meshes.*/ -#ifndef AI_CALCTANGENTSPROCESS_H_INC -#define AI_CALCTANGENTSPROCESS_H_INC - -#include "BaseProcess.h" - -struct aiMesh; - -namespace Assimp -{ - -// --------------------------------------------------------------------------- -/** The CalcTangentsProcess calculates the tangent and bitangent for any vertex - * of all meshes. It is expected to be run before the JoinVerticesProcess runs - * because the joining of vertices also considers tangents and bitangents for - * uniqueness. - */ -class ASSIMP_API_WINONLY CalcTangentsProcess : public BaseProcess -{ -public: - - CalcTangentsProcess(); - ~CalcTangentsProcess(); - -public: - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag. - * @param pFlags The processing flags the importer was called with. - * A bitwise combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, - * false if not. - */ - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - /** Called prior to ExecuteOnScene(). - * The function is a request to the process to update its configuration - * basing on the Importer's configuration property list. - */ - void SetupProperties(const Importer* pImp); - - - // setter for configMaxAngle - inline void SetMaxSmoothAngle(float f) - { - configMaxAngle =f; - } - -protected: - - // ------------------------------------------------------------------- - /** Calculates tangents and bitangents for a specific mesh. - * @param pMesh The mesh to process. - * @param meshIndex Index of the mesh - */ - bool ProcessMesh( aiMesh* pMesh, unsigned int meshIndex); - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - -private: - - /** Configuration option: maximum smoothing angle, in radians*/ - float configMaxAngle; - unsigned int configSourceUV; -}; - -} // end of namespace Assimp - -#endif // AI_CALCTANGENTSPROCESS_H_INC diff --git a/thirdparty/assimp/code/Common/Assimp.cpp b/thirdparty/assimp/code/Common/Assimp.cpp new file mode 100644 index 0000000000..178b2c01d0 --- /dev/null +++ b/thirdparty/assimp/code/Common/Assimp.cpp @@ -0,0 +1,695 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CApi/CInterfaceIOWrapper.h" +#include "Importer.h" +#include "ScenePrivate.h" + +#include + +// ------------------------------------------------------------------------------------------------ +#ifndef ASSIMP_BUILD_SINGLETHREADED +# include +# include +#endif +// ------------------------------------------------------------------------------------------------ +using namespace Assimp; + +namespace Assimp { + // underlying structure for aiPropertyStore + typedef BatchLoader::PropertyMap PropertyMap; + + /** Stores the LogStream objects for all active C log streams */ + struct mpred { + bool operator () (const aiLogStream& s0, const aiLogStream& s1) const { + return s0.callback LogStreamMap; + + /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */ + typedef std::list PredefLogStreamMap; + + /** Local storage of all active log streams */ + static LogStreamMap gActiveLogStreams; + + /** Local storage of LogStreams allocated by #aiGetPredefinedLogStream */ + static PredefLogStreamMap gPredefinedStreams; + + /** Error message of the last failed import process */ + static std::string gLastErrorString; + + /** Verbose logging active or not? */ + static aiBool gVerboseLogging = false; + + /** will return all registered importers. */ + void GetImporterInstanceList(std::vector< BaseImporter* >& out); + + /** will delete all registered importers. */ + void DeleteImporterInstanceList(std::vector< BaseImporter* >& out); +} // namespace assimp + + +#ifndef ASSIMP_BUILD_SINGLETHREADED +/** Global mutex to manage the access to the log-stream map */ +static std::mutex gLogStreamMutex; +#endif + +// ------------------------------------------------------------------------------------------------ +// Custom LogStream implementation for the C-API +class LogToCallbackRedirector : public LogStream { +public: + explicit LogToCallbackRedirector(const aiLogStream& s) + : stream (s) { + ai_assert(NULL != s.callback); + } + + ~LogToCallbackRedirector() { +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard lock(gLogStreamMutex); +#endif + // (HACK) Check whether the 'stream.user' pointer points to a + // custom LogStream allocated by #aiGetPredefinedLogStream. + // In this case, we need to delete it, too. Of course, this + // might cause strange problems, but the chance is quite low. + + PredefLogStreamMap::iterator it = std::find(gPredefinedStreams.begin(), + gPredefinedStreams.end(), (Assimp::LogStream*)stream.user); + + if (it != gPredefinedStreams.end()) { + delete *it; + gPredefinedStreams.erase(it); + } + } + + /** @copydoc LogStream::write */ + void write(const char* message) { + stream.callback(message,stream.user); + } + +private: + aiLogStream stream; +}; + +// ------------------------------------------------------------------------------------------------ +void ReportSceneNotFoundError() { + ASSIMP_LOG_ERROR("Unable to find the Assimp::Importer for this aiScene. " + "The C-API does not accept scenes produced by the C++ API and vice versa"); + + ai_assert(false); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the given file and returns its content. +const aiScene* aiImportFile( const char* pFile, unsigned int pFlags) { + return aiImportFileEx(pFile,pFlags,NULL); +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* aiImportFileEx( const char* pFile, unsigned int pFlags, aiFileIO* pFS) { + return aiImportFileExWithProperties(pFile, pFlags, pFS, NULL); +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* aiImportFileExWithProperties( const char* pFile, unsigned int pFlags, + aiFileIO* pFS, const aiPropertyStore* props) { + ai_assert(NULL != pFile); + + const aiScene* scene = NULL; + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // create an Importer for this file + Assimp::Importer* imp = new Assimp::Importer(); + + // copy properties + if(props) { + const PropertyMap* pp = reinterpret_cast(props); + ImporterPimpl* pimpl = imp->Pimpl(); + pimpl->mIntProperties = pp->ints; + pimpl->mFloatProperties = pp->floats; + pimpl->mStringProperties = pp->strings; + pimpl->mMatrixProperties = pp->matrices; + } + // setup a custom IO system if necessary + if (pFS) { + imp->SetIOHandler( new CIOSystemWrapper (pFS) ); + } + + // and have it read the file + scene = imp->ReadFile( pFile, pFlags); + + // if succeeded, store the importer in the scene and keep it alive + if( scene) { + ScenePrivateData* priv = const_cast( ScenePriv(scene) ); + priv->mOrigImporter = imp; + } else { + // if failed, extract error code and destroy the import + gLastErrorString = imp->GetErrorString(); + delete imp; + } + + // return imported data. If the import failed the pointer is NULL anyways + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + + return scene; +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* aiImportFileFromMemory( + const char* pBuffer, + unsigned int pLength, + unsigned int pFlags, + const char* pHint) +{ + return aiImportFileFromMemoryWithProperties(pBuffer, pLength, pFlags, pHint, NULL); +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* aiImportFileFromMemoryWithProperties( + const char* pBuffer, + unsigned int pLength, + unsigned int pFlags, + const char* pHint, + const aiPropertyStore* props) +{ + ai_assert( NULL != pBuffer ); + ai_assert( 0 != pLength ); + + const aiScene* scene = NULL; + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // create an Importer for this file + Assimp::Importer* imp = new Assimp::Importer(); + + // copy properties + if(props) { + const PropertyMap* pp = reinterpret_cast(props); + ImporterPimpl* pimpl = imp->Pimpl(); + pimpl->mIntProperties = pp->ints; + pimpl->mFloatProperties = pp->floats; + pimpl->mStringProperties = pp->strings; + pimpl->mMatrixProperties = pp->matrices; + } + + // and have it read the file from the memory buffer + scene = imp->ReadFileFromMemory( pBuffer, pLength, pFlags,pHint); + + // if succeeded, store the importer in the scene and keep it alive + if( scene) { + ScenePrivateData* priv = const_cast( ScenePriv(scene) ); + priv->mOrigImporter = imp; + } + else { + // if failed, extract error code and destroy the import + gLastErrorString = imp->GetErrorString(); + delete imp; + } + // return imported data. If the import failed the pointer is NULL anyways + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return scene; +} + +// ------------------------------------------------------------------------------------------------ +// Releases all resources associated with the given import process. +void aiReleaseImport( const aiScene* pScene) +{ + if (!pScene) { + return; + } + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData* priv = ScenePriv(pScene); + if( !priv || !priv->mOrigImporter) { + delete pScene; + } + else { + // deleting the Importer also deletes the scene + // Note: the reason that this is not written as 'delete priv->mOrigImporter' + // is a suspected bug in gcc 4.4+ (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52339) + Importer* importer = priv->mOrigImporter; + delete importer; + } + + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const aiScene* aiApplyPostProcessing(const aiScene* pScene, + unsigned int pFlags) +{ + const aiScene* sc = NULL; + + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData* priv = ScenePriv(pScene); + if( !priv || !priv->mOrigImporter) { + ReportSceneNotFoundError(); + return NULL; + } + + sc = priv->mOrigImporter->ApplyPostProcessing(pFlags); + + if (!sc) { + aiReleaseImport(pScene); + return NULL; + } + + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return sc; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const aiScene *aiApplyCustomizedPostProcessing( const aiScene *scene, + BaseProcess* process, + bool requestValidation ) { + const aiScene* sc( NULL ); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData* priv = ScenePriv( scene ); + if ( NULL == priv || NULL == priv->mOrigImporter ) { + ReportSceneNotFoundError(); + return NULL; + } + + sc = priv->mOrigImporter->ApplyCustomizedPostProcessing( process, requestValidation ); + + if ( !sc ) { + aiReleaseImport( scene ); + return NULL; + } + + ASSIMP_END_EXCEPTION_REGION( const aiScene* ); + + return sc; +} + +// ------------------------------------------------------------------------------------------------ +void CallbackToLogRedirector (const char* msg, char* dt) +{ + ai_assert( NULL != msg ); + ai_assert( NULL != dt ); + LogStream* s = (LogStream*)dt; + + s->write(msg); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiLogStream aiGetPredefinedLogStream(aiDefaultLogStream pStream,const char* file) +{ + aiLogStream sout; + + ASSIMP_BEGIN_EXCEPTION_REGION(); + LogStream* stream = LogStream::createDefaultStream(pStream,file); + if (!stream) { + sout.callback = NULL; + sout.user = NULL; + } + else { + sout.callback = &CallbackToLogRedirector; + sout.user = (char*)stream; + } + gPredefinedStreams.push_back(stream); + ASSIMP_END_EXCEPTION_REGION(aiLogStream); + return sout; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiAttachLogStream( const aiLogStream* stream ) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard lock(gLogStreamMutex); +#endif + + LogStream* lg = new LogToCallbackRedirector(*stream); + gActiveLogStreams[*stream] = lg; + + if (DefaultLogger::isNullLogger()) { + DefaultLogger::create(NULL,(gVerboseLogging == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL)); + } + DefaultLogger::get()->attachStream(lg); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiReturn aiDetachLogStream( const aiLogStream* stream) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard lock(gLogStreamMutex); +#endif + // find the log-stream associated with this data + LogStreamMap::iterator it = gActiveLogStreams.find( *stream); + // it should be there... else the user is playing fools with us + if( it == gActiveLogStreams.end()) { + return AI_FAILURE; + } + DefaultLogger::get()->detatchStream( it->second ); + delete it->second; + + gActiveLogStreams.erase( it); + + if (gActiveLogStreams.empty()) { + DefaultLogger::kill(); + } + ASSIMP_END_EXCEPTION_REGION(aiReturn); + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiDetachAllLogStreams(void) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard lock(gLogStreamMutex); +#endif + Logger *logger( DefaultLogger::get() ); + if ( NULL == logger ) { + return; + } + + for (LogStreamMap::iterator it = gActiveLogStreams.begin(); it != gActiveLogStreams.end(); ++it) { + logger->detatchStream( it->second ); + delete it->second; + } + gActiveLogStreams.clear(); + DefaultLogger::kill(); + + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiEnableVerboseLogging(aiBool d) +{ + if (!DefaultLogger::isNullLogger()) { + DefaultLogger::get()->setLogSeverity((d == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL)); + } + gVerboseLogging = d; +} + +// ------------------------------------------------------------------------------------------------ +// Returns the error text of the last failed import process. +const char* aiGetErrorString() +{ + return gLastErrorString.c_str(); +} + +// ----------------------------------------------------------------------------------------------- +// Return the description of a importer given its index +const aiImporterDesc* aiGetImportFormatDescription( size_t pIndex) +{ + return Importer().GetImporterInfo(pIndex); +} + +// ----------------------------------------------------------------------------------------------- +// Return the number of importers +size_t aiGetImportFormatCount(void) +{ + return Importer().GetImporterCount(); +} + +// ------------------------------------------------------------------------------------------------ +// Returns the error text of the last failed import process. +aiBool aiIsExtensionSupported(const char* szExtension) +{ + ai_assert(NULL != szExtension); + aiBool candoit=AI_FALSE; + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // FIXME: no need to create a temporary Importer instance just for that .. + Assimp::Importer tmp; + candoit = tmp.IsExtensionSupported(std::string(szExtension)) ? AI_TRUE : AI_FALSE; + + ASSIMP_END_EXCEPTION_REGION(aiBool); + return candoit; +} + +// ------------------------------------------------------------------------------------------------ +// Get a list of all file extensions supported by ASSIMP +void aiGetExtensionList(aiString* szOut) +{ + ai_assert(NULL != szOut); + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // FIXME: no need to create a temporary Importer instance just for that .. + Assimp::Importer tmp; + tmp.GetExtensionList(*szOut); + + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Get the memory requirements for a particular import. +void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn, + C_STRUCT aiMemoryInfo* in) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData* priv = ScenePriv(pIn); + if( !priv || !priv->mOrigImporter) { + ReportSceneNotFoundError(); + return; + } + + return priv->mOrigImporter->GetMemoryRequirements(*in); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiPropertyStore* aiCreatePropertyStore(void) +{ + return reinterpret_cast( new PropertyMap() ); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiReleasePropertyStore(aiPropertyStore* p) +{ + delete reinterpret_cast(p); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyInteger +ASSIMP_API void aiSetImportPropertyInteger(aiPropertyStore* p, const char* szName, int value) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap* pp = reinterpret_cast(p); + SetGenericProperty(pp->ints,szName,value); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyFloat +ASSIMP_API void aiSetImportPropertyFloat(aiPropertyStore* p, const char* szName, ai_real value) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap* pp = reinterpret_cast(p); + SetGenericProperty(pp->floats,szName,value); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyString +ASSIMP_API void aiSetImportPropertyString(aiPropertyStore* p, const char* szName, + const C_STRUCT aiString* st) +{ + if (!st) { + return; + } + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap* pp = reinterpret_cast(p); + SetGenericProperty(pp->strings,szName,std::string(st->C_Str())); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyMatrix +ASSIMP_API void aiSetImportPropertyMatrix(aiPropertyStore* p, const char* szName, + const C_STRUCT aiMatrix4x4* mat) +{ + if (!mat) { + return; + } + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap* pp = reinterpret_cast(p); + SetGenericProperty(pp->matrices,szName,*mat); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Rotation matrix to quaternion +ASSIMP_API void aiCreateQuaternionFromMatrix(aiQuaternion* quat,const aiMatrix3x3* mat) +{ + ai_assert( NULL != quat ); + ai_assert( NULL != mat ); + *quat = aiQuaternion(*mat); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix decomposition +ASSIMP_API void aiDecomposeMatrix(const aiMatrix4x4* mat,aiVector3D* scaling, + aiQuaternion* rotation, + aiVector3D* position) +{ + ai_assert( NULL != rotation ); + ai_assert( NULL != position ); + ai_assert( NULL != scaling ); + ai_assert( NULL != mat ); + mat->Decompose(*scaling,*rotation,*position); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix transpose +ASSIMP_API void aiTransposeMatrix3(aiMatrix3x3* mat) +{ + ai_assert(NULL != mat); + mat->Transpose(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiTransposeMatrix4(aiMatrix4x4* mat) +{ + ai_assert(NULL != mat); + mat->Transpose(); +} + +// ------------------------------------------------------------------------------------------------ +// Vector transformation +ASSIMP_API void aiTransformVecByMatrix3(aiVector3D* vec, + const aiMatrix3x3* mat) +{ + ai_assert( NULL != mat ); + ai_assert( NULL != vec); + *vec *= (*mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiTransformVecByMatrix4(aiVector3D* vec, + const aiMatrix4x4* mat) +{ + ai_assert( NULL != mat ); + ai_assert( NULL != vec ); + + *vec *= (*mat); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix multiplication +ASSIMP_API void aiMultiplyMatrix4( + aiMatrix4x4* dst, + const aiMatrix4x4* src) +{ + ai_assert( NULL != dst ); + ai_assert( NULL != src ); + *dst = (*dst) * (*src); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMultiplyMatrix3( + aiMatrix3x3* dst, + const aiMatrix3x3* src) +{ + ai_assert( NULL != dst ); + ai_assert( NULL != src ); + *dst = (*dst) * (*src); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix identity +ASSIMP_API void aiIdentityMatrix3( + aiMatrix3x3* mat) +{ + ai_assert(NULL != mat); + *mat = aiMatrix3x3(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiIdentityMatrix4( + aiMatrix4x4* mat) +{ + ai_assert(NULL != mat); + *mat = aiMatrix4x4(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API C_STRUCT const aiImporterDesc* aiGetImporterDesc( const char *extension ) { + if( NULL == extension ) { + return NULL; + } + const aiImporterDesc *desc( NULL ); + std::vector< BaseImporter* > out; + GetImporterInstanceList( out ); + for( size_t i = 0; i < out.size(); ++i ) { + if( 0 == strncmp( out[ i ]->GetInfo()->mFileExtensions, extension, strlen( extension ) ) ) { + desc = out[ i ]->GetInfo(); + break; + } + } + + DeleteImporterInstanceList(out); + + return desc; +} + +// ------------------------------------------------------------------------------------------------ diff --git a/thirdparty/assimp/code/Common/BaseImporter.cpp b/thirdparty/assimp/code/Common/BaseImporter.cpp new file mode 100644 index 0000000000..0a5694aa0e --- /dev/null +++ b/thirdparty/assimp/code/Common/BaseImporter.cpp @@ -0,0 +1,620 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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 +#include +#include "FileSystemFilter.h" +#include "Importer.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BaseImporter::BaseImporter() AI_NO_EXCEPT +: m_progress() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BaseImporter::~BaseImporter() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file and returns the imported data. +aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) { + m_progress = pImp->GetProgressHandler(); + if (nullptr == m_progress) { + return nullptr; + } + + ai_assert(m_progress); + + // Gather configuration properties for this run + SetupProperties( pImp ); + + // Construct a file system filter to improve our success ratio at reading external files + FileSystemFilter filter(pFile,pIOHandler); + + // create a scene object to hold the data + std::unique_ptr sc(new aiScene()); + + // dispatch importing + try + { + InternReadFile( pFile, sc.get(), &filter); + + } catch( const std::exception& err ) { + // extract error description + m_ErrorText = err.what(); + ASSIMP_LOG_ERROR(m_ErrorText); + return nullptr; + } + + // return what we gathered from the import. + return sc.release(); +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::SetupProperties(const Importer* /*pImp*/) +{ + // the default implementation does nothing +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::GetExtensionList(std::set& extensions) { + const aiImporterDesc* desc = GetInfo(); + ai_assert(desc != nullptr); + + const char* ext = desc->mFileExtensions; + ai_assert(ext != nullptr ); + + const char* last = ext; + do { + if (!*ext || *ext == ' ') { + extensions.insert(std::string(last,ext-last)); + ai_assert(ext-last > 0); + last = ext; + while(*last == ' ') { + ++last; + } + } + } + while(*ext++); +} + +// ------------------------------------------------------------------------------------------------ +/*static*/ bool BaseImporter::SearchFileHeaderForToken( IOSystem* pIOHandler, + const std::string& pFile, + const char** tokens, + unsigned int numTokens, + unsigned int searchBytes /* = 200 */, + bool tokensSol /* false */, + bool noAlphaBeforeTokens /* false */) +{ + ai_assert( nullptr != tokens ); + ai_assert( 0 != numTokens ); + ai_assert( 0 != searchBytes); + + if ( nullptr == pIOHandler ) { + return false; + } + + std::unique_ptr pStream (pIOHandler->Open(pFile)); + if (pStream.get() ) { + // read 200 characters from the file + std::unique_ptr _buffer (new char[searchBytes+1 /* for the '\0' */]); + char *buffer( _buffer.get() ); + const size_t read( pStream->Read(buffer,1,searchBytes) ); + if( 0 == read ) { + return false; + } + + for( size_t i = 0; i < read; ++i ) { + buffer[ i ] = static_cast( ::tolower( buffer[ i ] ) ); + } + + // It is not a proper handling of unicode files here ... + // ehm ... but it works in most cases. + char* cur = buffer,*cur2 = buffer,*end = &buffer[read]; + while (cur != end) { + if( *cur ) { + *cur2++ = *cur; + } + ++cur; + } + *cur2 = '\0'; + + std::string token; + for (unsigned int i = 0; i < numTokens; ++i ) { + ai_assert( nullptr != tokens[i] ); + const size_t len( strlen( tokens[ i ] ) ); + token.clear(); + const char *ptr( tokens[ i ] ); + for ( size_t tokIdx = 0; tokIdx < len; ++tokIdx ) { + token.push_back( static_cast( tolower( *ptr ) ) ); + ++ptr; + } + const char* r = strstr( buffer, token.c_str() ); + if( !r ) { + continue; + } + // We need to make sure that we didn't accidentially identify the end of another token as our token, + // e.g. in a previous version the "gltf " present in some gltf files was detected as "f " + if (noAlphaBeforeTokens && (r != buffer && isalpha(r[-1]))) { + continue; + } + // We got a match, either we don't care where it is, or it happens to + // be in the beginning of the file / line + if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') { + ASSIMP_LOG_DEBUG_F( "Found positive match for header keyword: ", tokens[i] ); + return true; + } + } + } + + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Simple check for file extension +/*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile, + const char* ext0, + const char* ext1, + const char* ext2) +{ + std::string::size_type pos = pFile.find_last_of('.'); + + // no file extension - can't read + if( pos == std::string::npos) + return false; + + const char* ext_real = & pFile[ pos+1 ]; + if( !ASSIMP_stricmp(ext_real,ext0) ) + return true; + + // check for other, optional, file extensions + if (ext1 && !ASSIMP_stricmp(ext_real,ext1)) + return true; + + if (ext2 && !ASSIMP_stricmp(ext_real,ext2)) + return true; + + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Get file extension from path +std::string BaseImporter::GetExtension( const std::string& file ) { + std::string::size_type pos = file.find_last_of('.'); + + // no file extension at all + if (pos == std::string::npos) { + return ""; + } + + + // thanks to Andy Maloney for the hint + std::string ret = file.substr( pos + 1 ); + std::transform( ret.begin(), ret.end(), ret.begin(), ToLower); + + return ret; +} + +// ------------------------------------------------------------------------------------------------ +// Check for magic bytes at the beginning of the file. +/* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile, + const void* _magic, unsigned int num, unsigned int offset, unsigned int size) +{ + ai_assert( size <= 16 ); + ai_assert( _magic ); + + if (!pIOHandler) { + return false; + } + union { + const char* magic; + const uint16_t* magic_u16; + const uint32_t* magic_u32; + }; + magic = reinterpret_cast(_magic); + std::unique_ptr pStream (pIOHandler->Open(pFile)); + if (pStream.get() ) { + + // skip to offset + pStream->Seek(offset,aiOrigin_SET); + + // read 'size' characters from the file + union { + char data[16]; + uint16_t data_u16[8]; + uint32_t data_u32[4]; + }; + if(size != pStream->Read(data,1,size)) { + return false; + } + + for (unsigned int i = 0; i < num; ++i) { + // also check against big endian versions of tokens with size 2,4 + // that's just for convenience, the chance that we cause conflicts + // is quite low and it can save some lines and prevent nasty bugs + if (2 == size) { + uint16_t rev = *magic_u16; + ByteSwap::Swap(&rev); + if (data_u16[0] == *magic_u16 || data_u16[0] == rev) { + return true; + } + } + else if (4 == size) { + uint32_t rev = *magic_u32; + ByteSwap::Swap(&rev); + if (data_u32[0] == *magic_u32 || data_u32[0] == rev) { + return true; + } + } + else { + // any length ... just compare + if(!memcmp(magic,data,size)) { + return true; + } + } + magic += size; + } + } + return false; +} + +#ifdef ASSIMP_USE_HUNTER +# include +#else +# include "../contrib/utf8cpp/source/utf8.h" +#endif + +// ------------------------------------------------------------------------------------------------ +// Convert to UTF8 data +void BaseImporter::ConvertToUTF8(std::vector& data) +{ + //ConversionResult result; + if(data.size() < 8) { + throw DeadlyImportError("File is too small"); + } + + // UTF 8 with BOM + if((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) { + ASSIMP_LOG_DEBUG("Found UTF-8 BOM ..."); + + std::copy(data.begin()+3,data.end(),data.begin()); + data.resize(data.size()-3); + return; + } + + + // UTF 32 BE with BOM + if(*((uint32_t*)&data.front()) == 0xFFFE0000) { + + // swap the endianness .. + for(uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) { + AI_SWAP4P(p); + } + } + + // UTF 32 LE with BOM + if(*((uint32_t*)&data.front()) == 0x0000FFFE) { + ASSIMP_LOG_DEBUG("Found UTF-32 BOM ..."); + + std::vector output; + int *ptr = (int*)&data[ 0 ]; + int *end = ptr + ( data.size() / sizeof(int) ) +1; + utf8::utf32to8( ptr, end, back_inserter(output)); + return; + } + + // UTF 16 BE with BOM + if(*((uint16_t*)&data.front()) == 0xFFFE) { + + // swap the endianness .. + for(uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) { + ByteSwap::Swap2(p); + } + } + + // UTF 16 LE with BOM + if(*((uint16_t*)&data.front()) == 0xFEFF) { + ASSIMP_LOG_DEBUG("Found UTF-16 BOM ..."); + + std::vector output; + utf8::utf16to8(data.begin(), data.end(), back_inserter(output)); + return; + } +} + +// ------------------------------------------------------------------------------------------------ +// Convert to UTF8 data to ISO-8859-1 +void BaseImporter::ConvertUTF8toISO8859_1(std::string& data) +{ + size_t size = data.size(); + size_t i = 0, j = 0; + + while(i < size) { + if ((unsigned char) data[i] < (size_t) 0x80) { + data[j] = data[i]; + } else if(i < size - 1) { + if((unsigned char) data[i] == 0xC2) { + data[j] = data[++i]; + } else if((unsigned char) data[i] == 0xC3) { + data[j] = ((unsigned char) data[++i] + 0x40); + } else { + std::stringstream stream; + stream << "UTF8 code " << std::hex << data[i] << data[i + 1] << " can not be converted into ISA-8859-1."; + ASSIMP_LOG_ERROR( stream.str() ); + + data[j++] = data[i++]; + data[j] = data[i]; + } + } else { + ASSIMP_LOG_ERROR("UTF8 code but only one character remaining"); + + data[j] = data[i]; + } + + i++; j++; + } + + data.resize(j); +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::TextFileToBuffer(IOStream* stream, + std::vector& data, + TextFileMode mode) +{ + ai_assert(nullptr != stream); + + const size_t fileSize = stream->FileSize(); + if (mode == FORBID_EMPTY) { + if(!fileSize) { + throw DeadlyImportError("File is empty"); + } + } + + data.reserve(fileSize+1); + data.resize(fileSize); + if(fileSize > 0) { + if(fileSize != stream->Read( &data[0], 1, fileSize)) { + throw DeadlyImportError("File read error"); + } + + ConvertToUTF8(data); + } + + // append a binary zero to simplify string parsing + data.push_back(0); +} + +// ------------------------------------------------------------------------------------------------ +namespace Assimp { + // Represents an import request + struct LoadRequest { + LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id) + : file(_file) + , flags(_flags) + , refCnt(1) + , scene(NULL) + , loaded(false) + , id(_id) { + if ( _map ) { + map = *_map; + } + } + + bool operator== ( const std::string& f ) const { + return file == f; + } + + const std::string file; + unsigned int flags; + unsigned int refCnt; + aiScene *scene; + bool loaded; + BatchLoader::PropertyMap map; + unsigned int id; + }; +} + +// ------------------------------------------------------------------------------------------------ +// BatchLoader::pimpl data structure +struct Assimp::BatchData { + BatchData( IOSystem* pIO, bool validate ) + : pIOSystem( pIO ) + , pImporter( nullptr ) + , next_id(0xffff) + , validate( validate ) { + ai_assert( nullptr != pIO ); + + pImporter = new Importer(); + pImporter->SetIOHandler( pIO ); + } + + ~BatchData() { + pImporter->SetIOHandler( nullptr ); /* get pointer back into our possession */ + delete pImporter; + } + + // IO system to be used for all imports + IOSystem* pIOSystem; + + // Importer used to load all meshes + Importer* pImporter; + + // List of all imports + std::list requests; + + // Base path + std::string pathBase; + + // Id for next item + unsigned int next_id; + + // Validation enabled state + bool validate; +}; + +typedef std::list::iterator LoadReqIt; + +// ------------------------------------------------------------------------------------------------ +BatchLoader::BatchLoader(IOSystem* pIO, bool validate ) { + ai_assert(nullptr != pIO); + + m_data = new BatchData( pIO, validate ); +} + +// ------------------------------------------------------------------------------------------------ +BatchLoader::~BatchLoader() +{ + // delete all scenes what have not been polled by the user + for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { + delete (*it).scene; + } + delete m_data; +} + +// ------------------------------------------------------------------------------------------------ +void BatchLoader::setValidation( bool enabled ) { + m_data->validate = enabled; +} + +// ------------------------------------------------------------------------------------------------ +bool BatchLoader::getValidation() const { + return m_data->validate; +} + +// ------------------------------------------------------------------------------------------------ +unsigned int BatchLoader::AddLoadRequest(const std::string& file, + unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/) +{ + ai_assert(!file.empty()); + + // check whether we have this loading request already + for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { + // Call IOSystem's path comparison function here + if ( m_data->pIOSystem->ComparePaths((*it).file,file)) { + if (map) { + if ( !( ( *it ).map == *map ) ) { + continue; + } + } + else if ( !( *it ).map.empty() ) { + continue; + } + + (*it).refCnt++; + return (*it).id; + } + } + + // no, we don't have it. So add it to the queue ... + m_data->requests.push_back(LoadRequest(file,steps,map, m_data->next_id)); + return m_data->next_id++; +} + +// ------------------------------------------------------------------------------------------------ +aiScene* BatchLoader::GetImport( unsigned int which ) +{ + for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { + if ((*it).id == which && (*it).loaded) { + aiScene* sc = (*it).scene; + if (!(--(*it).refCnt)) { + m_data->requests.erase(it); + } + return sc; + } + } + return nullptr; +} + +// ------------------------------------------------------------------------------------------------ +void BatchLoader::LoadAll() +{ + // no threaded implementation for the moment + for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { + // force validation in debug builds + unsigned int pp = (*it).flags; + if ( m_data->validate ) { + pp |= aiProcess_ValidateDataStructure; + } + + // setup config properties if necessary + ImporterPimpl* pimpl = m_data->pImporter->Pimpl(); + pimpl->mFloatProperties = (*it).map.floats; + pimpl->mIntProperties = (*it).map.ints; + pimpl->mStringProperties = (*it).map.strings; + pimpl->mMatrixProperties = (*it).map.matrices; + + if (!DefaultLogger::isNullLogger()) + { + ASSIMP_LOG_INFO("%%% BEGIN EXTERNAL FILE %%%"); + ASSIMP_LOG_INFO_F("File: ", (*it).file); + } + m_data->pImporter->ReadFile((*it).file,pp); + (*it).scene = m_data->pImporter->GetOrphanedScene(); + (*it).loaded = true; + + ASSIMP_LOG_INFO("%%% END EXTERNAL FILE %%%"); + } +} diff --git a/thirdparty/assimp/code/Common/BaseProcess.cpp b/thirdparty/assimp/code/Common/BaseProcess.cpp new file mode 100644 index 0000000000..e247be418d --- /dev/null +++ b/thirdparty/assimp/code/Common/BaseProcess.cpp @@ -0,0 +1,107 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of BaseProcess */ + +#include +#include "BaseProcess.h" +#include +#include +#include "Importer.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BaseProcess::BaseProcess() AI_NO_EXCEPT +: shared() +, progress() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BaseProcess::~BaseProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +void BaseProcess::ExecuteOnScene( Importer* pImp) +{ + ai_assert(NULL != pImp && NULL != pImp->Pimpl()->mScene); + + progress = pImp->GetProgressHandler(); + ai_assert(progress); + + SetupProperties( pImp ); + + // catch exceptions thrown inside the PostProcess-Step + try + { + Execute(pImp->Pimpl()->mScene); + + } catch( const std::exception& err ) { + + // extract error description + pImp->Pimpl()->mErrorString = err.what(); + ASSIMP_LOG_ERROR(pImp->Pimpl()->mErrorString); + + // and kill the partially imported data + delete pImp->Pimpl()->mScene; + pImp->Pimpl()->mScene = nullptr; + } +} + +// ------------------------------------------------------------------------------------------------ +void BaseProcess::SetupProperties(const Importer* /*pImp*/) +{ + // the default implementation does nothing +} + +// ------------------------------------------------------------------------------------------------ +bool BaseProcess::RequireVerboseFormat() const +{ + return true; +} + diff --git a/thirdparty/assimp/code/Common/BaseProcess.h b/thirdparty/assimp/code/Common/BaseProcess.h new file mode 100644 index 0000000000..4d5c7a76be --- /dev/null +++ b/thirdparty/assimp/code/Common/BaseProcess.h @@ -0,0 +1,290 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Base class of all import post processing steps */ +#ifndef INCLUDED_AI_BASEPROCESS_H +#define INCLUDED_AI_BASEPROCESS_H + +#include +#include + +struct aiScene; + +namespace Assimp { + +class Importer; + +// --------------------------------------------------------------------------- +/** Helper class to allow post-processing steps to interact with each other. + * + * The class maintains a simple property list that can be used by pp-steps + * to provide additional information to other steps. This is primarily + * intended for cross-step optimizations. + */ +class SharedPostProcessInfo +{ +public: + + struct Base + { + virtual ~Base() + {} + }; + + //! Represents data that is allocated on the heap, thus needs to be deleted + template + struct THeapData : public Base + { + explicit THeapData(T* in) + : data (in) + {} + + ~THeapData() + { + delete data; + } + T* data; + }; + + //! Represents static, by-value data not allocated on the heap + template + struct TStaticData : public Base + { + explicit TStaticData(T in) + : data (in) + {} + + ~TStaticData() + {} + + T data; + }; + + // some typedefs for cleaner code + typedef unsigned int KeyType; + typedef std::map 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 + void AddProperty( const char* name, T* in ){ + AddProperty(name,(Base*)new THeapData(in)); + } + + //! Add a static by-value property to the list + template + void AddProperty( const char* name, T in ){ + AddProperty(name,(Base*)new TStaticData(in)); + } + + + //! Get a heap property + template + bool GetProperty( const char* name, T*& out ) const + { + THeapData* t = (THeapData*)GetPropertyInternal(name); + if(!t) + { + out = NULL; + return false; + } + out = t->data; + return true; + } + + //! Get a static, by-value property + template + bool GetProperty( const char* name, T& out ) const + { + TStaticData* t = (TStaticData*)GetPropertyInternal(name); + if(!t)return false; + out = t->data; + return true; + } + + //! Remove a property of a specific type + void RemoveProperty( const char* name) { + SetGenericPropertyPtr(pmap,name,NULL); + } + +private: + + void AddProperty( const char* name, Base* data) { + SetGenericPropertyPtr(pmap,name,data); + } + + Base* GetPropertyInternal( const char* name) const { + return GetGenericProperty(pmap,name,NULL); + } + +private: + + //! Map of all stored properties + PropertyMap pmap; +}; + +#if 0 + +// --------------------------------------------------------------------------- +/** @brief Represents a dependency table for a postprocessing steps. + * + * For future use. + */ + struct PPDependencyTable + { + unsigned int execute_me_before_these; + unsigned int execute_me_after_these; + unsigned int only_if_these_are_not_specified; + unsigned int mutually_exclusive_with; + }; + +#endif + + +#define AI_SPP_SPATIAL_SORT "$Spat" + +// --------------------------------------------------------------------------- +/** The BaseProcess defines a common interface for all post processing steps. + * A post processing step is run after a successful import if the caller + * specified the corresponding flag when calling ReadFile(). + * Enum #aiPostProcessSteps defines which flags are available. + * After a successful import the Importer iterates over its internal array + * of processes and calls IsActive() on each process to evaluate if the step + * should be executed. If the function returns true, the class' Execute() + * function is called subsequently. + */ +class ASSIMP_API_WINONLY BaseProcess { + friend class Importer; + +public: + /** Constructor to be privately used by Importer */ + BaseProcess() AI_NO_EXCEPT; + + /** Destructor, private as well */ + virtual ~BaseProcess(); + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. A + * bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + virtual bool IsActive( unsigned int pFlags) const = 0; + + // ------------------------------------------------------------------- + /** Check whether this step expects its input vertex data to be + * in verbose format. */ + virtual bool RequireVerboseFormat() const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * The function deletes the scene if the postprocess step fails ( + * the object pointer will be set to NULL). + * @param pImp Importer instance (pImp->mScene must be valid) + */ + void ExecuteOnScene( Importer* pImp); + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + virtual void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * A process should throw an ImportErrorException* if it fails. + * This method must be implemented by deriving classes. + * @param pScene The imported data to work at. + */ + virtual void Execute( aiScene* pScene) = 0; + + + // ------------------------------------------------------------------- + /** Assign a new SharedPostProcessInfo to the step. This object + * allows multiple postprocess steps to share data. + * @param sh May be NULL + */ + inline void SetSharedData(SharedPostProcessInfo* sh) { + shared = sh; + } + + // ------------------------------------------------------------------- + /** Get the shared data that is assigned to the step. + */ + inline SharedPostProcessInfo* GetSharedData() { + return shared; + } + +protected: + + /** See the doc of #SharedPostProcessInfo for more details */ + SharedPostProcessInfo* shared; + + /** Currently active progress handler */ + ProgressHandler* progress; +}; + + +} // end of namespace Assimp + +#endif // AI_BASEPROCESS_H_INC diff --git a/thirdparty/assimp/code/Common/Bitmap.cpp b/thirdparty/assimp/code/Common/Bitmap.cpp new file mode 100644 index 0000000000..b22b71ea9e --- /dev/null +++ b/thirdparty/assimp/code/Common/Bitmap.cpp @@ -0,0 +1,155 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Bitmap.cpp + * @brief Defines bitmap format helper for textures + * + * Used for file formats which embed their textures into the model file. + */ + + +#include +#include +#include +#include + +namespace Assimp { + + void Bitmap::Save(aiTexture* texture, IOStream* file) { + if(file != NULL) { + Header header; + DIB dib; + + dib.size = DIB::dib_size; + dib.width = texture->mWidth; + dib.height = texture->mHeight; + dib.planes = 1; + dib.bits_per_pixel = 8 * mBytesPerPixel; + dib.compression = 0; + dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height; + dib.x_resolution = 0; + dib.y_resolution = 0; + dib.nb_colors = 0; + dib.nb_important_colors = 0; + + header.type = 0x4D42; // 'BM' + header.offset = Header::header_size + DIB::dib_size; + header.size = header.offset + dib.image_size; + header.reserved1 = 0; + header.reserved2 = 0; + + WriteHeader(header, file); + WriteDIB(dib, file); + WriteData(texture, file); + } + } + + template + inline + std::size_t Copy(uint8_t* data, const T &field) { +#ifdef AI_BUILD_BIG_ENDIAN + T field_swapped=AI_BE(field); + std::memcpy(data, &field_swapped, sizeof(field)); return sizeof(field); +#else + std::memcpy(data, &AI_BE(field), sizeof(field)); return sizeof(field); +#endif + } + + void Bitmap::WriteHeader(Header& header, IOStream* file) { + uint8_t data[Header::header_size]; + + std::size_t offset = 0; + + offset += Copy(&data[offset], header.type); + offset += Copy(&data[offset], header.size); + offset += Copy(&data[offset], header.reserved1); + offset += Copy(&data[offset], header.reserved2); + Copy(&data[offset], header.offset); + + file->Write(data, Header::header_size, 1); + } + + void Bitmap::WriteDIB(DIB& dib, IOStream* file) { + uint8_t data[DIB::dib_size]; + + std::size_t offset = 0; + + offset += Copy(&data[offset], dib.size); + offset += Copy(&data[offset], dib.width); + offset += Copy(&data[offset], dib.height); + offset += Copy(&data[offset], dib.planes); + offset += Copy(&data[offset], dib.bits_per_pixel); + offset += Copy(&data[offset], dib.compression); + offset += Copy(&data[offset], dib.image_size); + offset += Copy(&data[offset], dib.x_resolution); + offset += Copy(&data[offset], dib.y_resolution); + offset += Copy(&data[offset], dib.nb_colors); + Copy(&data[offset], dib.nb_important_colors); + + file->Write(data, DIB::dib_size, 1); + } + + void Bitmap::WriteData(aiTexture* texture, IOStream* file) { + static const std::size_t padding_offset = 4; + static const uint8_t padding_data[padding_offset] = {0x0, 0x0, 0x0, 0x0}; + + unsigned int padding = (padding_offset - ((mBytesPerPixel * texture->mWidth) % padding_offset)) % padding_offset; + uint8_t pixel[mBytesPerPixel]; + + for(std::size_t i = 0; i < texture->mHeight; ++i) { + for(std::size_t j = 0; j < texture->mWidth; ++j) { + const aiTexel& texel = texture->pcData[(texture->mHeight - i - 1) * texture->mWidth + j]; // Bitmap files are stored in bottom-up format + + pixel[0] = texel.r; + pixel[1] = texel.g; + pixel[2] = texel.b; + pixel[3] = texel.a; + + file->Write(pixel, mBytesPerPixel, 1); + } + + file->Write(padding_data, padding, 1); + } + } + +} diff --git a/thirdparty/assimp/code/Common/CreateAnimMesh.cpp b/thirdparty/assimp/code/Common/CreateAnimMesh.cpp new file mode 100644 index 0000000000..98b60e5319 --- /dev/null +++ b/thirdparty/assimp/code/Common/CreateAnimMesh.cpp @@ -0,0 +1,88 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (C) 2016 The Qt Company Ltd. +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#include + +namespace Assimp { + +aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh) +{ + aiAnimMesh *animesh = new aiAnimMesh; + animesh->mNumVertices = mesh->mNumVertices; + if (mesh->mVertices) { + animesh->mVertices = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mVertices, mesh->mVertices, mesh->mNumVertices * sizeof(aiVector3D)); + } + if (mesh->mNormals) { + animesh->mNormals = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mNormals, mesh->mNormals, mesh->mNumVertices * sizeof(aiVector3D)); + } + if (mesh->mTangents) { + animesh->mTangents = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mTangents, mesh->mTangents, mesh->mNumVertices * sizeof(aiVector3D)); + } + if (mesh->mBitangents) { + animesh->mBitangents = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mBitangents, mesh->mBitangents, mesh->mNumVertices * sizeof(aiVector3D)); + } + + for (int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) { + if (mesh->mColors[i]) { + animesh->mColors[i] = new aiColor4D[animesh->mNumVertices]; + std::memcpy(animesh->mColors[i], mesh->mColors[i], mesh->mNumVertices * sizeof(aiColor4D)); + } else { + animesh->mColors[i] = NULL; + } + } + + for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (mesh->mTextureCoords[i]) { + animesh->mTextureCoords[i] = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mTextureCoords[i], mesh->mTextureCoords[i], mesh->mNumVertices * sizeof(aiVector3D)); + } else { + animesh->mTextureCoords[i] = NULL; + } + } + return animesh; +} + +} // end of namespace Assimp diff --git a/thirdparty/assimp/code/Common/DefaultIOStream.cpp b/thirdparty/assimp/code/Common/DefaultIOStream.cpp new file mode 100644 index 0000000000..1c100b6189 --- /dev/null +++ b/thirdparty/assimp/code/Common/DefaultIOStream.cpp @@ -0,0 +1,154 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file DefaultIOStream.cpp + * @brief Default File I/O implementation for #Importer + */ + + +#include +#include +#include +#include + +using namespace Assimp; + +// ---------------------------------------------------------------------------------- +DefaultIOStream::~DefaultIOStream() +{ + if (mFile) { + ::fclose(mFile); + mFile = nullptr; + } +} + +// ---------------------------------------------------------------------------------- +size_t DefaultIOStream::Read(void* pvBuffer, + size_t pSize, + size_t pCount) +{ + ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount); + return (mFile ? ::fread(pvBuffer, pSize, pCount, mFile) : 0); +} + +// ---------------------------------------------------------------------------------- +size_t DefaultIOStream::Write(const void* pvBuffer, + size_t pSize, + size_t pCount) +{ + ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount); + return (mFile ? ::fwrite(pvBuffer, pSize, pCount, mFile) : 0); +} + +// ---------------------------------------------------------------------------------- +aiReturn DefaultIOStream::Seek(size_t pOffset, + aiOrigin pOrigin) +{ + if (!mFile) { + return AI_FAILURE; + } + + // Just to check whether our enum maps one to one with the CRT constants + static_assert(aiOrigin_CUR == SEEK_CUR && + aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET, "aiOrigin_CUR == SEEK_CUR && \ + aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET"); + + // do the seek + return (0 == ::fseek(mFile, (long)pOffset,(int)pOrigin) ? AI_SUCCESS : AI_FAILURE); +} + +// ---------------------------------------------------------------------------------- +size_t DefaultIOStream::Tell() const +{ + if (!mFile) { + return 0; + } + return ::ftell(mFile); +} + +// ---------------------------------------------------------------------------------- +size_t DefaultIOStream::FileSize() const +{ + if (! mFile || mFilename.empty()) { + return 0; + } + + if (SIZE_MAX == mCachedSize ) { + + // Although fseek/ftell would allow us to reuse the existing file handle here, + // it is generally unsafe because: + // - For binary streams, it is not technically well-defined + // - For text files the results are meaningless + // That's why we use the safer variant fstat here. + // + // See here for details: + // https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file +#if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) + struct __stat64 fileStat; + //using fileno + fstat avoids having to handle the filename + int err = _fstat64( _fileno(mFile), &fileStat ); + if (0 != err) + return 0; + mCachedSize = (size_t) (fileStat.st_size); +#elif defined __GNUC__ || defined __APPLE__ || defined __MACH__ || defined __FreeBSD__ + struct stat fileStat; + int err = stat(mFilename.c_str(), &fileStat ); + if (0 != err) + return 0; + const unsigned long long cachedSize = fileStat.st_size; + mCachedSize = static_cast< size_t >( cachedSize ); +#else +# error "Unknown platform" +#endif + } + return mCachedSize; +} + +// ---------------------------------------------------------------------------------- +void DefaultIOStream::Flush() +{ + if (mFile) { + ::fflush(mFile); + } +} + +// ---------------------------------------------------------------------------------- diff --git a/thirdparty/assimp/code/Common/DefaultIOSystem.cpp b/thirdparty/assimp/code/Common/DefaultIOSystem.cpp new file mode 100644 index 0000000000..d40b67de32 --- /dev/null +++ b/thirdparty/assimp/code/Common/DefaultIOSystem.cpp @@ -0,0 +1,257 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file Default implementation of IOSystem using the standard C file functions */ + +#include + +#include +#include +#include +#include +#include + +#ifdef __unix__ +#include +#include +#endif + +#ifdef _WIN32 +#include +#endif + +using namespace Assimp; + +// maximum path length +// XXX http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html +#ifdef PATH_MAX +# define PATHLIMIT PATH_MAX +#else +# define PATHLIMIT 4096 +#endif + +// ------------------------------------------------------------------------------------------------ +// Tests for the existence of a file at the given path. +bool DefaultIOSystem::Exists( const char* pFile) const +{ +#ifdef _WIN32 + wchar_t fileName16[PATHLIMIT]; + +#ifndef WindowsStore + bool isUnicode = IsTextUnicode(pFile, static_cast(strlen(pFile)), NULL) != 0; + if (isUnicode) { + + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT); + struct __stat64 filestat; + if (0 != _wstat64(fileName16, &filestat)) { + return false; + } + } else { +#endif + FILE* file = ::fopen(pFile, "rb"); + if (!file) + return false; + + ::fclose(file); +#ifndef WindowsStore + } +#endif +#else + FILE* file = ::fopen( pFile, "rb"); + if( !file) + return false; + + ::fclose( file); +#endif + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Open a new file with a given path. +IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode) +{ + ai_assert(NULL != strFile); + ai_assert(NULL != strMode); + FILE* file; +#ifdef _WIN32 + wchar_t fileName16[PATHLIMIT]; +#ifndef WindowsStore + bool isUnicode = IsTextUnicode(strFile, static_cast(strlen(strFile)), NULL) != 0; + if (isUnicode) { + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT); + std::string mode8(strMode); + file = ::_wfopen(fileName16, std::wstring(mode8.begin(), mode8.end()).c_str()); + } else { +#endif + file = ::fopen(strFile, strMode); +#ifndef WindowsStore + } +#endif +#else + file = ::fopen(strFile, strMode); +#endif + if (nullptr == file) + return nullptr; + + return new DefaultIOStream(file, (std::string) strFile); +} + +// ------------------------------------------------------------------------------------------------ +// Closes the given file and releases all resources associated with it. +void DefaultIOSystem::Close( IOStream* pFile) +{ + delete pFile; +} + +// ------------------------------------------------------------------------------------------------ +// Returns the operation specific directory separator +char DefaultIOSystem::getOsSeparator() const +{ +#ifndef _WIN32 + return '/'; +#else + return '\\'; +#endif +} + +// ------------------------------------------------------------------------------------------------ +// IOSystem default implementation (ComparePaths isn't a pure virtual function) +bool IOSystem::ComparePaths (const char* one, const char* second) const +{ + return !ASSIMP_stricmp(one,second); +} + +// ------------------------------------------------------------------------------------------------ +// Convert a relative path into an absolute path +inline static void MakeAbsolutePath (const char* in, char* _out) +{ + ai_assert(in && _out); +#if defined( _MSC_VER ) || defined( __MINGW32__ ) +#ifndef WindowsStore + bool isUnicode = IsTextUnicode(in, static_cast(strlen(in)), NULL) != 0; + if (isUnicode) { + wchar_t out16[PATHLIMIT]; + wchar_t in16[PATHLIMIT]; + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, in, -1, out16, PATHLIMIT); + wchar_t* ret = ::_wfullpath(out16, in16, PATHLIMIT); + if (ret) { + WideCharToMultiByte(CP_UTF8, MB_PRECOMPOSED, out16, -1, _out, PATHLIMIT, nullptr, nullptr); + } + if (!ret) { + // preserve the input path, maybe someone else is able to fix + // the path before it is accessed (e.g. our file system filter) + ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in)); + strcpy(_out, in); + } + + } else { +#endif + char* ret = :: _fullpath(_out, in, PATHLIMIT); + if (!ret) { + // preserve the input path, maybe someone else is able to fix + // the path before it is accessed (e.g. our file system filter) + ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in)); + strcpy(_out, in); + } +#ifndef WindowsStore + } +#endif +#else + // use realpath + char* ret = realpath(in, _out); + if(!ret) { + // preserve the input path, maybe someone else is able to fix + // the path before it is accessed (e.g. our file system filter) + ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in)); + strcpy(_out,in); + } +#endif +} + +// ------------------------------------------------------------------------------------------------ +// DefaultIOSystem's more specialized implementation +bool DefaultIOSystem::ComparePaths (const char* one, const char* second) const +{ + // chances are quite good both paths are formatted identically, + // so we can hopefully return here already + if( !ASSIMP_stricmp(one,second) ) + return true; + + char temp1[PATHLIMIT]; + char temp2[PATHLIMIT]; + + MakeAbsolutePath (one, temp1); + MakeAbsolutePath (second, temp2); + + return !ASSIMP_stricmp(temp1,temp2); +} + +// ------------------------------------------------------------------------------------------------ +std::string DefaultIOSystem::fileName( const std::string &path ) +{ + std::string ret = path; + std::size_t last = ret.find_last_of("\\/"); + if (last != std::string::npos) ret = ret.substr(last + 1); + return ret; +} + +// ------------------------------------------------------------------------------------------------ +std::string DefaultIOSystem::completeBaseName( const std::string &path ) +{ + std::string ret = fileName(path); + std::size_t pos = ret.find_last_of('.'); + if(pos != ret.npos) ret = ret.substr(0, pos); + return ret; +} + +// ------------------------------------------------------------------------------------------------ +std::string DefaultIOSystem::absolutePath( const std::string &path ) +{ + std::string ret = path; + std::size_t last = ret.find_last_of("\\/"); + if (last != std::string::npos) ret = ret.substr(0, last); + return ret; +} + +// ------------------------------------------------------------------------------------------------ + +#undef PATHLIMIT diff --git a/thirdparty/assimp/code/Common/DefaultLogger.cpp b/thirdparty/assimp/code/Common/DefaultLogger.cpp new file mode 100644 index 0000000000..de3528d2b4 --- /dev/null +++ b/thirdparty/assimp/code/Common/DefaultLogger.cpp @@ -0,0 +1,418 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file DefaultLogger.cpp + * @brief Implementation of DefaultLogger (and Logger) + */ + +// Default log streams +#include "Win32DebugLogStream.h" +#include "StdOStreamLogStream.h" +#include "FileLogStream.h" +#include + +#include +#include +#include +#include +#include +#include + +#ifndef ASSIMP_BUILD_SINGLETHREADED +# include +# include + std::mutex loggerMutex; +#endif + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +NullLogger DefaultLogger::s_pNullLogger; +Logger *DefaultLogger::m_pLogger = &DefaultLogger::s_pNullLogger; + +static const unsigned int SeverityAll = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging; + +// ---------------------------------------------------------------------------------- +// Represents a log-stream + its error severity +struct LogStreamInfo { + unsigned int m_uiErrorSeverity; + LogStream *m_pStream; + + // Constructor + LogStreamInfo( unsigned int uiErrorSev, LogStream *pStream ) : + m_uiErrorSeverity( uiErrorSev ), + m_pStream( pStream ) { + // empty + } + + // Destructor + ~LogStreamInfo() { + delete m_pStream; + } +}; + +// ---------------------------------------------------------------------------------- +// Construct a default log stream +LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams, + const char* name /*= "AssimpLog.txt"*/, + IOSystem* io /*= NULL*/) +{ + switch (streams) + { + // This is a platform-specific feature + case aiDefaultLogStream_DEBUGGER: +#ifdef WIN32 + return new Win32DebugLogStream(); +#else + return nullptr; +#endif + + // Platform-independent default streams + case aiDefaultLogStream_STDERR: + return new StdOStreamLogStream(std::cerr); + case aiDefaultLogStream_STDOUT: + return new StdOStreamLogStream(std::cout); + case aiDefaultLogStream_FILE: + return (name && *name ? new FileLogStream(name,io) : nullptr ); + default: + // We don't know this default log stream, so raise an assertion + ai_assert(false); + + }; + + // For compilers without dead code path detection + return NULL; +} + +// ---------------------------------------------------------------------------------- +// Creates the only singleton instance +Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/, + LogSeverity severity /*= NORMAL*/, + unsigned int defStreams /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/, + IOSystem* io /*= NULL*/) { + // enter the mutex here to avoid concurrency problems +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard lock(loggerMutex); +#endif + + if ( m_pLogger && !isNullLogger() ) { + delete m_pLogger; + } + + m_pLogger = new DefaultLogger( severity ); + + // Attach default log streams + // Stream the log to the MSVC debugger? + if ( defStreams & aiDefaultLogStream_DEBUGGER ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_DEBUGGER ) ); + } + + // Stream the log to COUT? + if ( defStreams & aiDefaultLogStream_STDOUT ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDOUT ) ); + } + + // Stream the log to CERR? + if ( defStreams & aiDefaultLogStream_STDERR ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDERR ) ); + } + + // Stream the log to a file + if ( defStreams & aiDefaultLogStream_FILE && name && *name ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_FILE, name, io ) ); + } + + return m_pLogger; +} + +// ---------------------------------------------------------------------------------- +void Logger::debug(const char* message) { + + // SECURITY FIX: otherwise it's easy to produce overruns since + // sometimes importers will include data from the input file + // (i.e. node names) in their messages. + if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { + return; + } + return OnDebug(message); +} + +// ---------------------------------------------------------------------------------- +void Logger::info(const char* message) { + + // SECURITY FIX: see above + if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { + return; + } + return OnInfo(message); +} + +// ---------------------------------------------------------------------------------- +void Logger::warn(const char* message) { + + // SECURITY FIX: see above + if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { + return; + } + return OnWarn(message); +} + +// ---------------------------------------------------------------------------------- +void Logger::error(const char* message) { + // SECURITY FIX: see above + if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { + return; + } + return OnError(message); +} + +// ---------------------------------------------------------------------------------- +void DefaultLogger::set( Logger *logger ) { + // enter the mutex here to avoid concurrency problems +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard lock(loggerMutex); +#endif + + if ( nullptr == logger ) { + logger = &s_pNullLogger; + } + if ( nullptr != m_pLogger && !isNullLogger() ) { + delete m_pLogger; + } + + DefaultLogger::m_pLogger = logger; +} + +// ---------------------------------------------------------------------------------- +bool DefaultLogger::isNullLogger() { + return m_pLogger == &s_pNullLogger; +} + +// ---------------------------------------------------------------------------------- +Logger *DefaultLogger::get() { + return m_pLogger; +} + +// ---------------------------------------------------------------------------------- +// Kills the only instance +void DefaultLogger::kill() { + // enter the mutex here to avoid concurrency problems +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard lock(loggerMutex); +#endif + + if ( m_pLogger == &s_pNullLogger ) { + return; + } + delete m_pLogger; + m_pLogger = &s_pNullLogger; +} + +// ---------------------------------------------------------------------------------- +// Debug message +void DefaultLogger::OnDebug( const char* message ) { + if ( m_Severity == Logger::NORMAL ) { + return; + } + + static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; + char msg[Size]; + ai_snprintf(msg, Size, "Debug, T%u: %s", GetThreadID(), message); + + WriteToStreams( msg, Logger::Debugging ); +} + +// ---------------------------------------------------------------------------------- +// Logs an info +void DefaultLogger::OnInfo( const char* message ){ + static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; + char msg[Size]; + ai_snprintf(msg, Size, "Info, T%u: %s", GetThreadID(), message ); + + WriteToStreams( msg , Logger::Info ); +} + +// ---------------------------------------------------------------------------------- +// Logs a warning +void DefaultLogger::OnWarn( const char* message ) { + static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; + char msg[Size]; + ai_snprintf(msg, Size, "Warn, T%u: %s", GetThreadID(), message ); + + WriteToStreams( msg, Logger::Warn ); +} + +// ---------------------------------------------------------------------------------- +// Logs an error +void DefaultLogger::OnError( const char* message ) { + static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; + char msg[ Size ]; + ai_snprintf(msg, Size, "Error, T%u: %s", GetThreadID(), message ); + + WriteToStreams( msg, Logger::Err ); +} + +// ---------------------------------------------------------------------------------- +// Will attach a new stream +bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) { + if ( nullptr == pStream ) { + return false; + } + + if (0 == severity) { + severity = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging; + } + + for ( StreamIt it = m_StreamArray.begin(); + it != m_StreamArray.end(); + ++it ) + { + if ( (*it)->m_pStream == pStream ) { + (*it)->m_uiErrorSeverity |= severity; + return true; + } + } + + LogStreamInfo *pInfo = new LogStreamInfo( severity, pStream ); + m_StreamArray.push_back( pInfo ); + return true; +} + +// ---------------------------------------------------------------------------------- +// Detach a stream +bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) { + if ( nullptr == pStream ) { + return false; + } + + if (0 == severity) { + severity = SeverityAll; + } + + bool res( false ); + for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) { + if ( (*it)->m_pStream == pStream ) { + (*it)->m_uiErrorSeverity &= ~severity; + if ( (*it)->m_uiErrorSeverity == 0 ) { + // don't delete the underlying stream 'cause the caller gains ownership again + (**it).m_pStream = nullptr; + delete *it; + m_StreamArray.erase( it ); + res = true; + break; + } + return true; + } + } + return res; +} + +// ---------------------------------------------------------------------------------- +// Constructor +DefaultLogger::DefaultLogger(LogSeverity severity) + : Logger ( severity ) + , noRepeatMsg (false) + , lastLen( 0 ) { + lastMsg[0] = '\0'; +} + +// ---------------------------------------------------------------------------------- +// Destructor +DefaultLogger::~DefaultLogger() { + for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) { + // also frees the underlying stream, we are its owner. + delete *it; + } +} + +// ---------------------------------------------------------------------------------- +// Writes message to stream +void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev ) { + ai_assert(nullptr != message); + + // Check whether this is a repeated message + if (! ::strncmp( message,lastMsg, lastLen-1)) + { + if (!noRepeatMsg) + { + noRepeatMsg = true; + message = "Skipping one or more lines with the same contents\n"; + } + else return; + } + else + { + // append a new-line character to the message to be printed + lastLen = ::strlen(message); + ::memcpy(lastMsg,message,lastLen+1); + ::strcat(lastMsg+lastLen,"\n"); + + message = lastMsg; + noRepeatMsg = false; + ++lastLen; + } + for ( ConstStreamIt it = m_StreamArray.begin(); + it != m_StreamArray.end(); + ++it) + { + if ( ErrorSev & (*it)->m_uiErrorSeverity ) + (*it)->m_pStream->write( message); + } +} + +// ---------------------------------------------------------------------------------- +// Returns thread id, if not supported only a zero will be returned. +unsigned int DefaultLogger::GetThreadID() +{ + // fixme: we can get this value via std::threads + // std::this_thread::get_id().hash() returns a (big) size_t, not sure if this is useful in this case. +#ifdef WIN32 + return (unsigned int)::GetCurrentThreadId(); +#else + return 0; // not supported +#endif +} + +// ---------------------------------------------------------------------------------- + +} // !namespace Assimp diff --git a/thirdparty/assimp/code/Common/DefaultProgressHandler.h b/thirdparty/assimp/code/Common/DefaultProgressHandler.h new file mode 100644 index 0000000000..bd2cce00be --- /dev/null +++ b/thirdparty/assimp/code/Common/DefaultProgressHandler.h @@ -0,0 +1,65 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file ProgressHandler.hpp + * @brief Abstract base class 'ProgressHandler'. + */ +#ifndef INCLUDED_AI_DEFAULTPROGRESSHANDLER_H +#define INCLUDED_AI_DEFAULTPROGRESSHANDLER_H + +#include + +namespace Assimp { + +// ------------------------------------------------------------------------------------ +/** @brief Internal default implementation of the #ProgressHandler interface. */ +class DefaultProgressHandler : public ProgressHandler { + + virtual bool Update(float /*percentage*/) { + return false; + } + + +}; // !class DefaultProgressHandler +} // Namespace Assimp + +#endif diff --git a/thirdparty/assimp/code/Common/Exporter.cpp b/thirdparty/assimp/code/Common/Exporter.cpp new file mode 100644 index 0000000000..090b561ae0 --- /dev/null +++ b/thirdparty/assimp/code/Common/Exporter.cpp @@ -0,0 +1,654 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Exporter.cpp + +Assimp export interface. While it's public interface bears many similarities +to the import interface (in fact, it is largely symmetric), the internal +implementations differs a lot. Exporters are considered stateless and are +simple callbacks which we maintain in a global list along with their +description strings. + +Here we implement only the C++ interface (Assimp::Exporter). +*/ + +#ifndef ASSIMP_BUILD_NO_EXPORT + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Common/DefaultProgressHandler.h" +#include "Common/BaseProcess.h" +#include "Common/ScenePrivate.h" +#include "PostProcessing/CalcTangentsProcess.h" +#include "PostProcessing/MakeVerboseFormat.h" +#include "PostProcessing/JoinVerticesProcess.h" +#include "PostProcessing/ConvertToLHProcess.h" +#include "PostProcessing/PretransformVertices.h" + +#include + +namespace Assimp { + +// PostStepRegistry.cpp +void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); + +// ------------------------------------------------------------------------------------------------ +// Exporter worker function prototypes. Should not be necessary to #ifndef them, it's just a prototype +// do not use const, because some exporter need to convert the scene temporary +void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneXFile(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneStep(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneObj(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneObjNoMtl(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneSTL(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportScenePly(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportScenePlyBinary(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* ); +void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*); + +// ------------------------------------------------------------------------------------------------ +// global array of all export formats which Assimp supports in its current build +Exporter::ExportFormatEntry gExporters[] = +{ +#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER + Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada ), +#endif + +#ifndef ASSIMP_BUILD_NO_X_EXPORTER + Exporter::ExportFormatEntry( "x", "X Files", "x", &ExportSceneXFile, + aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs ), +#endif + +#ifndef ASSIMP_BUILD_NO_STEP_EXPORTER + Exporter::ExportFormatEntry( "stp", "Step Files", "stp", &ExportSceneStep, 0 ), +#endif + +#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER + Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj, + aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ), + Exporter::ExportFormatEntry( "objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl, + aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ), +#endif + +#ifndef ASSIMP_BUILD_NO_STL_EXPORTER + Exporter::ExportFormatEntry( "stl", "Stereolithography", "stl" , &ExportSceneSTL, + aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices + ), + Exporter::ExportFormatEntry( "stlb", "Stereolithography (binary)", "stl" , &ExportSceneSTLBinary, + aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices + ), +#endif + +#ifndef ASSIMP_BUILD_NO_PLY_EXPORTER + Exporter::ExportFormatEntry( "ply", "Stanford Polygon Library", "ply" , &ExportScenePly, + aiProcess_PreTransformVertices + ), + Exporter::ExportFormatEntry( "plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary, + aiProcess_PreTransformVertices + ), +#endif + +#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER + Exporter::ExportFormatEntry( "3ds", "Autodesk 3DS (legacy)", "3ds" , &ExportScene3DS, + aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices ), +#endif + +#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER + Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), + Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), + Exporter::ExportFormatEntry( "gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), + Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), +#endif + +#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER + Exporter::ExportFormatEntry( "assbin", "Assimp Binary File", "assbin" , &ExportSceneAssbin, 0 ), +#endif + +#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER + Exporter::ExportFormatEntry( "assxml", "Assimp XML Document", "assxml" , &ExportSceneAssxml, 0 ), +#endif + +#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER + Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0 ), +#endif + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ), + Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ), +#endif + +#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER + Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ), +#endif + +#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER + Exporter::ExportFormatEntry( "assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0) +#endif +}; + +#define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0])) + + +class ExporterPimpl { +public: + ExporterPimpl() + : blob() + , mIOSystem(new Assimp::DefaultIOSystem()) + , mIsDefaultIOHandler(true) + , mProgressHandler( nullptr ) + , mIsDefaultProgressHandler( true ) + , mPostProcessingSteps() + , mError() + , mExporters() { + GetPostProcessingStepInstanceList(mPostProcessingSteps); + + // grab all built-in exporters + if ( 0 != ( ASSIMP_NUM_EXPORTERS ) ) { + mExporters.resize( ASSIMP_NUM_EXPORTERS ); + std::copy( gExporters, gExporters + ASSIMP_NUM_EXPORTERS, mExporters.begin() ); + } + } + + ~ExporterPimpl() { + delete blob; + + // Delete all post-processing plug-ins + for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) { + delete mPostProcessingSteps[a]; + } + delete mProgressHandler; + } + +public: + aiExportDataBlob* blob; + std::shared_ptr< Assimp::IOSystem > mIOSystem; + bool mIsDefaultIOHandler; + + /** The progress handler */ + ProgressHandler *mProgressHandler; + bool mIsDefaultProgressHandler; + + /** Post processing steps we can apply at the imported data. */ + std::vector< BaseProcess* > mPostProcessingSteps; + + /** Last fatal export error */ + std::string mError; + + /** Exporters, this includes those registered using #Assimp::Exporter::RegisterExporter */ + std::vector mExporters; +}; + +} // end of namespace Assimp + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +Exporter :: Exporter() +: pimpl(new ExporterPimpl()) { + pimpl->mProgressHandler = new DefaultProgressHandler(); +} + +// ------------------------------------------------------------------------------------------------ +Exporter::~Exporter() { + FreeBlob(); + delete pimpl; +} + +// ------------------------------------------------------------------------------------------------ +void Exporter::SetIOHandler( IOSystem* pIOHandler) { + pimpl->mIsDefaultIOHandler = !pIOHandler; + pimpl->mIOSystem.reset(pIOHandler); +} + +// ------------------------------------------------------------------------------------------------ +IOSystem* Exporter::GetIOHandler() const { + return pimpl->mIOSystem.get(); +} + +// ------------------------------------------------------------------------------------------------ +bool Exporter::IsDefaultIOHandler() const { + return pimpl->mIsDefaultIOHandler; +} + +// ------------------------------------------------------------------------------------------------ +void Exporter::SetProgressHandler(ProgressHandler* pHandler) { + ai_assert(nullptr != pimpl); + + if ( nullptr == pHandler) { + // Release pointer in the possession of the caller + pimpl->mProgressHandler = new DefaultProgressHandler(); + pimpl->mIsDefaultProgressHandler = true; + return; + } + + if (pimpl->mProgressHandler == pHandler) { + return; + } + + delete pimpl->mProgressHandler; + pimpl->mProgressHandler = pHandler; + pimpl->mIsDefaultProgressHandler = false; +} + +// ------------------------------------------------------------------------------------------------ +const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId, + unsigned int pPreprocessing, const ExportProperties* pProperties) { + if (pimpl->blob) { + delete pimpl->blob; + pimpl->blob = nullptr; + } + + std::shared_ptr old = pimpl->mIOSystem; + BlobIOSystem* blobio = new BlobIOSystem(); + pimpl->mIOSystem = std::shared_ptr( blobio ); + + if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) { + pimpl->mIOSystem = old; + return nullptr; + } + + pimpl->blob = blobio->GetBlobChain(); + pimpl->mIOSystem = old; + + return pimpl->blob; +} + +// ------------------------------------------------------------------------------------------------ +bool IsVerboseFormat(const aiMesh* mesh) { + // avoid slow vector specialization + std::vector seen(mesh->mNumVertices,0); + for(unsigned int i = 0; i < mesh->mNumFaces; ++i) { + const aiFace& f = mesh->mFaces[i]; + for(unsigned int j = 0; j < f.mNumIndices; ++j) { + if(++seen[f.mIndices[j]] == 2) { + // found a duplicate index + return false; + } + } + } + + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool IsVerboseFormat(const aiScene* pScene) { + for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + if(!IsVerboseFormat(pScene->mMeshes[i])) { + return false; + } + } + + return true; +} + +// ------------------------------------------------------------------------------------------------ +aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath, + unsigned int pPreprocessing, const ExportProperties* pProperties) { + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // when they create scenes from scratch, users will likely create them not in verbose + // format. They will likely not be aware that there is a flag in the scene to indicate + // this, however. To avoid surprises and bug reports, we check for duplicates in + // meshes upfront. + const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || IsVerboseFormat(pScene); + + pimpl->mProgressHandler->UpdateFileWrite(0, 4); + + pimpl->mError = ""; + for (size_t i = 0; i < pimpl->mExporters.size(); ++i) { + const Exporter::ExportFormatEntry& exp = pimpl->mExporters[i]; + if (!strcmp(exp.mDescription.id,pFormatId)) { + try { + // Always create a full copy of the scene. We might optimize this one day, + // but for now it is the most pragmatic way. + aiScene* scenecopy_tmp = nullptr; + SceneCombiner::CopyScene(&scenecopy_tmp,pScene); + + pimpl->mProgressHandler->UpdateFileWrite(1, 4); + + std::unique_ptr scenecopy(scenecopy_tmp); + const ScenePrivateData* const priv = ScenePriv(pScene); + + // steps that are not idempotent, i.e. we might need to run them again, usually to get back to the + // original state before the step was applied first. When checking which steps we don't need + // to run, those are excluded. + const unsigned int nonIdempotentSteps = aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_MakeLeftHanded; + + // Erase all pp steps that were already applied to this scene + const unsigned int pp = (exp.mEnforcePP | pPreprocessing) & ~(priv && !priv->mIsCopy + ? (priv->mPPStepsApplied & ~nonIdempotentSteps) + : 0u); + + // If no extra post-processing was specified, and we obtained this scene from an + // Assimp importer, apply the reverse steps automatically. + // TODO: either drop this, or document it. Otherwise it is just a bad surprise. + //if (!pPreprocessing && priv) { + // pp |= (nonIdempotentSteps & priv->mPPStepsApplied); + //} + + // If the input scene is not in verbose format, but there is at least post-processing step that relies on it, + // we need to run the MakeVerboseFormat step first. + bool must_join_again = false; + if (!is_verbose_format) { + bool verbosify = false; + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { + BaseProcess* const p = pimpl->mPostProcessingSteps[a]; + + if (p->IsActive(pp) && p->RequireVerboseFormat()) { + verbosify = true; + break; + } + } + + if (verbosify || (exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { + ASSIMP_LOG_DEBUG("export: Scene data not in verbose format, applying MakeVerboseFormat step first"); + + MakeVerboseFormatProcess proc; + proc.Execute(scenecopy.get()); + + if(!(exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { + must_join_again = true; + } + } + } + + pimpl->mProgressHandler->UpdateFileWrite(2, 4); + + if (pp) { + // the three 'conversion' steps need to be executed first because all other steps rely on the standard data layout + { + FlipWindingOrderProcess step; + if (step.IsActive(pp)) { + step.Execute(scenecopy.get()); + } + } + + { + FlipUVsProcess step; + if (step.IsActive(pp)) { + step.Execute(scenecopy.get()); + } + } + + { + MakeLeftHandedProcess step; + if (step.IsActive(pp)) { + step.Execute(scenecopy.get()); + } + } + + bool exportPointCloud(false); + if (nullptr != pProperties) { + exportPointCloud = pProperties->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); + } + + // dispatch other processes + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { + BaseProcess* const p = pimpl->mPostProcessingSteps[a]; + + if (p->IsActive(pp) + && !dynamic_cast(p) + && !dynamic_cast(p) + && !dynamic_cast(p)) { + if (dynamic_cast(p) && exportPointCloud) { + continue; + } + p->Execute(scenecopy.get()); + } + } + ScenePrivateData* const privOut = ScenePriv(scenecopy.get()); + ai_assert(nullptr != privOut); + + privOut->mPPStepsApplied |= pp; + } + + pimpl->mProgressHandler->UpdateFileWrite(3, 4); + + if(must_join_again) { + JoinVerticesProcess proc; + proc.Execute(scenecopy.get()); + } + + ExportProperties emptyProperties; // Never pass NULL ExportProperties so Exporters don't have to worry. + exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties ? pProperties : &emptyProperties); + + pimpl->mProgressHandler->UpdateFileWrite(4, 4); + } catch (DeadlyExportError& err) { + pimpl->mError = err.what(); + return AI_FAILURE; + } + return AI_SUCCESS; + } + } + + pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId; + ASSIMP_END_EXCEPTION_REGION(aiReturn); + + return AI_FAILURE; +} + +// ------------------------------------------------------------------------------------------------ +const char* Exporter::GetErrorString() const { + return pimpl->mError.c_str(); +} + +// ------------------------------------------------------------------------------------------------ +void Exporter::FreeBlob() { + delete pimpl->blob; + pimpl->blob = nullptr; + + pimpl->mError = ""; +} + +// ------------------------------------------------------------------------------------------------ +const aiExportDataBlob* Exporter::GetBlob() const { + return pimpl->blob; +} + +// ------------------------------------------------------------------------------------------------ +const aiExportDataBlob* Exporter::GetOrphanedBlob() const { + const aiExportDataBlob* tmp = pimpl->blob; + pimpl->blob = nullptr; + return tmp; +} + +// ------------------------------------------------------------------------------------------------ +size_t Exporter::GetExportFormatCount() const { + return pimpl->mExporters.size(); +} + +// ------------------------------------------------------------------------------------------------ +const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const { + if (index >= GetExportFormatCount()) { + return nullptr; + } + + // Return from static storage if the requested index is built-in. + if (index < sizeof(gExporters) / sizeof(gExporters[0])) { + return &gExporters[index].mDescription; + } + + return &pimpl->mExporters[index].mDescription; +} + +// ------------------------------------------------------------------------------------------------ +aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) { + for(const ExportFormatEntry& e : pimpl->mExporters) { + if (!strcmp(e.mDescription.id,desc.mDescription.id)) { + return aiReturn_FAILURE; + } + } + + pimpl->mExporters.push_back(desc); + return aiReturn_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +void Exporter::UnregisterExporter(const char* id) { + for(std::vector::iterator it = pimpl->mExporters.begin(); + it != pimpl->mExporters.end(); ++it) { + if (!strcmp((*it).mDescription.id,id)) { + pimpl->mExporters.erase(it); + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +ExportProperties::ExportProperties() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +ExportProperties::ExportProperties(const ExportProperties &other) +: mIntProperties(other.mIntProperties) +, mFloatProperties(other.mFloatProperties) +, mStringProperties(other.mStringProperties) +, mMatrixProperties(other.mMatrixProperties) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool ExportProperties::SetPropertyInteger(const char* szName, int iValue) { + return SetGenericProperty(mIntProperties, szName,iValue); +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool ExportProperties::SetPropertyFloat(const char* szName, ai_real iValue) { + return SetGenericProperty(mFloatProperties, szName,iValue); +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool ExportProperties::SetPropertyString(const char* szName, const std::string& value) { + return SetGenericProperty(mStringProperties, szName,value); +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool ExportProperties::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { + return SetGenericProperty(mMatrixProperties, szName,value); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +int ExportProperties::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const { + return GetGenericProperty(mIntProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +ai_real ExportProperties::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const { + return GetGenericProperty(mFloatProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +const std::string ExportProperties::GetPropertyString(const char* szName, + const std::string& iErrorReturn /*= ""*/) const { + return GetGenericProperty(mStringProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +const aiMatrix4x4 ExportProperties::GetPropertyMatrix(const char* szName, + const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const { + return GetGenericProperty(mMatrixProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyInteger(const char* szName) const { + return HasGenericProperty(mIntProperties, szName); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyBool(const char* szName) const { + return HasGenericProperty(mIntProperties, szName); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyFloat(const char* szName) const { + return HasGenericProperty(mFloatProperties, szName); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyString(const char* szName) const { + return HasGenericProperty(mStringProperties, szName); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyMatrix(const char* szName) const { + return HasGenericProperty(mMatrixProperties, szName); +} + + +#endif // !ASSIMP_BUILD_NO_EXPORT diff --git a/thirdparty/assimp/code/Common/FileLogStream.h b/thirdparty/assimp/code/Common/FileLogStream.h new file mode 100644 index 0000000000..740c503192 --- /dev/null +++ b/thirdparty/assimp/code/Common/FileLogStream.h @@ -0,0 +1,107 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/** @file FileLofStream.h +*/ +#ifndef ASSIMP_FILELOGSTREAM_H_INC +#define ASSIMP_FILELOGSTREAM_H_INC + +#include +#include +#include + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** @class FileLogStream + * @brief Logstream to write into a file. + */ +class FileLogStream : + public LogStream +{ +public: + FileLogStream( const char* file, IOSystem* io = NULL ); + ~FileLogStream(); + void write( const char* message ); + +private: + IOStream *m_pStream; +}; + +// ---------------------------------------------------------------------------------- +// Constructor +inline FileLogStream::FileLogStream( const char* file, IOSystem* io ) : + m_pStream(NULL) +{ + if ( !file || 0 == *file ) + return; + + // If no IOSystem is specified: take a default one + if (!io) + { + DefaultIOSystem FileSystem; + m_pStream = FileSystem.Open( file, "wt"); + } + else m_pStream = io->Open( file, "wt" ); +} + +// ---------------------------------------------------------------------------------- +// Destructor +inline FileLogStream::~FileLogStream() +{ + // The virtual d'tor should destroy the underlying file + delete m_pStream; +} + +// ---------------------------------------------------------------------------------- +// Write method +inline void FileLogStream::write( const char* message ) +{ + if (m_pStream != NULL) + { + m_pStream->Write(message, sizeof(char), ::strlen(message)); + m_pStream->Flush(); + } +} + +// ---------------------------------------------------------------------------------- +} // !Namespace Assimp + +#endif // !! ASSIMP_FILELOGSTREAM_H_INC diff --git a/thirdparty/assimp/code/Common/FileSystemFilter.h b/thirdparty/assimp/code/Common/FileSystemFilter.h new file mode 100644 index 0000000000..9923cdbdd3 --- /dev/null +++ b/thirdparty/assimp/code/Common/FileSystemFilter.h @@ -0,0 +1,345 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2008, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FileSystemFilter.h + * Implements a filter system to filter calls to Exists() and Open() + * in order to improve the success rate of file opening ... + */ +#pragma once +#ifndef AI_FILESYSTEMFILTER_H_INC +#define AI_FILESYSTEMFILTER_H_INC + +#include +#include +#include +#include + +namespace Assimp { + +inline bool IsHex(char s) { + return (s>='0' && s<='9') || (s>='a' && s<='f') || (s>='A' && s<='F'); +} + +// --------------------------------------------------------------------------- +/** File system filter + */ +class FileSystemFilter : public IOSystem +{ +public: + /** Constructor. */ + FileSystemFilter(const std::string& file, IOSystem* old) + : mWrapped (old) + , mSrc_file(file) + , mSep(mWrapped->getOsSeparator()) { + ai_assert(nullptr != mWrapped); + + // Determine base directory + mBase = mSrc_file; + std::string::size_type ss2; + if (std::string::npos != (ss2 = mBase.find_last_of("\\/"))) { + mBase.erase(ss2,mBase.length()-ss2); + } else { + mBase = ""; + } + + // make sure the directory is terminated properly + char s; + + if ( mBase.empty() ) { + mBase = "."; + mBase += getOsSeparator(); + } else if ((s = *(mBase.end()-1)) != '\\' && s != '/') { + mBase += getOsSeparator(); + } + + DefaultLogger::get()->info("Import root directory is \'" + mBase + "\'"); + } + + /** Destructor. */ + ~FileSystemFilter() { + // empty + } + + // ------------------------------------------------------------------- + /** Tests for the existence of a file at the given path. */ + bool Exists( const char* pFile) const { + ai_assert( nullptr != mWrapped ); + + std::string tmp = pFile; + + // Currently this IOSystem is also used to open THE ONE FILE. + if (tmp != mSrc_file) { + BuildPath(tmp); + Cleanup(tmp); + } + + return mWrapped->Exists(tmp); + } + + // ------------------------------------------------------------------- + /** Returns the directory separator. */ + char getOsSeparator() const { + return mSep; + } + + // ------------------------------------------------------------------- + /** Open a new file with a given path. */ + IOStream* Open( const char* pFile, const char* pMode = "rb") { + ai_assert( nullptr != mWrapped ); + if ( nullptr == pFile || nullptr == pMode ) { + return nullptr; + } + + ai_assert( nullptr != pFile ); + ai_assert( nullptr != pMode ); + + // First try the unchanged path + IOStream* s = mWrapped->Open(pFile,pMode); + + if (nullptr == s) { + std::string tmp = pFile; + + // Try to convert between absolute and relative paths + BuildPath(tmp); + s = mWrapped->Open(tmp,pMode); + + if (nullptr == s) { + // Finally, look for typical issues with paths + // and try to correct them. This is our last + // resort. + tmp = pFile; + Cleanup(tmp); + BuildPath(tmp); + s = mWrapped->Open(tmp,pMode); + } + } + + return s; + } + + // ------------------------------------------------------------------- + /** Closes the given file and releases all resources associated with it. */ + void Close( IOStream* pFile) { + ai_assert( nullptr != mWrapped ); + return mWrapped->Close(pFile); + } + + // ------------------------------------------------------------------- + /** Compare two paths */ + bool ComparePaths (const char* one, const char* second) const { + ai_assert( nullptr != mWrapped ); + return mWrapped->ComparePaths (one,second); + } + + // ------------------------------------------------------------------- + /** Pushes a new directory onto the directory stack. */ + bool PushDirectory(const std::string &path ) { + ai_assert( nullptr != mWrapped ); + return mWrapped->PushDirectory(path); + } + + // ------------------------------------------------------------------- + /** Returns the top directory from the stack. */ + const std::string &CurrentDirectory() const { + ai_assert( nullptr != mWrapped ); + return mWrapped->CurrentDirectory(); + } + + // ------------------------------------------------------------------- + /** Returns the number of directories stored on the stack. */ + size_t StackSize() const { + ai_assert( nullptr != mWrapped ); + return mWrapped->StackSize(); + } + + // ------------------------------------------------------------------- + /** Pops the top directory from the stack. */ + bool PopDirectory() { + ai_assert( nullptr != mWrapped ); + return mWrapped->PopDirectory(); + } + + // ------------------------------------------------------------------- + /** Creates an new directory at the given path. */ + bool CreateDirectory(const std::string &path) { + ai_assert( nullptr != mWrapped ); + return mWrapped->CreateDirectory(path); + } + + // ------------------------------------------------------------------- + /** Will change the current directory to the given path. */ + bool ChangeDirectory(const std::string &path) { + ai_assert( nullptr != mWrapped ); + return mWrapped->ChangeDirectory(path); + } + + // ------------------------------------------------------------------- + /** Delete file. */ + bool DeleteFile(const std::string &file) { + ai_assert( nullptr != mWrapped ); + return mWrapped->DeleteFile(file); + } + +private: + // ------------------------------------------------------------------- + /** Build a valid path from a given relative or absolute path. + */ + void BuildPath (std::string& in) const { + ai_assert( nullptr != mWrapped ); + // if we can already access the file, great. + if (in.length() < 3 || mWrapped->Exists(in)) { + return; + } + + // Determine whether this is a relative path (Windows-specific - most assets are packaged on Windows). + if (in[1] != ':') { + + // append base path and try + const std::string tmp = mBase + in; + if (mWrapped->Exists(tmp)) { + in = tmp; + return; + } + } + + // Chop of the file name and look in the model directory, if + // this fails try all sub paths of the given path, i.e. + // if the given path is foo/bar/something.lwo, try + // /something.lwo + // /bar/something.lwo + // /foo/bar/something.lwo + std::string::size_type pos = in.rfind('/'); + if (std::string::npos == pos) { + pos = in.rfind('\\'); + } + + if (std::string::npos != pos) { + std::string tmp; + std::string::size_type last_dirsep = std::string::npos; + + while(true) { + tmp = mBase; + tmp += mSep; + + std::string::size_type dirsep = in.rfind('/', last_dirsep); + if (std::string::npos == dirsep) { + dirsep = in.rfind('\\', last_dirsep); + } + + if (std::string::npos == dirsep || dirsep == 0) { + // we did try this already. + break; + } + + last_dirsep = dirsep-1; + + tmp += in.substr(dirsep+1, in.length()-pos); + if (mWrapped->Exists(tmp)) { + in = tmp; + return; + } + } + } + + // hopefully the underlying file system has another few tricks to access this file ... + } + + // ------------------------------------------------------------------- + /** Cleanup the given path + */ + void Cleanup (std::string& in) const { + if(in.empty()) { + return; + } + + // Remove a very common issue when we're parsing file names: spaces at the + // beginning of the path. + char last = 0; + std::string::iterator it = in.begin(); + while (IsSpaceOrNewLine( *it ))++it; + if (it != in.begin()) { + in.erase(in.begin(),it+1); + } + + const char separator = getOsSeparator(); + for (it = in.begin(); it != in.end(); ++it) { + // Exclude :// and \\, which remain untouched. + // https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632 + if ( !strncmp(&*it, "://", 3 )) { + it += 3; + continue; + } + if (it == in.begin() && !strncmp(&*it, "\\\\", 2)) { + it += 2; + continue; + } + + // Cleanup path delimiters + if (*it == '/' || (*it) == '\\') { + *it = separator; + + // And we're removing double delimiters, frequent issue with + // incorrectly composited paths ... + if (last == *it) { + it = in.erase(it); + --it; + } + } else if (*it == '%' && in.end() - it > 2) { + // Hex sequence in URIs + if( IsHex((&*it)[0]) && IsHex((&*it)[1]) ) { + *it = HexOctetToDecimal(&*it); + it = in.erase(it+1,it+2); + --it; + } + } + + last = *it; + } + } + +private: + IOSystem *mWrapped; + std::string mSrc_file, mBase; + char mSep; +}; + +} //!ns Assimp + +#endif //AI_DEFAULTIOSYSTEM_H_INC diff --git a/thirdparty/assimp/code/Common/IFF.h b/thirdparty/assimp/code/Common/IFF.h new file mode 100644 index 0000000000..91d7d48289 --- /dev/null +++ b/thirdparty/assimp/code/Common/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 + +namespace Assimp { +namespace IFF { + +///////////////////////////////////////////////////////////////////////////////// +//! Describes an IFF chunk header +///////////////////////////////////////////////////////////////////////////////// +struct ChunkHeader +{ + //! Type of the chunk header - FourCC + uint32_t type; + + //! Length of the chunk data, in bytes + uint32_t length; +}; + + +///////////////////////////////////////////////////////////////////////////////// +//! Describes an IFF sub chunk header +///////////////////////////////////////////////////////////////////////////////// +struct SubChunkHeader +{ + //! Type of the chunk header - FourCC + uint32_t type; + + //! Length of the chunk data, in bytes + uint16_t length; +}; + + +#define AI_IFF_FOURCC(a,b,c,d) ((uint32_t) (((uint8_t)a << 24u) | \ + ((uint8_t)b << 16u) | ((uint8_t)c << 8u) | ((uint8_t)d))) + + +#define AI_IFF_FOURCC_FORM AI_IFF_FOURCC('F','O','R','M') + + +///////////////////////////////////////////////////////////////////////////////// +//! Load a chunk header +//! @param outFile Pointer to the file data - points to the chunk data afterwards +//! @return Copy of the chunk header +///////////////////////////////////////////////////////////////////////////////// +inline ChunkHeader LoadChunk(uint8_t*& outFile) +{ + ChunkHeader head; + ::memcpy(&head.type, outFile, 4); + outFile += 4; + ::memcpy(&head.length, outFile, 4); + outFile += 4; + AI_LSWAP4(head.length); + AI_LSWAP4(head.type); + return head; +} + +///////////////////////////////////////////////////////////////////////////////// +//! Load a sub chunk header +//! @param outFile Pointer to the file data - points to the chunk data afterwards +//! @return Copy of the sub chunk header +///////////////////////////////////////////////////////////////////////////////// +inline SubChunkHeader LoadSubChunk(uint8_t*& outFile) +{ + SubChunkHeader head; + ::memcpy(&head.type, outFile, 4); + outFile += 4; + ::memcpy(&head.length, outFile, 2); + outFile += 2; + AI_LSWAP2(head.length); + AI_LSWAP4(head.type); + return head; +} + +///////////////////////////////////////////////////////////////////////////////// +//! Read the file header and return the type of the file and its size +//! @param outFile Pointer to the file data. The buffer must at +//! least be 12 bytes large. +//! @param fileType Receives the type of the file +//! @return 0 if everything was OK, otherwise an error message +///////////////////////////////////////////////////////////////////////////////// +inline const char* ReadHeader(uint8_t* outFile, uint32_t& fileType) +{ + ChunkHeader head = LoadChunk(outFile); + if(AI_IFF_FOURCC_FORM != head.type) + { + return "The file is not an IFF file: FORM chunk is missing"; + } + ::memcpy(&fileType, outFile, 4); + AI_LSWAP4(fileType); + return 0; +} + + +}} + +#endif // !! AI_IFF_H_INCLUDED diff --git a/thirdparty/assimp/code/Common/Importer.cpp b/thirdparty/assimp/code/Common/Importer.cpp new file mode 100644 index 0000000000..91b50859a0 --- /dev/null +++ b/thirdparty/assimp/code/Common/Importer.cpp @@ -0,0 +1,1174 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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 +#include +#include + +// ------------------------------------------------------------------------------------------------ +/* Uncomment this line to prevent Assimp from catching unknown exceptions. + * + * Note that any Exception except DeadlyImportError may lead to + * undefined behaviour -> loaders could remain in an unusable state and + * further imports with the same Importer instance could fail/crash/burn ... + */ +// ------------------------------------------------------------------------------------------------ +#ifndef ASSIMP_BUILD_DEBUG +# define ASSIMP_CATCH_GLOBAL_EXCEPTIONS +#endif + +// ------------------------------------------------------------------------------------------------ +// Internal headers +// ------------------------------------------------------------------------------------------------ +#include "Common/Importer.h" +#include "Common/BaseProcess.h" +#include "Common/DefaultProgressHandler.h" +#include "PostProcessing/ProcessHelper.h" +#include "Common/ScenePreprocessor.h" +#include "Common/ScenePrivate.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS +# include "PostProcessing/ValidateDataStructure.h" +#endif + +using namespace Assimp::Profiling; +using namespace Assimp::Formatter; + +namespace Assimp { + // ImporterRegistry.cpp + void GetImporterInstanceList(std::vector< BaseImporter* >& out); + void DeleteImporterInstanceList(std::vector< BaseImporter* >& out); + + // PostStepRegistry.cpp + void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); +} + +using namespace Assimp; +using namespace Assimp::Intern; + +// ------------------------------------------------------------------------------------------------ +// Intern::AllocateFromAssimpHeap serves as abstract base class. It overrides +// new and delete (and their array counterparts) of public API classes (e.g. Logger) to +// utilize our DLL heap. +// See http://www.gotw.ca/publications/mill15.htm +// ------------------------------------------------------------------------------------------------ +void* AllocateFromAssimpHeap::operator new ( size_t num_bytes) { + return ::operator new(num_bytes); +} + +void* AllocateFromAssimpHeap::operator new ( size_t num_bytes, const std::nothrow_t& ) throw() { + try { + return AllocateFromAssimpHeap::operator new( num_bytes ); + } + catch( ... ) { + return NULL; + } +} + +void AllocateFromAssimpHeap::operator delete ( void* data) { + return ::operator delete(data); +} + +void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes) { + return ::operator new[](num_bytes); +} + +void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw() { + try { + return AllocateFromAssimpHeap::operator new[]( num_bytes ); + } + catch( ... ) { + return NULL; + } +} + +void AllocateFromAssimpHeap::operator delete[] ( void* data) { + return ::operator delete[](data); +} + +// ------------------------------------------------------------------------------------------------ +// Importer constructor. +Importer::Importer() + : pimpl( new ImporterPimpl ) { + pimpl->mScene = NULL; + pimpl->mErrorString = ""; + + // Allocate a default IO handler + pimpl->mIOHandler = new DefaultIOSystem; + pimpl->mIsDefaultHandler = true; + pimpl->bExtraVerbose = false; // disable extra verbose mode by default + + pimpl->mProgressHandler = new DefaultProgressHandler(); + pimpl->mIsDefaultProgressHandler = true; + + GetImporterInstanceList(pimpl->mImporter); + GetPostProcessingStepInstanceList(pimpl->mPostProcessingSteps); + + // Allocate a SharedPostProcessInfo object and store pointers to it in all post-process steps in the list. + pimpl->mPPShared = new SharedPostProcessInfo(); + for (std::vector::iterator it = pimpl->mPostProcessingSteps.begin(); + it != pimpl->mPostProcessingSteps.end(); + ++it) { + + (*it)->SetSharedData(pimpl->mPPShared); + } +} + +// ------------------------------------------------------------------------------------------------ +// Destructor of Importer +Importer::~Importer() +{ + // Delete all import plugins + DeleteImporterInstanceList(pimpl->mImporter); + + // Delete all post-processing plug-ins + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) + delete pimpl->mPostProcessingSteps[a]; + + // Delete the assigned IO and progress handler + delete pimpl->mIOHandler; + delete pimpl->mProgressHandler; + + // Kill imported scene. Destructor's should do that recursively + delete pimpl->mScene; + + // Delete shared post-processing data + delete pimpl->mPPShared; + + // and finally the pimpl itself + delete pimpl; +} + +// ------------------------------------------------------------------------------------------------ +// Register a custom post-processing step +aiReturn Importer::RegisterPPStep(BaseProcess* pImp) +{ + ai_assert(NULL != pImp); + ASSIMP_BEGIN_EXCEPTION_REGION(); + + pimpl->mPostProcessingSteps.push_back(pImp); + ASSIMP_LOG_INFO("Registering custom post-processing step"); + + ASSIMP_END_EXCEPTION_REGION(aiReturn); + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +// Register a custom loader plugin +aiReturn Importer::RegisterLoader(BaseImporter* pImp) +{ + ai_assert(NULL != pImp); + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // -------------------------------------------------------------------- + // Check whether we would have two loaders for the same file extension + // This is absolutely OK, but we should warn the developer of the new + // loader that his code will probably never be called if the first + // loader is a bit too lazy in his file checking. + // -------------------------------------------------------------------- + std::set st; + std::string baked; + pImp->GetExtensionList(st); + + for(std::set::const_iterator it = st.begin(); it != st.end(); ++it) { + +#ifdef ASSIMP_BUILD_DEBUG + if (IsExtensionSupported(*it)) { + ASSIMP_LOG_WARN_F("The file extension ", *it, " is already in use"); + } +#endif + baked += *it; + } + + // add the loader + pimpl->mImporter.push_back(pImp); + ASSIMP_LOG_INFO_F("Registering custom importer for these file extensions: ", baked); + ASSIMP_END_EXCEPTION_REGION(aiReturn); + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +// Unregister a custom loader plugin +aiReturn Importer::UnregisterLoader(BaseImporter* pImp) +{ + if(!pImp) { + // unregistering a NULL importer is no problem for us ... really! + return AI_SUCCESS; + } + + ASSIMP_BEGIN_EXCEPTION_REGION(); + std::vector::iterator it = std::find(pimpl->mImporter.begin(), + pimpl->mImporter.end(),pImp); + + if (it != pimpl->mImporter.end()) { + pimpl->mImporter.erase(it); + ASSIMP_LOG_INFO("Unregistering custom importer: "); + return AI_SUCCESS; + } + ASSIMP_LOG_WARN("Unable to remove custom importer: I can't find you ..."); + ASSIMP_END_EXCEPTION_REGION(aiReturn); + return AI_FAILURE; +} + +// ------------------------------------------------------------------------------------------------ +// Unregister a custom loader plugin +aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) +{ + if(!pImp) { + // unregistering a NULL ppstep is no problem for us ... really! + return AI_SUCCESS; + } + + ASSIMP_BEGIN_EXCEPTION_REGION(); + std::vector::iterator it = std::find(pimpl->mPostProcessingSteps.begin(), + pimpl->mPostProcessingSteps.end(),pImp); + + if (it != pimpl->mPostProcessingSteps.end()) { + pimpl->mPostProcessingSteps.erase(it); + ASSIMP_LOG_INFO("Unregistering custom post-processing step"); + return AI_SUCCESS; + } + ASSIMP_LOG_WARN("Unable to remove custom post-processing step: I can't find you .."); + ASSIMP_END_EXCEPTION_REGION(aiReturn); + return AI_FAILURE; +} + +// ------------------------------------------------------------------------------------------------ +// Supplies a custom IO handler to the importer to open and access files. +void Importer::SetIOHandler( IOSystem* pIOHandler) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + // If the new handler is zero, allocate a default IO implementation. + if (!pIOHandler) + { + // Release pointer in the possession of the caller + pimpl->mIOHandler = new DefaultIOSystem(); + pimpl->mIsDefaultHandler = true; + } + // Otherwise register the custom handler + else if (pimpl->mIOHandler != pIOHandler) + { + delete pimpl->mIOHandler; + pimpl->mIOHandler = pIOHandler; + pimpl->mIsDefaultHandler = false; + } + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Get the currently set IO handler +IOSystem* Importer::GetIOHandler() const { + return pimpl->mIOHandler; +} + +// ------------------------------------------------------------------------------------------------ +// Check whether a custom IO handler is currently set +bool Importer::IsDefaultIOHandler() const { + return pimpl->mIsDefaultHandler; +} + +// ------------------------------------------------------------------------------------------------ +// Supplies a custom progress handler to get regular callbacks during importing +void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { + ASSIMP_BEGIN_EXCEPTION_REGION(); + // If the new handler is zero, allocate a default implementation. + if (!pHandler) + { + // Release pointer in the possession of the caller + pimpl->mProgressHandler = new DefaultProgressHandler(); + pimpl->mIsDefaultProgressHandler = true; + } + // Otherwise register the custom handler + else if (pimpl->mProgressHandler != pHandler) + { + delete pimpl->mProgressHandler; + pimpl->mProgressHandler = pHandler; + pimpl->mIsDefaultProgressHandler = false; + } + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Get the currently set progress handler +ProgressHandler* Importer::GetProgressHandler() const { + return pimpl->mProgressHandler; +} + +// ------------------------------------------------------------------------------------------------ +// Check whether a custom progress handler is currently set +bool Importer::IsDefaultProgressHandler() const { + return pimpl->mIsDefaultProgressHandler; +} + +// ------------------------------------------------------------------------------------------------ +// Validate post process step flags +bool _ValidateFlags(unsigned int pFlags) +{ + if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals) { + ASSIMP_LOG_ERROR("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible"); + return false; + } + if (pFlags & aiProcess_OptimizeGraph && pFlags & aiProcess_PreTransformVertices) { + ASSIMP_LOG_ERROR("#aiProcess_OptimizeGraph and #aiProcess_PreTransformVertices are incompatible"); + return false; + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Free the current scene +void Importer::FreeScene( ) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + + delete pimpl->mScene; + pimpl->mScene = NULL; + + pimpl->mErrorString = ""; + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Get the current error string, if any +const char* Importer::GetErrorString() const +{ + /* Must remain valid as long as ReadFile() or FreeFile() are not called */ + return pimpl->mErrorString.c_str(); +} + +// ------------------------------------------------------------------------------------------------ +// Enable extra-verbose mode +void Importer::SetExtraVerbose(bool bDo) +{ + pimpl->bExtraVerbose = bDo; +} + +// ------------------------------------------------------------------------------------------------ +// Get the current scene +const aiScene* Importer::GetScene() const +{ + return pimpl->mScene; +} + +// ------------------------------------------------------------------------------------------------ +// Orphan the current scene and return it. +aiScene* Importer::GetOrphanedScene() +{ + aiScene* s = pimpl->mScene; + + ASSIMP_BEGIN_EXCEPTION_REGION(); + pimpl->mScene = NULL; + + pimpl->mErrorString = ""; /* reset error string */ + ASSIMP_END_EXCEPTION_REGION(aiScene*); + return s; +} + +// ------------------------------------------------------------------------------------------------ +// Validate post-processing flags +bool Importer::ValidateFlags(unsigned int pFlags) const +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + // run basic checks for mutually exclusive flags + if(!_ValidateFlags(pFlags)) { + return false; + } + + // ValidateDS does not anymore occur in the pp list, it plays an awesome extra role ... +#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + if (pFlags & aiProcess_ValidateDataStructure) { + return false; + } +#endif + pFlags &= ~aiProcess_ValidateDataStructure; + + // Now iterate through all bits which are set in the flags and check whether we find at least + // one pp plugin which handles it. + for (unsigned int mask = 1; mask < (1u << (sizeof(unsigned int)*8-1));mask <<= 1) { + + if (pFlags & mask) { + + bool have = false; + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { + if (pimpl->mPostProcessingSteps[a]-> IsActive(mask) ) { + + have = true; + break; + } + } + if (!have) { + return false; + } + } + } + ASSIMP_END_EXCEPTION_REGION(bool); + return true; +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, + size_t pLength, + unsigned int pFlags, + const char* pHint /*= ""*/) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + if (!pHint) { + pHint = ""; + } + + if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) { + pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()"; + return NULL; + } + + // prevent deletion of the previous IOHandler + IOSystem* io = pimpl->mIOHandler; + pimpl->mIOHandler = NULL; + + SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength,io)); + + // read the file and recover the previous IOSystem + static const size_t BufSize(Importer::MaxLenHint + 28); + char fbuff[BufSize]; + ai_snprintf(fbuff, BufSize, "%s.%s",AI_MEMORYIO_MAGIC_FILENAME,pHint); + + ReadFile(fbuff,pFlags); + SetIOHandler(io); + + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return pimpl->mScene; +} + +// ------------------------------------------------------------------------------------------------ +void WriteLogOpening(const std::string& file) +{ + ASSIMP_LOG_INFO_F("Load ", file); + + // print a full version dump. This is nice because we don't + // need to ask the authors of incoming bug reports for + // the library version they're using - a log dump is + // sufficient. + const unsigned int flags( aiGetCompileFlags() ); + std::stringstream stream; + stream << "Assimp " << aiGetVersionMajor() << "." << aiGetVersionMinor() << "." << aiGetVersionRevision() << " " +#if defined(ASSIMP_BUILD_ARCHITECTURE) + << ASSIMP_BUILD_ARCHITECTURE +#elif defined(_M_IX86) || defined(__x86_32__) || defined(__i386__) + << "x86" +#elif defined(_M_X64) || defined(__x86_64__) + << "amd64" +#elif defined(_M_IA64) || defined(__ia64__) + << "itanium" +#elif defined(__ppc__) || defined(__powerpc__) + << "ppc32" +#elif defined(__powerpc64__) + << "ppc64" +#elif defined(__arm__) + << "arm" +#else + << "" +#endif + << " " +#if defined(ASSIMP_BUILD_COMPILER) + << ( ASSIMP_BUILD_COMPILER ) +#elif defined(_MSC_VER) + << "msvc" +#elif defined(__GNUC__) + << "gcc" +#else + << "" +#endif + +#ifdef ASSIMP_BUILD_DEBUG + << " debug" +#endif + + << (flags & ASSIMP_CFLAGS_NOBOOST ? " noboost" : "") + << (flags & ASSIMP_CFLAGS_SHARED ? " shared" : "") + << (flags & ASSIMP_CFLAGS_SINGLETHREADED ? " singlethreaded" : ""); + + ASSIMP_LOG_DEBUG(stream.str()); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the given file and returns its contents if successful. +const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + const std::string pFile(_pFile); + + // ---------------------------------------------------------------------- + // Put a large try block around everything to catch all std::exception's + // that might be thrown by STL containers or by new(). + // ImportErrorException's are throw by ourselves and caught elsewhere. + //----------------------------------------------------------------------- + + WriteLogOpening(pFile); + +#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS + try +#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS + { + // Check whether this Importer instance has already loaded + // a scene. In this case we need to delete the old one + if (pimpl->mScene) { + + ASSIMP_LOG_DEBUG("(Deleting previous scene)"); + FreeScene(); + } + + // First check if the file is accessible at all + if( !pimpl->mIOHandler->Exists( pFile)) { + + pimpl->mErrorString = "Unable to open file \"" + pFile + "\"."; + ASSIMP_LOG_ERROR(pimpl->mErrorString); + return NULL; + } + + std::unique_ptr profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL); + if (profiler) { + profiler->BeginRegion("total"); + } + + // Find an worker class which can handle the file + BaseImporter* imp = NULL; + SetPropertyInteger("importerIndex", -1); + for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { + + if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) { + imp = pimpl->mImporter[a]; + SetPropertyInteger("importerIndex", a); + break; + } + } + + if (!imp) { + // not so bad yet ... try format auto detection. + const std::string::size_type s = pFile.find_last_of('.'); + if (s != std::string::npos) { + ASSIMP_LOG_INFO("File extension not known, trying signature-based detection"); + for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { + if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) { + imp = pimpl->mImporter[a]; + SetPropertyInteger("importerIndex", a); + break; + } + } + } + // Put a proper error message if no suitable importer was found + if( !imp) { + pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\"."; + ASSIMP_LOG_ERROR(pimpl->mErrorString); + return NULL; + } + } + + // Get file size for progress handler + IOStream * fileIO = pimpl->mIOHandler->Open( pFile ); + uint32_t fileSize = 0; + if (fileIO) + { + fileSize = static_cast(fileIO->FileSize()); + pimpl->mIOHandler->Close( fileIO ); + } + + // Dispatch the reading to the worker class for this format + const aiImporterDesc *desc( imp->GetInfo() ); + std::string ext( "unknown" ); + if ( NULL != desc ) { + ext = desc->mName; + } + ASSIMP_LOG_INFO("Found a matching importer for this file format: " + ext + "." ); + pimpl->mProgressHandler->UpdateFileRead( 0, fileSize ); + + if (profiler) { + profiler->BeginRegion("import"); + } + + pimpl->mScene = imp->ReadFile( this, pFile, pimpl->mIOHandler); + pimpl->mProgressHandler->UpdateFileRead( fileSize, fileSize ); + + if (profiler) { + profiler->EndRegion("import"); + } + + SetPropertyString("sourceFilePath", pFile); + + // If successful, apply all active post processing steps to the imported data + if( pimpl->mScene) { + +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + // The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called. + if (pFlags & aiProcess_ValidateDataStructure) + { + ValidateDSProcess ds; + ds.ExecuteOnScene (this); + if (!pimpl->mScene) { + return NULL; + } + } +#endif // no validation + + // Preprocess the scene and prepare it for post-processing + if (profiler) { + profiler->BeginRegion("preprocess"); + } + + ScenePreprocessor pre(pimpl->mScene); + pre.ProcessScene(); + + if (profiler) { + profiler->EndRegion("preprocess"); + } + + // Ensure that the validation process won't be called twice + ApplyPostProcessing(pFlags & (~aiProcess_ValidateDataStructure)); + } + // if failed, extract the error string + else if( !pimpl->mScene) { + pimpl->mErrorString = imp->GetErrorText(); + } + + // clear any data allocated by post-process steps + pimpl->mPPShared->Clean(); + + if (profiler) { + profiler->EndRegion("total"); + } + } +#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS + catch (std::exception &e) + { +#if (defined _MSC_VER) && (defined _CPPRTTI) + // if we have RTTI get the full name of the exception that occurred + pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what(); +#else + pimpl->mErrorString = std::string("std::exception: ") + e.what(); +#endif + + ASSIMP_LOG_ERROR(pimpl->mErrorString); + delete pimpl->mScene; pimpl->mScene = NULL; + } +#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS + + // either successful or failure - the pointer expresses it anyways + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return pimpl->mScene; +} + + +// ------------------------------------------------------------------------------------------------ +// Apply post-processing to the currently bound scene +const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + // Return immediately if no scene is active + if (!pimpl->mScene) { + return NULL; + } + + // If no flags are given, return the current scene with no further action + if (!pFlags) { + return pimpl->mScene; + } + + // In debug builds: run basic flag validation + ai_assert(_ValidateFlags(pFlags)); + ASSIMP_LOG_INFO("Entering post processing pipeline"); + +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + // The ValidateDS process plays an exceptional role. It isn't contained in the global + // list of post-processing steps, so we need to call it manually. + if (pFlags & aiProcess_ValidateDataStructure) + { + ValidateDSProcess ds; + ds.ExecuteOnScene (this); + if (!pimpl->mScene) { + return NULL; + } + } +#endif // no validation +#ifdef ASSIMP_BUILD_DEBUG + if (pimpl->bExtraVerbose) + { +#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + ASSIMP_LOG_ERROR("Verbose Import is not available due to build settings"); +#endif // no validation + pFlags |= aiProcess_ValidateDataStructure; + } +#else + if (pimpl->bExtraVerbose) { + ASSIMP_LOG_WARN("Not a debug build, ignoring extra verbose setting"); + } +#endif // ! DEBUG + + std::unique_ptr profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL); + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { + + BaseProcess* process = pimpl->mPostProcessingSteps[a]; + pimpl->mProgressHandler->UpdatePostProcess(static_cast(a), static_cast(pimpl->mPostProcessingSteps.size()) ); + if( process->IsActive( pFlags)) { + + if (profiler) { + profiler->BeginRegion("postprocess"); + } + + process->ExecuteOnScene ( this ); + + if (profiler) { + profiler->EndRegion("postprocess"); + } + } + if( !pimpl->mScene) { + break; + } +#ifdef ASSIMP_BUILD_DEBUG + +#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + continue; +#endif // no validation + + // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step + if (pimpl->bExtraVerbose) { + ASSIMP_LOG_DEBUG("Verbose Import: re-validating data structures"); + + ValidateDSProcess ds; + ds.ExecuteOnScene (this); + if( !pimpl->mScene) { + ASSIMP_LOG_ERROR("Verbose Import: failed to re-validate data structures"); + break; + } + } +#endif // ! DEBUG + } + pimpl->mProgressHandler->UpdatePostProcess( static_cast(pimpl->mPostProcessingSteps.size()), + static_cast(pimpl->mPostProcessingSteps.size()) ); + + // update private scene flags + if( pimpl->mScene ) + ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags; + + // clear any data allocated by post-process steps + pimpl->mPPShared->Clean(); + ASSIMP_LOG_INFO("Leaving post processing pipeline"); + + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return pimpl->mScene; +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) { + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // Return immediately if no scene is active + if ( NULL == pimpl->mScene ) { + return NULL; + } + + // If no flags are given, return the current scene with no further action + if ( NULL == rootProcess ) { + return pimpl->mScene; + } + + // In debug builds: run basic flag validation + ASSIMP_LOG_INFO( "Entering customized post processing pipeline" ); + +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + // The ValidateDS process plays an exceptional role. It isn't contained in the global + // list of post-processing steps, so we need to call it manually. + if ( requestValidation ) + { + ValidateDSProcess ds; + ds.ExecuteOnScene( this ); + if ( !pimpl->mScene ) { + return NULL; + } + } +#endif // no validation +#ifdef ASSIMP_BUILD_DEBUG + if ( pimpl->bExtraVerbose ) + { +#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + ASSIMP_LOG_ERROR( "Verbose Import is not available due to build settings" ); +#endif // no validation + } +#else + if ( pimpl->bExtraVerbose ) { + ASSIMP_LOG_WARN( "Not a debug build, ignoring extra verbose setting" ); + } +#endif // ! DEBUG + + std::unique_ptr profiler( GetPropertyInteger( AI_CONFIG_GLOB_MEASURE_TIME, 0 ) ? new Profiler() : NULL ); + + if ( profiler ) { + profiler->BeginRegion( "postprocess" ); + } + + rootProcess->ExecuteOnScene( this ); + + if ( profiler ) { + profiler->EndRegion( "postprocess" ); + } + + // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step + if ( pimpl->bExtraVerbose || requestValidation ) { + ASSIMP_LOG_DEBUG( "Verbose Import: revalidating data structures" ); + + ValidateDSProcess ds; + ds.ExecuteOnScene( this ); + if ( !pimpl->mScene ) { + ASSIMP_LOG_ERROR( "Verbose Import: failed to revalidate data structures" ); + } + } + + // clear any data allocated by post-process steps + pimpl->mPPShared->Clean(); + ASSIMP_LOG_INFO( "Leaving customized post processing pipeline" ); + + ASSIMP_END_EXCEPTION_REGION( const aiScene* ); + + return pimpl->mScene; +} + +// ------------------------------------------------------------------------------------------------ +// Helper function to check whether an extension is supported by ASSIMP +bool Importer::IsExtensionSupported(const char* szExtension) const +{ + return nullptr != GetImporter(szExtension); +} + +// ------------------------------------------------------------------------------------------------ +size_t Importer::GetImporterCount() const +{ + return pimpl->mImporter.size(); +} + +// ------------------------------------------------------------------------------------------------ +const aiImporterDesc* Importer::GetImporterInfo(size_t index) const +{ + if (index >= pimpl->mImporter.size()) { + return NULL; + } + return pimpl->mImporter[index]->GetInfo(); +} + + +// ------------------------------------------------------------------------------------------------ +BaseImporter* Importer::GetImporter (size_t index) const +{ + if (index >= pimpl->mImporter.size()) { + return NULL; + } + return pimpl->mImporter[index]; +} + +// ------------------------------------------------------------------------------------------------ +// Find a loader plugin for a given file extension +BaseImporter* Importer::GetImporter (const char* szExtension) const +{ + return GetImporter(GetImporterIndex(szExtension)); +} + +// ------------------------------------------------------------------------------------------------ +// Find a loader plugin for a given file extension +size_t Importer::GetImporterIndex (const char* szExtension) const { + ai_assert(nullptr != szExtension); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // skip over wildcard and dot characters at string head -- + for ( ; *szExtension == '*' || *szExtension == '.'; ++szExtension ); + + std::string ext(szExtension); + if (ext.empty()) { + return static_cast(-1); + } + std::transform( ext.begin(), ext.end(), ext.begin(), ToLower ); + + std::set str; + for (std::vector::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { + str.clear(); + + (*i)->GetExtensionList(str); + for (std::set::const_iterator it = str.begin(); it != str.end(); ++it) { + if (ext == *it) { + return std::distance(static_cast< std::vector::const_iterator >(pimpl->mImporter.begin()), i); + } + } + } + ASSIMP_END_EXCEPTION_REGION(size_t); + return static_cast(-1); +} + +// ------------------------------------------------------------------------------------------------ +// Helper function to build a list of all file extensions supported by ASSIMP +void Importer::GetExtensionList(aiString& szOut) const +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + std::set str; + for (std::vector::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { + (*i)->GetExtensionList(str); + } + + // List can be empty + if( !str.empty() ) { + for (std::set::const_iterator it = str.begin();; ) { + szOut.Append("*."); + szOut.Append((*it).c_str()); + + if (++it == str.end()) { + break; + } + szOut.Append(";"); + } + } + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool Importer::SetPropertyInteger(const char* szName, int iValue) +{ + bool existing; + ASSIMP_BEGIN_EXCEPTION_REGION(); + existing = SetGenericProperty(pimpl->mIntProperties, szName,iValue); + ASSIMP_END_EXCEPTION_REGION(bool); + return existing; +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) +{ + bool existing; + ASSIMP_BEGIN_EXCEPTION_REGION(); + existing = SetGenericProperty(pimpl->mFloatProperties, szName,iValue); + ASSIMP_END_EXCEPTION_REGION(bool); + return existing; +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool Importer::SetPropertyString(const char* szName, const std::string& value) +{ + bool existing; + ASSIMP_BEGIN_EXCEPTION_REGION(); + existing = SetGenericProperty(pimpl->mStringProperties, szName,value); + ASSIMP_END_EXCEPTION_REGION(bool); + return existing; +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) +{ + bool existing; + ASSIMP_BEGIN_EXCEPTION_REGION(); + existing = SetGenericProperty(pimpl->mMatrixProperties, szName,value); + ASSIMP_END_EXCEPTION_REGION(bool); + return existing; +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +int Importer::GetPropertyInteger(const char* szName, + int iErrorReturn /*= 0xffffffff*/) const +{ + return GetGenericProperty(pimpl->mIntProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +ai_real Importer::GetPropertyFloat(const char* szName, + ai_real iErrorReturn /*= 10e10*/) const +{ + return GetGenericProperty(pimpl->mFloatProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +const std::string Importer::GetPropertyString(const char* szName, + const std::string& iErrorReturn /*= ""*/) const +{ + return GetGenericProperty(pimpl->mStringProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, + const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const +{ + return GetGenericProperty(pimpl->mMatrixProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get the memory requirements of a single node +inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) +{ + iScene += sizeof(aiNode); + iScene += sizeof(unsigned int) * pcNode->mNumMeshes; + iScene += sizeof(void*) * pcNode->mNumChildren; + + for (unsigned int i = 0; i < pcNode->mNumChildren;++i) { + AddNodeWeight(iScene,pcNode->mChildren[i]); + } +} + +// ------------------------------------------------------------------------------------------------ +// Get the memory requirements of the scene +void Importer::GetMemoryRequirements(aiMemoryInfo& in) const +{ + in = aiMemoryInfo(); + aiScene* mScene = pimpl->mScene; + + // return if we have no scene loaded + if (!pimpl->mScene) + return; + + + in.total = sizeof(aiScene); + + // add all meshes + for (unsigned int i = 0; i < mScene->mNumMeshes;++i) + { + in.meshes += sizeof(aiMesh); + if (mScene->mMeshes[i]->HasPositions()) { + in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; + } + + if (mScene->mMeshes[i]->HasNormals()) { + in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; + } + + if (mScene->mMeshes[i]->HasTangentsAndBitangents()) { + in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices * 2; + } + + for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) { + if (mScene->mMeshes[i]->HasVertexColors(a)) { + in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices; + } + else break; + } + for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) { + if (mScene->mMeshes[i]->HasTextureCoords(a)) { + in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; + } + else break; + } + if (mScene->mMeshes[i]->HasBones()) { + in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones; + for (unsigned int p = 0; p < mScene->mMeshes[i]->mNumBones;++p) { + in.meshes += sizeof(aiBone); + in.meshes += mScene->mMeshes[i]->mBones[p]->mNumWeights * sizeof(aiVertexWeight); + } + } + in.meshes += (sizeof(aiFace) + 3 * sizeof(unsigned int))*mScene->mMeshes[i]->mNumFaces; + } + in.total += in.meshes; + + // add all embedded textures + for (unsigned int i = 0; i < mScene->mNumTextures;++i) { + const aiTexture* pc = mScene->mTextures[i]; + in.textures += sizeof(aiTexture); + if (pc->mHeight) { + in.textures += 4 * pc->mHeight * pc->mWidth; + } + else in.textures += pc->mWidth; + } + in.total += in.textures; + + // add all animations + for (unsigned int i = 0; i < mScene->mNumAnimations;++i) { + const aiAnimation* pc = mScene->mAnimations[i]; + in.animations += sizeof(aiAnimation); + + // add all bone anims + for (unsigned int a = 0; a < pc->mNumChannels; ++a) { + const aiNodeAnim* pc2 = pc->mChannels[i]; + in.animations += sizeof(aiNodeAnim); + in.animations += pc2->mNumPositionKeys * sizeof(aiVectorKey); + in.animations += pc2->mNumScalingKeys * sizeof(aiVectorKey); + in.animations += pc2->mNumRotationKeys * sizeof(aiQuatKey); + } + } + in.total += in.animations; + + // add all cameras and all lights + in.total += in.cameras = sizeof(aiCamera) * mScene->mNumCameras; + in.total += in.lights = sizeof(aiLight) * mScene->mNumLights; + + // add all nodes + AddNodeWeight(in.nodes,mScene->mRootNode); + in.total += in.nodes; + + // add all materials + for (unsigned int i = 0; i < mScene->mNumMaterials;++i) { + const aiMaterial* pc = mScene->mMaterials[i]; + in.materials += sizeof(aiMaterial); + in.materials += pc->mNumAllocated * sizeof(void*); + + for (unsigned int a = 0; a < pc->mNumProperties;++a) { + in.materials += pc->mProperties[a]->mDataLength; + } + } + in.total += in.materials; +} diff --git a/thirdparty/assimp/code/Common/Importer.h b/thirdparty/assimp/code/Common/Importer.h new file mode 100644 index 0000000000..a439d99c2f --- /dev/null +++ b/thirdparty/assimp/code/Common/Importer.h @@ -0,0 +1,247 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Importer.h mostly internal stuff for use by #Assimp::Importer */ +#pragma once +#ifndef INCLUDED_AI_IMPORTER_H +#define INCLUDED_AI_IMPORTER_H + +#include +#include +#include +#include + +struct aiScene; + +namespace Assimp { + class ProgressHandler; + class IOSystem; + class BaseImporter; + class BaseProcess; + class SharedPostProcessInfo; + + +//! @cond never +// --------------------------------------------------------------------------- +/** @brief Internal PIMPL implementation for Assimp::Importer + * + * Using this idiom here allows us to drop the dependency from + * std::vector and std::map in the public headers. Furthermore we are dropping + * any STL interface problems caused by mismatching STL settings. All + * size calculation are now done by us, not the app heap. */ +class ImporterPimpl { +public: + // Data type to store the key hash + typedef unsigned int KeyType; + + // typedefs for our four configuration maps. + // We don't need more, so there is no need for a generic solution + typedef std::map IntPropertyMap; + typedef std::map FloatPropertyMap; + typedef std::map StringPropertyMap; + typedef std::map MatrixPropertyMap; + + /** IO handler to use for all file accesses. */ + IOSystem* mIOHandler; + bool mIsDefaultHandler; + + /** Progress handler for feedback. */ + ProgressHandler* mProgressHandler; + bool mIsDefaultProgressHandler; + + /** Format-specific importer worker objects - one for each format we can read.*/ + std::vector< BaseImporter* > mImporter; + + /** Post processing steps we can apply at the imported data. */ + std::vector< BaseProcess* > mPostProcessingSteps; + + /** The imported data, if ReadFile() was successful, NULL otherwise. */ + aiScene* mScene; + + /** The error description, if there was one. */ + std::string mErrorString; + + /** List of integer properties */ + IntPropertyMap mIntProperties; + + /** List of floating-point properties */ + FloatPropertyMap mFloatProperties; + + /** List of string properties */ + StringPropertyMap mStringProperties; + + /** List of Matrix properties */ + MatrixPropertyMap mMatrixProperties; + + /** Used for testing - extra verbose mode causes the ValidateDataStructure-Step + * to be executed before and after every single post-process step */ + bool bExtraVerbose; + + /** Used by post-process steps to share data */ + SharedPostProcessInfo* mPPShared; + + /// The default class constructor. + ImporterPimpl() AI_NO_EXCEPT; +}; + +inline +ImporterPimpl::ImporterPimpl() AI_NO_EXCEPT +: mIOHandler( nullptr ) +, mIsDefaultHandler( false ) +, mProgressHandler( nullptr ) +, mIsDefaultProgressHandler( false ) +, mImporter() +, mPostProcessingSteps() +, mScene( nullptr ) +, mErrorString() +, mIntProperties() +, mFloatProperties() +, mStringProperties() +, mMatrixProperties() +, bExtraVerbose( false ) +, mPPShared( nullptr ) { + // empty +} +//! @endcond + + +struct BatchData; + +// --------------------------------------------------------------------------- +/** FOR IMPORTER PLUGINS ONLY: A helper class to the pleasure of importers + * that need to load many external meshes recursively. + * + * The class uses several threads to load these meshes (or at least it + * could, this has not yet been implemented at the moment). + * + * @note The class may not be used by more than one thread*/ +class ASSIMP_API BatchLoader +{ + // friend of Importer + +public: + //! @cond never + // ------------------------------------------------------------------- + /** Wraps a full list of configuration properties for an importer. + * Properties can be set using SetGenericProperty */ + struct PropertyMap + { + ImporterPimpl::IntPropertyMap ints; + ImporterPimpl::FloatPropertyMap floats; + ImporterPimpl::StringPropertyMap strings; + ImporterPimpl::MatrixPropertyMap matrices; + + bool operator == (const PropertyMap& prop) const { + // fixme: really isocpp? gcc complains + return ints == prop.ints && floats == prop.floats && strings == prop.strings && matrices == prop.matrices; + } + + bool empty () const { + return ints.empty() && floats.empty() && strings.empty() && matrices.empty(); + } + }; + //! @endcond + +public: + // ------------------------------------------------------------------- + /** Construct a batch loader from a given IO system to be used + * to access external files + */ + explicit BatchLoader(IOSystem* pIO, bool validate = false ); + + // ------------------------------------------------------------------- + /** The class destructor. + */ + ~BatchLoader(); + + // ------------------------------------------------------------------- + /** Sets the validation step. True for enable validation during postprocess. + * @param enable True for validation. + */ + void setValidation( bool enabled ); + + // ------------------------------------------------------------------- + /** Returns the current validation step. + * @return The current validation step. + */ + bool getValidation() const; + + // ------------------------------------------------------------------- + /** Add a new file to the list of files to be loaded. + * @param file File to be loaded + * @param steps Post-processing steps to be executed on the file + * @param map Optional configuration properties + * @return 'Load request channel' - an unique ID that can later + * be used to access the imported file data. + * @see GetImport */ + unsigned int AddLoadRequest ( + const std::string& file, + unsigned int steps = 0, + const PropertyMap* map = NULL + ); + + // ------------------------------------------------------------------- + /** Get an imported scene. + * This polls the import from the internal request list. + * If an import is requested several times, this function + * can be called several times, too. + * + * @param which LRWC returned by AddLoadRequest(). + * @return NULL if there is no scene with this file name + * in the queue of the scene hasn't been loaded yet. */ + aiScene* GetImport( + unsigned int which + ); + + // ------------------------------------------------------------------- + /** Waits until all scenes have been loaded. This returns + * immediately if no scenes are queued.*/ + void LoadAll(); + +private: + // No need to have that in the public API ... + BatchData *m_data; +}; + +} // Namespace Assimp + +#endif // INCLUDED_AI_IMPORTER_H diff --git a/thirdparty/assimp/code/Common/ImporterRegistry.cpp b/thirdparty/assimp/code/Common/ImporterRegistry.cpp new file mode 100644 index 0000000000..32ac3b4168 --- /dev/null +++ b/thirdparty/assimp/code/Common/ImporterRegistry.cpp @@ -0,0 +1,371 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file ImporterRegistry.cpp + +Central registry for all importers available. Do not edit this file +directly (unless you are adding new loaders), instead use the +corresponding preprocessor flag to selectively disable formats. +*/ + +#include +#include + +// ------------------------------------------------------------------------------------------------ +// Importers +// (include_new_importers_here) +// ------------------------------------------------------------------------------------------------ +#ifndef ASSIMP_BUILD_NO_X_IMPORTER +# include "X/XFileImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER +# include "AMF/AMFImporter.hpp" +#endif +#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER +# include "3DS/3DSLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER +# include "MD3/MD3Loader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER +# include "MDL/MDLLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER +# include "MD2/MD2Loader.h" +#endif +#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER +# include "Ply/PlyLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER +# include "ASE/ASELoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER +# include "Obj/ObjFileImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_HMP_IMPORTER +# include "HMP/HMPLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER +# include "SMD/SMDLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER +# include "MDC/MDCLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER +# include "MD5/MD5Loader.h" +#endif +#ifndef ASSIMP_BUILD_NO_STL_IMPORTER +# include "STL/STLLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER +# include "LWO/LWOLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER +# include "DXF/DXFLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER +# include "NFF/NFFLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER +# include "Raw/RawLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_SIB_IMPORTER +# include "SIB/SIBImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER +# include "OFF/OFFLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_AC_IMPORTER +# include "AC/ACLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER +# include "BVH/BVHLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER +# include "Irr/IRRMeshLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER +# include "Irr/IRRLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER +# include "Q3D/Q3DLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER +# include "B3D/B3DImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER +# include "Collada/ColladaLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER +# include "Terragen/TerragenLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_CSM_IMPORTER +# include "CSM/CSMLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_3D_IMPORTER +# include "Unreal/UnrealLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_LWS_IMPORTER +# include "LWS/LWSLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER +# include "Ogre/OgreImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER +# include "OpenGEX/OpenGEXImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER +# include "MS3D/MS3DLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_COB_IMPORTER +# include "COB/COBLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER +# include "Blender/BlenderLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER +# include "Q3BSP/Q3BSPFileImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_NDO_IMPORTER +# include "NDO/NDOLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER +# include "Importer/IFC/IFCLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_XGL_IMPORTER +# include "XGL/XGLLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER +# include "FBX/FBXImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER +# include "Assbin/AssbinLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER +# include "glTF/glTFImporter.h" +# include "glTF2/glTF2Importer.h" +#endif +#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER +# include "C4D/C4DImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER +# include "3MF/D3MFImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER +# include "X3D/X3DImporter.hpp" +#endif +#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER +# include "MMD/MMDImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER +# include "Importer/StepFile/StepFileImporter.h" +#endif + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +void GetImporterInstanceList(std::vector< BaseImporter* >& out) +{ + // ---------------------------------------------------------------------------- + // Add an instance of each worker class here + // (register_new_importers_here) + // ---------------------------------------------------------------------------- + out.reserve(64); +#if (!defined ASSIMP_BUILD_NO_X_IMPORTER) + out.push_back( new XFileImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_OBJ_IMPORTER) + out.push_back( new ObjFileImporter()); +#endif +#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER + out.push_back( new AMFImporter() ); +#endif +#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER) + out.push_back( new Discreet3DSImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_MD3_IMPORTER) + out.push_back( new MD3Importer()); +#endif +#if (!defined ASSIMP_BUILD_NO_MD2_IMPORTER) + out.push_back( new MD2Importer()); +#endif +#if (!defined ASSIMP_BUILD_NO_PLY_IMPORTER) + out.push_back( new PLYImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_MDL_IMPORTER) + out.push_back( new MDLImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_ASE_IMPORTER) + #if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER) + out.push_back( new ASEImporter()); +# endif +#endif +#if (!defined ASSIMP_BUILD_NO_HMP_IMPORTER) + out.push_back( new HMPImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_SMD_IMPORTER) + out.push_back( new SMDImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_MDC_IMPORTER) + out.push_back( new MDCImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_MD5_IMPORTER) + out.push_back( new MD5Importer()); +#endif +#if (!defined ASSIMP_BUILD_NO_STL_IMPORTER) + out.push_back( new STLImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER) + out.push_back( new LWOImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_DXF_IMPORTER) + out.push_back( new DXFImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_NFF_IMPORTER) + out.push_back( new NFFImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_RAW_IMPORTER) + out.push_back( new RAWImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_SIB_IMPORTER) + out.push_back( new SIBImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_OFF_IMPORTER) + out.push_back( new OFFImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_AC_IMPORTER) + out.push_back( new AC3DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_BVH_IMPORTER) + out.push_back( new BVHLoader()); +#endif +#if (!defined ASSIMP_BUILD_NO_IRRMESH_IMPORTER) + out.push_back( new IRRMeshImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_IRR_IMPORTER) + out.push_back( new IRRImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_Q3D_IMPORTER) + out.push_back( new Q3DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_B3D_IMPORTER) + out.push_back( new B3DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_COLLADA_IMPORTER) + out.push_back( new ColladaLoader()); +#endif +#if (!defined ASSIMP_BUILD_NO_TERRAGEN_IMPORTER) + out.push_back( new TerragenImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_CSM_IMPORTER) + out.push_back( new CSMImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_3D_IMPORTER) + out.push_back( new UnrealImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_LWS_IMPORTER) + out.push_back( new LWSImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_OGRE_IMPORTER) + out.push_back( new Ogre::OgreImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_OPENGEX_IMPORTER ) + out.push_back( new OpenGEX::OpenGEXImporter() ); +#endif +#if (!defined ASSIMP_BUILD_NO_MS3D_IMPORTER) + out.push_back( new MS3DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_COB_IMPORTER) + out.push_back( new COBImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_BLEND_IMPORTER) + out.push_back( new BlenderImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_Q3BSP_IMPORTER) + out.push_back( new Q3BSPFileImporter() ); +#endif +#if (!defined ASSIMP_BUILD_NO_NDO_IMPORTER) + out.push_back( new NDOImporter() ); +#endif +#if (!defined ASSIMP_BUILD_NO_IFC_IMPORTER) + out.push_back( new IFCImporter() ); +#endif +#if ( !defined ASSIMP_BUILD_NO_XGL_IMPORTER ) + out.push_back( new XGLImporter() ); +#endif +#if ( !defined ASSIMP_BUILD_NO_FBX_IMPORTER ) + out.push_back( new FBXImporter() ); +#endif +#if ( !defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER ) + out.push_back( new AssbinImporter() ); +#endif +#if ( !defined ASSIMP_BUILD_NO_GLTF_IMPORTER ) + out.push_back( new glTFImporter() ); + out.push_back( new glTF2Importer() ); +#endif +#if ( !defined ASSIMP_BUILD_NO_C4D_IMPORTER ) + out.push_back( new C4DImporter() ); +#endif +#if ( !defined ASSIMP_BUILD_NO_3MF_IMPORTER ) + out.push_back( new D3MFImporter() ); +#endif +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + out.push_back( new X3DImporter() ); +#endif +#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER + out.push_back( new MMDImporter() ); +#endif +#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER + out.push_back(new StepFile::StepFileImporter()); +#endif +} + +/** will delete all registered importers. */ +void DeleteImporterInstanceList(std::vector< BaseImporter* >& deleteList){ + for(size_t i= 0; i +#include + +namespace Assimp { + +// ------------------------------------------------------------------------------- +/** Compute the signed area of a triangle. + * The function accepts an unconstrained template parameter for use with + * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ +template +inline double GetArea2D(const T& v1, const T& v2, const T& v3) +{ + return 0.5 * (v1.x * ((double)v3.y - v2.y) + v2.x * ((double)v1.y - v3.y) + v3.x * ((double)v2.y - v1.y)); +} + +// ------------------------------------------------------------------------------- +/** Test if a given point p2 is on the left side of the line formed by p0-p1. + * The function accepts an unconstrained template parameter for use with + * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ +template +inline bool OnLeftSideOfLine2D(const T& p0, const T& p1,const T& p2) +{ + return GetArea2D(p0,p2,p1) > 0; +} + +// ------------------------------------------------------------------------------- +/** Test if a given point is inside a given triangle in R2. + * The function accepts an unconstrained template parameter for use with + * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ +template +inline bool PointInTriangle2D(const T& p0, const T& p1,const T& p2, const T& pp) +{ + // Point in triangle test using baryzentric coordinates + const aiVector2D v0 = p1 - p0; + const aiVector2D v1 = p2 - p0; + const aiVector2D v2 = pp - p0; + + double dot00 = v0 * v0; + double dot01 = v0 * v1; + double dot02 = v0 * v2; + double dot11 = v1 * v1; + double dot12 = v1 * v2; + + const double invDenom = 1 / (dot00 * dot11 - dot01 * dot01); + dot11 = (dot11 * dot02 - dot01 * dot12) * invDenom; + dot00 = (dot00 * dot12 - dot01 * dot02) * invDenom; + + return (dot11 > 0) && (dot00 > 0) && (dot11 + dot00 < 1); +} + + +// ------------------------------------------------------------------------------- +/** Check whether the winding order of a given polygon is counter-clockwise. + * The function accepts an unconstrained template parameter, but is intended + * to be used only with aiVector2D and aiVector3D (z axis is ignored, only + * x and y are taken into account). + * @note Code taken from http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Ian/applet1.html and translated to C++ + */ +template +inline bool IsCCW(T* in, size_t npoints) { + double aa, bb, cc, b, c, theta; + double convex_turn; + double convex_sum = 0; + + ai_assert(npoints >= 3); + + for (size_t i = 0; i < npoints - 2; i++) { + aa = ((in[i+2].x - in[i].x) * (in[i+2].x - in[i].x)) + + ((-in[i+2].y + in[i].y) * (-in[i+2].y + in[i].y)); + + bb = ((in[i+1].x - in[i].x) * (in[i+1].x - in[i].x)) + + ((-in[i+1].y + in[i].y) * (-in[i+1].y + in[i].y)); + + cc = ((in[i+2].x - in[i+1].x) * + (in[i+2].x - in[i+1].x)) + + ((-in[i+2].y + in[i+1].y) * + (-in[i+2].y + in[i+1].y)); + + b = std::sqrt(bb); + c = std::sqrt(cc); + theta = std::acos((bb + cc - aa) / (2 * b * c)); + + if (OnLeftSideOfLine2D(in[i],in[i+2],in[i+1])) { + // if (convex(in[i].x, in[i].y, + // in[i+1].x, in[i+1].y, + // in[i+2].x, in[i+2].y)) { + convex_turn = AI_MATH_PI_F - theta; + convex_sum += convex_turn; + } + else { + convex_sum -= AI_MATH_PI_F - theta; + } + } + aa = ((in[1].x - in[npoints-2].x) * + (in[1].x - in[npoints-2].x)) + + ((-in[1].y + in[npoints-2].y) * + (-in[1].y + in[npoints-2].y)); + + bb = ((in[0].x - in[npoints-2].x) * + (in[0].x - in[npoints-2].x)) + + ((-in[0].y + in[npoints-2].y) * + (-in[0].y + in[npoints-2].y)); + + cc = ((in[1].x - in[0].x) * (in[1].x - in[0].x)) + + ((-in[1].y + in[0].y) * (-in[1].y + in[0].y)); + + b = std::sqrt(bb); + c = std::sqrt(cc); + theta = std::acos((bb + cc - aa) / (2 * b * c)); + + //if (convex(in[npoints-2].x, in[npoints-2].y, + // in[0].x, in[0].y, + // in[1].x, in[1].y)) { + if (OnLeftSideOfLine2D(in[npoints-2],in[1],in[0])) { + convex_turn = AI_MATH_PI_F - theta; + convex_sum += convex_turn; + } + else { + convex_sum -= AI_MATH_PI_F - theta; + } + + return convex_sum >= (2 * AI_MATH_PI_F); +} + + +// ------------------------------------------------------------------------------- +/** Compute the normal of an arbitrary polygon in R3. + * + * The code is based on Newell's formula, that is a polygons normal is the ratio + * of its area when projected onto the three coordinate axes. + * + * @param out Receives the output normal + * @param num Number of input vertices + * @param x X data source. x[ofs_x*n] is the n'th element. + * @param y Y data source. y[ofs_y*n] is the y'th element + * @param z Z data source. z[ofs_z*n] is the z'th element + * + * @note The data arrays must have storage for at least num+2 elements. Using + * this method is much faster than the 'other' NewellNormal() + */ +template +inline void NewellNormal (aiVector3t& out, int num, TReal* x, TReal* y, TReal* z) +{ + // Duplicate the first two vertices at the end + x[(num+0)*ofs_x] = x[0]; + x[(num+1)*ofs_x] = x[ofs_x]; + + y[(num+0)*ofs_y] = y[0]; + y[(num+1)*ofs_y] = y[ofs_y]; + + z[(num+0)*ofs_z] = z[0]; + z[(num+1)*ofs_z] = z[ofs_z]; + + TReal sum_xy = 0.0, sum_yz = 0.0, sum_zx = 0.0; + + TReal *xptr = x +ofs_x, *xlow = x, *xhigh = x + ofs_x*2; + TReal *yptr = y +ofs_y, *ylow = y, *yhigh = y + ofs_y*2; + TReal *zptr = z +ofs_z, *zlow = z, *zhigh = z + ofs_z*2; + + for (int tmp=0; tmp < num; tmp++) { + sum_xy += (*xptr) * ( (*yhigh) - (*ylow) ); + sum_yz += (*yptr) * ( (*zhigh) - (*zlow) ); + sum_zx += (*zptr) * ( (*xhigh) - (*xlow) ); + + xptr += ofs_x; + xlow += ofs_x; + xhigh += ofs_x; + + yptr += ofs_y; + ylow += ofs_y; + yhigh += ofs_y; + + zptr += ofs_z; + zlow += ofs_z; + zhigh += ofs_z; + } + out = aiVector3t(sum_yz,sum_zx,sum_xy); +} + +} // ! Assimp + +#endif diff --git a/thirdparty/assimp/code/Common/PostStepRegistry.cpp b/thirdparty/assimp/code/Common/PostStepRegistry.cpp new file mode 100644 index 0000000000..ef58f8ddfd --- /dev/null +++ b/thirdparty/assimp/code/Common/PostStepRegistry.cpp @@ -0,0 +1,258 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file ImporterRegistry.cpp + +Central registry for all postprocessing steps available. Do not edit this file +directly (unless you are adding new steps), instead use the +corresponding preprocessor flag to selectively disable steps. +*/ + +#include "PostProcessing/ProcessHelper.h" + +#ifndef ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS +# include "PostProcessing/CalcTangentsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS +# include "PostProcessing/JoinVerticesProcess.h" +#endif +#if !(defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS && defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS && defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS) +# include "PostProcessing/ConvertToLHProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS +# include "PostProcessing/TriangulateProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_DROPFACENORMALS_PROCESS +# include "PostProcessing/DropFaceNormalsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS +# include "PostProcessing/GenFaceNormalsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS +# include "PostProcessing/GenVertexNormalsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_REMOVEVC_PROCESS +# include "PostProcessing/RemoveVCProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS +# include "PostProcessing/SplitLargeMeshes.h" +#endif +#ifndef ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS +# include "PostProcessing/PretransformVertices.h" +#endif +#ifndef ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS +# include "PostProcessing/LimitBoneWeightsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS +# include "PostProcessing/ValidateDataStructure.h" +#endif +#ifndef ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS +# include "PostProcessing/ImproveCacheLocality.h" +#endif +#ifndef ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS +# include "PostProcessing/FixNormalsStep.h" +#endif +#ifndef ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS +# include "PostProcessing/RemoveRedundantMaterials.h" +#endif +#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS) +# include "PostProcessing/EmbedTexturesProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS +# include "PostProcessing/FindInvalidDataProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS +# include "PostProcessing/FindDegenerates.h" +#endif +#ifndef ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS +# include "PostProcessing/SortByPTypeProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS +# include "PostProcessing/ComputeUVMappingProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS +# include "PostProcessing/TextureTransform.h" +#endif +#ifndef ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS +# include "PostProcessing/FindInstancesProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS +# include "PostProcessing/OptimizeMeshes.h" +#endif +#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS +# include "PostProcessing/OptimizeGraph.h" +#endif +#ifndef ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS +# include "Common/SplitByBoneCountProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS +# include "PostProcessing/DeboneProcess.h" +#endif +#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) +# include "PostProcessing/ScaleProcess.h" +#endif +#if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS) +# include "PostProcessing/GenBoundingBoxesProcess.h" +#endif + + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out) +{ + // ---------------------------------------------------------------------------- + // Add an instance of each post processing step here in the order + // of sequence it is executed. Steps that are added here are not + // validated - as RegisterPPStep() does - all dependencies must be given. + // ---------------------------------------------------------------------------- + out.reserve(31); +#if (!defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS) + out.push_back( new MakeLeftHandedProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS) + out.push_back( new FlipUVsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS) + out.push_back( new FlipWindingOrderProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_REMOVEVC_PROCESS) + out.push_back( new RemoveVCProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS) + out.push_back( new RemoveRedundantMatsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS) + out.push_back( new EmbedTexturesProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS) + out.push_back( new FindInstancesProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS) + out.push_back( new OptimizeGraphProcess()); +#endif +#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS + out.push_back( new ComputeUVMappingProcess()); +#endif +#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS + out.push_back( new TextureTransformStep()); +#endif +#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) + out.push_back( new ScaleProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS) + out.push_back( new PretransformVertices()); +#endif +#if (!defined ASSIMP_BUILD_NO_TRIANGULATE_PROCESS) + out.push_back( new TriangulateProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS) + //find degenerates should run after triangulation (to sort out small + //generated triangles) but before sort by p types (in case there are lines + //and points generated and inserted into a mesh) + out.push_back( new FindDegeneratesProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS) + out.push_back( new SortByPTypeProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS) + out.push_back( new FindInvalidDataProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS) + out.push_back( new OptimizeMeshesProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS) + out.push_back( new FixInfacingNormalsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS) + out.push_back( new SplitByBoneCountProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS) + out.push_back( new SplitLargeMeshesProcess_Triangle()); +#endif +#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS) + out.push_back( new DropFaceNormalsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS) + out.push_back( new GenFaceNormalsProcess()); +#endif + // ......................................................................... + // DON'T change the order of these five .. + // XXX this is actually a design weakness that dates back to the time + // when Importer would maintain the postprocessing step list exclusively. + // Now that others access it too, we need a better solution. + out.push_back( new ComputeSpatialSortProcess()); + // ......................................................................... + +#if (!defined ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS) + out.push_back( new GenVertexNormalsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS) + out.push_back( new CalcTangentsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_JOINVERTICES_PROCESS) + out.push_back( new JoinVerticesProcess()); +#endif + + // ......................................................................... + out.push_back( new DestroySpatialSortProcess()); + // ......................................................................... + +#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS) + out.push_back( new SplitLargeMeshesProcess_Vertex()); +#endif +#if (!defined ASSIMP_BUILD_NO_DEBONE_PROCESS) + out.push_back( new DeboneProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS) + out.push_back( new LimitBoneWeightsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS) + out.push_back( new ImproveCacheLocalityProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS) + out.push_back(new GenBoundingBoxesProcess); +#endif +} + +} diff --git a/thirdparty/assimp/code/Common/RemoveComments.cpp b/thirdparty/assimp/code/Common/RemoveComments.cpp new file mode 100644 index 0000000000..91700a7699 --- /dev/null +++ b/thirdparty/assimp/code/Common/RemoveComments.cpp @@ -0,0 +1,113 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file RemoveComments.cpp + * @brief Defines the CommentRemover utility class + */ + +#include +#include + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +// Remove line comments from a file +void CommentRemover::RemoveLineComments(const char* szComment, + char* szBuffer, char chReplacement /* = ' ' */) +{ + // validate parameters + ai_assert(NULL != szComment && NULL != szBuffer && *szComment); + + const size_t len = strlen(szComment); + while (*szBuffer) { + + // skip over quotes + if (*szBuffer == '\"' || *szBuffer == '\'') + while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\''); + + if (!strncmp(szBuffer,szComment,len)) { + while (!IsLineEnd(*szBuffer)) + *szBuffer++ = chReplacement; + + if (!*szBuffer) { + break; + } + } + ++szBuffer; + } +} + +// ------------------------------------------------------------------------------------------------ +// Remove multi-line comments from a file +void CommentRemover::RemoveMultiLineComments(const char* szCommentStart, + const char* szCommentEnd,char* szBuffer, + char chReplacement) +{ + // validate parameters + ai_assert(NULL != szCommentStart && NULL != szCommentEnd && + NULL != szBuffer && *szCommentStart && *szCommentEnd); + + const size_t len = strlen(szCommentEnd); + const size_t len2 = strlen(szCommentStart); + + while (*szBuffer) { + // skip over quotes + if (*szBuffer == '\"' || *szBuffer == '\'') + while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\''); + + if (!strncmp(szBuffer,szCommentStart,len2)) { + while (*szBuffer) { + if (!::strncmp(szBuffer,szCommentEnd,len)) { + for (unsigned int i = 0; i < len;++i) + *szBuffer++ = chReplacement; + + break; + } + *szBuffer++ = chReplacement; + } + continue; + } + ++szBuffer; + } +} + +} // !! Assimp diff --git a/thirdparty/assimp/code/Common/SGSpatialSort.cpp b/thirdparty/assimp/code/Common/SGSpatialSort.cpp new file mode 100644 index 0000000000..120070b0aa --- /dev/null +++ b/thirdparty/assimp/code/Common/SGSpatialSort.cpp @@ -0,0 +1,168 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the helper class to quickly find +vertices close to a given position. Special implementation for +the 3ds loader handling smooth groups correctly */ + +#include + +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& poResults, + bool exactMatch /*= false*/) const +{ + float dist = pPosition * mPlaneNormal; + float minDist = dist - pRadius, maxDist = dist + pRadius; + + // clear the array + poResults.clear(); + + // quick check for positions outside the range + if( mPositions.empty() ) + return; + if( maxDist < mPositions.front().mDistance) + return; + if( minDist > mPositions.back().mDistance) + return; + + // do a binary search for the minimal distance to start the iteration there + unsigned int index = (unsigned int)mPositions.size() / 2; + unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; + while( binaryStepSize > 1) + { + if( mPositions[index].mDistance < minDist) + index += binaryStepSize; + else + index -= binaryStepSize; + + binaryStepSize /= 2; + } + + // depending on the direction of the last step we need to single step a bit back or forth + // to find the actual beginning element of the range + while( index > 0 && mPositions[index].mDistance > minDist) + index--; + while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist) + index++; + + // Mow start iterating from there until the first position lays outside of the distance range. + // Add all positions inside the distance range within the given radius to the result aray + + float squareEpsilon = pRadius * pRadius; + std::vector::const_iterator it = mPositions.begin() + index; + std::vector::const_iterator end = mPositions.end(); + + if (exactMatch) + { + while( it->mDistance < maxDist) + { + if((it->mPosition - pPosition).SquareLength() < squareEpsilon && it->mSmoothGroups == pSG) + { + poResults.push_back( it->mIndex); + } + ++it; + if( end == it )break; + } + } + else + { + // if the given smoothing group is 0, we'll return all surrounding vertices + if (!pSG) + { + while( it->mDistance < maxDist) + { + if((it->mPosition - pPosition).SquareLength() < squareEpsilon) + poResults.push_back( it->mIndex); + ++it; + if( end == it)break; + } + } + else while( it->mDistance < maxDist) + { + if((it->mPosition - pPosition).SquareLength() < squareEpsilon && + (it->mSmoothGroups & pSG || !it->mSmoothGroups)) + { + poResults.push_back( it->mIndex); + } + ++it; + if( end == it)break; + } + } +} + + diff --git a/thirdparty/assimp/code/Common/SceneCombiner.cpp b/thirdparty/assimp/code/Common/SceneCombiner.cpp new file mode 100644 index 0000000000..e445bd7434 --- /dev/null +++ b/thirdparty/assimp/code/Common/SceneCombiner.cpp @@ -0,0 +1,1300 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +// TODO: refactor entire file to get rid of the "flat-copy" first approach +// to copying structures. This easily breaks in the most unintuitive way +// possible as new fields are added to assimp structures. + +// ---------------------------------------------------------------------------- +/** + * @file Implements Assimp::SceneCombiner. This is a smart utility + * class that combines multiple scenes, meshes, ... into one. Currently + * these utilities are used by the IRR and LWS loaders and the + * OptimizeGraph step. + */ +// ---------------------------------------------------------------------------- +#include +#include +#include +#include +#include +#include "time.h" +#include +#include +#include +#include +#include "ScenePrivate.h" + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +// Add a prefix to a string +inline +void PrefixString(aiString& string,const char* prefix, unsigned int len) { + // If the string is already prefixed, we won't prefix it a second time + if (string.length >= 1 && string.data[0] == '$') + return; + + if (len+string.length>=MAXLEN-1) { + ASSIMP_LOG_DEBUG("Can't add an unique prefix because the string is too long"); + ai_assert(false); + return; + } + + // Add the prefix + ::memmove(string.data+len,string.data,string.length+1); + ::memcpy (string.data, prefix, len); + + // And update the string's length + string.length += len; +} + +// ------------------------------------------------------------------------------------------------ +// Add node identifiers to a hashing set +void SceneCombiner::AddNodeHashes(aiNode* node, std::set& hashes) { + // Add node name to hashing set if it is non-empty - empty nodes are allowed + // and they can't have any anims assigned so its absolutely safe to duplicate them. + if (node->mName.length) { + hashes.insert( SuperFastHash(node->mName.data, static_cast(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& input, unsigned int cur) { + const unsigned int hash = SuperFastHash(name.data, static_cast(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& input, unsigned int cur) { + ai_assert(NULL != prefix); + const unsigned int hash = SuperFastHash(node->mName.data, static_cast(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& src, unsigned int flags) { + if ( nullptr == _dest ) { + return; + } + + // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it + if (src.empty()) { + if (*_dest) { + (*_dest)->~aiScene(); + SceneCombiner::CopySceneFlat(_dest,src[0]); + } + else *_dest = src[0]; + return; + } + if (*_dest)(*_dest)->~aiScene(); + else *_dest = new aiScene(); + + // Create a dummy scene to serve as master for the others + aiScene* master = new aiScene(); + master->mRootNode = new aiNode(); + master->mRootNode->mName.Set(""); + + std::vector 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& srcList) { + unsigned int cnt; + for ( cnt = 0; cnt < attach->mNumChildren; ++cnt ) { + AttachToGraph( attach->mChildren[ cnt ], srcList ); + } + + cnt = 0; + for (std::vector::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& src) { + ai_assert(NULL != master); + AttachToGraph(master->mRootNode,src); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master, std::vector& srcList, unsigned int flags) { + if ( nullptr == _dest ) { + return; + } + + // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it + if (srcList.empty()) { + if (*_dest) { + SceneCombiner::CopySceneFlat(_dest,master); + } + else *_dest = master; + return; + } + if (*_dest) { + (*_dest)->~aiScene(); + new (*_dest) aiScene(); + } + else *_dest = new aiScene(); + + aiScene* dest = *_dest; + + std::vector 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 duplicates(src.size(),UINT_MAX); + + // this helper array is used as lookup table several times + std::vector offset(src.size()); + + // Find duplicate scenes + for (unsigned int i = 0; i < src.size();++i) { + if (duplicates[i] != i && duplicates[i] != UINT_MAX) { + continue; + } + + duplicates[i] = i; + for ( unsigned int a = i+1; a < src.size(); ++a) { + if (src[i].scene == src[a].scene) { + duplicates[a] = i; + } + } + } + + // Generate unique names for all named stuff? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) + { +#if 0 + // Construct a proper random number generator + boost::mt19937 rng( ); + boost::uniform_int<> dist(1u,1 << 24u); + boost::variate_generator > rndGen(rng, dist); +#endif + for (unsigned int i = 1; i < src.size();++i) + { + //if (i != duplicates[i]) + //{ + // // duplicate scenes share the same UID + // ::strcpy( src[i].id, src[duplicates[i]].id ); + // src[i].idlen = src[duplicates[i]].idlen; + + // continue; + //} + + src[i].idlen = ai_snprintf(src[i].id, 32, "$%.6X$_",i); + + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + + // Compute hashes for all identifiers in this scene and store them + // in a sorted table (for convenience I'm using std::set). We hash + // just the node and animation channel names, all identifiers except + // the material names should be caught by doing this. + AddNodeHashes(src[i]->mRootNode,src[i].hashes); + + for (unsigned int a = 0; a < src[i]->mNumAnimations;++a) { + aiAnimation* anim = src[i]->mAnimations[a]; + src[i].hashes.insert(SuperFastHash(anim->mName.data,static_cast(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: *, + // where n is the index of the texture. + aiString& s = *((aiString*)prop->mData); + if ('*' == s.data[0]) { + // Offset the index and write it back .. + const unsigned int idx = strtoul10(&s.data[1]) + offset[n]; + ASSIMP_itoa10(&s.data[1],sizeof(s.data)-1,idx); + } + } + + // Need to generate new, unique material names? + else if (!::strcmp( prop->mKey.data,"$mat.name" ) && flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) + { + aiString* pcSrc = (aiString*) prop->mData; + PrefixString(*pcSrc, (*cur).id, (*cur).idlen); + } + } + } + ++pip; + } + + offset[n] = cnt; + cnt = (unsigned int)(pip - dest->mMaterials); + } + } + + // generate the output mesh list + again an offset table for all mesh indices + if (dest->mNumMeshes) + { + aiMesh** pip = dest->mMeshes = new aiMesh*[dest->mNumMeshes]; + cnt = 0; + for ( unsigned int n = 0; n < src.size();++n ) + { + SceneHelper* cur = &src[n]; + for (unsigned int i = 0; i < (*cur)->mNumMeshes;++i) + { + if (n != duplicates[n]) { + if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) + Copy(pip, (*cur)->mMeshes[i]); + + else continue; + } + else *pip = (*cur)->mMeshes[i]; + + // update the material index of the mesh + (*pip)->mMaterialIndex += offset[n]; + ++pip; + } + + // reuse the offset array - store now the mesh offset in it + offset[n] = cnt; + cnt = (unsigned int)(pip - dest->mMeshes); + } + } + + std::vector 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 ... $_ + // We could also use a counter, but using a random value allows us to + // use just one prefix if we are joining multiple scene hierarchies recursively. + // Chances are quite good we don't collide, so we try that ... + // ---------------------------------------------------------------------------- + + // Allocate space for light sources, cameras and animations + aiLight** ppLights = dest->mLights = (dest->mNumLights + ? new aiLight*[dest->mNumLights] : NULL); + + aiCamera** ppCameras = dest->mCameras = (dest->mNumCameras + ? new aiCamera*[dest->mNumCameras] : NULL); + + aiAnimation** ppAnims = dest->mAnimations = (dest->mNumAnimations + ? new aiAnimation*[dest->mNumAnimations] : NULL); + + for ( int n = static_cast(src.size()-1); n >= 0 ;--n ) /* !!! important !!! */ + { + SceneHelper* cur = &src[n]; + aiNode* node; + + // To offset or not to offset, this is the question + if (n != (int)duplicates[n]) + { + // Get full scene-graph copy + Copy( &node, (*cur)->mRootNode ); + OffsetNodeMeshIndices(node,offset[duplicates[n]]); + + if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) { + // (note:) they are already 'offseted' by offset[duplicates[n]] + OffsetNodeMeshIndices(node,offset[n] - offset[duplicates[n]]); + } + } + else // if (n == duplicates[n]) + { + node = (*cur)->mRootNode; + OffsetNodeMeshIndices(node,offset[n]); + } + if (n) // src[0] is the master node + nodes.push_back(NodeAttachmentInfo( node,srcList[n-1].attachToNode,n )); + + // add name prefixes? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { + + // or the whole scenegraph + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + AddNodePrefixesChecked(node,(*cur).id,(*cur).idlen,src,n); + } + else AddNodePrefixes(node,(*cur).id,(*cur).idlen); + + // meshes + for (unsigned int i = 0; i < (*cur)->mNumMeshes;++i) { + aiMesh* mesh = (*cur)->mMeshes[i]; + + // rename all bones + for (unsigned int a = 0; a < mesh->mNumBones;++a) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch(mesh->mBones[a]->mName,src,n)) + continue; + } + PrefixString(mesh->mBones[a]->mName,(*cur).id,(*cur).idlen); + } + } + } + + // -------------------------------------------------------------------- + // Copy light sources + for (unsigned int i = 0; i < (*cur)->mNumLights;++i,++ppLights) + { + if (n != (int)duplicates[n]) // duplicate scene? + { + Copy(ppLights, (*cur)->mLights[i]); + } + else *ppLights = (*cur)->mLights[i]; + + + // Add name prefixes? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch((*ppLights)->mName,src,n)) + continue; + } + + PrefixString((*ppLights)->mName,(*cur).id,(*cur).idlen); + } + } + + // -------------------------------------------------------------------- + // Copy cameras + for (unsigned int i = 0; i < (*cur)->mNumCameras;++i,++ppCameras) { + if (n != (int)duplicates[n]) // duplicate scene? + { + Copy(ppCameras, (*cur)->mCameras[i]); + } + else *ppCameras = (*cur)->mCameras[i]; + + // Add name prefixes? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch((*ppCameras)->mName,src,n)) + continue; + } + + PrefixString((*ppCameras)->mName,(*cur).id,(*cur).idlen); + } + } + + // -------------------------------------------------------------------- + // Copy animations + for (unsigned int i = 0; i < (*cur)->mNumAnimations;++i,++ppAnims) { + if (n != (int)duplicates[n]) // duplicate scene? + { + Copy(ppAnims, (*cur)->mAnimations[i]); + } + else *ppAnims = (*cur)->mAnimations[i]; + + // Add name prefixes? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch((*ppAnims)->mName,src,n)) + continue; + } + + PrefixString((*ppAnims)->mName,(*cur).id,(*cur).idlen); + + // don't forget to update all node animation channels + for (unsigned int a = 0; a < (*ppAnims)->mNumChannels;++a) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch((*ppAnims)->mChannels[a]->mNodeName,src,n)) + continue; + } + + PrefixString((*ppAnims)->mChannels[a]->mNodeName,(*cur).id,(*cur).idlen); + } + } + } + } + + // Now build the output graph + AttachToGraph ( master, nodes); + dest->mRootNode = master->mRootNode; + + // Check whether we succeeded at building the output graph + for (std::vector ::iterator it = nodes.begin(); + it != nodes.end(); ++it) + { + if (!(*it).resolved) { + if (flags & AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS) { + // search for this attachment point in all other imported scenes, too. + for ( unsigned int n = 0; n < src.size();++n ) { + if (n != (*it).src_idx) { + AttachToGraph(src[n].scene,nodes); + if ((*it).resolved) + break; + } + } + } + if (!(*it).resolved) { + ASSIMP_LOG_ERROR_F( "SceneCombiner: Failed to resolve attachment ", (*it).node->mName.data, + " ", (*it).attachToNode->mName.data ); + } + } + } + + // now delete all input scenes. Make sure duplicate scenes aren't + // deleted more than one time + for ( unsigned int n = 0; n < src.size();++n ) { + if (n != duplicates[n]) // duplicate scene? + continue; + + aiScene* deleteMe = src[n].scene; + + // We need to delete the arrays before the destructor is called - + // we are reusing the array members + delete[] deleteMe->mMeshes; deleteMe->mMeshes = NULL; + delete[] deleteMe->mCameras; deleteMe->mCameras = NULL; + delete[] deleteMe->mLights; deleteMe->mLights = NULL; + delete[] deleteMe->mMaterials; deleteMe->mMaterials = NULL; + delete[] deleteMe->mAnimations; deleteMe->mAnimations = NULL; + + deleteMe->mRootNode = NULL; + + // Now we can safely delete the scene + delete deleteMe; + } + + // Check flags + if (!dest->mNumMeshes || !dest->mNumMaterials) { + dest->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + } + + // We're finished +} + +// ------------------------------------------------------------------------------------------------ +// Build a list of unique bones +void SceneCombiner::BuildUniqueBoneList(std::list& asBones, + std::vector::const_iterator it, + std::vector::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::iterator it2 = asBones.begin(); + std::list::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::const_iterator it, + std::vector::const_iterator end) +{ + if ( nullptr == out || out->mNumBones == 0 ) { + return; + } + + // find we need to build an unique list of all bones. + // we work with hashes to make the comparisons MUCH faster, + // at least if we have many bones. + std::list asBones; + BuildUniqueBoneList( asBones, it, end ); + + // now create the output bones + out->mNumBones = 0; + out->mBones = new aiBone*[asBones.size()]; + + for (std::list::const_iterator boneIt = asBones.begin(),boneEnd = asBones.end(); boneIt != boneEnd; ++boneIt ) { + // Allocate a bone and setup it's name + aiBone* pc = out->mBones[out->mNumBones++] = new aiBone(); + pc->mName = aiString( *( boneIt->second )); + + std::vector< BoneSrcIndex >::const_iterator wend = boneIt->pSrcBones.end(); + + // Loop through all bones to be joined for this bone + for (std::vector< BoneSrcIndex >::const_iterator wmit = boneIt->pSrcBones.begin(); wmit != wend; ++wmit) { + pc->mNumWeights += (*wmit).first->mNumWeights; + + // NOTE: different offset matrices for bones with equal names + // are - at the moment - not handled correctly. + if (wmit != boneIt->pSrcBones.begin() && pc->mOffsetMatrix != wmit->first->mOffsetMatrix) { + ASSIMP_LOG_WARN("Bones with equal names but different offset matrices can't be joined at the moment"); + continue; + } + pc->mOffsetMatrix = wmit->first->mOffsetMatrix; + } + + // Allocate the vertex weight array + aiVertexWeight* avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights]; + + // And copy the final weights - adjust the vertex IDs by the + // face index offset of the corresponding mesh. + for (std::vector< BoneSrcIndex >::const_iterator wmit = (*boneIt).pSrcBones.begin(); wmit != (*boneIt).pSrcBones.end(); ++wmit) { + if (wmit == wend) { + break; + } + + aiBone* pip = (*wmit).first; + for (unsigned int mp = 0; mp < pip->mNumWeights;++mp,++avw) { + const aiVertexWeight& vfi = pip->mWeights[mp]; + avw->mWeight = vfi.mWeight; + avw->mVertexId = vfi.mVertexId + (*wmit).second; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Merge a list of meshes +void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/, + std::vector::const_iterator begin, + std::vector::const_iterator end) +{ + if ( nullptr == _out ) { + return; + } + + if (begin == end) { + *_out = NULL; // no meshes ... + return; + } + + // Allocate the output mesh + aiMesh* out = *_out = new aiMesh(); + out->mMaterialIndex = (*begin)->mMaterialIndex; + + std::string name; + // Find out how much output storage we'll need + for (std::vector::const_iterator it = begin; it != end; ++it) { + const char *meshName( (*it)->mName.C_Str() ); + name += std::string( meshName ); + if ( it != end - 1 ) { + name += "."; + } + out->mNumVertices += (*it)->mNumVertices; + out->mNumFaces += (*it)->mNumFaces; + out->mNumBones += (*it)->mNumBones; + + // combine primitive type flags + out->mPrimitiveTypes |= (*it)->mPrimitiveTypes; + } + out->mName.Set( name.c_str() ); + + if (out->mNumVertices) { + aiVector3D* pv2; + + // copy vertex positions + if ((**begin).HasPositions()) { + + pv2 = out->mVertices = new aiVector3D[out->mNumVertices]; + for (std::vector::const_iterator it = begin; it != end; ++it) { + if ((*it)->mVertices) { + ::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D)); + } + else ASSIMP_LOG_WARN("JoinMeshes: Positions expected but input mesh contains no positions"); + pv2 += (*it)->mNumVertices; + } + } + // copy normals + if ((**begin).HasNormals()) { + + pv2 = out->mNormals = new aiVector3D[out->mNumVertices]; + for (std::vector::const_iterator it = begin; it != end;++it) { + if ((*it)->mNormals) { + ::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D)); + } else { + ASSIMP_LOG_WARN( "JoinMeshes: Normals expected but input mesh contains no normals" ); + } + pv2 += (*it)->mNumVertices; + } + } + // copy tangents and bi-tangents + if ((**begin).HasTangentsAndBitangents()) { + + pv2 = out->mTangents = new aiVector3D[out->mNumVertices]; + aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices]; + + for (std::vector::const_iterator it = begin; it != end;++it) { + if ((*it)->mTangents) { + ::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices*sizeof(aiVector3D)); + ::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D)); + } else { + ASSIMP_LOG_WARN( "JoinMeshes: Tangents expected but input mesh contains no tangents" ); + } + pv2 += (*it)->mNumVertices; + pv2b += (*it)->mNumVertices; + } + } + // copy texture coordinates + unsigned int n = 0; + while ((**begin).HasTextureCoords(n)) { + out->mNumUVComponents[n] = (*begin)->mNumUVComponents[n]; + + pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices]; + for (std::vector::const_iterator it = begin; it != end;++it) { + if ((*it)->mTextureCoords[n]) { + ::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D)); + } else { + ASSIMP_LOG_WARN( "JoinMeshes: UVs expected but input mesh contains no UVs" ); + } + pv2 += (*it)->mNumVertices; + } + ++n; + } + // copy vertex colors + n = 0; + while ((**begin).HasVertexColors(n)) { + aiColor4D *pVec2 = out->mColors[n] = new aiColor4D[out->mNumVertices]; + for ( std::vector::const_iterator it = begin; it != end; ++it ) { + if ((*it)->mColors[n]) { + ::memcpy( pVec2, (*it)->mColors[ n ], (*it)->mNumVertices * sizeof( aiColor4D ) ) ; + } else { + ASSIMP_LOG_WARN( "JoinMeshes: VCs expected but input mesh contains no VCs" ); + } + pVec2 += (*it)->mNumVertices; + } + ++n; + } + } + + if (out->mNumFaces) // just for safety + { + // copy faces + out->mFaces = new aiFace[out->mNumFaces]; + aiFace* pf2 = out->mFaces; + + unsigned int ofs = 0; + for (std::vector::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::const_iterator it = begin; it != end;++it) + delete *it; +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::MergeMaterials(aiMaterial** dest, + std::vector::const_iterator begin, + std::vector::const_iterator end) +{ + if ( nullptr == dest ) { + return; + } + + if (begin == end) { + *dest = NULL; // no materials ... + return; + } + + // Allocate the output material + aiMaterial* out = *dest = new aiMaterial(); + + // Get the maximal number of properties + unsigned int size = 0; + for (std::vector::const_iterator it = begin; it != end; ++it) { + size += (*it)->mNumProperties; + } + + out->Clear(); + delete[] out->mProperties; + + out->mNumAllocated = size; + out->mNumProperties = 0; + out->mProperties = new aiMaterialProperty*[out->mNumAllocated]; + + for (std::vector::const_iterator it = begin; it != end; ++it) { + for(unsigned int i = 0; i < (*it)->mNumProperties; ++i) { + aiMaterialProperty* sprop = (*it)->mProperties[i]; + + // Test if we already have a matching property + const aiMaterialProperty* prop_exist; + if(aiGetMaterialProperty(out, sprop->mKey.C_Str(), sprop->mSemantic, sprop->mIndex, &prop_exist) != AI_SUCCESS) { + // If not, we add it to the new material + aiMaterialProperty* prop = out->mProperties[out->mNumProperties] = new aiMaterialProperty(); + + prop->mDataLength = sprop->mDataLength; + prop->mData = new char[prop->mDataLength]; + ::memcpy(prop->mData, sprop->mData, prop->mDataLength); + + prop->mIndex = sprop->mIndex; + prop->mSemantic = sprop->mSemantic; + prop->mKey = sprop->mKey; + prop->mType = sprop->mType; + + out->mNumProperties++; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +template +inline +void CopyPtrArray (Type**& dest, const Type* const * src, ai_uint num) { + if (!num) { + dest = NULL; + return; + } + dest = new Type*[num]; + for (ai_uint i = 0; i < num;++i) { + SceneCombiner::Copy(&dest[i],src[i]); + } +} + +// ------------------------------------------------------------------------------------------------ +template +inline +void GetArrayCopy(Type*& dest, ai_uint num ) { + if ( !dest ) { + return; + } + Type* old = dest; + + dest = new Type[num]; + ::memcpy(dest, old, sizeof(Type) * num); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::CopySceneFlat(aiScene** _dest,const aiScene* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + // reuse the old scene or allocate a new? + if (*_dest) { + (*_dest)->~aiScene(); + new (*_dest) aiScene(); + } else { + *_dest = new aiScene(); + } + + ::memcpy(*_dest,src,sizeof(aiScene)); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src,bool allocate) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + if (allocate) { + *_dest = new aiScene(); + } + aiScene* dest = *_dest; + ai_assert(nullptr != dest); + + // copy metadata + if ( nullptr != src->mMetaData ) { + dest->mMetaData = new aiMetadata( *src->mMetaData ); + } + + // copy animations + dest->mNumAnimations = src->mNumAnimations; + CopyPtrArray(dest->mAnimations,src->mAnimations, + dest->mNumAnimations); + + // copy textures + dest->mNumTextures = src->mNumTextures; + CopyPtrArray(dest->mTextures,src->mTextures, + dest->mNumTextures); + + // copy materials + dest->mNumMaterials = src->mNumMaterials; + CopyPtrArray(dest->mMaterials,src->mMaterials, + dest->mNumMaterials); + + // copy lights + dest->mNumLights = src->mNumLights; + CopyPtrArray(dest->mLights,src->mLights, + dest->mNumLights); + + // copy cameras + dest->mNumCameras = src->mNumCameras; + CopyPtrArray(dest->mCameras,src->mCameras, + dest->mNumCameras); + + // copy meshes + dest->mNumMeshes = src->mNumMeshes; + CopyPtrArray(dest->mMeshes,src->mMeshes, + dest->mNumMeshes); + + // now - copy the root node of the scene (deep copy, too) + Copy( &dest->mRootNode, src->mRootNode); + + // and keep the flags ... + dest->mFlags = src->mFlags; + + // source private data might be NULL if the scene is user-allocated (i.e. for use with the export API) + if (dest->mPrivate != NULL) { + ScenePriv(dest)->mPPStepsApplied = ScenePriv(src) ? ScenePriv(src)->mPPStepsApplied : 0; + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy( aiMesh** _dest, const aiMesh* src ) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiMesh* dest = *_dest = new aiMesh(); + + // get a flat copy + ::memcpy(dest,src,sizeof(aiMesh)); + + // and reallocate all arrays + GetArrayCopy( dest->mVertices, dest->mNumVertices ); + GetArrayCopy( dest->mNormals , dest->mNumVertices ); + GetArrayCopy( dest->mTangents, dest->mNumVertices ); + GetArrayCopy( dest->mBitangents, dest->mNumVertices ); + + unsigned int n = 0; + while (dest->HasTextureCoords(n)) + GetArrayCopy( dest->mTextureCoords[n++], dest->mNumVertices ); + + n = 0; + while (dest->HasVertexColors(n)) + GetArrayCopy( dest->mColors[n++], dest->mNumVertices ); + + // make a deep copy of all bones + CopyPtrArray(dest->mBones,dest->mBones,dest->mNumBones); + + // make a deep copy of all faces + GetArrayCopy(dest->mFaces,dest->mNumFaces); + for (unsigned int i = 0; i < dest->mNumFaces;++i) { + aiFace& f = dest->mFaces[i]; + GetArrayCopy(f.mIndices,f.mNumIndices); + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy (aiMaterial** _dest, const aiMaterial* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiMaterial* dest = (aiMaterial*) ( *_dest = new aiMaterial() ); + + dest->Clear(); + delete[] dest->mProperties; + + dest->mNumAllocated = src->mNumAllocated; + dest->mNumProperties = src->mNumProperties; + dest->mProperties = new aiMaterialProperty* [dest->mNumAllocated]; + + for (unsigned int i = 0; i < dest->mNumProperties;++i) + { + aiMaterialProperty* prop = dest->mProperties[i] = new aiMaterialProperty(); + aiMaterialProperty* sprop = src->mProperties[i]; + + prop->mDataLength = sprop->mDataLength; + prop->mData = new char[prop->mDataLength]; + ::memcpy(prop->mData,sprop->mData,prop->mDataLength); + + prop->mIndex = sprop->mIndex; + prop->mSemantic = sprop->mSemantic; + prop->mKey = sprop->mKey; + prop->mType = sprop->mType; + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiTexture** _dest, const aiTexture* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiTexture* dest = *_dest = new aiTexture(); + + // get a flat copy + ::memcpy(dest,src,sizeof(aiTexture)); + + // and reallocate all arrays. We must do it manually here + const char* old = (const char*)dest->pcData; + if (old) + { + unsigned int cpy; + if (!dest->mHeight)cpy = dest->mWidth; + else cpy = dest->mHeight * dest->mWidth * sizeof(aiTexel); + + if (!cpy) + { + dest->pcData = NULL; + return; + } + // the cast is legal, the aiTexel c'tor does nothing important + dest->pcData = (aiTexel*) new char[cpy]; + ::memcpy(dest->pcData, old, cpy); + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy( aiAnimation** _dest, const aiAnimation* src ) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiAnimation* dest = *_dest = new aiAnimation(); + + // get a flat copy + ::memcpy(dest,src,sizeof(aiAnimation)); + + // and reallocate all arrays + CopyPtrArray( dest->mChannels, src->mChannels, dest->mNumChannels ); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiNodeAnim** _dest, const aiNodeAnim* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiNodeAnim* dest = *_dest = new aiNodeAnim(); + + // get a flat copy + ::memcpy(dest,src,sizeof(aiNodeAnim)); + + // and reallocate all arrays + GetArrayCopy( dest->mPositionKeys, dest->mNumPositionKeys ); + GetArrayCopy( dest->mScalingKeys, dest->mNumScalingKeys ); + GetArrayCopy( dest->mRotationKeys, dest->mNumRotationKeys ); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy( aiCamera** _dest,const aiCamera* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiCamera* dest = *_dest = new aiCamera(); + + // get a flat copy, that's already OK + ::memcpy(dest,src,sizeof(aiCamera)); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiLight** _dest, const aiLight* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiLight* dest = *_dest = new aiLight(); + + // get a flat copy, that's already OK + ::memcpy(dest,src,sizeof(aiLight)); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiBone** _dest, const aiBone* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiBone* dest = *_dest = new aiBone(); + + // get a flat copy + ::memcpy(dest,src,sizeof(aiBone)); + + // and reallocate all arrays + GetArrayCopy( dest->mWeights, dest->mNumWeights ); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy (aiNode** _dest, const aiNode* src) +{ + ai_assert(NULL != _dest && NULL != src); + + aiNode* dest = *_dest = new aiNode(); + + // get a flat copy + ::memcpy(dest,src,sizeof(aiNode)); + + if (src->mMetaData) { + Copy(&dest->mMetaData, src->mMetaData); + } + + // and reallocate all arrays + GetArrayCopy( dest->mMeshes, dest->mNumMeshes ); + CopyPtrArray( dest->mChildren, src->mChildren,dest->mNumChildren); + + // need to set the mParent fields to the created aiNode. + for( unsigned int i = 0; i < dest->mNumChildren; i ++ ) { + dest->mChildren[i]->mParent = dest; + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiMetadata** _dest, const aiMetadata* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + if ( 0 == src->mNumProperties ) { + return; + } + + aiMetadata* dest = *_dest = aiMetadata::Alloc( src->mNumProperties ); + std::copy(src->mKeys, src->mKeys + src->mNumProperties, dest->mKeys); + + dest->mValues = new aiMetadataEntry[src->mNumProperties]; + for (unsigned int i = 0; i < src->mNumProperties; ++i) { + aiMetadataEntry& in = src->mValues[i]; + aiMetadataEntry& out = dest->mValues[i]; + out.mType = in.mType; + switch (dest->mValues[i].mType) { + case AI_BOOL: + out.mData = new bool(*static_cast(in.mData)); + break; + case AI_INT32: + out.mData = new int32_t(*static_cast(in.mData)); + break; + case AI_UINT64: + out.mData = new uint64_t(*static_cast(in.mData)); + break; + case AI_FLOAT: + out.mData = new float(*static_cast(in.mData)); + break; + case AI_DOUBLE: + out.mData = new double(*static_cast(in.mData)); + break; + case AI_AISTRING: + out.mData = new aiString(*static_cast(in.mData)); + break; + case AI_AIVECTOR3D: + out.mData = new aiVector3D(*static_cast(in.mData)); + break; + default: + ai_assert(false); + break; + } + } +} + +} // Namespace Assimp + diff --git a/thirdparty/assimp/code/Common/ScenePreprocessor.cpp b/thirdparty/assimp/code/Common/ScenePreprocessor.cpp new file mode 100644 index 0000000000..432a3d7666 --- /dev/null +++ b/thirdparty/assimp/code/Common/ScenePreprocessor.cpp @@ -0,0 +1,261 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#include "ScenePreprocessor.h" +#include +#include +#include + + +using namespace Assimp; + +// --------------------------------------------------------------------------------------------- +void ScenePreprocessor::ProcessScene () +{ + ai_assert(scene != NULL); + + // Process all meshes + for (unsigned int i = 0; i < scene->mNumMeshes;++i) + ProcessMesh(scene->mMeshes[i]); + + // - nothing to do for nodes for the moment + // - nothing to do for textures for the moment + // - nothing to do for lights for the moment + // - nothing to do for cameras for the moment + + // Process all animations + for (unsigned int i = 0; i < scene->mNumAnimations;++i) + ProcessAnimation(scene->mAnimations[i]); + + // Generate a default material if none was specified + if (!scene->mNumMaterials && scene->mNumMeshes) { + scene->mMaterials = new aiMaterial*[2]; + aiMaterial* helper; + + aiString name; + + scene->mMaterials[scene->mNumMaterials] = helper = new aiMaterial(); + aiColor3D clr(0.6f,0.6f,0.6f); + helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE); + + // setup the default name to make this material identifiable + name.Set(AI_DEFAULT_MATERIAL_NAME); + helper->AddProperty(&name,AI_MATKEY_NAME); + + ASSIMP_LOG_DEBUG("ScenePreprocessor: Adding default material \'" AI_DEFAULT_MATERIAL_NAME "\'"); + + for (unsigned int i = 0; i < scene->mNumMeshes;++i) { + scene->mMeshes[i]->mMaterialIndex = scene->mNumMaterials; + } + + scene->mNumMaterials++; + } +} + +// --------------------------------------------------------------------------------------------- +void ScenePreprocessor::ProcessMesh (aiMesh* mesh) +{ + // If aiMesh::mNumUVComponents is *not* set assign the default value of 2 + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (!mesh->mTextureCoords[i]) { + mesh->mNumUVComponents[i] = 0; + } else { + if (!mesh->mNumUVComponents[i]) + mesh->mNumUVComponents[i] = 2; + + aiVector3D* p = mesh->mTextureCoords[i], *end = p+mesh->mNumVertices; + + // Ensure unused components are zeroed. This will make 1D texture channels work + // as if they were 2D channels .. just in case an application doesn't handle + // this case + if (2 == mesh->mNumUVComponents[i]) { + for (; p != end; ++p) + p->z = 0.f; + } + else if (1 == mesh->mNumUVComponents[i]) { + for (; p != end; ++p) + p->z = p->y = 0.f; + } + else if (3 == mesh->mNumUVComponents[i]) { + // Really 3D coordinates? Check whether the third coordinate is != 0 for at least one element + for (; p != end; ++p) { + if (p->z != 0) + break; + } + if (p == end) { + ASSIMP_LOG_WARN("ScenePreprocessor: UVs are declared to be 3D but they're obviously not. Reverting to 2D."); + mesh->mNumUVComponents[i] = 2; + } + } + } + } + + // If the information which primitive types are there in the + // mesh is currently not available, compute it. + if (!mesh->mPrimitiveTypes) { + for (unsigned int a = 0; a < mesh->mNumFaces; ++a) { + aiFace& face = mesh->mFaces[a]; + switch (face.mNumIndices) + { + case 3u: + mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + + case 2u: + mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + + case 1u: + mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + + default: + mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + break; + } + } + } + + // If tangents and normals are given but no bitangents compute them + if (mesh->mTangents && mesh->mNormals && !mesh->mBitangents) { + + mesh->mBitangents = new aiVector3D[mesh->mNumVertices]; + for (unsigned int i = 0; i < mesh->mNumVertices;++i) { + mesh->mBitangents[i] = mesh->mNormals[i] ^ mesh->mTangents[i]; + } + } +} + +// --------------------------------------------------------------------------------------------- +void ScenePreprocessor::ProcessAnimation (aiAnimation* anim) +{ + double first = 10e10, last = -10e10; + for (unsigned int i = 0; i < anim->mNumChannels;++i) { + aiNodeAnim* channel = anim->mChannels[i]; + + /* If the exact duration of the animation is not given + * compute it now. + */ + if (anim->mDuration == -1.) { + + // Position keys + for (unsigned int j = 0; j < channel->mNumPositionKeys;++j) { + aiVectorKey& key = channel->mPositionKeys[j]; + first = std::min (first, key.mTime); + last = std::max (last, key.mTime); + } + + // Scaling keys + for (unsigned int j = 0; j < channel->mNumScalingKeys;++j ) { + aiVectorKey& key = channel->mScalingKeys[j]; + first = std::min (first, key.mTime); + last = std::max (last, key.mTime); + } + + // Rotation keys + for (unsigned int j = 0; j < channel->mNumRotationKeys;++j ) { + aiQuatKey& key = channel->mRotationKeys[ j ]; + first = std::min (first, key.mTime); + last = std::max (last, key.mTime); + } + } + + /* Check whether the animation channel has no rotation + * or position tracks. In this case we generate a dummy + * track from the information we have in the transformation + * matrix of the corresponding node. + */ + if (!channel->mNumRotationKeys || !channel->mNumPositionKeys || !channel->mNumScalingKeys) { + // Find the node that belongs to this animation + aiNode* node = scene->mRootNode->FindNode(channel->mNodeName); + if (node) // ValidateDS will complain later if 'node' is NULL + { + // Decompose the transformation matrix of the node + aiVector3D scaling, position; + aiQuaternion rotation; + + node->mTransformation.Decompose(scaling, rotation,position); + + // No rotation keys? Generate a dummy track + if (!channel->mNumRotationKeys) { + channel->mNumRotationKeys = 1; + channel->mRotationKeys = new aiQuatKey[1]; + aiQuatKey& q = channel->mRotationKeys[0]; + + q.mTime = 0.; + q.mValue = rotation; + + ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy rotation track has been generated"); + } + + // No scaling keys? Generate a dummy track + if (!channel->mNumScalingKeys) { + channel->mNumScalingKeys = 1; + channel->mScalingKeys = new aiVectorKey[1]; + aiVectorKey& q = channel->mScalingKeys[0]; + + q.mTime = 0.; + q.mValue = scaling; + + ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy scaling track has been generated"); + } + + // No position keys? Generate a dummy track + if (!channel->mNumPositionKeys) { + channel->mNumPositionKeys = 1; + channel->mPositionKeys = new aiVectorKey[1]; + aiVectorKey& q = channel->mPositionKeys[0]; + + q.mTime = 0.; + q.mValue = position; + + ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy position track has been generated"); + } + } + } + } + + if (anim->mDuration == -1.) { + ASSIMP_LOG_DEBUG("ScenePreprocessor: Setting animation duration"); + anim->mDuration = last - std::min( first, 0. ); + } +} diff --git a/thirdparty/assimp/code/Common/ScenePreprocessor.h b/thirdparty/assimp/code/Common/ScenePreprocessor.h new file mode 100644 index 0000000000..3f4c8d7c3f --- /dev/null +++ b/thirdparty/assimp/code/Common/ScenePreprocessor.h @@ -0,0 +1,125 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to search all meshes for + degenerated faces */ +#ifndef AI_SCENE_PREPROCESSOR_H_INC +#define AI_SCENE_PREPROCESSOR_H_INC + +#include +#include + +struct aiScene; +struct aiAnimation; +struct aiMesh; + +class ScenePreprocessorTest; +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** ScenePreprocessor: Preprocess a scene before any post-processing + * steps are executed. + * + * The step computes data that needn't necessarily be provided by the + * importer, such as aiMesh::mPrimitiveTypes. +*/ +// ---------------------------------------------------------------------------------- +class ASSIMP_API ScenePreprocessor +{ + // Make ourselves a friend of the corresponding test unit. + friend class ::ScenePreprocessorTest; +public: + + // ---------------------------------------------------------------- + /** Default c'tpr. Use SetScene() to assign a scene to the object. + */ + ScenePreprocessor() + : scene (NULL) + {} + + /** Constructs the object and assigns a specific scene to it + */ + ScenePreprocessor(aiScene* _scene) + : scene (_scene) + {} + + // ---------------------------------------------------------------- + /** Assign a (new) scene to the object. + * + * One 'SceneProcessor' can be used for multiple scenes. + * Call ProcessScene to have the scene preprocessed. + * @param sc Scene to be processed. + */ + void SetScene (aiScene* sc) { + scene = sc; + } + + // ---------------------------------------------------------------- + /** Preprocess the current scene + */ + void ProcessScene (); + +protected: + + // ---------------------------------------------------------------- + /** Preprocess an animation in the scene + * @param anim Anim to be preprocessed. + */ + void ProcessAnimation (aiAnimation* anim); + + + // ---------------------------------------------------------------- + /** Preprocess a mesh in the scene + * @param mesh Mesh to be preprocessed. + */ + void ProcessMesh (aiMesh* mesh); + +protected: + + //! Scene we're currently working on + aiScene* scene; +}; + + +} // ! end namespace Assimp + +#endif // include guard diff --git a/thirdparty/assimp/code/Common/ScenePrivate.h b/thirdparty/assimp/code/Common/ScenePrivate.h new file mode 100644 index 0000000000..f336aafc9a --- /dev/null +++ b/thirdparty/assimp/code/Common/ScenePrivate.h @@ -0,0 +1,105 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Stuff to deal with aiScene::mPrivate + */ +#pragma once +#ifndef AI_SCENEPRIVATE_H_INCLUDED +#define AI_SCENEPRIVATE_H_INCLUDED + +#include +#include + +namespace Assimp { + +// Forward declarations +class Importer; + +struct ScenePrivateData { + // The struct constructor. + ScenePrivateData() AI_NO_EXCEPT; + + // Importer that originally loaded the scene though the C-API + // If set, this object is owned by this private data instance. + Assimp::Importer* mOrigImporter; + + // List of post-processing steps already applied to the scene. + unsigned int mPPStepsApplied; + + // true if the scene is a copy made with aiCopyScene() + // or the corresponding C++ API. This means that user code + // may have made modifications to it, so mPPStepsApplied + // and mOrigImporter are no longer safe to rely on and only + // serve informative purposes. + bool mIsCopy; +}; + +inline +ScenePrivateData::ScenePrivateData() AI_NO_EXCEPT +: mOrigImporter( nullptr ) +, mPPStepsApplied( 0 ) +, mIsCopy( false ) { + // empty +} + +// Access private data stored in the scene +inline +ScenePrivateData* ScenePriv(aiScene* in) { + ai_assert( nullptr != in ); + if ( nullptr == in ) { + return nullptr; + } + return static_cast(in->mPrivate); +} + +inline +const ScenePrivateData* ScenePriv(const aiScene* in) { + ai_assert( nullptr != in ); + if ( nullptr == in ) { + return nullptr; + } + return static_cast(in->mPrivate); +} + +} // Namespace Assimp + +#endif // AI_SCENEPRIVATE_H_INCLUDED diff --git a/thirdparty/assimp/code/Common/SkeletonMeshBuilder.cpp b/thirdparty/assimp/code/Common/SkeletonMeshBuilder.cpp new file mode 100644 index 0000000000..06cfe034e9 --- /dev/null +++ b/thirdparty/assimp/code/Common/SkeletonMeshBuilder.cpp @@ -0,0 +1,270 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file SkeletonMeshBuilder.cpp + * @brief Implementation of a little class to construct a dummy mesh for a skeleton + */ + +#include +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// The constructor processes the given scene and adds a mesh there. +SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root, bool bKnobsOnly) +{ + // nothing to do if there's mesh data already present at the scene + if( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL) + return; + + if (!root) + root = pScene->mRootNode; + + mKnobsOnly = bKnobsOnly; + + // build some faces around each node + CreateGeometry( root ); + + // create a mesh to hold all the generated faces + pScene->mNumMeshes = 1; + pScene->mMeshes = new aiMesh*[1]; + pScene->mMeshes[0] = CreateMesh(); + // and install it at the root node + root->mNumMeshes = 1; + root->mMeshes = new unsigned int[1]; + root->mMeshes[0] = 0; + + // create a dummy material for the mesh + if(pScene->mNumMaterials==0){ + pScene->mNumMaterials = 1; + pScene->mMaterials = new aiMaterial*[1]; + pScene->mMaterials[0] = CreateMaterial(); + } +} + +// ------------------------------------------------------------------------------------------------ +// Recursively builds a simple mesh representation for the given node +void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode) +{ + // add a joint entry for the node. + const unsigned int vertexStartIndex = static_cast(mVertices.size()); + + // now build the geometry. + if( pNode->mNumChildren > 0 && !mKnobsOnly) + { + // If the node has children, we build little pointers to each of them + for( unsigned int a = 0; a < pNode->mNumChildren; a++) + { + // find a suitable coordinate system + const aiMatrix4x4& childTransform = pNode->mChildren[a]->mTransformation; + aiVector3D childpos( childTransform.a4, childTransform.b4, childTransform.c4); + ai_real distanceToChild = childpos.Length(); + if( distanceToChild < 0.0001) + continue; + aiVector3D up = aiVector3D( childpos).Normalize(); + + aiVector3D orth( 1.0, 0.0, 0.0); + if( std::fabs( orth * up) > 0.99) + orth.Set( 0.0, 1.0, 0.0); + + aiVector3D front = (up ^ orth).Normalize(); + aiVector3D side = (front ^ up).Normalize(); + + unsigned int localVertexStart = static_cast(mVertices.size()); + mVertices.push_back( -front * distanceToChild * (ai_real)0.1); + mVertices.push_back( childpos); + mVertices.push_back( -side * distanceToChild * (ai_real)0.1); + mVertices.push_back( -side * distanceToChild * (ai_real)0.1); + mVertices.push_back( childpos); + mVertices.push_back( front * distanceToChild * (ai_real)0.1); + mVertices.push_back( front * distanceToChild * (ai_real)0.1); + mVertices.push_back( childpos); + mVertices.push_back( side * distanceToChild * (ai_real)0.1); + mVertices.push_back( side * distanceToChild * (ai_real)0.1); + mVertices.push_back( childpos); + mVertices.push_back( -front * distanceToChild * (ai_real)0.1); + + mFaces.push_back( Face( localVertexStart + 0, localVertexStart + 1, localVertexStart + 2)); + mFaces.push_back( Face( localVertexStart + 3, localVertexStart + 4, localVertexStart + 5)); + mFaces.push_back( Face( localVertexStart + 6, localVertexStart + 7, localVertexStart + 8)); + mFaces.push_back( Face( localVertexStart + 9, localVertexStart + 10, localVertexStart + 11)); + } + } + else + { + // if the node has no children, it's an end node. Put a little knob there instead + aiVector3D ownpos( pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4); + ai_real sizeEstimate = ownpos.Length() * ai_real( 0.18 ); + + mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); + mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); + mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); + mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); + + mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); + mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); + mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); + mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); + mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); + + mFaces.push_back( Face( vertexStartIndex + 0, vertexStartIndex + 1, vertexStartIndex + 2)); + mFaces.push_back( Face( vertexStartIndex + 3, vertexStartIndex + 4, vertexStartIndex + 5)); + mFaces.push_back( Face( vertexStartIndex + 6, vertexStartIndex + 7, vertexStartIndex + 8)); + mFaces.push_back( Face( vertexStartIndex + 9, vertexStartIndex + 10, vertexStartIndex + 11)); + mFaces.push_back( Face( vertexStartIndex + 12, vertexStartIndex + 13, vertexStartIndex + 14)); + mFaces.push_back( Face( vertexStartIndex + 15, vertexStartIndex + 16, vertexStartIndex + 17)); + mFaces.push_back( Face( vertexStartIndex + 18, vertexStartIndex + 19, vertexStartIndex + 20)); + mFaces.push_back( Face( vertexStartIndex + 21, vertexStartIndex + 22, vertexStartIndex + 23)); + } + + unsigned int numVertices = static_cast(mVertices.size() - vertexStartIndex); + if( numVertices > 0) + { + // create a bone affecting all the newly created vertices + aiBone* bone = new aiBone; + mBones.push_back( bone); + bone->mName = pNode->mName; + + // calculate the bone offset matrix by concatenating the inverse transformations of all parents + bone->mOffsetMatrix = aiMatrix4x4( pNode->mTransformation).Inverse(); + for( aiNode* parent = pNode->mParent; parent != NULL; parent = parent->mParent) + bone->mOffsetMatrix = aiMatrix4x4( parent->mTransformation).Inverse() * bone->mOffsetMatrix; + + // add all the vertices to the bone's influences + bone->mNumWeights = numVertices; + bone->mWeights = new aiVertexWeight[numVertices]; + for( unsigned int a = 0; a < numVertices; a++) + bone->mWeights[a] = aiVertexWeight( vertexStartIndex + a, 1.0); + + // HACK: (thom) transform all vertices to the bone's local space. Should be done before adding + // them to the array, but I'm tired now and I'm annoyed. + aiMatrix4x4 boneToMeshTransform = aiMatrix4x4( bone->mOffsetMatrix).Inverse(); + for( unsigned int a = vertexStartIndex; a < mVertices.size(); a++) + mVertices[a] = boneToMeshTransform * mVertices[a]; + } + + // and finally recurse into the children list + for( unsigned int a = 0; a < pNode->mNumChildren; a++) + CreateGeometry( pNode->mChildren[a]); +} + +// ------------------------------------------------------------------------------------------------ +// Creates the mesh from the internally accumulated stuff and returns it. +aiMesh* SkeletonMeshBuilder::CreateMesh() +{ + aiMesh* mesh = new aiMesh(); + + // add points + mesh->mNumVertices = static_cast(mVertices.size()); + mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices); + + mesh->mNormals = new aiVector3D[mesh->mNumVertices]; + + // add faces + mesh->mNumFaces = static_cast(mFaces.size()); + mesh->mFaces = new aiFace[mesh->mNumFaces]; + for( unsigned int a = 0; a < mesh->mNumFaces; a++) + { + const Face& inface = mFaces[a]; + aiFace& outface = mesh->mFaces[a]; + outface.mNumIndices = 3; + outface.mIndices = new unsigned int[3]; + outface.mIndices[0] = inface.mIndices[0]; + outface.mIndices[1] = inface.mIndices[1]; + outface.mIndices[2] = inface.mIndices[2]; + + // Compute per-face normals ... we don't want the bones to be smoothed ... they're built to visualize + // the skeleton, so it's good if there's a visual difference to the rest of the geometry + aiVector3D nor = ((mVertices[inface.mIndices[2]] - mVertices[inface.mIndices[0]]) ^ + (mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]])); + + if (nor.Length() < 1e-5) /* ensure that FindInvalidData won't remove us ...*/ + nor = aiVector3D(1.0,0.0,0.0); + + for (unsigned int n = 0; n < 3; ++n) + mesh->mNormals[inface.mIndices[n]] = nor; + } + + // add the bones + mesh->mNumBones = static_cast(mBones.size()); + mesh->mBones = new aiBone*[mesh->mNumBones]; + std::copy( mBones.begin(), mBones.end(), mesh->mBones); + + // default + mesh->mMaterialIndex = 0; + + return mesh; +} + +// ------------------------------------------------------------------------------------------------ +// Creates a dummy material and returns it. +aiMaterial* SkeletonMeshBuilder::CreateMaterial() +{ + aiMaterial* matHelper = new aiMaterial; + + // Name + aiString matName( std::string( "SkeletonMaterial")); + matHelper->AddProperty( &matName, AI_MATKEY_NAME); + + // Prevent backface culling + const int no_cull = 1; + matHelper->AddProperty(&no_cull,1,AI_MATKEY_TWOSIDED); + + return matHelper; +} diff --git a/thirdparty/assimp/code/Common/SpatialSort.cpp b/thirdparty/assimp/code/Common/SpatialSort.cpp new file mode 100644 index 0000000000..a4f3a4e4b8 --- /dev/null +++ b/thirdparty/assimp/code/Common/SpatialSort.cpp @@ -0,0 +1,342 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the helper class to quickly find vertices close to a given position */ + +#include +#include + +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 (pPositions); + const aiVector3D* vec = reinterpret_cast (tempPointer + a * pElementOffset); + + // store position by index and distance + ai_real distance = *vec * mPlaneNormal; + mPositions.push_back( Entry( static_cast(a+initial), *vec, distance)); + } + + if (pFinalize) { + // now sort the array ascending by distance. + Finalize(); + } +} + +// ------------------------------------------------------------------------------------------------ +// Returns an iterator for all positions close to the given position. +void SpatialSort::FindPositions( const aiVector3D& pPosition, + ai_real pRadius, std::vector& poResults) const +{ + const ai_real dist = pPosition * mPlaneNormal; + const ai_real minDist = dist - pRadius, maxDist = dist + pRadius; + + // clear the array + poResults.clear(); + + // quick check for positions outside the range + if( mPositions.size() == 0) + return; + if( maxDist < mPositions.front().mDistance) + return; + if( minDist > mPositions.back().mDistance) + return; + + // do a binary search for the minimal distance to start the iteration there + unsigned int index = (unsigned int)mPositions.size() / 2; + unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; + while( binaryStepSize > 1) + { + if( mPositions[index].mDistance < minDist) + index += binaryStepSize; + else + index -= binaryStepSize; + + binaryStepSize /= 2; + } + + // depending on the direction of the last step we need to single step a bit back or forth + // to find the actual beginning element of the range + while( index > 0 && mPositions[index].mDistance > minDist) + index--; + while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist) + index++; + + // Mow start iterating from there until the first position lays outside of the distance range. + // Add all positions inside the distance range within the given radius to the result aray + std::vector::const_iterator it = mPositions.begin() + index; + const ai_real pSquared = pRadius*pRadius; + while( it->mDistance < maxDist) + { + if( (it->mPosition - pPosition).SquareLength() < pSquared) + poResults.push_back( it->mIndex); + ++it; + if( it == mPositions.end()) + break; + } + + // that's it +} + +namespace { + + // Binary, signed-integer representation of a single-precision floating-point value. + // IEEE 754 says: "If two floating-point numbers in the same format are ordered then they are + // ordered the same way when their bits are reinterpreted as sign-magnitude integers." + // This allows us to convert all floating-point numbers to signed integers of arbitrary size + // and then use them to work with ULPs (Units in the Last Place, for high-precision + // computations) or to compare them (integer comparisons are faster than floating-point + // comparisons on many platforms). + typedef ai_int BinFloat; + + // -------------------------------------------------------------------------------------------- + // Converts the bit pattern of a floating-point number to its signed integer representation. + BinFloat ToBinary( const ai_real & pValue) { + + // If this assertion fails, signed int is not big enough to store a float on your platform. + // Please correct the declaration of BinFloat a few lines above - but do it in a portable, + // #ifdef'd manner! + static_assert( sizeof(BinFloat) >= sizeof(ai_real), "sizeof(BinFloat) >= sizeof(ai_real)"); + + #if defined( _MSC_VER) + // If this assertion fails, Visual C++ has finally moved to ILP64. This means that this + // code has just become legacy code! Find out the current value of _MSC_VER and modify + // the #if above so it evaluates false on the current and all upcoming VC versions (or + // on the current platform, if LP64 or LLP64 are still used on other platforms). + static_assert( sizeof(BinFloat) == sizeof(ai_real), "sizeof(BinFloat) == sizeof(ai_real)"); + + // This works best on Visual C++, but other compilers have their problems with it. + const BinFloat binValue = reinterpret_cast(pValue); + #else + // On many compilers, reinterpreting a float address as an integer causes aliasing + // problems. This is an ugly but more or less safe way of doing it. + union { + ai_real asFloat; + BinFloat asBin; + } conversion; + conversion.asBin = 0; // zero empty space in case sizeof(BinFloat) > sizeof(float) + conversion.asFloat = pValue; + const BinFloat binValue = conversion.asBin; + #endif + + // floating-point numbers are of sign-magnitude format, so find out what signed number + // representation we must convert negative values to. + // See http://en.wikipedia.org/wiki/Signed_number_representations. + + // Two's complement? + if( (-42 == (~42 + 1)) && (binValue & 0x80000000)) + return BinFloat(1 << (CHAR_BIT * sizeof(BinFloat) - 1)) - binValue; + // One's complement? + else if ( (-42 == ~42) && (binValue & 0x80000000)) + return BinFloat(-0) - binValue; + // Sign-magnitude? + else if( (-42 == (42 | (-0))) && (binValue & 0x80000000)) // -0 = 1000... binary + return binValue; + else + return binValue; + } + +} // namespace + +// ------------------------------------------------------------------------------------------------ +// Fills an array with indices of all positions identical to the given position. In opposite to +// FindPositions(), not an epsilon is used but a (very low) tolerance of four floating-point units. +void SpatialSort::FindIdenticalPositions( const aiVector3D& pPosition, + std::vector& poResults) const +{ + // Epsilons have a huge disadvantage: they are of constant precision, while floating-point + // values are of log2 precision. If you apply e=0.01 to 100, the epsilon is rather small, but + // if you apply it to 0.001, it is enormous. + + // The best way to overcome this is the unit in the last place (ULP). A precision of 2 ULPs + // tells us that a float does not differ more than 2 bits from the "real" value. ULPs are of + // logarithmic precision - around 1, they are 1*(2^24) and around 10000, they are 0.00125. + + // For standard C math, we can assume a precision of 0.5 ULPs according to IEEE 754. The + // incoming vertex positions might have already been transformed, probably using rather + // inaccurate SSE instructions, so we assume a tolerance of 4 ULPs to safely identify + // identical vertex positions. + static const int toleranceInULPs = 4; + // An interesting point is that the inaccuracy grows linear with the number of operations: + // multiplying to numbers, each inaccurate to four ULPs, results in an inaccuracy of four ULPs + // plus 0.5 ULPs for the multiplication. + // To compute the distance to the plane, a dot product is needed - that is a multiplication and + // an addition on each number. + static const int distanceToleranceInULPs = toleranceInULPs + 1; + // The squared distance between two 3D vectors is computed the same way, but with an additional + // subtraction. + static const int distance3DToleranceInULPs = distanceToleranceInULPs + 1; + + // Convert the plane distance to its signed integer representation so the ULPs tolerance can be + // applied. For some reason, VC won't optimize two calls of the bit pattern conversion. + const BinFloat minDistBinary = ToBinary( pPosition * mPlaneNormal) - distanceToleranceInULPs; + const BinFloat maxDistBinary = minDistBinary + 2 * distanceToleranceInULPs; + + // clear the array in this strange fashion because a simple clear() would also deallocate + // the array which we want to avoid + poResults.resize( 0 ); + + // do a binary search for the minimal distance to start the iteration there + unsigned int index = (unsigned int)mPositions.size() / 2; + unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; + while( binaryStepSize > 1) + { + // Ugly, but conditional jumps are faster with integers than with floats + if( minDistBinary > ToBinary(mPositions[index].mDistance)) + index += binaryStepSize; + else + index -= binaryStepSize; + + binaryStepSize /= 2; + } + + // depending on the direction of the last step we need to single step a bit back or forth + // to find the actual beginning element of the range + while( index > 0 && minDistBinary < ToBinary(mPositions[index].mDistance) ) + index--; + while( index < (mPositions.size() - 1) && minDistBinary > ToBinary(mPositions[index].mDistance)) + index++; + + // Now start iterating from there until the first position lays outside of the distance range. + // Add all positions inside the distance range within the tolerance to the result array + std::vector::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& fill, ai_real pRadius) const +{ + fill.resize(mPositions.size(),UINT_MAX); + ai_real dist, maxDist; + + unsigned int t=0; + const ai_real pSquared = pRadius*pRadius; + for (size_t i = 0; i < mPositions.size();) { + dist = mPositions[i].mPosition * mPlaneNormal; + maxDist = dist + pRadius; + + fill[mPositions[i].mIndex] = t; + const aiVector3D& oldpos = mPositions[i].mPosition; + for (++i; i < fill.size() && mPositions[i].mDistance < maxDist + && (mPositions[i].mPosition - oldpos).SquareLength() < pSquared; ++i) + { + fill[mPositions[i].mIndex] = t; + } + ++t; + } + +#ifdef ASSIMP_BUILD_DEBUG + + // debug invariant: mPositions[i].mIndex values must range from 0 to mPositions.size()-1 + for (size_t i = 0; i < fill.size(); ++i) { + ai_assert(fill[i] +#include + +#include +#include + +using namespace Assimp; +using namespace Assimp::Formatter; + +// ------------------------------------------------------------------------------------------------ +// Constructor +SplitByBoneCountProcess::SplitByBoneCountProcess() +{ + // set default, might be overridden by importer config + mMaxBoneCount = AI_SBBC_DEFAULT_MAX_BONES; +} + +// ------------------------------------------------------------------------------------------------ +// Destructor +SplitByBoneCountProcess::~SplitByBoneCountProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag. +bool SplitByBoneCountProcess::IsActive( unsigned int pFlags) const +{ + return !!(pFlags & aiProcess_SplitByBoneCount); +} + +// ------------------------------------------------------------------------------------------------ +// Updates internal properties +void SplitByBoneCountProcess::SetupProperties(const Importer* pImp) +{ + mMaxBoneCount = pImp->GetPropertyInteger(AI_CONFIG_PP_SBBC_MAX_BONES,AI_SBBC_DEFAULT_MAX_BONES); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SplitByBoneCountProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("SplitByBoneCountProcess begin"); + + // early out + bool isNecessary = false; + for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) + if( pScene->mMeshes[a]->mNumBones > mMaxBoneCount ) + isNecessary = true; + + if( !isNecessary ) + { + ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess early-out: no meshes with more than " << mMaxBoneCount << " bones." ); + return; + } + + // we need to do something. Let's go. + mSubMeshIndices.clear(); + mSubMeshIndices.resize( pScene->mNumMeshes); + + // build a new array of meshes for the scene + std::vector meshes; + + for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) + { + aiMesh* srcMesh = pScene->mMeshes[a]; + + std::vector newMeshes; + SplitMesh( pScene->mMeshes[a], newMeshes); + + // mesh was split + if( !newMeshes.empty() ) + { + // store new meshes and indices of the new meshes + for( unsigned int b = 0; b < newMeshes.size(); ++b) + { + mSubMeshIndices[a].push_back( static_cast(meshes.size())); + meshes.push_back( newMeshes[b]); + } + + // and destroy the source mesh. It should be completely contained inside the new submeshes + delete srcMesh; + } + else + { + // Mesh is kept unchanged - store it's new place in the mesh array + mSubMeshIndices[a].push_back( static_cast(meshes.size())); + meshes.push_back( srcMesh); + } + } + + // rebuild the scene's mesh array + pScene->mNumMeshes = static_cast(meshes.size()); + delete [] pScene->mMeshes; + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + std::copy( meshes.begin(), meshes.end(), pScene->mMeshes); + + // recurse through all nodes and translate the node's mesh indices to fit the new mesh array + UpdateNode( pScene->mRootNode); + + ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess end: split " << mSubMeshIndices.size() << " meshes into " << meshes.size() << " submeshes." ); +} + +// ------------------------------------------------------------------------------------------------ +// Splits the given mesh by bone count. +void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector& poNewMeshes) const +{ + // skip if not necessary + if( pMesh->mNumBones <= mMaxBoneCount ) + return; + + // necessary optimisation: build a list of all affecting bones for each vertex + // TODO: (thom) maybe add a custom allocator here to avoid allocating tens of thousands of small arrays + typedef std::pair BoneWeight; + std::vector< std::vector > vertexBones( pMesh->mNumVertices); + for( unsigned int a = 0; a < pMesh->mNumBones; ++a) + { + const aiBone* bone = pMesh->mBones[a]; + for( unsigned int b = 0; b < bone->mNumWeights; ++b) + vertexBones[ bone->mWeights[b].mVertexId ].push_back( BoneWeight( a, bone->mWeights[b].mWeight)); + } + + unsigned int numFacesHandled = 0; + std::vector isFaceHandled( pMesh->mNumFaces, false); + while( numFacesHandled < pMesh->mNumFaces ) + { + // which bones are used in the current submesh + unsigned int numBones = 0; + std::vector isBoneUsed( pMesh->mNumBones, false); + // indices of the faces which are going to go into this submesh + std::vector subMeshFaces; + subMeshFaces.reserve( pMesh->mNumFaces); + // accumulated vertex count of all the faces in this submesh + unsigned int numSubMeshVertices = 0; + // a small local array of new bones for the current face. State of all used bones for that face + // can only be updated AFTER the face is completely analysed. Thanks to imre for the fix. + std::vector newBonesAtCurrentFace; + + // add faces to the new submesh as long as all bones affecting the faces' vertices fit in the limit + for( unsigned int a = 0; a < pMesh->mNumFaces; ++a) + { + // skip if the face is already stored in a submesh + if( isFaceHandled[a] ) + continue; + + const aiFace& face = pMesh->mFaces[a]; + // check every vertex if its bones would still fit into the current submesh + for( unsigned int b = 0; b < face.mNumIndices; ++b ) + { + const std::vector& vb = vertexBones[face.mIndices[b]]; + for( unsigned int c = 0; c < vb.size(); ++c) + { + unsigned int boneIndex = vb[c].first; + // if the bone is already used in this submesh, it's ok + if( isBoneUsed[boneIndex] ) + continue; + + // if it's not used, yet, we would need to add it. Store its bone index + if( std::find( newBonesAtCurrentFace.begin(), newBonesAtCurrentFace.end(), boneIndex) == newBonesAtCurrentFace.end() ) + newBonesAtCurrentFace.push_back( boneIndex); + } + } + + // leave out the face if the new bones required for this face don't fit the bone count limit anymore + if( numBones + newBonesAtCurrentFace.size() > mMaxBoneCount ) + continue; + + // mark all new bones as necessary + while( !newBonesAtCurrentFace.empty() ) + { + unsigned int newIndex = newBonesAtCurrentFace.back(); + newBonesAtCurrentFace.pop_back(); // this also avoids the deallocation which comes with a clear() + if( isBoneUsed[newIndex] ) + continue; + + isBoneUsed[newIndex] = true; + numBones++; + } + + // store the face index and the vertex count + subMeshFaces.push_back( a); + numSubMeshVertices += face.mNumIndices; + + // remember that this face is handled + isFaceHandled[a] = true; + numFacesHandled++; + } + + // create a new mesh to hold this subset of the source mesh + aiMesh* newMesh = new aiMesh; + if( pMesh->mName.length > 0 ) + newMesh->mName.Set( format() << pMesh->mName.data << "_sub" << poNewMeshes.size()); + newMesh->mMaterialIndex = pMesh->mMaterialIndex; + newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes; + poNewMeshes.push_back( newMesh); + + // create all the arrays for this mesh if the old mesh contained them + newMesh->mNumVertices = numSubMeshVertices; + newMesh->mNumFaces = static_cast(subMeshFaces.size()); + newMesh->mVertices = new aiVector3D[newMesh->mNumVertices]; + if( pMesh->HasNormals() ) + newMesh->mNormals = new aiVector3D[newMesh->mNumVertices]; + if( pMesh->HasTangentsAndBitangents() ) + { + newMesh->mTangents = new aiVector3D[newMesh->mNumVertices]; + newMesh->mBitangents = new aiVector3D[newMesh->mNumVertices]; + } + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) + { + if( pMesh->HasTextureCoords( a) ) + newMesh->mTextureCoords[a] = new aiVector3D[newMesh->mNumVertices]; + newMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a]; + } + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a ) + { + if( pMesh->HasVertexColors( a) ) + newMesh->mColors[a] = new aiColor4D[newMesh->mNumVertices]; + } + + // and copy over the data, generating faces with linear indices along the way + newMesh->mFaces = new aiFace[subMeshFaces.size()]; + unsigned int nvi = 0; // next vertex index + std::vector previousVertexIndices( numSubMeshVertices, std::numeric_limits::max()); // per new vertex: its index in the source mesh + for( unsigned int a = 0; a < subMeshFaces.size(); ++a ) + { + const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]]; + aiFace& dstFace = newMesh->mFaces[a]; + dstFace.mNumIndices = srcFace.mNumIndices; + dstFace.mIndices = new unsigned int[dstFace.mNumIndices]; + + // accumulate linearly all the vertices of the source face + for( unsigned int b = 0; b < dstFace.mNumIndices; ++b ) + { + unsigned int srcIndex = srcFace.mIndices[b]; + dstFace.mIndices[b] = nvi; + previousVertexIndices[nvi] = srcIndex; + + newMesh->mVertices[nvi] = pMesh->mVertices[srcIndex]; + if( pMesh->HasNormals() ) + newMesh->mNormals[nvi] = pMesh->mNormals[srcIndex]; + if( pMesh->HasTangentsAndBitangents() ) + { + newMesh->mTangents[nvi] = pMesh->mTangents[srcIndex]; + newMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex]; + } + for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c ) + { + if( pMesh->HasTextureCoords( c) ) + newMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex]; + } + for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c ) + { + if( pMesh->HasVertexColors( c) ) + newMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex]; + } + + nvi++; + } + } + + ai_assert( nvi == numSubMeshVertices ); + + // Create the bones for the new submesh: first create the bone array + newMesh->mNumBones = 0; + newMesh->mBones = new aiBone*[numBones]; + + std::vector mappedBoneIndex( pMesh->mNumBones, std::numeric_limits::max()); + for( unsigned int a = 0; a < pMesh->mNumBones; ++a ) + { + if( !isBoneUsed[a] ) + continue; + + // create the new bone + const aiBone* srcBone = pMesh->mBones[a]; + aiBone* dstBone = new aiBone; + mappedBoneIndex[a] = newMesh->mNumBones; + newMesh->mBones[newMesh->mNumBones++] = dstBone; + dstBone->mName = srcBone->mName; + dstBone->mOffsetMatrix = srcBone->mOffsetMatrix; + dstBone->mNumWeights = 0; + } + + ai_assert( newMesh->mNumBones == numBones ); + + // iterate over all new vertices and count which bones affected its old vertex in the source mesh + for( unsigned int a = 0; a < numSubMeshVertices; ++a ) + { + unsigned int oldIndex = previousVertexIndices[a]; + const std::vector& bonesOnThisVertex = vertexBones[oldIndex]; + + for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b ) + { + unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ]; + if( newBoneIndex != std::numeric_limits::max() ) + newMesh->mBones[newBoneIndex]->mNumWeights++; + } + } + + // allocate all bone weight arrays accordingly + for( unsigned int a = 0; a < newMesh->mNumBones; ++a ) + { + aiBone* bone = newMesh->mBones[a]; + ai_assert( bone->mNumWeights > 0 ); + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; + bone->mNumWeights = 0; // for counting up in the next step + } + + // now copy all the bone vertex weights for all the vertices which made it into the new submesh + for( unsigned int a = 0; a < numSubMeshVertices; ++a) + { + // find the source vertex for it in the source mesh + unsigned int previousIndex = previousVertexIndices[a]; + // these bones were affecting it + const std::vector& bonesOnThisVertex = vertexBones[previousIndex]; + // all of the bones affecting it should be present in the new submesh, or else + // the face it comprises shouldn't be present + for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b) + { + unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ]; + ai_assert( newBoneIndex != std::numeric_limits::max() ); + aiVertexWeight* dstWeight = newMesh->mBones[newBoneIndex]->mWeights + newMesh->mBones[newBoneIndex]->mNumWeights; + newMesh->mBones[newBoneIndex]->mNumWeights++; + + dstWeight->mVertexId = a; + dstWeight->mWeight = bonesOnThisVertex[b].second; + } + } + + // I have the strange feeling that this will break apart at some point in time... + } +} + +// ------------------------------------------------------------------------------------------------ +// Recursively updates the node's mesh list to account for the changed mesh list +void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const +{ + // rebuild the node's mesh index list + if( pNode->mNumMeshes > 0 ) + { + std::vector newMeshList; + for( unsigned int a = 0; a < pNode->mNumMeshes; ++a) + { + unsigned int srcIndex = pNode->mMeshes[a]; + const std::vector& replaceMeshes = mSubMeshIndices[srcIndex]; + newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end()); + } + + delete [] pNode->mMeshes; + pNode->mNumMeshes = static_cast(newMeshList.size()); + pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; + std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes); + } + + // do that also recursively for all children + for( unsigned int a = 0; a < pNode->mNumChildren; ++a ) + { + UpdateNode( pNode->mChildren[a]); + } +} diff --git a/thirdparty/assimp/code/Common/SplitByBoneCountProcess.h b/thirdparty/assimp/code/Common/SplitByBoneCountProcess.h new file mode 100644 index 0000000000..6c904a9df4 --- /dev/null +++ b/thirdparty/assimp/code/Common/SplitByBoneCountProcess.h @@ -0,0 +1,111 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/// @file SplitByBoneCountProcess.h +/// Defines a post processing step to split meshes with many bones into submeshes +#ifndef AI_SPLITBYBONECOUNTPROCESS_H_INC +#define AI_SPLITBYBONECOUNTPROCESS_H_INC + +#include +#include "BaseProcess.h" + +#include +#include + +namespace Assimp +{ + + +/** Postprocessing filter to split meshes with many bones into submeshes + * so that each submesh has a certain max bone count. + * + * Applied BEFORE the JoinVertices-Step occurs. + * Returns NON-UNIQUE vertices, splits by bone count. +*/ +class SplitByBoneCountProcess : public BaseProcess +{ +public: + + SplitByBoneCountProcess(); + ~SplitByBoneCountProcess(); + +public: + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. A + * bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + bool IsActive( unsigned int pFlags) const; + + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + virtual void SetupProperties(const Importer* pImp); + +protected: + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + /// Splits the given mesh by bone count. + /// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split. + /// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary. + void SplitMesh( const aiMesh* pMesh, std::vector& poNewMeshes) const; + + /// Recursively updates the node's mesh list to account for the changed mesh list + void UpdateNode( aiNode* pNode) const; + +public: + /// Max bone count. Splitting occurs if a mesh has more than that number of bones. + size_t mMaxBoneCount; + + /// Per mesh index: Array of indices of the new submeshes. + std::vector< std::vector > mSubMeshIndices; +}; + +} // end of namespace Assimp + +#endif // !!AI_SPLITBYBONECOUNTPROCESS_H_INC diff --git a/thirdparty/assimp/code/Common/StandardShapes.cpp b/thirdparty/assimp/code/Common/StandardShapes.cpp new file mode 100644 index 0000000000..2e5100130f --- /dev/null +++ b/thirdparty/assimp/code/Common/StandardShapes.cpp @@ -0,0 +1,507 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file StandardShapes.cpp + * @brief Implementation of the StandardShapes class + * + * The primitive geometry data comes from + * http://geometrictools.com/Documentation/PlatonicSolids.pdf. + */ + +#include +#include +#include +#include +#include + +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& positions) +{ + // assume this to be constant - (fixme: must be 1.0? I think so) + const ai_real fl1 = positions[0].Length(); + + unsigned int origSize = (unsigned int)positions.size(); + for (unsigned int i = 0 ; i < origSize ; i+=3) + { + aiVector3D& tv0 = positions[i]; + aiVector3D& tv1 = positions[i+1]; + aiVector3D& tv2 = positions[i+2]; + + aiVector3D a = tv0, b = tv1, c = tv2; + aiVector3D v1 = aiVector3D(a.x+b.x, a.y+b.y, a.z+b.z).Normalize()*fl1; + aiVector3D v2 = aiVector3D(a.x+c.x, a.y+c.y, a.z+c.z).Normalize()*fl1; + aiVector3D v3 = aiVector3D(b.x+c.x, b.y+c.y, b.z+c.z).Normalize()*fl1; + + tv0 = v1; tv1 = v3; tv2 = v2; // overwrite the original + ADD_TRIANGLE(v1, v2, a); + ADD_TRIANGLE(v2, v3, c); + ADD_TRIANGLE(v3, v1, b); + } +} + +// ------------------------------------------------------------------------------------------------ +// Construct a mesh from given vertex positions +aiMesh* StandardShapes::MakeMesh(const std::vector& positions, + unsigned int numIndices) +{ + if (positions.empty() || !numIndices) return NULL; + + // Determine which kinds of primitives the mesh consists of + aiMesh* out = new aiMesh(); + switch (numIndices) { + case 1: + out->mPrimitiveTypes = aiPrimitiveType_POINT; + break; + case 2: + out->mPrimitiveTypes = aiPrimitiveType_LINE; + break; + case 3: + out->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + break; + default: + out->mPrimitiveTypes = aiPrimitiveType_POLYGON; + break; + }; + + out->mNumFaces = (unsigned int)positions.size() / numIndices; + out->mFaces = new aiFace[out->mNumFaces]; + for (unsigned int i = 0, a = 0; i < out->mNumFaces;++i) { + aiFace& f = out->mFaces[i]; + f.mNumIndices = numIndices; + f.mIndices = new unsigned int[numIndices]; + for (unsigned int j = 0; i < numIndices; ++i, ++a) { + f.mIndices[j] = a; + } + } + out->mNumVertices = (unsigned int)positions.size(); + out->mVertices = new aiVector3D[out->mNumVertices]; + ::memcpy(out->mVertices,&positions[0],out->mNumVertices*sizeof(aiVector3D)); + + return out; +} + +// ------------------------------------------------------------------------------------------------ +// Construct a mesh with a specific shape (callback) +aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)( + std::vector&)) +{ + std::vector 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&, bool)) +{ + std::vector 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&)) +{ + std::vector temp; + (*GenerateFunc)(num,temp); + return MakeMesh(temp,3); +} + +// ------------------------------------------------------------------------------------------------ +// Build an incosahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeIcosahedron(std::vector& positions) +{ + positions.reserve(positions.size()+60); + + const ai_real t = ( ai_real( 1.0 )+ ai_real( 2.236067977 ) ) / ai_real( 2.0 ); + const ai_real s = std::sqrt(ai_real(1.0) + t*t); + + const aiVector3D v0 = aiVector3D(t,1.0, 0.0)/s; + const aiVector3D v1 = aiVector3D(-t,1.0, 0.0)/s; + const aiVector3D v2 = aiVector3D(t,-1.0, 0.0)/s; + const aiVector3D v3 = aiVector3D(-t,-1.0, 0.0)/s; + const aiVector3D v4 = aiVector3D(1.0, 0.0, t)/s; + const aiVector3D v5 = aiVector3D(1.0, 0.0,-t)/s; + const aiVector3D v6 = aiVector3D(-1.0, 0.0,t)/s; + const aiVector3D v7 = aiVector3D(-1.0, 0.0,-t)/s; + const aiVector3D v8 = aiVector3D(0.0, t, 1.0)/s; + const aiVector3D v9 = aiVector3D(0.0,-t, 1.0)/s; + const aiVector3D v10 = aiVector3D(0.0, t,-1.0)/s; + const aiVector3D v11 = aiVector3D(0.0,-t,-1.0)/s; + + ADD_TRIANGLE(v0,v8,v4); + ADD_TRIANGLE(v0,v5,v10); + ADD_TRIANGLE(v2,v4,v9); + ADD_TRIANGLE(v2,v11,v5); + + ADD_TRIANGLE(v1,v6,v8); + ADD_TRIANGLE(v1,v10,v7); + ADD_TRIANGLE(v3,v9,v6); + ADD_TRIANGLE(v3,v7,v11); + + ADD_TRIANGLE(v0,v10,v8); + ADD_TRIANGLE(v1,v8,v10); + ADD_TRIANGLE(v2,v9,v11); + ADD_TRIANGLE(v3,v11,v9); + + ADD_TRIANGLE(v4,v2,v0); + ADD_TRIANGLE(v5,v0,v2); + ADD_TRIANGLE(v6,v1,v3); + ADD_TRIANGLE(v7,v3,v1); + + ADD_TRIANGLE(v8,v6,v4); + ADD_TRIANGLE(v9,v4,v6); + ADD_TRIANGLE(v10,v5,v7); + ADD_TRIANGLE(v11,v7,v5); + return 3; +} + +// ------------------------------------------------------------------------------------------------ +// Build a dodecahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeDodecahedron(std::vector& positions, + bool polygons /*= false*/) +{ + positions.reserve(positions.size()+108); + + const ai_real a = ai_real( 1.0 ) / ai_real(1.7320508); + const ai_real b = std::sqrt(( ai_real( 3.0 )- ai_real( 2.23606797))/ ai_real( 6.0) ); + const ai_real c = std::sqrt(( ai_real( 3.0 )+ ai_real( 2.23606797f))/ ai_real( 6.0) ); + + const aiVector3D v0 = aiVector3D(a,a,a); + const aiVector3D v1 = aiVector3D(a,a,-a); + const aiVector3D v2 = aiVector3D(a,-a,a); + const aiVector3D v3 = aiVector3D(a,-a,-a); + const aiVector3D v4 = aiVector3D(-a,a,a); + const aiVector3D v5 = aiVector3D(-a,a,-a); + const aiVector3D v6 = aiVector3D(-a,-a,a); + const aiVector3D v7 = aiVector3D(-a,-a,-a); + const aiVector3D v8 = aiVector3D(b,c,0.0); + const aiVector3D v9 = aiVector3D(-b,c,0.0); + const aiVector3D v10 = aiVector3D(b,-c,0.0); + const aiVector3D v11 = aiVector3D(-b,-c,0.0); + const aiVector3D v12 = aiVector3D(c, 0.0, b); + const aiVector3D v13 = aiVector3D(c, 0.0, -b); + const aiVector3D v14 = aiVector3D(-c, 0.0, b); + const aiVector3D v15 = aiVector3D(-c, 0.0, -b); + const aiVector3D v16 = aiVector3D(0.0, b, c); + const aiVector3D v17 = aiVector3D(0.0, -b, c); + const aiVector3D v18 = aiVector3D(0.0, b, -c); + const aiVector3D v19 = aiVector3D(0.0, -b, -c); + + ADD_PENTAGON(v0, v8, v9, v4, v16); + ADD_PENTAGON(v0, v12, v13, v1, v8); + ADD_PENTAGON(v0, v16, v17, v2, v12); + ADD_PENTAGON(v8, v1, v18, v5, v9); + ADD_PENTAGON(v12, v2, v10, v3, v13); + ADD_PENTAGON(v16, v4, v14, v6, v17); + ADD_PENTAGON(v9, v5, v15, v14, v4); + + ADD_PENTAGON(v6, v11, v10, v2, v17); + ADD_PENTAGON(v3, v19, v18, v1, v13); + ADD_PENTAGON(v7, v15, v5, v18, v19); + ADD_PENTAGON(v7, v11, v6, v14, v15); + ADD_PENTAGON(v7, v19, v3, v10, v11); + return (polygons ? 5 : 3); +} + +// ------------------------------------------------------------------------------------------------ +// Build an octahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeOctahedron(std::vector& positions) +{ + positions.reserve(positions.size()+24); + + const aiVector3D v0 = aiVector3D(1.0, 0.0, 0.0) ; + const aiVector3D v1 = aiVector3D(-1.0, 0.0, 0.0); + const aiVector3D v2 = aiVector3D(0.0, 1.0, 0.0); + const aiVector3D v3 = aiVector3D(0.0, -1.0, 0.0); + const aiVector3D v4 = aiVector3D(0.0, 0.0, 1.0); + const aiVector3D v5 = aiVector3D(0.0, 0.0, -1.0); + + ADD_TRIANGLE(v4,v0,v2); + ADD_TRIANGLE(v4,v2,v1); + ADD_TRIANGLE(v4,v1,v3); + ADD_TRIANGLE(v4,v3,v0); + + ADD_TRIANGLE(v5,v2,v0); + ADD_TRIANGLE(v5,v1,v2); + ADD_TRIANGLE(v5,v3,v1); + ADD_TRIANGLE(v5,v0,v3); + return 3; +} + +// ------------------------------------------------------------------------------------------------ +// Build a tetrahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeTetrahedron(std::vector& positions) +{ + positions.reserve(positions.size()+9); + + const ai_real invThree = ai_real( 1.0 ) / ai_real( 3.0 ); + const ai_real a = ai_real( 1.41421 ) * invThree; + const ai_real b = ai_real( 2.4494 ) * invThree; + + const aiVector3D v0 = aiVector3D(0.0,0.0,1.0); + const aiVector3D v1 = aiVector3D(2*a,0,-invThree ); + const aiVector3D v2 = aiVector3D(-a,b,-invThree ); + const aiVector3D v3 = aiVector3D(-a,-b,-invThree ); + + ADD_TRIANGLE(v0,v1,v2); + ADD_TRIANGLE(v0,v2,v3); + ADD_TRIANGLE(v0,v3,v1); + ADD_TRIANGLE(v1,v3,v2); + return 3; +} + +// ------------------------------------------------------------------------------------------------ +// Build a hexahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeHexahedron(std::vector& positions, + bool polygons /*= false*/) +{ + positions.reserve(positions.size()+36); + const ai_real length = ai_real(1.0)/ai_real(1.73205080); + + const aiVector3D v0 = aiVector3D(-1.0,-1.0,-1.0)*length; + const aiVector3D v1 = aiVector3D(1.0,-1.0,-1.0)*length; + const aiVector3D v2 = aiVector3D(1.0,1.0,-1.0)*length; + const aiVector3D v3 = aiVector3D(-1.0,1.0,-1.0)*length; + const aiVector3D v4 = aiVector3D(-1.0,-1.0,1.0)*length; + const aiVector3D v5 = aiVector3D(1.0,-1.0,1.0)*length; + const aiVector3D v6 = aiVector3D(1.0,1.0,1.0)*length; + const aiVector3D v7 = aiVector3D(-1.0,1.0,1.0)*length; + + ADD_QUAD(v0,v3,v2,v1); + ADD_QUAD(v0,v1,v5,v4); + ADD_QUAD(v0,v4,v7,v3); + ADD_QUAD(v6,v5,v1,v2); + ADD_QUAD(v6,v2,v3,v7); + ADD_QUAD(v6,v7,v4,v5); + return (polygons ? 4 : 3); +} + +// Cleanup ... +#undef ADD_TRIANGLE +#undef ADD_QUAD +#undef ADD_PENTAGON + +// ------------------------------------------------------------------------------------------------ +// Create a subdivision sphere +void StandardShapes::MakeSphere(unsigned int tess, + std::vector& positions) +{ + // Reserve enough storage. Every subdivision + // splits each triangle in 4, the icosahedron consists of 60 verts + positions.reserve(positions.size()+60 * integer_pow(4, tess)); + + // Construct an icosahedron to start with + MakeIcosahedron(positions); + + // ... and subdivide it until the requested output + // tessellation is reached + for (unsigned int i = 0; i& positions,bool bOpen /*= false */) +{ + // Sorry, a cone with less than 3 segments makes ABSOLUTELY NO SENSE + if (tess < 3 || !height) + return; + + size_t old = positions.size(); + + // No negative radii + radius1 = std::fabs(radius1); + radius2 = std::fabs(radius2); + + ai_real halfHeight = height / ai_real(2.0); + + // radius1 is always the smaller one + if (radius2 > radius1) + { + std::swap(radius2,radius1); + halfHeight = -halfHeight; + } + else old = SIZE_MAX; + + // Use a large epsilon to check whether the cone is pointy + if (radius1 < (radius2-radius1)*10e-3)radius1 = 0.0; + + // We will need 3*2 verts per segment + 3*2 verts per segment + // if the cone is closed + const unsigned int mem = tess*6 + (!bOpen ? tess*3 * (radius1 ? 2 : 1) : 0); + positions.reserve(positions.size () + mem); + + // Now construct all segments + const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess; + const ai_real angle_max = (ai_real)AI_MATH_TWO_PI; + + ai_real s = 1.0; // std::cos(angle == 0); + ai_real t = 0.0; // std::sin(angle == 0); + + for (ai_real angle = 0.0; angle < angle_max; ) + { + const aiVector3D v1 = aiVector3D (s * radius1, -halfHeight, t * radius1 ); + const aiVector3D v2 = aiVector3D (s * radius2, halfHeight, t * radius2 ); + + const ai_real next = angle + angle_delta; + ai_real s2 = std::cos(next); + ai_real t2 = std::sin(next); + + const aiVector3D v3 = aiVector3D (s2 * radius2, halfHeight, t2 * radius2 ); + const aiVector3D v4 = aiVector3D (s2 * radius1, -halfHeight, t2 * radius1 ); + + positions.push_back(v1); + positions.push_back(v2); + positions.push_back(v3); + positions.push_back(v4); + positions.push_back(v1); + positions.push_back(v3); + + if (!bOpen) + { + // generate the end 'cap' + positions.push_back(aiVector3D(s * radius2, halfHeight, t * radius2 )); + positions.push_back(aiVector3D(s2 * radius2, halfHeight, t2 * radius2 )); + positions.push_back(aiVector3D(0.0, halfHeight, 0.0)); + + + if (radius1) + { + // generate the other end 'cap' + positions.push_back(aiVector3D(s * radius1, -halfHeight, t * radius1 )); + positions.push_back(aiVector3D(s2 * radius1, -halfHeight, t2 * radius1 )); + positions.push_back(aiVector3D(0.0, -halfHeight, 0.0)); + + } + } + s = s2; + t = t2; + angle = next; + } + + // Need to flip face order? + if ( SIZE_MAX != old ) { + for (size_t p = old; p < positions.size();p += 3) { + std::swap(positions[p],positions[p+1]); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Build a circle +void StandardShapes::MakeCircle(ai_real radius, unsigned int tess, + std::vector& positions) +{ + // Sorry, a circle with less than 3 segments makes ABSOLUTELY NO SENSE + if (tess < 3 || !radius) + return; + + radius = std::fabs(radius); + + // We will need 3 vertices per segment + positions.reserve(positions.size()+tess*3); + + const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess; + const ai_real angle_max = (ai_real)AI_MATH_TWO_PI; + + ai_real s = 1.0; // std::cos(angle == 0); + ai_real t = 0.0; // std::sin(angle == 0); + + for (ai_real angle = 0.0; angle < angle_max; ) + { + positions.push_back(aiVector3D(s * radius,0.0,t * radius)); + angle += angle_delta; + s = std::cos(angle); + t = std::sin(angle); + positions.push_back(aiVector3D(s * radius,0.0,t * radius)); + + positions.push_back(aiVector3D(0.0,0.0,0.0)); + } +} + +} // ! Assimp diff --git a/thirdparty/assimp/code/Common/StdOStreamLogStream.h b/thirdparty/assimp/code/Common/StdOStreamLogStream.h new file mode 100644 index 0000000000..893e261a2b --- /dev/null +++ b/thirdparty/assimp/code/Common/StdOStreamLogStream.h @@ -0,0 +1,101 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file StdOStreamLogStream.h +* @brief Implementation of StdOStreamLogStream +*/ + +#ifndef AI_STROSTREAMLOGSTREAM_H_INC +#define AI_STROSTREAMLOGSTREAM_H_INC + +#include +#include + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** @class StdOStreamLogStream + * @brief Logs into a std::ostream + */ +class StdOStreamLogStream : public LogStream { +public: + /** @brief Construction from an existing std::ostream + * @param _ostream Output stream to be used + */ + explicit StdOStreamLogStream(std::ostream& _ostream); + + /** @brief Destructor */ + ~StdOStreamLogStream(); + + /** @brief Writer */ + void write(const char* message); + +private: + std::ostream& mOstream; +}; + +// --------------------------------------------------------------------------- +// Default constructor +inline StdOStreamLogStream::StdOStreamLogStream(std::ostream& _ostream) +: mOstream (_ostream){ + // empty +} + +// --------------------------------------------------------------------------- +// Default constructor +inline StdOStreamLogStream::~StdOStreamLogStream() { + // empty +} + +// --------------------------------------------------------------------------- +// Write method +inline void StdOStreamLogStream::write(const char* message) { + mOstream << message; + mOstream.flush(); +} + +// --------------------------------------------------------------------------- + +} // Namespace Assimp + +#endif // guard diff --git a/thirdparty/assimp/code/Common/Subdivision.cpp b/thirdparty/assimp/code/Common/Subdivision.cpp new file mode 100644 index 0000000000..60c54939f5 --- /dev/null +++ b/thirdparty/assimp/code/Common/Subdivision.cpp @@ -0,0 +1,589 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(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 +#include +#include +#include +#include + +#include "PostProcessing/ProcessHelper.h" + +#include + +using namespace Assimp; +void mydummy() {} + +// ------------------------------------------------------------------------------------------------ +/** Subdivider stub class to implement the Catmull-Clarke subdivision algorithm. The + * implementation is basing on recursive refinement. Directly evaluating the result is also + * possible and much quicker, but it depends on lengthy matrix lookup tables. */ +// ------------------------------------------------------------------------------------------------ +class CatmullClarkSubdivider : public Subdivider { +public: + void Subdivide (aiMesh* mesh, aiMesh*& out, unsigned int num, bool discard_input); + void Subdivide (aiMesh** smesh, size_t nmesh, + aiMesh** out, unsigned int num, bool discard_input); + + // --------------------------------------------------------------------------- + /** Intermediate description of an edge between two corners of a polygon*/ + // --------------------------------------------------------------------------- + struct Edge + { + Edge() + : ref(0) + {} + Vertex edge_point, midpoint; + unsigned int ref; + }; + + typedef std::vector UIntVector; + typedef std::map 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 id0out+nmesh); + if (!num) { + // No subdivision at all. Need to copy all the meshes .. argh. + if (discard_input) { + for (size_t s = 0; s < nmesh; ++s) { + out[s] = smesh[s]; + smesh[s] = NULL; + } + } + else { + for (size_t s = 0; s < nmesh; ++s) { + SceneCombiner::Copy(out+s,smesh[s]); + } + } + return; + } + + std::vector inmeshes; + std::vector outmeshes; + std::vector maptbl; + + inmeshes.reserve(nmesh); + outmeshes.reserve(nmesh); + maptbl.reserve(nmesh); + + // Remove pure line and point meshes from the working set to reduce the + // number of edge cases the subdivider is forced to deal with. Line and + // point meshes are simply passed through. + for (size_t s = 0; s < nmesh; ++s) { + aiMesh* i = smesh[s]; + // FIX - mPrimitiveTypes might not yet be initialized + if (i->mPrimitiveTypes && (i->mPrimitiveTypes & (aiPrimitiveType_LINE|aiPrimitiveType_POINT))==i->mPrimitiveTypes) { + ASSIMP_LOG_DEBUG("Catmull-Clark Subdivider: Skipping pure line/point mesh"); + + if (discard_input) { + out[s] = i; + smesh[s] = NULL; + } + else { + SceneCombiner::Copy(out+s,i); + } + continue; + } + + outmeshes.push_back(NULL);inmeshes.push_back(i); + maptbl.push_back(static_cast(s)); + } + + // Do the actual subdivision on the preallocated storage. InternSubdivide + // *always* assumes that enough storage is available, it does not bother + // checking any ranges. + ai_assert(inmeshes.size()==outmeshes.size()&&inmeshes.size()==maptbl.size()); + if (inmeshes.empty()) { + ASSIMP_LOG_WARN("Catmull-Clark Subdivider: Pure point/line scene, I can't do anything"); + return; + } + InternSubdivide(&inmeshes.front(),inmeshes.size(),&outmeshes.front(),num); + for (unsigned int i = 0; i < maptbl.size(); ++i) { + ai_assert(nullptr != outmeshes[i]); + out[maptbl[i]] = outmeshes[i]; + } + + if (discard_input) { + for (size_t s = 0; s < nmesh; ++s) { + delete smesh[s]; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Note - this is an implementation of the standard (recursive) Cm-Cl algorithm without further +// optimizations (except we're using some nice LUTs). A description of the algorithm can be found +// here: http://en.wikipedia.org/wiki/Catmull-Clark_subdivision_surface +// +// The code is mostly O(n), however parts are O(nlogn) which is therefore the algorithm's +// expected total runtime complexity. The implementation is able to work in-place on the same +// mesh arrays. Calling #InternSubdivide() directly is not encouraged. The code can operate +// in-place unless 'smesh' and 'out' are equal (no strange overlaps or reorderings). +// Previous data is replaced/deleted then. +// ------------------------------------------------------------------------------------------------ +void CatmullClarkSubdivider::InternSubdivide ( + const aiMesh* const * smesh, + size_t nmesh, + aiMesh** out, + unsigned int num + ) +{ + ai_assert(NULL != smesh && NULL != out); + INIT_EDGE_HASH_TEMPORARIES(); + + // no subdivision requested or end of recursive refinement + if (!num) { + return; + } + + UIntVector maptbl; + SpatialSort spatial; + + // --------------------------------------------------------------------- + // 0. Offset table to index all meshes continuously, generate a spatially + // sorted representation of all vertices in all meshes. + // --------------------------------------------------------------------- + typedef std::pair IntPair; + std::vector 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 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(face.mNumIndices); + nfacesout += face.mNumIndices; + } + } + + { + // we want edges to go away before the recursive calls so begin a new scope + EdgeMap edges; + + // --------------------------------------------------------------------- + // 2. Set each edge point to be the average of all neighbouring + // face points and original points. Every edge exists twice + // if there is a neighboring face. + // --------------------------------------------------------------------- + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh* mesh = smesh[t]; + + for (unsigned int i = 0; i < mesh->mNumFaces;++i) { + const aiFace& face = mesh->mFaces[i]; + + for (unsigned int p =0; p< face.mNumIndices; ++p) { + const unsigned int id[] = { + face.mIndices[p], + face.mIndices[p==face.mNumIndices-1?0:p+1] + }; + const unsigned int mp[] = { + maptbl[FLATTEN_VERTEX_IDX(t,id[0])], + maptbl[FLATTEN_VERTEX_IDX(t,id[1])] + }; + + Edge& e = edges[MAKE_EDGE_HASH(mp[0],mp[1])]; + e.ref++; + if (e.ref<=2) { + if (e.ref==1) { // original points (end points) - add only once + e.edge_point = e.midpoint = Vertex(mesh,id[0])+Vertex(mesh,id[1]); + e.midpoint *= 0.5f; + } + e.edge_point += centroids[FLATTEN_FACE_IDX(t,i)]; + } + } + } + } + + // --------------------------------------------------------------------- + // 3. Normalize edge points + // --------------------------------------------------------------------- + {unsigned int bad_cnt = 0; + for (EdgeMap::iterator it = edges.begin(); it != edges.end(); ++it) { + if ((*it).second.ref < 2) { + ai_assert((*it).second.ref); + ++bad_cnt; + } + (*it).second.edge_point *= 1.f/((*it).second.ref+2.f); + } + + if (bad_cnt) { + // Report the number of bad edges. bad edges are referenced by less than two + // faces in the mesh. They occur at outer model boundaries in non-closed + // shapes. + ASSIMP_LOG_DEBUG_F("Catmull-Clark Subdivider: got ", bad_cnt, " bad edges touching only one face (totally ", + static_cast(edges.size()), " edges). "); + }} + + // --------------------------------------------------------------------- + // 4. Compute a vertex-face adjacency table. We can't reuse the code + // from VertexTriangleAdjacency because we need the table for multiple + // meshes and out vertex indices need to be mapped to distinct values + // first. + // --------------------------------------------------------------------- + UIntVector faceadjac(nfacesout), cntadjfac(maptbl.size(),0), ofsadjvec(maptbl.size()+1,0); { + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh* const minp = smesh[t]; + for (unsigned int i = 0; i < minp->mNumFaces; ++i) { + + const aiFace& f = minp->mFaces[i]; + for (unsigned int n = 0; n < f.mNumIndices; ++n) { + ++cntadjfac[maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]]; + } + } + } + unsigned int cur = 0; + for (size_t i = 0; i < cntadjfac.size(); ++i) { + ofsadjvec[i+1] = cur; + cur += cntadjfac[i]; + } + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh* const minp = smesh[t]; + for (unsigned int i = 0; i < minp->mNumFaces; ++i) { + + const aiFace& f = minp->mFaces[i]; + for (unsigned int n = 0; n < f.mNumIndices; ++n) { + faceadjac[ofsadjvec[1+maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]]++] = FLATTEN_FACE_IDX(t,i); + } + } + } + + // check the other way round for consistency +#ifdef ASSIMP_BUILD_DEBUG + + for (size_t t = 0; t < ofsadjvec.size()-1; ++t) { + for (unsigned int m = 0; m < cntadjfac[t]; ++m) { + const unsigned int fidx = faceadjac[ofsadjvec[t]+m]; + ai_assert(fidx < totfaces); + for (size_t n = 1; n < nmesh; ++n) { + + if (moffsets[n].first > fidx) { + const aiMesh* msh = smesh[--n]; + const aiFace& f = msh->mFaces[fidx-moffsets[n].first]; + + bool haveit = false; + for (unsigned int i = 0; i < f.mNumIndices; ++i) { + if (maptbl[FLATTEN_VERTEX_IDX(n,f.mIndices[i])]==(unsigned int)t) { + haveit = true; + break; + } + } + ai_assert(haveit); + if (!haveit) { + ASSIMP_LOG_DEBUG("Catmull-Clark Subdivider: Index not used"); + } + break; + } + } + } + } + +#endif + } + +#define GET_ADJACENT_FACES_AND_CNT(vidx,fstartout,numout) \ + fstartout = &faceadjac[ofsadjvec[vidx]], numout = cntadjfac[vidx] + + typedef std::pair TouchedOVertex; + std::vector new_points(num_unique,TouchedOVertex(false,Vertex())); + // --------------------------------------------------------------------- + // 5. Spawn a quad from each face point to the corresponding edge points + // the original points being the fourth quad points. + // --------------------------------------------------------------------- + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh* const minp = smesh[t]; + aiMesh* const mout = out[t] = new aiMesh(); + + for (unsigned int a = 0; a < minp->mNumFaces; ++a) { + mout->mNumFaces += minp->mFaces[a].mNumIndices; + } + + // We need random access to the old face buffer, so reuse is not possible. + mout->mFaces = new aiFace[mout->mNumFaces]; + + mout->mNumVertices = mout->mNumFaces*4; + mout->mVertices = new aiVector3D[mout->mNumVertices]; + + // quads only, keep material index + mout->mPrimitiveTypes = aiPrimitiveType_POLYGON; + mout->mMaterialIndex = minp->mMaterialIndex; + + if (minp->HasNormals()) { + mout->mNormals = new aiVector3D[mout->mNumVertices]; + } + + if (minp->HasTangentsAndBitangents()) { + mout->mTangents = new aiVector3D[mout->mNumVertices]; + mout->mBitangents = new aiVector3D[mout->mNumVertices]; + } + + for(unsigned int i = 0; minp->HasTextureCoords(i); ++i) { + mout->mTextureCoords[i] = new aiVector3D[mout->mNumVertices]; + mout->mNumUVComponents[i] = minp->mNumUVComponents[i]; + } + + for(unsigned int i = 0; minp->HasVertexColors(i); ++i) { + mout->mColors[i] = new aiColor4D[mout->mNumVertices]; + } + + mout->mNumVertices = mout->mNumFaces<<2u; + for (unsigned int i = 0, v = 0, n = 0; i < minp->mNumFaces;++i) { + + const aiFace& face = minp->mFaces[i]; + for (unsigned int a = 0; a < face.mNumIndices;++a) { + + // Get a clean new face. + aiFace& faceOut = mout->mFaces[n++]; + faceOut.mIndices = new unsigned int [faceOut.mNumIndices = 4]; + + // Spawn a new quadrilateral (ccw winding) for this original point between: + // a) face centroid + centroids[FLATTEN_FACE_IDX(t,i)].SortBack(mout,faceOut.mIndices[0]=v++); + + // b) adjacent edge on the left, seen from the centroid + const Edge& e0 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])], + maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a==face.mNumIndices-1?0:a+1]) + ])]; // fixme: replace with mod face.mNumIndices? + + // c) adjacent edge on the right, seen from the centroid + const Edge& e1 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])], + maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[!a?face.mNumIndices-1:a-1]) + ])]; // fixme: replace with mod face.mNumIndices? + + e0.edge_point.SortBack(mout,faceOut.mIndices[3]=v++); + e1.edge_point.SortBack(mout,faceOut.mIndices[1]=v++); + + // d= original point P with distinct index i + // F := 0 + // R := 0 + // n := 0 + // for each face f containing i + // F := F+ centroid of f + // R := R+ midpoint of edge of f from i to i+1 + // n := n+1 + // + // (F+2R+(n-3)P)/n + const unsigned int org = maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])]; + TouchedOVertex& ov = new_points[org]; + + if (!ov.first) { + ov.first = true; + + const unsigned int* adj; unsigned int cnt; + GET_ADJACENT_FACES_AND_CNT(org,adj,cnt); + + if (cnt < 3) { + ov.second = Vertex(minp,face.mIndices[a]); + } + else { + + Vertex F,R; + for (unsigned int o = 0; o < cnt; ++o) { + ai_assert(adj[o] < totfaces); + F += centroids[adj[o]]; + + // adj[0] is a global face index - search the face in the mesh list + const aiMesh* mp = NULL; + size_t nidx; + + if (adj[o] < moffsets[0].first) { + mp = smesh[nidx=0]; + } + else { + for (nidx = 1; nidx<= nmesh; ++nidx) { + if (nidx == nmesh ||moffsets[nidx].first > adj[o]) { + mp = smesh[--nidx]; + break; + } + } + } + + ai_assert(adj[o]-moffsets[nidx].first < mp->mNumFaces); + const aiFace& f = mp->mFaces[adj[o]-moffsets[nidx].first]; + bool haveit = false; + + // find our original point in the face + for (unsigned int m = 0; m < f.mNumIndices; ++m) { + if (maptbl[FLATTEN_VERTEX_IDX(nidx,f.mIndices[m])] == org) { + + // add *both* edges. this way, we can be sure that we add + // *all* adjacent edges to R. In a closed shape, every + // edge is added twice - so we simply leave out the + // factor 2.f in the amove formula and get the right + // result. + + const Edge& c0 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX( + nidx,f.mIndices[!m?f.mNumIndices-1:m-1])])]; + // fixme: replace with mod face.mNumIndices? + + const Edge& c1 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX( + nidx,f.mIndices[m==f.mNumIndices-1?0:m+1])])]; + // fixme: replace with mod face.mNumIndices? + R += c0.midpoint+c1.midpoint; + + haveit = true; + break; + } + } + + // this invariant *must* hold if the vertex-to-face adjacency table is valid + ai_assert(haveit); + if ( !haveit ) { + ASSIMP_LOG_WARN( "OBJ: no name for material library specified." ); + } + } + + const float div = static_cast(cnt), divsq = 1.f/(div*div); + ov.second = Vertex(minp,face.mIndices[a])*((div-3.f) / div) + R*divsq + F*divsq; + } + } + ov.second.SortBack(mout,faceOut.mIndices[2]=v++); + } + } + } + } // end of scope for edges, freeing its memory + + // --------------------------------------------------------------------- + // 7. Apply the next subdivision step. + // --------------------------------------------------------------------- + if (num != 1) { + std::vector tmp(nmesh); + InternSubdivide (out,nmesh,&tmp.front(),num-1); + for (size_t i = 0; i < nmesh; ++i) { + delete out[i]; + out[i] = tmp[i]; + } + } +} diff --git a/thirdparty/assimp/code/Common/TargetAnimation.cpp b/thirdparty/assimp/code/Common/TargetAnimation.cpp new file mode 100644 index 0000000000..b8062499ff --- /dev/null +++ b/thirdparty/assimp/code/Common/TargetAnimation.cpp @@ -0,0 +1,248 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#include "TargetAnimation.h" +#include +#include + +using namespace Assimp; + + +// ------------------------------------------------------------------------------------------------ +KeyIterator::KeyIterator(const std::vector* _objPos, + const std::vector* _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 +inline T Interpolate(const T& one, const T& two, ai_real val) +{ + return one + (two-one)*val; +} + +// ------------------------------------------------------------------------------------------------ +void KeyIterator::operator ++() +{ + // If we are already at the end of all keyframes, return + if (reachedEnd) { + return; + } + + // Now search in all arrays for the time value closest + // to our current position on the time line + double d0,d1; + + d0 = objPos->at ( std::min ( nextObjPos, static_cast(objPos->size()-1)) ).mTime; + d1 = targetObjPos->at( std::min ( nextTargetObjPos, static_cast(targetObjPos->size()-1)) ).mTime; + + // Easiest case - all are identical. In this + // case we don't need to interpolate so we can + // return earlier + if ( d0 == d1 ) + { + curTime = d0; + curPosition = objPos->at(nextObjPos).mValue; + curTargetPosition = targetObjPos->at(nextTargetObjPos).mValue; + + // increment counters + if (objPos->size() != nextObjPos-1) + ++nextObjPos; + + if (targetObjPos->size() != nextTargetObjPos-1) + ++nextTargetObjPos; + } + + // An object position key is closest to us + else if (d0 < d1) + { + curTime = d0; + + // interpolate the other + if (1 == targetObjPos->size() || !nextTargetObjPos) { + curTargetPosition = targetObjPos->at(0).mValue; + } + else + { + const aiVectorKey& last = targetObjPos->at(nextTargetObjPos); + const aiVectorKey& first = targetObjPos->at(nextTargetObjPos-1); + + curTargetPosition = Interpolate(first.mValue, last.mValue, (ai_real) ( + (curTime-first.mTime) / (last.mTime-first.mTime) )); + } + + if (objPos->size() != nextObjPos-1) + ++nextObjPos; + } + // A target position key is closest to us + else + { + curTime = d1; + + // interpolate the other + if (1 == objPos->size() || !nextObjPos) { + curPosition = objPos->at(0).mValue; + } + else + { + const aiVectorKey& last = objPos->at(nextObjPos); + const aiVectorKey& first = objPos->at(nextObjPos-1); + + curPosition = Interpolate(first.mValue, last.mValue, (ai_real) ( + (curTime-first.mTime) / (last.mTime-first.mTime))); + } + + if (targetObjPos->size() != nextTargetObjPos-1) + ++nextTargetObjPos; + } + + if (nextObjPos >= objPos->size()-1 && + nextTargetObjPos >= targetObjPos->size()-1) + { + // We reached the very last keyframe + reachedEnd = true; + } +} + +// ------------------------------------------------------------------------------------------------ +void TargetAnimationHelper::SetTargetAnimationChannel ( + const std::vector* _targetPositions) +{ + ai_assert(NULL != _targetPositions); + targetPositions = _targetPositions; +} + +// ------------------------------------------------------------------------------------------------ +void TargetAnimationHelper::SetMainAnimationChannel ( + const std::vector* _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* distanceTrack) +{ + ai_assert(NULL != targetPositions && NULL != distanceTrack); + + // TODO: in most cases we won't need the extra array + std::vector real; + + std::vector* fill = (distanceTrack == objectPositions ? &real : distanceTrack); + fill->reserve(std::max( objectPositions->size(), targetPositions->size() )); + + // Iterate through all object keys and interpolate their values if necessary. + // Then get the corresponding target position, compute the difference + // vector between object and target position. Then compute a rotation matrix + // that rotates the base vector of the object coordinate system at that time + // to match the diff vector. + + KeyIterator iter(objectPositions,targetPositions,&fixedMain); + for (;!iter.Finished();++iter) + { + const aiVector3D& position = iter.GetCurPosition(); + const aiVector3D& tposition = iter.GetCurTargetPosition(); + + // diff vector + aiVector3D diff = tposition - position; + ai_real f = diff.Length(); + + // output distance vector + if (f) + { + fill->push_back(aiVectorKey()); + aiVectorKey& v = fill->back(); + v.mTime = iter.GetCurTime(); + v.mValue = diff; + + diff /= f; + } + else + { + // FIXME: handle this + } + + // diff is now the vector in which our camera is pointing + } + + if (real.size()) { + *distanceTrack = real; + } +} diff --git a/thirdparty/assimp/code/Common/TargetAnimation.h b/thirdparty/assimp/code/Common/TargetAnimation.h new file mode 100644 index 0000000000..91634ab5aa --- /dev/null +++ b/thirdparty/assimp/code/Common/TargetAnimation.h @@ -0,0 +1,183 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a helper class for the ASE and 3DS loaders to + help them compute camera and spot light animation channels */ +#ifndef AI_TARGET_ANIMATION_H_INC +#define AI_TARGET_ANIMATION_H_INC + +#include +#include + +namespace Assimp { + + + +// --------------------------------------------------------------------------- +/** Helper class to iterate through all keys in an animation channel. + * + * Missing tracks are interpolated. This is a helper class for + * TargetAnimationHelper, but it can be freely used for other purposes. +*/ +class KeyIterator +{ +public: + + + // ------------------------------------------------------------------ + /** Constructs a new key iterator + * + * @param _objPos Object position track. May be NULL. + * @param _targetObjPos Target object position track. May be NULL. + * @param defaultObjectPos Default object position to be used if + * no animated track is available. May be NULL. + * @param defaultTargetPos Default target position to be used if + * no animated track is available. May be NULL. + */ + KeyIterator(const std::vector* _objPos, + const std::vector* _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* objPos,*targetObjPos; + + unsigned int nextObjPos, nextTargetObjPos; + std::vector defaultObjPos,defaultTargetObjPos; +}; + +// --------------------------------------------------------------------------- +/** Helper class for the 3DS and ASE loaders to compute camera and spot light + * animations. + * + * 3DS and ASE store the differently to Assimp - there is an animation + * channel for the camera/spot light itself and a separate position + * animation channels specifying the position of the camera/spot light + * look-at target */ +class TargetAnimationHelper +{ +public: + + TargetAnimationHelper() + : targetPositions (NULL) + , objectPositions (NULL) + {} + + + // ------------------------------------------------------------------ + /** Sets the target animation channel + * + * This channel specifies the position of the camera/spot light + * target at a specific position. + * + * @param targetPositions Translation channel*/ + void SetTargetAnimationChannel (const + std::vector* targetPositions); + + + // ------------------------------------------------------------------ + /** Sets the main animation channel + * + * @param objectPositions Translation channel */ + void SetMainAnimationChannel ( const + std::vector* 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* distanceTrack ); + + +private: + + const std::vector* targetPositions,*objectPositions; + aiVector3D fixedMain; +}; + + +} // ! end namespace Assimp + +#endif // include guard diff --git a/thirdparty/assimp/code/Common/Version.cpp b/thirdparty/assimp/code/Common/Version.cpp new file mode 100644 index 0000000000..cc94340ac8 --- /dev/null +++ b/thirdparty/assimp/code/Common/Version.cpp @@ -0,0 +1,185 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +// Actually just a dummy, used by the compiler to build the precompiled header. + +#include +#include +#include "ScenePrivate.h" + +static const unsigned int MajorVersion = 4; +static const unsigned int MinorVersion = 1; + +// -------------------------------------------------------------------------------- +// Legal information string - don't remove this. +static const char* LEGAL_INFORMATION = + +"Open Asset Import Library (Assimp).\n" +"A free C/C++ library to import various 3D file formats into applications\n\n" + +"(c) 2008-2017, assimp team\n" +"License under the terms and conditions of the 3-clause BSD license\n" +"http://assimp.sourceforge.net\n" +; + +// ------------------------------------------------------------------------------------------------ +// Get legal string +ASSIMP_API const char* aiGetLegalString () { + return LEGAL_INFORMATION; +} + +// ------------------------------------------------------------------------------------------------ +// Get Assimp minor version +ASSIMP_API unsigned int aiGetVersionMinor () { + return MinorVersion; +} + +// ------------------------------------------------------------------------------------------------ +// Get Assimp major version +ASSIMP_API unsigned int aiGetVersionMajor () { + return MajorVersion; +} + +// ------------------------------------------------------------------------------------------------ +// Get flags used for compilation +ASSIMP_API unsigned int aiGetCompileFlags () { + + unsigned int flags = 0; + +#ifdef ASSIMP_BUILD_BOOST_WORKAROUND + flags |= ASSIMP_CFLAGS_NOBOOST; +#endif +#ifdef ASSIMP_BUILD_SINGLETHREADED + flags |= ASSIMP_CFLAGS_SINGLETHREADED; +#endif +#ifdef ASSIMP_BUILD_DEBUG + flags |= ASSIMP_CFLAGS_DEBUG; +#endif +#ifdef ASSIMP_BUILD_DLL_EXPORT + flags |= ASSIMP_CFLAGS_SHARED; +#endif +#ifdef _STLPORT_VERSION + flags |= ASSIMP_CFLAGS_STLPORT; +#endif + + return flags; +} + +// include current build revision, which is even updated from time to time -- :-) +#include "revision.h" + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API unsigned int aiGetVersionRevision() { + return GitVersion; +} + +ASSIMP_API const char *aiGetBranchName() { + return GitBranch; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiScene::aiScene() +: mFlags(0) +, mRootNode(nullptr) +, mNumMeshes(0) +, mMeshes(nullptr) +, mNumMaterials(0) +, mMaterials(nullptr) +, mNumAnimations(0) +, mAnimations(nullptr) +, mNumTextures(0) +, mTextures(nullptr) +, mNumLights(0) +, mLights(nullptr) +, mNumCameras(0) +, mCameras(nullptr) +, mMetaData(nullptr) +, mPrivate(new Assimp::ScenePrivateData()) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiScene::~aiScene() { + // delete all sub-objects recursively + delete mRootNode; + + // To make sure we won't crash if the data is invalid it's + // much better to check whether both mNumXXX and mXXX are + // valid instead of relying on just one of them. + if (mNumMeshes && mMeshes) + for( unsigned int a = 0; a < mNumMeshes; a++) + delete mMeshes[a]; + delete [] mMeshes; + + if (mNumMaterials && mMaterials) { + for (unsigned int a = 0; a < mNumMaterials; ++a ) { + delete mMaterials[ a ]; + } + } + delete [] mMaterials; + + if (mNumAnimations && mAnimations) + for( unsigned int a = 0; a < mNumAnimations; a++) + delete mAnimations[a]; + delete [] mAnimations; + + if (mNumTextures && mTextures) + for( unsigned int a = 0; a < mNumTextures; a++) + delete mTextures[a]; + delete [] mTextures; + + if (mNumLights && mLights) + for( unsigned int a = 0; a < mNumLights; a++) + delete mLights[a]; + delete [] mLights; + + if (mNumCameras && mCameras) + for( unsigned int a = 0; a < mNumCameras; a++) + delete mCameras[a]; + delete [] mCameras; + + aiMetadata::Dealloc(mMetaData); + mMetaData = nullptr; + + delete static_cast( mPrivate ); +} + diff --git a/thirdparty/assimp/code/Common/VertexTriangleAdjacency.cpp b/thirdparty/assimp/code/Common/VertexTriangleAdjacency.cpp new file mode 100644 index 0000000000..7cfd1a3505 --- /dev/null +++ b/thirdparty/assimp/code/Common/VertexTriangleAdjacency.cpp @@ -0,0 +1,134 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the VertexTriangleAdjacency helper class + */ + +// internal headers +#include "VertexTriangleAdjacency.h" +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces, + unsigned int iNumFaces, + unsigned int iNumVertices /*= 0*/, + bool bComputeNumTriangles /*= false*/) +{ + // compute the number of referenced vertices if it wasn't specified by the caller + const aiFace* const pcFaceEnd = pcFaces + iNumFaces; + if (!iNumVertices) { + for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) { + ai_assert( nullptr != pcFace ); + ai_assert(3 == pcFace->mNumIndices); + iNumVertices = std::max(iNumVertices,pcFace->mIndices[0]); + iNumVertices = std::max(iNumVertices,pcFace->mIndices[1]); + iNumVertices = std::max(iNumVertices,pcFace->mIndices[2]); + } + } + + mNumVertices = iNumVertices; + + unsigned int* pi; + + // allocate storage + if (bComputeNumTriangles) { + pi = mLiveTriangles = new unsigned int[iNumVertices+1]; + ::memset(mLiveTriangles,0,sizeof(unsigned int)*(iNumVertices+1)); + mOffsetTable = new unsigned int[iNumVertices+2]+1; + } else { + pi = mOffsetTable = new unsigned int[iNumVertices+2]+1; + ::memset(mOffsetTable,0,sizeof(unsigned int)*(iNumVertices+1)); + mLiveTriangles = NULL; // important, otherwise the d'tor would crash + } + + // get a pointer to the end of the buffer + unsigned int* piEnd = pi+iNumVertices; + *piEnd++ = 0u; + + // first pass: compute the number of faces referencing each vertex + for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) + { + unsigned nind = pcFace->mNumIndices; + unsigned * ind = pcFace->mIndices; + if (nind > 0) pi[ind[0]]++; + if (nind > 1) pi[ind[1]]++; + if (nind > 2) pi[ind[2]]++; + } + + // second pass: compute the final offset table + unsigned int iSum = 0; + unsigned int* piCurOut = this->mOffsetTable; + for (unsigned int* piCur = pi; piCur != piEnd;++piCur,++piCurOut) { + + unsigned int iLastSum = iSum; + iSum += *piCur; + *piCurOut = iLastSum; + } + pi = this->mOffsetTable; + + // third pass: compute the final table + this->mAdjacencyTable = new unsigned int[iSum]; + iSum = 0; + for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace,++iSum) { + unsigned nind = pcFace->mNumIndices; + unsigned * ind = pcFace->mIndices; + + if (nind > 0) mAdjacencyTable[pi[ind[0]]++] = iSum; + if (nind > 1) mAdjacencyTable[pi[ind[1]]++] = iSum; + if (nind > 2) mAdjacencyTable[pi[ind[2]]++] = iSum; + } + // fourth pass: undo the offset computations made during the third pass + // We could do this in a separate buffer, but this would be TIMES slower. + --mOffsetTable; + *mOffsetTable = 0u; +} +// ------------------------------------------------------------------------------------------------ +VertexTriangleAdjacency::~VertexTriangleAdjacency() +{ + // delete allocated storage + delete[] mOffsetTable; + delete[] mAdjacencyTable; + delete[] mLiveTriangles; +} diff --git a/thirdparty/assimp/code/Common/VertexTriangleAdjacency.h b/thirdparty/assimp/code/Common/VertexTriangleAdjacency.h new file mode 100644 index 0000000000..f3be47612d --- /dev/null +++ b/thirdparty/assimp/code/Common/VertexTriangleAdjacency.h @@ -0,0 +1,117 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a helper class to compute a vertex-triangle adjacency map */ +#ifndef AI_VTADJACENCY_H_INC +#define AI_VTADJACENCY_H_INC + +#include "BaseProcess.h" +#include +#include + +struct aiMesh; +struct aiFace; + +namespace Assimp { + +// -------------------------------------------------------------------------------------------- +/** @brief The VertexTriangleAdjacency class computes a vertex-triangle + * adjacency map from a given index buffer. + * + * @note Although it is called #VertexTriangleAdjacency, the current version does also + * support arbitrary polygons. */ +// -------------------------------------------------------------------------------------------- +class ASSIMP_API VertexTriangleAdjacency { +public: + // ---------------------------------------------------------------------------- + /** @brief Construction from an existing index buffer + * @param pcFaces Index buffer + * @param iNumFaces Number of faces in the buffer + * @param iNumVertices Number of referenced vertices. This value + * is computed automatically if 0 is specified. + * @param bComputeNumTriangles If you want the class to compute + * a list containing the number of referenced triangles per vertex + * per vertex - pass true. */ + VertexTriangleAdjacency(aiFace* pcFaces,unsigned int iNumFaces, + unsigned int iNumVertices = 0, + bool bComputeNumTriangles = true); + + // ---------------------------------------------------------------------------- + /** @brief Destructor */ + ~VertexTriangleAdjacency(); + + // ---------------------------------------------------------------------------- + /** @brief Get all triangles adjacent to a vertex + * @param iVertIndex Index of the vertex + * @return A pointer to the adjacency list. */ + unsigned int* GetAdjacentTriangles(unsigned int iVertIndex) const { + ai_assert(iVertIndex < mNumVertices); + return &mAdjacencyTable[ mOffsetTable[iVertIndex]]; + } + + // ---------------------------------------------------------------------------- + /** @brief Get the number of triangles that are referenced by + * a vertex. This function returns a reference that can be modified + * @param iVertIndex Index of the vertex + * @return Number of referenced triangles */ + unsigned int& GetNumTrianglesPtr(unsigned int iVertIndex) { + ai_assert( iVertIndex < mNumVertices ); + ai_assert( nullptr != mLiveTriangles ); + return mLiveTriangles[iVertIndex]; + } + + //! Offset table + unsigned int* mOffsetTable; + + //! Adjacency table + unsigned int* mAdjacencyTable; + + //! Table containing the number of referenced triangles per vertex + unsigned int* mLiveTriangles; + + //! Debug: Number of referenced vertices + unsigned int mNumVertices; +}; + +} //! ns Assimp + +#endif // !! AI_VTADJACENCY_H_INC diff --git a/thirdparty/assimp/code/Common/Win32DebugLogStream.h b/thirdparty/assimp/code/Common/Win32DebugLogStream.h new file mode 100644 index 0000000000..a6063a261e --- /dev/null +++ b/thirdparty/assimp/code/Common/Win32DebugLogStream.h @@ -0,0 +1,95 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Win32DebugLogStream.h +* @brief Implementation of Win32DebugLogStream +*/ +#ifndef AI_WIN32DEBUGLOGSTREAM_H_INC +#define AI_WIN32DEBUGLOGSTREAM_H_INC + +#ifdef _WIN32 + +#include +#include "windows.h" + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** @class Win32DebugLogStream + * @brief Logs into the debug stream from win32. + */ +class Win32DebugLogStream : public LogStream { +public: + /** @brief Default constructor */ + Win32DebugLogStream(); + + /** @brief Destructor */ + ~Win32DebugLogStream(); + + /** @brief Writer */ + void write(const char* messgae); +}; + +// --------------------------------------------------------------------------- +inline +Win32DebugLogStream::Win32DebugLogStream(){ + // empty +} + +// --------------------------------------------------------------------------- +inline +Win32DebugLogStream::~Win32DebugLogStream(){ + // empty +} + +// --------------------------------------------------------------------------- +inline +void Win32DebugLogStream::write(const char* message) { + ::OutputDebugStringA( message); +} + +// --------------------------------------------------------------------------- +} // Namespace Assimp + +#endif // ! _WIN32 +#endif // guard diff --git a/thirdparty/assimp/code/Common/assbin_chunks.h b/thirdparty/assimp/code/Common/assbin_chunks.h new file mode 100644 index 0000000000..15e4af5e7d --- /dev/null +++ b/thirdparty/assimp/code/Common/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 +(.assbin) is fast loading. Optional deflate compression helps reduce file size. The XML +flavour, .assxml or simply .xml, is just a plain-to-xml conversion of aiScene. + +ASSBIN is Assimp's binary interchange format. assimp_cmd (<root>/tools/assimp_cmd) 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 <root>/doc/AssXML_Scheme.xml. 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 <root>/code/assbin_chunks.h header contains some magic constants +for use by stand-alone ASSBIN loaders. Also, Assimp's own file writer can be found +in <root>/tools/assimp_cmd/WriteDumb.cpp (yes, the 'b' is no typo ...). + +@verbatim + +------------------------------------------------------------------------------- +1. File structure: +------------------------------------------------------------------------------- + +---------------------- +| Header (512 bytes) | +---------------------- +| Variable chunks | +---------------------- + +------------------------------------------------------------------------------- +2. Definitions: +------------------------------------------------------------------------------- + +integer is four bytes wide, stored in little-endian byte order. +short is two bytes wide, stored in little-endian byte order. +byte is a single byte. +string is an integer n followed by n UTF-8 characters, not terminated by zero +float is an IEEE 754 single-precision floating-point value +double is an IEEE 754 double-precision floating-point value +t[n] is an array of n elements of type t + +------------------------------------------------------------------------------- +2. Header: +------------------------------------------------------------------------------- + +byte[44] Magic identification string for ASSBIN files. + 'ASSIMP.binary' + +integer Major version of the Assimp library which wrote the file +integer Minor version of the Assimp library which wrote the file + match these against ASSBIN_VERSION_MAJOR and ASSBIN_VERSION_MINOR + +integer SVN revision of the Assimp library (intended for our internal + debugging - if you write Ass files from your own APPs, set this value to 0. +integer Assimp compile flags + +short 0 for normal files, 1 for shortened dumps for regression tests + these should have the file extension assbin.regress + +short 1 if the data after the header is compressed with the DEFLATE algorithm, + 0 for uncompressed files. + For compressed files, the first integer after the header is + always the uncompressed data size + +byte[256] Zero-terminated source file name, UTF-8 +byte[128] Zero-terminated command line parameters passed to assimp_cmd, UTF-8 + +byte[64] Reserved for future use +---> Total length: 512 bytes + +------------------------------------------------------------------------------- +3. Chunks: +------------------------------------------------------------------------------- + +integer Magic chunk ID (ASSBIN_CHUNK_XXX) +integer Chunk data length, in bytes + (unknown chunks are possible, a good reader skips over them) + (chunk-data-length does not include the first two integers) + +byte[n] chunk-data-length bytes of data, depending on the chunk type + +Chunks can contain nested chunks. Nested chunks are ALWAYS at the end of the chunk, +their size is included in chunk-data-length. + +The chunk layout for all ASSIMP data structures is derived from their C declarations. +The general 'rule' to get from Assimp headers to the serialized layout is: + + 1. POD members (i.e. aiMesh::mPrimitiveTypes, aiMesh::mNumVertices), + in order of declaration. + + 2. Array-members (aiMesh::mFaces, aiMesh::mVertices, aiBone::mWeights), + in order of declaration. + + 2. Object array members (i.e aiMesh::mBones, aiScene::mMeshes) are stored in + subchunks directly following the data written in 1.) and 2.) + + + Of course, there are some exceptions to this general order: + +[[aiScene]] + + - The root node holding the scene structure is naturally stored in + a ASSBIN_CHUNK_AINODE subchunk following 1.) and 2.) (which is + empty for aiScene). + +[[aiMesh]] + + - mTextureCoords and mNumUVComponents are serialized as follows: + + [number of used uv channels times] + integer mNumUVComponents[n] + float mTextureCoords[n][3] + + -> more than AI_MAX_TEXCOORD_CHANNELS can be stored. This allows Assimp + builds with different settings for AI_MAX_TEXCOORD_CHANNELS to exchange + data. + -> the on-disk format always uses 3 floats to write UV coordinates. + If mNumUVComponents[0] is 1, the corresponding mTextureCoords array + consists of 3 floats. + + - The array member block of aiMesh is prefixed with an integer that specifies + the kinds of vertex components actually present in the mesh. This is a + bitwise combination of the ASSBIN_MESH_HAS_xxx constants. + +[[aiFace]] + + - mNumIndices is stored as short + - mIndices are written as short, if aiMesh::mNumVertices<65536 + +[[aiNode]] + + - mParent is omitted + +[[aiLight]] + + - mAttenuationXXX not written if aiLight::mType == aiLightSource_DIRECTIONAL + - mAngleXXX not written if aiLight::mType != aiLightSource_SPOT + +[[aiMaterial]] + + - mNumAllocated is omitted, for obvious reasons :-) + + + @endverbatim*/ + + +#define ASSBIN_HEADER_LENGTH 512 + +// these are the magic chunk identifiers for the binary ASS file format +#define ASSBIN_CHUNK_AICAMERA 0x1234 +#define ASSBIN_CHUNK_AILIGHT 0x1235 +#define ASSBIN_CHUNK_AITEXTURE 0x1236 +#define ASSBIN_CHUNK_AIMESH 0x1237 +#define ASSBIN_CHUNK_AINODEANIM 0x1238 +#define ASSBIN_CHUNK_AISCENE 0x1239 +#define ASSBIN_CHUNK_AIBONE 0x123a +#define ASSBIN_CHUNK_AIANIMATION 0x123b +#define ASSBIN_CHUNK_AINODE 0x123c +#define ASSBIN_CHUNK_AIMATERIAL 0x123d +#define ASSBIN_CHUNK_AIMATERIALPROPERTY 0x123e + +#define ASSBIN_MESH_HAS_POSITIONS 0x1 +#define ASSBIN_MESH_HAS_NORMALS 0x2 +#define ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS 0x4 +#define ASSBIN_MESH_HAS_TEXCOORD_BASE 0x100 +#define ASSBIN_MESH_HAS_COLOR_BASE 0x10000 + +#define ASSBIN_MESH_HAS_TEXCOORD(n) (ASSBIN_MESH_HAS_TEXCOORD_BASE << n) +#define ASSBIN_MESH_HAS_COLOR(n) (ASSBIN_MESH_HAS_COLOR_BASE << n) + + +#endif // INCLUDED_ASSBIN_CHUNKS_H diff --git a/thirdparty/assimp/code/Common/scene.cpp b/thirdparty/assimp/code/Common/scene.cpp new file mode 100644 index 0000000000..2acb348d81 --- /dev/null +++ b/thirdparty/assimp/code/Common/scene.cpp @@ -0,0 +1,140 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#include + +aiNode::aiNode() +: mName("") +, mParent(NULL) +, mNumChildren(0) +, mChildren(NULL) +, mNumMeshes(0) +, mMeshes(NULL) +, mMetaData(NULL) { + // empty +} + +aiNode::aiNode(const std::string& name) +: mName(name) +, mParent(NULL) +, mNumChildren(0) +, mChildren(NULL) +, mNumMeshes(0) +, mMeshes(NULL) +, mMetaData(NULL) { + // empty +} + +/** Destructor */ +aiNode::~aiNode() { + // delete all children recursively + // to make sure we won't crash if the data is invalid ... + if (mChildren && mNumChildren) + { + for (unsigned int a = 0; a < mNumChildren; a++) + delete mChildren[a]; + } + delete[] mChildren; + delete[] mMeshes; + delete mMetaData; +} + +const aiNode *aiNode::FindNode(const char* name) const { + if (nullptr == name) { + return nullptr; + } + if (!::strcmp(mName.data, name)) { + return this; + } + for (unsigned int i = 0; i < mNumChildren; ++i) { + const aiNode* const p = mChildren[i]->FindNode(name); + if (p) { + return p; + } + } + // there is definitely no sub-node with this name + return nullptr; +} + +aiNode *aiNode::FindNode(const char* name) { + if (!::strcmp(mName.data, name))return this; + for (unsigned int i = 0; i < mNumChildren; ++i) + { + aiNode* const p = mChildren[i]->FindNode(name); + if (p) { + return p; + } + } + // there is definitely no sub-node with this name + return nullptr; +} + +void aiNode::addChildren(unsigned int numChildren, aiNode **children) { + if (nullptr == children || 0 == numChildren) { + return; + } + + for (unsigned int i = 0; i < numChildren; i++) { + aiNode *child = children[i]; + if (nullptr != child) { + child->mParent = this; + } + } + + if (mNumChildren > 0) { + aiNode **tmp = new aiNode*[mNumChildren]; + ::memcpy(tmp, mChildren, sizeof(aiNode*) * mNumChildren); + delete[] mChildren; + mChildren = new aiNode*[mNumChildren + numChildren]; + ::memcpy(mChildren, tmp, sizeof(aiNode*) * mNumChildren); + ::memcpy(&mChildren[mNumChildren], children, sizeof(aiNode*)* numChildren); + mNumChildren += numChildren; + delete[] tmp; + } + else { + mChildren = new aiNode*[numChildren]; + for (unsigned int i = 0; i < numChildren; i++) { + mChildren[i] = children[i]; + } + mNumChildren = numChildren; + } +} diff --git a/thirdparty/assimp/code/Common/simd.cpp b/thirdparty/assimp/code/Common/simd.cpp new file mode 100644 index 0000000000..04615f408e --- /dev/null +++ b/thirdparty/assimp/code/Common/simd.cpp @@ -0,0 +1,79 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#include "simd.h" + +namespace Assimp { + +bool CPUSupportsSSE2() { +#if defined(__x86_64__) || defined(_M_X64) + //* x86_64 always has SSE2 instructions */ + return true; +#elif defined(__GNUC__) && defined(i386) + // for GCC x86 we check cpuid + unsigned int d; + __asm__( + "pushl %%ebx\n\t" + "cpuid\n\t" + "popl %%ebx\n\t" + : "=d" ( d ) + :"a" ( 1 ) ); + return ( d & 0x04000000 ) != 0; +#elif (defined(_MSC_VER) && defined(_M_IX86)) + // also check cpuid for MSVC x86 + unsigned int d; + __asm { + xor eax, eax + inc eax + push ebx + cpuid + pop ebx + mov d, edx + } + return ( d & 0x04000000 ) != 0; +#else + return false; +#endif +} + + +} // Namespace Assimp diff --git a/thirdparty/assimp/code/Common/simd.h b/thirdparty/assimp/code/Common/simd.h new file mode 100644 index 0000000000..3eecdd4581 --- /dev/null +++ b/thirdparty/assimp/code/Common/simd.h @@ -0,0 +1,53 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#pragma once + +#include + +namespace Assimp { + +/// @brief Checks if the platform supports SSE2 optimization +/// @return true, if SSE2 is supported. false if SSE2 is not supported. +bool ASSIMP_API CPUSupportsSSE2(); + +} // Namespace Assimp diff --git a/thirdparty/assimp/code/ComputeUVMappingProcess.cpp b/thirdparty/assimp/code/ComputeUVMappingProcess.cpp deleted file mode 100644 index bb571a551b..0000000000 --- a/thirdparty/assimp/code/ComputeUVMappingProcess.cpp +++ /dev/null @@ -1,506 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file GenUVCoords step */ - - -#include "ComputeUVMappingProcess.h" -#include "ProcessHelper.h" -#include - -using namespace Assimp; - -namespace { - - const static aiVector3D base_axis_y(0.0,1.0,0.0); - const static aiVector3D base_axis_x(1.0,0.0,0.0); - const static aiVector3D base_axis_z(0.0,0.0,1.0); - const static ai_real angle_epsilon = ai_real( 0.95 ); -} - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -ComputeUVMappingProcess::ComputeUVMappingProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -ComputeUVMappingProcess::~ComputeUVMappingProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool ComputeUVMappingProcess::IsActive( unsigned int pFlags) const -{ - return (pFlags & aiProcess_GenUVCoords) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Check whether a ray intersects a plane and find the intersection point -inline bool PlaneIntersect(const aiRay& ray, const aiVector3D& planePos, - const aiVector3D& planeNormal, aiVector3D& pos) -{ - const ai_real b = planeNormal * (planePos - ray.pos); - ai_real h = ray.dir * planeNormal; - if ((h < 10e-5 && h > -10e-5) || (h = b/h) < 0) - return false; - - pos = ray.pos + (ray.dir * h); - return true; -} - -// ------------------------------------------------------------------------------------------------ -// Find the first empty UV channel in a mesh -inline unsigned int FindEmptyUVChannel (aiMesh* mesh) -{ - for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS;++m) - if (!mesh->mTextureCoords[m])return m; - - ASSIMP_LOG_ERROR("Unable to compute UV coordinates, no free UV slot found"); - return UINT_MAX; -} - -// ------------------------------------------------------------------------------------------------ -// Try to remove UV seams -void RemoveUVSeams (aiMesh* mesh, aiVector3D* out) -{ - // TODO: just a very rough algorithm. I think it could be done - // much easier, but I don't know how and am currently too tired to - // to think about a better solution. - - const static ai_real LOWER_LIMIT = ai_real( 0.1 ); - const static ai_real UPPER_LIMIT = ai_real( 0.9 ); - - const static ai_real LOWER_EPSILON = ai_real( 10e-3 ); - const static ai_real UPPER_EPSILON = ai_real( 1.0-10e-3 ); - - for (unsigned int fidx = 0; fidx < mesh->mNumFaces;++fidx) - { - const aiFace& face = mesh->mFaces[fidx]; - if (face.mNumIndices < 3) continue; // triangles and polygons only, please - - unsigned int small = face.mNumIndices, large = small; - bool zero = false, one = false, round_to_zero = false; - - // Check whether this face lies on a UV seam. We can just guess, - // but the assumption that a face with at least one very small - // on the one side and one very large U coord on the other side - // lies on a UV seam should work for most cases. - for (unsigned int n = 0; n < face.mNumIndices;++n) - { - if (out[face.mIndices[n]].x < LOWER_LIMIT) - { - small = n; - - // If we have a U value very close to 0 we can't - // round the others to 0, too. - if (out[face.mIndices[n]].x <= LOWER_EPSILON) - zero = true; - else round_to_zero = true; - } - if (out[face.mIndices[n]].x > UPPER_LIMIT) - { - large = n; - - // If we have a U value very close to 1 we can't - // round the others to 1, too. - if (out[face.mIndices[n]].x >= UPPER_EPSILON) - one = true; - } - } - if (small != face.mNumIndices && large != face.mNumIndices) - { - for (unsigned int n = 0; n < face.mNumIndices;++n) - { - // If the u value is over the upper limit and no other u - // value of that face is 0, round it to 0 - if (out[face.mIndices[n]].x > UPPER_LIMIT && !zero) - out[face.mIndices[n]].x = 0.0; - - // If the u value is below the lower limit and no other u - // value of that face is 1, round it to 1 - else if (out[face.mIndices[n]].x < LOWER_LIMIT && !one) - out[face.mIndices[n]].x = 1.0; - - // The face contains both 0 and 1 as UV coords. This can occur - // for faces which have an edge that lies directly on the seam. - // Due to numerical inaccuracies one U coord becomes 0, the - // other 1. But we do still have a third UV coord to determine - // to which side we must round to. - else if (one && zero) - { - if (round_to_zero && out[face.mIndices[n]].x >= UPPER_EPSILON) - out[face.mIndices[n]].x = 0.0; - else if (!round_to_zero && out[face.mIndices[n]].x <= LOWER_EPSILON) - out[face.mIndices[n]].x = 1.0; - } - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out) -{ - aiVector3D center, min, max; - FindMeshCenter(mesh, center, min, max); - - // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ... - // currently the mapping axis will always be one of x,y,z, except if the - // PretransformVertices step is used (it transforms the meshes into worldspace, - // thus changing the mapping axis) - if (axis * base_axis_x >= angle_epsilon) { - - // For each point get a normalized projection vector in the sphere, - // get its longitude and latitude and map them to their respective - // UV axes. Problems occur around the poles ... unsolvable. - // - // The spherical coordinate system looks like this: - // x = cos(lon)*cos(lat) - // y = sin(lon)*cos(lat) - // z = sin(lat) - // - // Thus we can derive: - // lat = arcsin (z) - // lon = arctan (y/x) - for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { - const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize(); - out[pnt] = aiVector3D((std::atan2(diff.z, diff.y) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, - (std::asin (diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); - } - } - else if (axis * base_axis_y >= angle_epsilon) { - // ... just the same again - for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { - const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize(); - out[pnt] = aiVector3D((std::atan2(diff.x, diff.z) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, - (std::asin (diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); - } - } - else if (axis * base_axis_z >= angle_epsilon) { - // ... just the same again - for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { - const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize(); - out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, - (std::asin (diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); - } - } - // slower code path in case the mapping axis is not one of the coordinate system axes - else { - aiMatrix4x4 mTrafo; - aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo); - - // again the same, except we're applying a transformation now - for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { - const aiVector3D diff = ((mTrafo*mesh->mVertices[pnt])-center).Normalize(); - out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, - (std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); - } - } - - - // Now find and remove UV seams. A seam occurs if a face has a tcoord - // close to zero on the one side, and a tcoord close to one on the - // other side. - RemoveUVSeams(mesh,out); -} - -// ------------------------------------------------------------------------------------------------ -void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out) -{ - aiVector3D center, min, max; - - // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ... - // currently the mapping axis will always be one of x,y,z, except if the - // PretransformVertices step is used (it transforms the meshes into worldspace, - // thus changing the mapping axis) - if (axis * base_axis_x >= angle_epsilon) { - FindMeshCenter(mesh, center, min, max); - const ai_real diff = max.x - min.x; - - // If the main axis is 'z', the z coordinate of a point 'p' is mapped - // directly to the texture V axis. The other axis is derived from - // the angle between ( p.x - c.x, p.y - c.y ) and (1,0), where - // 'c' is the center point of the mesh. - for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { - const aiVector3D& pos = mesh->mVertices[pnt]; - aiVector3D& uv = out[pnt]; - - uv.y = (pos.x - min.x) / diff; - uv.x = (std::atan2( pos.z - center.z, pos.y - center.y) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI; - } - } - else if (axis * base_axis_y >= angle_epsilon) { - FindMeshCenter(mesh, center, min, max); - const ai_real diff = max.y - min.y; - - // just the same ... - for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { - const aiVector3D& pos = mesh->mVertices[pnt]; - aiVector3D& uv = out[pnt]; - - uv.y = (pos.y - min.y) / diff; - uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI; - } - } - else if (axis * base_axis_z >= angle_epsilon) { - FindMeshCenter(mesh, center, min, max); - const ai_real diff = max.z - min.z; - - // just the same ... - for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { - const aiVector3D& pos = mesh->mVertices[pnt]; - aiVector3D& uv = out[pnt]; - - uv.y = (pos.z - min.z) / diff; - uv.x = (std::atan2( pos.y - center.y, pos.x - center.x) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI; - } - } - // slower code path in case the mapping axis is not one of the coordinate system axes - else { - aiMatrix4x4 mTrafo; - aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo); - FindMeshCenterTransformed(mesh, center, min, max,mTrafo); - const ai_real diff = max.y - min.y; - - // again the same, except we're applying a transformation now - for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt){ - const aiVector3D pos = mTrafo* mesh->mVertices[pnt]; - aiVector3D& uv = out[pnt]; - - uv.y = (pos.y - min.y) / diff; - uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI; - } - } - - // Now find and remove UV seams. A seam occurs if a face has a tcoord - // close to zero on the one side, and a tcoord close to one on the - // other side. - RemoveUVSeams(mesh,out); -} - -// ------------------------------------------------------------------------------------------------ -void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out) -{ - ai_real diffu,diffv; - aiVector3D center, min, max; - - // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ... - // currently the mapping axis will always be one of x,y,z, except if the - // PretransformVertices step is used (it transforms the meshes into worldspace, - // thus changing the mapping axis) - if (axis * base_axis_x >= angle_epsilon) { - FindMeshCenter(mesh, center, min, max); - diffu = max.z - min.z; - diffv = max.y - min.y; - - for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { - const aiVector3D& pos = mesh->mVertices[pnt]; - out[pnt].Set((pos.z - min.z) / diffu,(pos.y - min.y) / diffv,0.0); - } - } - else if (axis * base_axis_y >= angle_epsilon) { - FindMeshCenter(mesh, center, min, max); - diffu = max.x - min.x; - diffv = max.z - min.z; - - for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { - const aiVector3D& pos = mesh->mVertices[pnt]; - out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0); - } - } - else if (axis * base_axis_z >= angle_epsilon) { - FindMeshCenter(mesh, center, min, max); - diffu = max.y - min.y; - diffv = max.z - min.z; - - for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { - const aiVector3D& pos = mesh->mVertices[pnt]; - out[pnt].Set((pos.y - min.y) / diffu,(pos.x - min.x) / diffv,0.0); - } - } - // slower code path in case the mapping axis is not one of the coordinate system axes - else - { - aiMatrix4x4 mTrafo; - aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo); - FindMeshCenterTransformed(mesh, center, min, max,mTrafo); - diffu = max.x - min.x; - diffv = max.z - min.z; - - // again the same, except we're applying a transformation now - for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { - const aiVector3D pos = mTrafo * mesh->mVertices[pnt]; - out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0); - } - } - - // shouldn't be necessary to remove UV seams ... -} - -// ------------------------------------------------------------------------------------------------ -void ComputeUVMappingProcess::ComputeBoxMapping( aiMesh*, aiVector3D* ) -{ - ASSIMP_LOG_ERROR("Mapping type currently not implemented"); -} - -// ------------------------------------------------------------------------------------------------ -void ComputeUVMappingProcess::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("GenUVCoordsProcess begin"); - char buffer[1024]; - - if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) - throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); - - std::list mappingStack; - - /* Iterate through all materials and search for non-UV mapped textures - */ - for (unsigned int i = 0; i < pScene->mNumMaterials;++i) - { - mappingStack.clear(); - aiMaterial* mat = pScene->mMaterials[i]; - for (unsigned int a = 0; a < mat->mNumProperties;++a) - { - aiMaterialProperty* prop = mat->mProperties[a]; - if (!::strcmp( prop->mKey.data, "$tex.mapping")) - { - aiTextureMapping& mapping = *((aiTextureMapping*)prop->mData); - if (aiTextureMapping_UV != mapping) - { - if (!DefaultLogger::isNullLogger()) - { - ai_snprintf(buffer, 1024, "Found non-UV mapped texture (%s,%u). Mapping type: %s", - TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex, - MappingTypeToString(mapping)); - - ASSIMP_LOG_INFO(buffer); - } - - if (aiTextureMapping_OTHER == mapping) - continue; - - MappingInfo info (mapping); - - // Get further properties - currently only the major axis - for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2) - { - aiMaterialProperty* prop2 = mat->mProperties[a2]; - if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex) - continue; - - if ( !::strcmp( prop2->mKey.data, "$tex.mapaxis")) { - info.axis = *((aiVector3D*)prop2->mData); - break; - } - } - - unsigned int idx( 99999999 ); - - // Check whether we have this mapping mode already - std::list::iterator it = std::find (mappingStack.begin(),mappingStack.end(), info); - if (mappingStack.end() != it) - { - idx = (*it).uv; - } - else - { - /* We have found a non-UV mapped texture. Now - * we need to find all meshes using this material - * that we can compute UV channels for them. - */ - for (unsigned int m = 0; m < pScene->mNumMeshes;++m) - { - aiMesh* mesh = pScene->mMeshes[m]; - unsigned int outIdx = 0; - if ( mesh->mMaterialIndex != i || ( outIdx = FindEmptyUVChannel(mesh) ) == UINT_MAX || - !mesh->mNumVertices) - { - continue; - } - - // Allocate output storage - aiVector3D* p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices]; - - switch (mapping) - { - case aiTextureMapping_SPHERE: - ComputeSphereMapping(mesh,info.axis,p); - break; - case aiTextureMapping_CYLINDER: - ComputeCylinderMapping(mesh,info.axis,p); - break; - case aiTextureMapping_PLANE: - ComputePlaneMapping(mesh,info.axis,p); - break; - case aiTextureMapping_BOX: - ComputeBoxMapping(mesh,p); - break; - default: - ai_assert(false); - } - if (m && idx != outIdx) - { - ASSIMP_LOG_WARN("UV index mismatch. Not all meshes assigned to " - "this material have equal numbers of UV channels. The UV index stored in " - "the material structure does therefore not apply for all meshes. "); - } - idx = outIdx; - } - info.uv = idx; - mappingStack.push_back(info); - } - - // Update the material property list - mapping = aiTextureMapping_UV; - ((aiMaterial*)mat)->AddProperty(&idx,1,AI_MATKEY_UVWSRC(prop->mSemantic,prop->mIndex)); - } - } - } - } - ASSIMP_LOG_DEBUG("GenUVCoordsProcess finished"); -} diff --git a/thirdparty/assimp/code/ComputeUVMappingProcess.h b/thirdparty/assimp/code/ComputeUVMappingProcess.h deleted file mode 100644 index 24f6bb7218..0000000000 --- a/thirdparty/assimp/code/ComputeUVMappingProcess.h +++ /dev/null @@ -1,148 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a post processing step to 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 -#include - -class ComputeUVMappingTest; - -namespace Assimp { - -// --------------------------------------------------------------------------- -/** ComputeUVMappingProcess - converts special mappings, such as spherical, - * cylindrical or boxed to proper UV coordinates for rendering. -*/ -class ComputeUVMappingProcess : public BaseProcess -{ -public: - ComputeUVMappingProcess(); - ~ComputeUVMappingProcess(); - -public: - - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag field. - * @param pFlags The processing flags the importer was called with. A bitwise - * combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, false if not. - */ - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - -protected: - - // ------------------------------------------------------------------- - /** Computes spherical UV coordinates for a mesh - * - * @param mesh Mesh to be processed - * @param axis Main axis - * @param out Receives output UV coordinates - */ - void ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis, - aiVector3D* out); - - // ------------------------------------------------------------------- - /** Computes cylindrical UV coordinates for a mesh - * - * @param mesh Mesh to be processed - * @param axis Main axis - * @param out Receives output UV coordinates - */ - void ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis, - aiVector3D* out); - - // ------------------------------------------------------------------- - /** Computes planar UV coordinates for a mesh - * - * @param mesh Mesh to be processed - * @param axis Main axis - * @param out Receives output UV coordinates - */ - void ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis, - aiVector3D* out); - - // ------------------------------------------------------------------- - /** Computes cubic UV coordinates for a mesh - * - * @param mesh Mesh to be processed - * @param out Receives output UV coordinates - */ - void ComputeBoxMapping(aiMesh* mesh, aiVector3D* out); - -private: - - // temporary structure to describe a mapping - struct MappingInfo - { - explicit MappingInfo(aiTextureMapping _type) - : type (_type) - , axis (0.f,1.f,0.f) - , uv (0u) - {} - - aiTextureMapping type; - aiVector3D axis; - unsigned int uv; - - bool operator== (const MappingInfo& other) - { - return type == other.type && axis == other.axis; - } - }; -}; - -} // end of namespace Assimp - -#endif // AI_COMPUTEUVMAPPING_H_INC diff --git a/thirdparty/assimp/code/ConvertToLHProcess.cpp b/thirdparty/assimp/code/ConvertToLHProcess.cpp deleted file mode 100644 index b7cd4f0bc6..0000000000 --- a/thirdparty/assimp/code/ConvertToLHProcess.cpp +++ /dev/null @@ -1,414 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file MakeLeftHandedProcess.cpp - * @brief Implementation of the post processing step to convert all - * imported data to a left-handed coordinate system. - * - * Face order & UV flip are also implemented here, for the sake of a - * better location. - */ - - -#include "ConvertToLHProcess.h" -#include -#include -#include - -using namespace Assimp; - -#ifndef ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS - -namespace { - -template -void flipUVs(aiMeshType* pMesh) { - if (pMesh == nullptr) { return; } - // mirror texture y coordinate - for (unsigned int tcIdx = 0; tcIdx < AI_MAX_NUMBER_OF_TEXTURECOORDS; tcIdx++) { - if (!pMesh->HasTextureCoords(tcIdx)) { - break; - } - - for (unsigned int vIdx = 0; vIdx < pMesh->mNumVertices; vIdx++) { - pMesh->mTextureCoords[tcIdx][vIdx].y = 1.0f - pMesh->mTextureCoords[tcIdx][vIdx].y; - } - } -} - -} // namespace - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -MakeLeftHandedProcess::MakeLeftHandedProcess() -: BaseProcess() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -MakeLeftHandedProcess::~MakeLeftHandedProcess() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool MakeLeftHandedProcess::IsActive( unsigned int pFlags) const -{ - return 0 != (pFlags & aiProcess_MakeLeftHanded); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void MakeLeftHandedProcess::Execute( aiScene* pScene) -{ - // Check for an existent root node to proceed - ai_assert(pScene->mRootNode != NULL); - ASSIMP_LOG_DEBUG("MakeLeftHandedProcess begin"); - - // recursively convert all the nodes - ProcessNode( pScene->mRootNode, aiMatrix4x4()); - - // process the meshes accordingly - for ( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) { - ProcessMesh( pScene->mMeshes[ a ] ); - } - - // process the materials accordingly - for ( unsigned int a = 0; a < pScene->mNumMaterials; ++a ) { - ProcessMaterial( pScene->mMaterials[ a ] ); - } - - // transform all animation channels as well - for( unsigned int a = 0; a < pScene->mNumAnimations; a++) - { - aiAnimation* anim = pScene->mAnimations[a]; - for( unsigned int b = 0; b < anim->mNumChannels; b++) - { - aiNodeAnim* nodeAnim = anim->mChannels[b]; - ProcessAnimation( nodeAnim); - } - } - ASSIMP_LOG_DEBUG("MakeLeftHandedProcess finished"); -} - -// ------------------------------------------------------------------------------------------------ -// Recursively converts a node, all of its children and all of its meshes -void MakeLeftHandedProcess::ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation) -{ - // mirror all base vectors at the local Z axis - pNode->mTransformation.c1 = -pNode->mTransformation.c1; - pNode->mTransformation.c2 = -pNode->mTransformation.c2; - pNode->mTransformation.c3 = -pNode->mTransformation.c3; - pNode->mTransformation.c4 = -pNode->mTransformation.c4; - - // now invert the Z axis again to keep the matrix determinant positive. - // The local meshes will be inverted accordingly so that the result should look just fine again. - pNode->mTransformation.a3 = -pNode->mTransformation.a3; - pNode->mTransformation.b3 = -pNode->mTransformation.b3; - pNode->mTransformation.c3 = -pNode->mTransformation.c3; - pNode->mTransformation.d3 = -pNode->mTransformation.d3; // useless, but anyways... - - // continue for all children - for( size_t a = 0; a < pNode->mNumChildren; ++a ) { - ProcessNode( pNode->mChildren[ a ], pParentGlobalRotation * pNode->mTransformation ); - } -} - -// ------------------------------------------------------------------------------------------------ -// Converts a single mesh to left handed coordinates. -void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) { - if ( nullptr == pMesh ) { - ASSIMP_LOG_ERROR( "Nullptr to mesh found." ); - return; - } - // mirror positions, normals and stuff along the Z axis - for( size_t a = 0; a < pMesh->mNumVertices; ++a) - { - pMesh->mVertices[a].z *= -1.0f; - if (pMesh->HasNormals()) { - pMesh->mNormals[a].z *= -1.0f; - } - if( pMesh->HasTangentsAndBitangents()) - { - pMesh->mTangents[a].z *= -1.0f; - pMesh->mBitangents[a].z *= -1.0f; - } - } - - // mirror anim meshes positions, normals and stuff along the Z axis - for (size_t m = 0; m < pMesh->mNumAnimMeshes; ++m) - { - for (size_t a = 0; a < pMesh->mAnimMeshes[m]->mNumVertices; ++a) - { - pMesh->mAnimMeshes[m]->mVertices[a].z *= -1.0f; - if (pMesh->mAnimMeshes[m]->HasNormals()) { - pMesh->mAnimMeshes[m]->mNormals[a].z *= -1.0f; - } - if (pMesh->mAnimMeshes[m]->HasTangentsAndBitangents()) - { - pMesh->mAnimMeshes[m]->mTangents[a].z *= -1.0f; - pMesh->mAnimMeshes[m]->mBitangents[a].z *= -1.0f; - } - } - } - - // mirror offset matrices of all bones - for( size_t a = 0; a < pMesh->mNumBones; ++a) - { - aiBone* bone = pMesh->mBones[a]; - bone->mOffsetMatrix.a3 = -bone->mOffsetMatrix.a3; - bone->mOffsetMatrix.b3 = -bone->mOffsetMatrix.b3; - bone->mOffsetMatrix.d3 = -bone->mOffsetMatrix.d3; - bone->mOffsetMatrix.c1 = -bone->mOffsetMatrix.c1; - bone->mOffsetMatrix.c2 = -bone->mOffsetMatrix.c2; - bone->mOffsetMatrix.c4 = -bone->mOffsetMatrix.c4; - } - - // mirror bitangents as well as they're derived from the texture coords - if( pMesh->HasTangentsAndBitangents()) - { - for( unsigned int a = 0; a < pMesh->mNumVertices; a++) - pMesh->mBitangents[a] *= -1.0f; - } -} - -// ------------------------------------------------------------------------------------------------ -// Converts a single material to left handed coordinates. -void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) { - if ( nullptr == _mat ) { - ASSIMP_LOG_ERROR( "Nullptr to aiMaterial found." ); - return; - } - - aiMaterial* mat = (aiMaterial*)_mat; - for (unsigned int a = 0; a < mat->mNumProperties;++a) { - aiMaterialProperty* prop = mat->mProperties[a]; - - // Mapping axis for UV mappings? - if (!::strcmp( prop->mKey.data, "$tex.mapaxis")) { - ai_assert( prop->mDataLength >= sizeof(aiVector3D)); /* something is wrong with the validation if we end up here */ - aiVector3D* pff = (aiVector3D*)prop->mData; - pff->z *= -1.f; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Converts the given animation to LH coordinates. -void MakeLeftHandedProcess::ProcessAnimation( aiNodeAnim* pAnim) -{ - // position keys - for( unsigned int a = 0; a < pAnim->mNumPositionKeys; a++) - pAnim->mPositionKeys[a].mValue.z *= -1.0f; - - // rotation keys - for( unsigned int a = 0; a < pAnim->mNumRotationKeys; a++) - { - /* That's the safe version, but the float errors add up. So we try the short version instead - aiMatrix3x3 rotmat = pAnim->mRotationKeys[a].mValue.GetMatrix(); - rotmat.a3 = -rotmat.a3; rotmat.b3 = -rotmat.b3; - rotmat.c1 = -rotmat.c1; rotmat.c2 = -rotmat.c2; - aiQuaternion rotquat( rotmat); - pAnim->mRotationKeys[a].mValue = rotquat; - */ - pAnim->mRotationKeys[a].mValue.x *= -1.0f; - pAnim->mRotationKeys[a].mValue.y *= -1.0f; - } -} - -#endif // !! ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS -#ifndef ASSIMP_BUILD_NO_FLIPUVS_PROCESS -// # FlipUVsProcess - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -FlipUVsProcess::FlipUVsProcess() -{} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -FlipUVsProcess::~FlipUVsProcess() -{} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool FlipUVsProcess::IsActive( unsigned int pFlags) const -{ - return 0 != (pFlags & aiProcess_FlipUVs); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void FlipUVsProcess::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("FlipUVsProcess begin"); - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - ProcessMesh(pScene->mMeshes[i]); - - for (unsigned int i = 0; i < pScene->mNumMaterials;++i) - ProcessMaterial(pScene->mMaterials[i]); - ASSIMP_LOG_DEBUG("FlipUVsProcess finished"); -} - -// ------------------------------------------------------------------------------------------------ -// Converts a single material -void FlipUVsProcess::ProcessMaterial (aiMaterial* _mat) -{ - aiMaterial* mat = (aiMaterial*)_mat; - for (unsigned int a = 0; a < mat->mNumProperties;++a) { - aiMaterialProperty* prop = mat->mProperties[a]; - if( !prop ) { - ASSIMP_LOG_DEBUG( "Property is null" ); - continue; - } - - // UV transformation key? - if (!::strcmp( prop->mKey.data, "$tex.uvtrafo")) { - ai_assert( prop->mDataLength >= sizeof(aiUVTransform)); /* something is wrong with the validation if we end up here */ - aiUVTransform* uv = (aiUVTransform*)prop->mData; - - // just flip it, that's everything - uv->mTranslation.y *= -1.f; - uv->mRotation *= -1.f; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Converts a single mesh -void FlipUVsProcess::ProcessMesh( aiMesh* pMesh) -{ - flipUVs(pMesh); - for (unsigned int idx = 0; idx < pMesh->mNumAnimMeshes; idx++) { - flipUVs(pMesh->mAnimMeshes[idx]); - } -} - -#endif // !ASSIMP_BUILD_NO_FLIPUVS_PROCESS -#ifndef ASSIMP_BUILD_NO_FLIPWINDING_PROCESS -// # FlipWindingOrderProcess - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -FlipWindingOrderProcess::FlipWindingOrderProcess() -{} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -FlipWindingOrderProcess::~FlipWindingOrderProcess() -{} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool FlipWindingOrderProcess::IsActive( unsigned int pFlags) const -{ - return 0 != (pFlags & aiProcess_FlipWindingOrder); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void FlipWindingOrderProcess::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("FlipWindingOrderProcess begin"); - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - ProcessMesh(pScene->mMeshes[i]); - ASSIMP_LOG_DEBUG("FlipWindingOrderProcess finished"); -} - -// ------------------------------------------------------------------------------------------------ -// Converts a single mesh -void FlipWindingOrderProcess::ProcessMesh( aiMesh* pMesh) -{ - // invert the order of all faces in this mesh - for( unsigned int a = 0; a < pMesh->mNumFaces; a++) - { - aiFace& face = pMesh->mFaces[a]; - for (unsigned int b = 0; b < face.mNumIndices / 2; b++) { - std::swap(face.mIndices[b], face.mIndices[face.mNumIndices - 1 - b]); - } - } - - // invert the order of all components in this mesh anim meshes - for (unsigned int m = 0; m < pMesh->mNumAnimMeshes; m++) { - aiAnimMesh* animMesh = pMesh->mAnimMeshes[m]; - unsigned int numVertices = animMesh->mNumVertices; - if (animMesh->HasPositions()) { - for (unsigned int a = 0; a < numVertices; a++) - { - std::swap(animMesh->mVertices[a], animMesh->mVertices[numVertices - 1 - a]); - } - } - if (animMesh->HasNormals()) { - for (unsigned int a = 0; a < numVertices; a++) - { - std::swap(animMesh->mNormals[a], animMesh->mNormals[numVertices - 1 - a]); - } - } - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; i++) { - if (animMesh->HasTextureCoords(i)) { - for (unsigned int a = 0; a < numVertices; a++) - { - std::swap(animMesh->mTextureCoords[i][a], animMesh->mTextureCoords[i][numVertices - 1 - a]); - } - } - } - if (animMesh->HasTangentsAndBitangents()) { - for (unsigned int a = 0; a < numVertices; a++) - { - std::swap(animMesh->mTangents[a], animMesh->mTangents[numVertices - 1 - a]); - std::swap(animMesh->mBitangents[a], animMesh->mBitangents[numVertices - 1 - a]); - } - } - for (unsigned int v = 0; v < AI_MAX_NUMBER_OF_COLOR_SETS; v++) { - if (animMesh->HasVertexColors(v)) { - for (unsigned int a = 0; a < numVertices; a++) - { - std::swap(animMesh->mColors[v][a], animMesh->mColors[v][numVertices - 1 - a]); - } - } - } - } -} - -#endif // !! ASSIMP_BUILD_NO_FLIPWINDING_PROCESS diff --git a/thirdparty/assimp/code/ConvertToLHProcess.h b/thirdparty/assimp/code/ConvertToLHProcess.h deleted file mode 100644 index 63351568d0..0000000000 --- a/thirdparty/assimp/code/ConvertToLHProcess.h +++ /dev/null @@ -1,170 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file 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 "BaseProcess.h" - -struct aiMesh; -struct aiNodeAnim; -struct aiNode; -struct aiMaterial; - -namespace Assimp { - -// ----------------------------------------------------------------------------------- -/** @brief The MakeLeftHandedProcess converts all imported data to a left-handed - * coordinate system. - * - * This implies a mirroring of the Z axis of the coordinate system. But to keep - * transformation matrices free from reflections we shift the reflection to other - * places. We mirror the meshes and adapt the rotations. - * - * @note RH-LH and LH-RH is the same, so this class can be used for both - */ -class MakeLeftHandedProcess : public BaseProcess -{ - - -public: - MakeLeftHandedProcess(); - ~MakeLeftHandedProcess(); - - // ------------------------------------------------------------------- - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - void Execute( aiScene* pScene); - -protected: - - // ------------------------------------------------------------------- - /** Recursively converts a node and all of its children - */ - void ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation); - - // ------------------------------------------------------------------- - /** Converts a single mesh to left handed coordinates. - * This means that positions, normals and tangents are mirrored at - * the local Z axis and the order of all faces are inverted. - * @param pMesh The mesh to convert. - */ - void ProcessMesh( aiMesh* pMesh); - - // ------------------------------------------------------------------- - /** Converts a single material to left-handed coordinates - * @param pMat Material to convert - */ - void ProcessMaterial( aiMaterial* pMat); - - // ------------------------------------------------------------------- - /** Converts the given animation to LH coordinates. - * The rotation and translation keys are transformed, the scale keys - * work in local space and can therefore be left untouched. - * @param pAnim The bone animation to transform - */ - void ProcessAnimation( aiNodeAnim* pAnim); -}; - - -// --------------------------------------------------------------------------- -/** Postprocessing step to flip the face order of the imported data - */ -class FlipWindingOrderProcess : public BaseProcess -{ - friend class Importer; - -public: - /** Constructor to be privately used by Importer */ - FlipWindingOrderProcess(); - - /** Destructor, private as well */ - ~FlipWindingOrderProcess(); - - // ------------------------------------------------------------------- - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - void Execute( aiScene* pScene); - -protected: - void ProcessMesh( aiMesh* pMesh); -}; - -// --------------------------------------------------------------------------- -/** Postprocessing step to flip the UV coordinate system of the import data - */ -class FlipUVsProcess : public BaseProcess -{ - friend class Importer; - -public: - /** Constructor to be privately used by Importer */ - FlipUVsProcess(); - - /** Destructor, private as well */ - ~FlipUVsProcess(); - - // ------------------------------------------------------------------- - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - void Execute( aiScene* pScene); - -protected: - void ProcessMesh( aiMesh* pMesh); - void ProcessMaterial( aiMaterial* mat); -}; - -} // end of namespace Assimp - -#endif // AI_CONVERTTOLHPROCESS_H_INC diff --git a/thirdparty/assimp/code/CreateAnimMesh.cpp b/thirdparty/assimp/code/CreateAnimMesh.cpp deleted file mode 100644 index 1a052849bb..0000000000 --- a/thirdparty/assimp/code/CreateAnimMesh.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (C) 2016 The Qt Company Ltd. -Copyright (c) 2006-2012, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -#include - -namespace Assimp { - -aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh) -{ - aiAnimMesh *animesh = new aiAnimMesh; - animesh->mVertices = NULL; - animesh->mNormals = NULL; - animesh->mTangents = NULL; - animesh->mBitangents = NULL; - animesh->mNumVertices = mesh->mNumVertices; - if (mesh->mVertices) { - animesh->mVertices = new aiVector3D[animesh->mNumVertices]; - std::memcpy(animesh->mVertices, mesh->mVertices, mesh->mNumVertices * sizeof(aiVector3D)); - } - if (mesh->mNormals) { - animesh->mNormals = new aiVector3D[animesh->mNumVertices]; - std::memcpy(animesh->mNormals, mesh->mNormals, mesh->mNumVertices * sizeof(aiVector3D)); - } - if (mesh->mTangents) { - animesh->mTangents = new aiVector3D[animesh->mNumVertices]; - std::memcpy(animesh->mTangents, mesh->mTangents, mesh->mNumVertices * sizeof(aiVector3D)); - } - if (mesh->mBitangents) { - animesh->mBitangents = new aiVector3D[animesh->mNumVertices]; - std::memcpy(animesh->mBitangents, mesh->mBitangents, mesh->mNumVertices * sizeof(aiVector3D)); - } - - for (int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) { - if (mesh->mColors[i]) { - animesh->mColors[i] = new aiColor4D[animesh->mNumVertices]; - std::memcpy(animesh->mColors[i], mesh->mColors[i], mesh->mNumVertices * sizeof(aiColor4D)); - } else { - animesh->mColors[i] = NULL; - } - } - - for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { - if (mesh->mTextureCoords[i]) { - animesh->mTextureCoords[i] = new aiVector3D[animesh->mNumVertices]; - std::memcpy(animesh->mTextureCoords[i], mesh->mTextureCoords[i], mesh->mNumVertices * sizeof(aiVector3D)); - } else { - animesh->mTextureCoords[i] = NULL; - } - } - return animesh; -} - -} // end of namespace Assimp diff --git a/thirdparty/assimp/code/DeboneProcess.cpp b/thirdparty/assimp/code/DeboneProcess.cpp deleted file mode 100644 index 83b8336bc9..0000000000 --- a/thirdparty/assimp/code/DeboneProcess.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/// @file DeboneProcess.cpp -/** Implementation of the DeboneProcess post processing step */ - - - -// internal headers of the post-processing framework -#include "ProcessHelper.h" -#include "DeboneProcess.h" -#include - - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -DeboneProcess::DeboneProcess() -{ - mNumBones = 0; - mNumBonesCanDoWithout = 0; - - mThreshold = AI_DEBONE_THRESHOLD; - mAllOrNone = false; -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -DeboneProcess::~DeboneProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool DeboneProcess::IsActive( unsigned int pFlags) const -{ - return (pFlags & aiProcess_Debone) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void DeboneProcess::SetupProperties(const Importer* pImp) -{ - // get the current value of the property - mAllOrNone = pImp->GetPropertyInteger(AI_CONFIG_PP_DB_ALL_OR_NONE,0)?true:false; - mThreshold = pImp->GetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD,AI_DEBONE_THRESHOLD); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void DeboneProcess::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("DeboneProcess begin"); - - if(!pScene->mNumMeshes) { - return; - } - - std::vector splitList(pScene->mNumMeshes); - for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { - splitList[a] = ConsiderMesh( pScene->mMeshes[a] ); - } - - int numSplits = 0; - - if(!!mNumBonesCanDoWithout && (!mAllOrNone||mNumBonesCanDoWithout==mNumBones)) { - for(unsigned int a = 0; a < pScene->mNumMeshes; a++) { - if(splitList[a]) { - numSplits++; - } - } - } - - if(numSplits) { - // we need to do something. Let's go. - //mSubMeshIndices.clear(); // really needed? - mSubMeshIndices.resize(pScene->mNumMeshes); // because we're doing it here anyway - - // build a new array of meshes for the scene - std::vector meshes; - - for(unsigned int a=0;amNumMeshes;a++) - { - aiMesh* srcMesh = pScene->mMeshes[a]; - - std::vector > newMeshes; - - if(splitList[a]) { - SplitMesh(srcMesh,newMeshes); - } - - // mesh was split - if(!newMeshes.empty()) { - unsigned int out = 0, in = srcMesh->mNumBones; - - // store new meshes and indices of the new meshes - for(unsigned int b=0;bmName:0; - - aiNode *theNode = find?pScene->mRootNode->FindNode(*find):0; - std::pair push_pair(static_cast(meshes.size()),theNode); - - mSubMeshIndices[a].push_back(push_pair); - meshes.push_back(newMeshes[b].first); - - out+=newMeshes[b].first->mNumBones; - } - - if(!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_INFO_F("Removed %u bones. Input bones:", in - out, ". Output bones: ", out); - } - - // and destroy the source mesh. It should be completely contained inside the new submeshes - delete srcMesh; - } - else { - // Mesh is kept unchanged - store it's new place in the mesh array - mSubMeshIndices[a].push_back(std::pair(static_cast(meshes.size()),(aiNode*)0)); - meshes.push_back(srcMesh); - } - } - - // rebuild the scene's mesh array - pScene->mNumMeshes = static_cast(meshes.size()); - delete [] pScene->mMeshes; - pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; - std::copy( meshes.begin(), meshes.end(), pScene->mMeshes); - - // recurse through all nodes and translate the node's mesh indices to fit the new mesh array - UpdateNode( pScene->mRootNode); - } - - ASSIMP_LOG_DEBUG("DeboneProcess end"); -} - -// ------------------------------------------------------------------------------------------------ -// Counts bones total/removable in a given mesh. -bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh) -{ - if(!pMesh->HasBones()) { - return false; - } - - bool split = false; - - //interstitial faces not permitted - bool isInterstitialRequired = false; - - std::vector isBoneNecessary(pMesh->mNumBones,false); - std::vector vertexBones(pMesh->mNumVertices,UINT_MAX); - - const unsigned int cUnowned = UINT_MAX; - const unsigned int cCoowned = UINT_MAX-1; - - for(unsigned int i=0;imNumBones;i++) { - for(unsigned int j=0;jmBones[i]->mNumWeights;j++) { - float w = pMesh->mBones[i]->mWeights[j].mWeight; - - if(w==0.0f) { - continue; - } - - unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId; - if(w>=mThreshold) { - - if(vertexBones[vid]!=cUnowned) { - if(vertexBones[vid]==i) //double entry - { - ASSIMP_LOG_WARN("Encountered double entry in bone weights"); - } - else //TODO: track attraction in order to break tie - { - vertexBones[vid] = cCoowned; - } - } - else vertexBones[vid] = i; - } - - if(!isBoneNecessary[i]) { - isBoneNecessary[i] = wmNumFaces;i++) { - unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]]; - - for(unsigned int j=1;jmFaces[i].mNumIndices;j++) { - unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]]; - - if(v!=w) { - if(vmNumBones) isBoneNecessary[v] = true; - if(wmNumBones) isBoneNecessary[w] = true; - } - } - } - } - - for(unsigned int i=0;imNumBones;i++) { - if(!isBoneNecessary[i]) { - mNumBonesCanDoWithout++; - split = true; - } - - mNumBones++; - } - return split; -} - -// ------------------------------------------------------------------------------------------------ -// Splits the given mesh by bone count. -void DeboneProcess::SplitMesh( const aiMesh* pMesh, std::vector< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const -{ - // same deal here as ConsiderMesh basically - - std::vector isBoneNecessary(pMesh->mNumBones,false); - std::vector vertexBones(pMesh->mNumVertices,UINT_MAX); - - const unsigned int cUnowned = UINT_MAX; - const unsigned int cCoowned = UINT_MAX-1; - - for(unsigned int i=0;imNumBones;i++) { - for(unsigned int j=0;jmBones[i]->mNumWeights;j++) { - float w = pMesh->mBones[i]->mWeights[j].mWeight; - - if(w==0.0f) { - continue; - } - - unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId; - - if(w>=mThreshold) { - if(vertexBones[vid]!=cUnowned) { - if(vertexBones[vid]==i) //double entry - { - ASSIMP_LOG_WARN("Encountered double entry in bone weights"); - } - else //TODO: track attraction in order to break tie - { - vertexBones[vid] = cCoowned; - } - } - else vertexBones[vid] = i; - } - - if(!isBoneNecessary[i]) { - isBoneNecessary[i] = w faceBones(pMesh->mNumFaces,UINT_MAX); - std::vector facesPerBone(pMesh->mNumBones,0); - - for(unsigned int i=0;imNumFaces;i++) { - unsigned int nInterstitial = 1; - - unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]]; - - for(unsigned int j=1;jmFaces[i].mNumIndices;j++) { - unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]]; - - if(v!=w) { - if(vmNumBones) isBoneNecessary[v] = true; - if(wmNumBones) isBoneNecessary[w] = true; - } - else nInterstitial++; - } - - if(vmNumBones &&nInterstitial==pMesh->mFaces[i].mNumIndices) { - faceBones[i] = v; //primitive belongs to bone #v - facesPerBone[v]++; - } - else nFacesUnowned++; - } - - // invalidate any "cojoined" faces - for(unsigned int i=0;imNumFaces;i++) { - if(faceBones[i]mNumBones&&isBoneNecessary[faceBones[i]]) - { - ai_assert(facesPerBone[faceBones[i]]>0); - facesPerBone[faceBones[i]]--; - - nFacesUnowned++; - faceBones[i] = cUnowned; - } - } - - if(nFacesUnowned) { - std::vector subFaces; - - for(unsigned int i=0;imNumFaces;i++) { - if(faceBones[i]==cUnowned) { - subFaces.push_back(i); - } - } - - aiMesh *baseMesh = MakeSubmesh(pMesh,subFaces,0); - std::pair push_pair(baseMesh,(const aiBone*)0); - - poNewMeshes.push_back(push_pair); - } - - for(unsigned int i=0;imNumBones;i++) { - - if(!isBoneNecessary[i]&&facesPerBone[i]>0) { - std::vector subFaces; - - for(unsigned int j=0;jmNumFaces;j++) { - if(faceBones[j]==i) { - subFaces.push_back(j); - } - } - - unsigned int f = AI_SUBMESH_FLAGS_SANS_BONES; - aiMesh *subMesh =MakeSubmesh(pMesh,subFaces,f); - - //Lifted from PretransformVertices.cpp - ApplyTransform(subMesh,pMesh->mBones[i]->mOffsetMatrix); - std::pair push_pair(subMesh,pMesh->mBones[i]); - - poNewMeshes.push_back(push_pair); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Recursively updates the node's mesh list to account for the changed mesh list -void DeboneProcess::UpdateNode(aiNode* pNode) const -{ - // rebuild the node's mesh index list - - std::vector newMeshList; - - // this will require two passes - - unsigned int m = static_cast(pNode->mNumMeshes), n = static_cast(mSubMeshIndices.size()); - - // first pass, look for meshes which have not moved - - for(unsigned int a=0;amMeshes[a]; - const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[srcIndex]; - unsigned int nSubmeshes = static_cast(subMeshes.size()); - - for(unsigned int b=0;b > &subMeshes = mSubMeshIndices[a]; - unsigned int nSubmeshes = static_cast(subMeshes.size()); - - for(unsigned int b=0;bmNumMeshes > 0 ) { - delete [] pNode->mMeshes; pNode->mMeshes = NULL; - } - - pNode->mNumMeshes = static_cast(newMeshList.size()); - - if(pNode->mNumMeshes) { - pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; - std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes); - } - - // do that also recursively for all children - for( unsigned int a = 0; a < pNode->mNumChildren; ++a ) { - UpdateNode( pNode->mChildren[a]); - } -} - -// ------------------------------------------------------------------------------------------------ -// Apply the node transformation to a mesh -void DeboneProcess::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const -{ - // Check whether we need to transform the coordinates at all - if (!mat.IsIdentity()) { - - if (mesh->HasPositions()) { - for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { - mesh->mVertices[i] = mat * mesh->mVertices[i]; - } - } - if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) { - aiMatrix4x4 mWorldIT = mat; - mWorldIT.Inverse().Transpose(); - - // TODO: implement Inverse() for aiMatrix3x3 - aiMatrix3x3 m = aiMatrix3x3(mWorldIT); - - if (mesh->HasNormals()) { - for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { - mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize(); - } - } - if (mesh->HasTangentsAndBitangents()) { - for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { - mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize(); - mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize(); - } - } - } - } -} diff --git a/thirdparty/assimp/code/DeboneProcess.h b/thirdparty/assimp/code/DeboneProcess.h deleted file mode 100644 index ba77aba70e..0000000000 --- a/thirdparty/assimp/code/DeboneProcess.h +++ /dev/null @@ -1,134 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** Defines a post processing step to limit the number of bones affecting a single vertex. */ -#ifndef AI_DEBONEPROCESS_H_INC -#define AI_DEBONEPROCESS_H_INC - -#include -#include -#include "BaseProcess.h" - -#include -#include - -class DeboneTest; - -namespace Assimp -{ - -#if (!defined AI_DEBONE_THRESHOLD) -# define AI_DEBONE_THRESHOLD 1.0f -#endif // !! AI_DEBONE_THRESHOLD - -// --------------------------------------------------------------------------- -/** This post processing step removes bones nearly losslessly or according to -* a configured threshold. In order to remove the bone, the primitives affected by -* the bone are split from the mesh. The split off (new) mesh is boneless. At any -* point in time, bones without affect upon a given mesh are to be removed. -*/ -class DeboneProcess : public BaseProcess -{ -public: - - DeboneProcess(); - ~DeboneProcess(); - -public: - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag. - * @param pFlags The processing flags the importer was called with. - * A bitwise combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, - * false if not. - */ - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - /** Called prior to ExecuteOnScene(). - * The function is a request to the process to update its configuration - * basing on the Importer's configuration property list. - */ - void SetupProperties(const Importer* pImp); - -protected: - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - /** Counts bones total/removable in a given mesh. - * @param pMesh The mesh to process. - */ - bool ConsiderMesh( const aiMesh* pMesh); - - /// Splits the given mesh by bone count. - /// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split. - /// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary. - void SplitMesh(const aiMesh* pMesh, std::vector< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const; - - /// Recursively updates the node's mesh list to account for the changed mesh list - void UpdateNode(aiNode* pNode) const; - - // ------------------------------------------------------------------- - // Apply transformation to a mesh - void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const; - -public: - /** Number of bones present in the scene. */ - unsigned int mNumBones; - unsigned int mNumBonesCanDoWithout; - - float mThreshold; - bool mAllOrNone; - - /// Per mesh index: Array of indices of the new submeshes. - std::vector< std::vector< std::pair< unsigned int,aiNode* > > > mSubMeshIndices; -}; - -} // end of namespace Assimp - -#endif // AI_DEBONEPROCESS_H_INC diff --git a/thirdparty/assimp/code/DefaultIOStream.cpp b/thirdparty/assimp/code/DefaultIOStream.cpp deleted file mode 100644 index 1c100b6189..0000000000 --- a/thirdparty/assimp/code/DefaultIOStream.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ -/** @file DefaultIOStream.cpp - * @brief Default File I/O implementation for #Importer - */ - - -#include -#include -#include -#include - -using namespace Assimp; - -// ---------------------------------------------------------------------------------- -DefaultIOStream::~DefaultIOStream() -{ - if (mFile) { - ::fclose(mFile); - mFile = nullptr; - } -} - -// ---------------------------------------------------------------------------------- -size_t DefaultIOStream::Read(void* pvBuffer, - size_t pSize, - size_t pCount) -{ - ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount); - return (mFile ? ::fread(pvBuffer, pSize, pCount, mFile) : 0); -} - -// ---------------------------------------------------------------------------------- -size_t DefaultIOStream::Write(const void* pvBuffer, - size_t pSize, - size_t pCount) -{ - ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount); - return (mFile ? ::fwrite(pvBuffer, pSize, pCount, mFile) : 0); -} - -// ---------------------------------------------------------------------------------- -aiReturn DefaultIOStream::Seek(size_t pOffset, - aiOrigin pOrigin) -{ - if (!mFile) { - return AI_FAILURE; - } - - // Just to check whether our enum maps one to one with the CRT constants - static_assert(aiOrigin_CUR == SEEK_CUR && - aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET, "aiOrigin_CUR == SEEK_CUR && \ - aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET"); - - // do the seek - return (0 == ::fseek(mFile, (long)pOffset,(int)pOrigin) ? AI_SUCCESS : AI_FAILURE); -} - -// ---------------------------------------------------------------------------------- -size_t DefaultIOStream::Tell() const -{ - if (!mFile) { - return 0; - } - return ::ftell(mFile); -} - -// ---------------------------------------------------------------------------------- -size_t DefaultIOStream::FileSize() const -{ - if (! mFile || mFilename.empty()) { - return 0; - } - - if (SIZE_MAX == mCachedSize ) { - - // Although fseek/ftell would allow us to reuse the existing file handle here, - // it is generally unsafe because: - // - For binary streams, it is not technically well-defined - // - For text files the results are meaningless - // That's why we use the safer variant fstat here. - // - // See here for details: - // https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file -#if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) - struct __stat64 fileStat; - //using fileno + fstat avoids having to handle the filename - int err = _fstat64( _fileno(mFile), &fileStat ); - if (0 != err) - return 0; - mCachedSize = (size_t) (fileStat.st_size); -#elif defined __GNUC__ || defined __APPLE__ || defined __MACH__ || defined __FreeBSD__ - struct stat fileStat; - int err = stat(mFilename.c_str(), &fileStat ); - if (0 != err) - return 0; - const unsigned long long cachedSize = fileStat.st_size; - mCachedSize = static_cast< size_t >( cachedSize ); -#else -# error "Unknown platform" -#endif - } - return mCachedSize; -} - -// ---------------------------------------------------------------------------------- -void DefaultIOStream::Flush() -{ - if (mFile) { - ::fflush(mFile); - } -} - -// ---------------------------------------------------------------------------------- diff --git a/thirdparty/assimp/code/DefaultIOSystem.cpp b/thirdparty/assimp/code/DefaultIOSystem.cpp deleted file mode 100644 index d40b67de32..0000000000 --- a/thirdparty/assimp/code/DefaultIOSystem.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ -/** @file Default implementation of IOSystem using the standard C file functions */ - -#include - -#include -#include -#include -#include -#include - -#ifdef __unix__ -#include -#include -#endif - -#ifdef _WIN32 -#include -#endif - -using namespace Assimp; - -// maximum path length -// XXX http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html -#ifdef PATH_MAX -# define PATHLIMIT PATH_MAX -#else -# define PATHLIMIT 4096 -#endif - -// ------------------------------------------------------------------------------------------------ -// Tests for the existence of a file at the given path. -bool DefaultIOSystem::Exists( const char* pFile) const -{ -#ifdef _WIN32 - wchar_t fileName16[PATHLIMIT]; - -#ifndef WindowsStore - bool isUnicode = IsTextUnicode(pFile, static_cast(strlen(pFile)), NULL) != 0; - if (isUnicode) { - - MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT); - struct __stat64 filestat; - if (0 != _wstat64(fileName16, &filestat)) { - return false; - } - } else { -#endif - FILE* file = ::fopen(pFile, "rb"); - if (!file) - return false; - - ::fclose(file); -#ifndef WindowsStore - } -#endif -#else - FILE* file = ::fopen( pFile, "rb"); - if( !file) - return false; - - ::fclose( file); -#endif - return true; -} - -// ------------------------------------------------------------------------------------------------ -// Open a new file with a given path. -IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode) -{ - ai_assert(NULL != strFile); - ai_assert(NULL != strMode); - FILE* file; -#ifdef _WIN32 - wchar_t fileName16[PATHLIMIT]; -#ifndef WindowsStore - bool isUnicode = IsTextUnicode(strFile, static_cast(strlen(strFile)), NULL) != 0; - if (isUnicode) { - MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT); - std::string mode8(strMode); - file = ::_wfopen(fileName16, std::wstring(mode8.begin(), mode8.end()).c_str()); - } else { -#endif - file = ::fopen(strFile, strMode); -#ifndef WindowsStore - } -#endif -#else - file = ::fopen(strFile, strMode); -#endif - if (nullptr == file) - return nullptr; - - return new DefaultIOStream(file, (std::string) strFile); -} - -// ------------------------------------------------------------------------------------------------ -// Closes the given file and releases all resources associated with it. -void DefaultIOSystem::Close( IOStream* pFile) -{ - delete pFile; -} - -// ------------------------------------------------------------------------------------------------ -// Returns the operation specific directory separator -char DefaultIOSystem::getOsSeparator() const -{ -#ifndef _WIN32 - return '/'; -#else - return '\\'; -#endif -} - -// ------------------------------------------------------------------------------------------------ -// IOSystem default implementation (ComparePaths isn't a pure virtual function) -bool IOSystem::ComparePaths (const char* one, const char* second) const -{ - return !ASSIMP_stricmp(one,second); -} - -// ------------------------------------------------------------------------------------------------ -// Convert a relative path into an absolute path -inline static void MakeAbsolutePath (const char* in, char* _out) -{ - ai_assert(in && _out); -#if defined( _MSC_VER ) || defined( __MINGW32__ ) -#ifndef WindowsStore - bool isUnicode = IsTextUnicode(in, static_cast(strlen(in)), NULL) != 0; - if (isUnicode) { - wchar_t out16[PATHLIMIT]; - wchar_t in16[PATHLIMIT]; - MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, in, -1, out16, PATHLIMIT); - wchar_t* ret = ::_wfullpath(out16, in16, PATHLIMIT); - if (ret) { - WideCharToMultiByte(CP_UTF8, MB_PRECOMPOSED, out16, -1, _out, PATHLIMIT, nullptr, nullptr); - } - if (!ret) { - // preserve the input path, maybe someone else is able to fix - // the path before it is accessed (e.g. our file system filter) - ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in)); - strcpy(_out, in); - } - - } else { -#endif - char* ret = :: _fullpath(_out, in, PATHLIMIT); - if (!ret) { - // preserve the input path, maybe someone else is able to fix - // the path before it is accessed (e.g. our file system filter) - ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in)); - strcpy(_out, in); - } -#ifndef WindowsStore - } -#endif -#else - // use realpath - char* ret = realpath(in, _out); - if(!ret) { - // preserve the input path, maybe someone else is able to fix - // the path before it is accessed (e.g. our file system filter) - ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in)); - strcpy(_out,in); - } -#endif -} - -// ------------------------------------------------------------------------------------------------ -// DefaultIOSystem's more specialized implementation -bool DefaultIOSystem::ComparePaths (const char* one, const char* second) const -{ - // chances are quite good both paths are formatted identically, - // so we can hopefully return here already - if( !ASSIMP_stricmp(one,second) ) - return true; - - char temp1[PATHLIMIT]; - char temp2[PATHLIMIT]; - - MakeAbsolutePath (one, temp1); - MakeAbsolutePath (second, temp2); - - return !ASSIMP_stricmp(temp1,temp2); -} - -// ------------------------------------------------------------------------------------------------ -std::string DefaultIOSystem::fileName( const std::string &path ) -{ - std::string ret = path; - std::size_t last = ret.find_last_of("\\/"); - if (last != std::string::npos) ret = ret.substr(last + 1); - return ret; -} - -// ------------------------------------------------------------------------------------------------ -std::string DefaultIOSystem::completeBaseName( const std::string &path ) -{ - std::string ret = fileName(path); - std::size_t pos = ret.find_last_of('.'); - if(pos != ret.npos) ret = ret.substr(0, pos); - return ret; -} - -// ------------------------------------------------------------------------------------------------ -std::string DefaultIOSystem::absolutePath( const std::string &path ) -{ - std::string ret = path; - std::size_t last = ret.find_last_of("\\/"); - if (last != std::string::npos) ret = ret.substr(0, last); - return ret; -} - -// ------------------------------------------------------------------------------------------------ - -#undef PATHLIMIT diff --git a/thirdparty/assimp/code/DefaultLogger.cpp b/thirdparty/assimp/code/DefaultLogger.cpp deleted file mode 100644 index de3528d2b4..0000000000 --- a/thirdparty/assimp/code/DefaultLogger.cpp +++ /dev/null @@ -1,418 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file DefaultLogger.cpp - * @brief Implementation of DefaultLogger (and Logger) - */ - -// Default log streams -#include "Win32DebugLogStream.h" -#include "StdOStreamLogStream.h" -#include "FileLogStream.h" -#include - -#include -#include -#include -#include -#include -#include - -#ifndef ASSIMP_BUILD_SINGLETHREADED -# include -# include - std::mutex loggerMutex; -#endif - -namespace Assimp { - -// ---------------------------------------------------------------------------------- -NullLogger DefaultLogger::s_pNullLogger; -Logger *DefaultLogger::m_pLogger = &DefaultLogger::s_pNullLogger; - -static const unsigned int SeverityAll = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging; - -// ---------------------------------------------------------------------------------- -// Represents a log-stream + its error severity -struct LogStreamInfo { - unsigned int m_uiErrorSeverity; - LogStream *m_pStream; - - // Constructor - LogStreamInfo( unsigned int uiErrorSev, LogStream *pStream ) : - m_uiErrorSeverity( uiErrorSev ), - m_pStream( pStream ) { - // empty - } - - // Destructor - ~LogStreamInfo() { - delete m_pStream; - } -}; - -// ---------------------------------------------------------------------------------- -// Construct a default log stream -LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams, - const char* name /*= "AssimpLog.txt"*/, - IOSystem* io /*= NULL*/) -{ - switch (streams) - { - // This is a platform-specific feature - case aiDefaultLogStream_DEBUGGER: -#ifdef WIN32 - return new Win32DebugLogStream(); -#else - return nullptr; -#endif - - // Platform-independent default streams - case aiDefaultLogStream_STDERR: - return new StdOStreamLogStream(std::cerr); - case aiDefaultLogStream_STDOUT: - return new StdOStreamLogStream(std::cout); - case aiDefaultLogStream_FILE: - return (name && *name ? new FileLogStream(name,io) : nullptr ); - default: - // We don't know this default log stream, so raise an assertion - ai_assert(false); - - }; - - // For compilers without dead code path detection - return NULL; -} - -// ---------------------------------------------------------------------------------- -// Creates the only singleton instance -Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/, - LogSeverity severity /*= NORMAL*/, - unsigned int defStreams /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/, - IOSystem* io /*= NULL*/) { - // enter the mutex here to avoid concurrency problems -#ifndef ASSIMP_BUILD_SINGLETHREADED - std::lock_guard lock(loggerMutex); -#endif - - if ( m_pLogger && !isNullLogger() ) { - delete m_pLogger; - } - - m_pLogger = new DefaultLogger( severity ); - - // Attach default log streams - // Stream the log to the MSVC debugger? - if ( defStreams & aiDefaultLogStream_DEBUGGER ) { - m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_DEBUGGER ) ); - } - - // Stream the log to COUT? - if ( defStreams & aiDefaultLogStream_STDOUT ) { - m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDOUT ) ); - } - - // Stream the log to CERR? - if ( defStreams & aiDefaultLogStream_STDERR ) { - m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDERR ) ); - } - - // Stream the log to a file - if ( defStreams & aiDefaultLogStream_FILE && name && *name ) { - m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_FILE, name, io ) ); - } - - return m_pLogger; -} - -// ---------------------------------------------------------------------------------- -void Logger::debug(const char* message) { - - // SECURITY FIX: otherwise it's easy to produce overruns since - // sometimes importers will include data from the input file - // (i.e. node names) in their messages. - if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { - return; - } - return OnDebug(message); -} - -// ---------------------------------------------------------------------------------- -void Logger::info(const char* message) { - - // SECURITY FIX: see above - if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { - return; - } - return OnInfo(message); -} - -// ---------------------------------------------------------------------------------- -void Logger::warn(const char* message) { - - // SECURITY FIX: see above - if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { - return; - } - return OnWarn(message); -} - -// ---------------------------------------------------------------------------------- -void Logger::error(const char* message) { - // SECURITY FIX: see above - if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { - return; - } - return OnError(message); -} - -// ---------------------------------------------------------------------------------- -void DefaultLogger::set( Logger *logger ) { - // enter the mutex here to avoid concurrency problems -#ifndef ASSIMP_BUILD_SINGLETHREADED - std::lock_guard lock(loggerMutex); -#endif - - if ( nullptr == logger ) { - logger = &s_pNullLogger; - } - if ( nullptr != m_pLogger && !isNullLogger() ) { - delete m_pLogger; - } - - DefaultLogger::m_pLogger = logger; -} - -// ---------------------------------------------------------------------------------- -bool DefaultLogger::isNullLogger() { - return m_pLogger == &s_pNullLogger; -} - -// ---------------------------------------------------------------------------------- -Logger *DefaultLogger::get() { - return m_pLogger; -} - -// ---------------------------------------------------------------------------------- -// Kills the only instance -void DefaultLogger::kill() { - // enter the mutex here to avoid concurrency problems -#ifndef ASSIMP_BUILD_SINGLETHREADED - std::lock_guard lock(loggerMutex); -#endif - - if ( m_pLogger == &s_pNullLogger ) { - return; - } - delete m_pLogger; - m_pLogger = &s_pNullLogger; -} - -// ---------------------------------------------------------------------------------- -// Debug message -void DefaultLogger::OnDebug( const char* message ) { - if ( m_Severity == Logger::NORMAL ) { - return; - } - - static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; - char msg[Size]; - ai_snprintf(msg, Size, "Debug, T%u: %s", GetThreadID(), message); - - WriteToStreams( msg, Logger::Debugging ); -} - -// ---------------------------------------------------------------------------------- -// Logs an info -void DefaultLogger::OnInfo( const char* message ){ - static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; - char msg[Size]; - ai_snprintf(msg, Size, "Info, T%u: %s", GetThreadID(), message ); - - WriteToStreams( msg , Logger::Info ); -} - -// ---------------------------------------------------------------------------------- -// Logs a warning -void DefaultLogger::OnWarn( const char* message ) { - static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; - char msg[Size]; - ai_snprintf(msg, Size, "Warn, T%u: %s", GetThreadID(), message ); - - WriteToStreams( msg, Logger::Warn ); -} - -// ---------------------------------------------------------------------------------- -// Logs an error -void DefaultLogger::OnError( const char* message ) { - static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; - char msg[ Size ]; - ai_snprintf(msg, Size, "Error, T%u: %s", GetThreadID(), message ); - - WriteToStreams( msg, Logger::Err ); -} - -// ---------------------------------------------------------------------------------- -// Will attach a new stream -bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) { - if ( nullptr == pStream ) { - return false; - } - - if (0 == severity) { - severity = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging; - } - - for ( StreamIt it = m_StreamArray.begin(); - it != m_StreamArray.end(); - ++it ) - { - if ( (*it)->m_pStream == pStream ) { - (*it)->m_uiErrorSeverity |= severity; - return true; - } - } - - LogStreamInfo *pInfo = new LogStreamInfo( severity, pStream ); - m_StreamArray.push_back( pInfo ); - return true; -} - -// ---------------------------------------------------------------------------------- -// Detach a stream -bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) { - if ( nullptr == pStream ) { - return false; - } - - if (0 == severity) { - severity = SeverityAll; - } - - bool res( false ); - for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) { - if ( (*it)->m_pStream == pStream ) { - (*it)->m_uiErrorSeverity &= ~severity; - if ( (*it)->m_uiErrorSeverity == 0 ) { - // don't delete the underlying stream 'cause the caller gains ownership again - (**it).m_pStream = nullptr; - delete *it; - m_StreamArray.erase( it ); - res = true; - break; - } - return true; - } - } - return res; -} - -// ---------------------------------------------------------------------------------- -// Constructor -DefaultLogger::DefaultLogger(LogSeverity severity) - : Logger ( severity ) - , noRepeatMsg (false) - , lastLen( 0 ) { - lastMsg[0] = '\0'; -} - -// ---------------------------------------------------------------------------------- -// Destructor -DefaultLogger::~DefaultLogger() { - for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) { - // also frees the underlying stream, we are its owner. - delete *it; - } -} - -// ---------------------------------------------------------------------------------- -// Writes message to stream -void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev ) { - ai_assert(nullptr != message); - - // Check whether this is a repeated message - if (! ::strncmp( message,lastMsg, lastLen-1)) - { - if (!noRepeatMsg) - { - noRepeatMsg = true; - message = "Skipping one or more lines with the same contents\n"; - } - else return; - } - else - { - // append a new-line character to the message to be printed - lastLen = ::strlen(message); - ::memcpy(lastMsg,message,lastLen+1); - ::strcat(lastMsg+lastLen,"\n"); - - message = lastMsg; - noRepeatMsg = false; - ++lastLen; - } - for ( ConstStreamIt it = m_StreamArray.begin(); - it != m_StreamArray.end(); - ++it) - { - if ( ErrorSev & (*it)->m_uiErrorSeverity ) - (*it)->m_pStream->write( message); - } -} - -// ---------------------------------------------------------------------------------- -// Returns thread id, if not supported only a zero will be returned. -unsigned int DefaultLogger::GetThreadID() -{ - // fixme: we can get this value via std::threads - // std::this_thread::get_id().hash() returns a (big) size_t, not sure if this is useful in this case. -#ifdef WIN32 - return (unsigned int)::GetCurrentThreadId(); -#else - return 0; // not supported -#endif -} - -// ---------------------------------------------------------------------------------- - -} // !namespace Assimp diff --git a/thirdparty/assimp/code/DefaultProgressHandler.h b/thirdparty/assimp/code/DefaultProgressHandler.h deleted file mode 100644 index bd2cce00be..0000000000 --- a/thirdparty/assimp/code/DefaultProgressHandler.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file ProgressHandler.hpp - * @brief Abstract base class 'ProgressHandler'. - */ -#ifndef INCLUDED_AI_DEFAULTPROGRESSHANDLER_H -#define INCLUDED_AI_DEFAULTPROGRESSHANDLER_H - -#include - -namespace Assimp { - -// ------------------------------------------------------------------------------------ -/** @brief Internal default implementation of the #ProgressHandler interface. */ -class DefaultProgressHandler : public ProgressHandler { - - virtual bool Update(float /*percentage*/) { - return false; - } - - -}; // !class DefaultProgressHandler -} // Namespace Assimp - -#endif diff --git a/thirdparty/assimp/code/DropFaceNormalsProcess.cpp b/thirdparty/assimp/code/DropFaceNormalsProcess.cpp deleted file mode 100644 index b11615bb82..0000000000 --- a/thirdparty/assimp/code/DropFaceNormalsProcess.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of the post processing step to drop face -* normals for all imported faces. -*/ - - -#include "DropFaceNormalsProcess.h" -#include -#include -#include -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -DropFaceNormalsProcess::DropFaceNormalsProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -DropFaceNormalsProcess::~DropFaceNormalsProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool DropFaceNormalsProcess::IsActive( unsigned int pFlags) const { - return (pFlags & aiProcess_DropNormals) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void DropFaceNormalsProcess::Execute( aiScene* pScene) { - ASSIMP_LOG_DEBUG("DropFaceNormalsProcess begin"); - - if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { - throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); - } - - bool bHas = false; - for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { - bHas |= this->DropMeshFaceNormals( pScene->mMeshes[a]); - } - if (bHas) { - ASSIMP_LOG_INFO("DropFaceNormalsProcess finished. " - "Face normals have been removed"); - } else { - ASSIMP_LOG_DEBUG("DropFaceNormalsProcess finished. " - "No normals were present"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* pMesh) { - if (NULL == pMesh->mNormals) { - return false; - } - - delete[] pMesh->mNormals; - pMesh->mNormals = nullptr; - return true; -} diff --git a/thirdparty/assimp/code/DropFaceNormalsProcess.h b/thirdparty/assimp/code/DropFaceNormalsProcess.h deleted file mode 100644 index 0d116663b7..0000000000 --- a/thirdparty/assimp/code/DropFaceNormalsProcess.h +++ /dev/null @@ -1,86 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a post processing step to compute face normals for all loaded faces*/ -#ifndef AI_DROPFACENORMALPROCESS_H_INC -#define AI_DROPFACENORMALPROCESS_H_INC - -#include "BaseProcess.h" -#include - -namespace Assimp -{ - -// --------------------------------------------------------------------------- -/** The DropFaceNormalsProcess computes face normals for all faces of all meshes -*/ -class ASSIMP_API_WINONLY DropFaceNormalsProcess : public BaseProcess -{ -public: - - DropFaceNormalsProcess(); - ~DropFaceNormalsProcess(); - -public: - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag field. - * @param pFlags The processing flags the importer was called with. A bitwise - * combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, false if not. - */ - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - - -private: - bool DropMeshFaceNormals(aiMesh* pcMesh); -}; - -} // end of namespace Assimp - -#endif // !!AI_DROPFACENORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/EmbedTexturesProcess.cpp b/thirdparty/assimp/code/EmbedTexturesProcess.cpp deleted file mode 100644 index 739382a057..0000000000 --- a/thirdparty/assimp/code/EmbedTexturesProcess.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -#include "EmbedTexturesProcess.h" -#include -#include "ProcessHelper.h" - -#include - -using namespace Assimp; - -EmbedTexturesProcess::EmbedTexturesProcess() -: BaseProcess() { -} - -EmbedTexturesProcess::~EmbedTexturesProcess() { -} - -bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const { - return (pFlags & aiProcess_EmbedTextures) != 0; -} - -void EmbedTexturesProcess::SetupProperties(const Importer* pImp) { - mRootPath = pImp->GetPropertyString("sourceFilePath"); - mRootPath = mRootPath.substr(0, mRootPath.find_last_of("\\/") + 1u); -} - -void EmbedTexturesProcess::Execute(aiScene* pScene) { - if (pScene == nullptr || pScene->mRootNode == nullptr) return; - - aiString path; - - uint32_t embeddedTexturesCount = 0u; - - for (auto matId = 0u; matId < pScene->mNumMaterials; ++matId) { - auto material = pScene->mMaterials[matId]; - - for (auto ttId = 1u; ttId < AI_TEXTURE_TYPE_MAX; ++ttId) { - auto tt = static_cast(ttId); - auto texturesCount = material->GetTextureCount(tt); - - for (auto texId = 0u; texId < texturesCount; ++texId) { - material->GetTexture(tt, texId, &path); - if (path.data[0] == '*') continue; // Already embedded - - // Indeed embed - if (addTexture(pScene, path.data)) { - auto embeddedTextureId = pScene->mNumTextures - 1u; - ::ai_snprintf(path.data, 1024, "*%u", embeddedTextureId); - material->AddProperty(&path, AI_MATKEY_TEXTURE(tt, texId)); - embeddedTexturesCount++; - } - } - } - } - - ASSIMP_LOG_INFO_F("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." ); -} - -bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { - std::streampos imageSize = 0; - std::string imagePath = path; - - // Test path directly - std::ifstream file(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { - ASSIMP_LOG_WARN_F("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder."); - - // Test path in root path - imagePath = mRootPath + path; - file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { - // Test path basename in root path - imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u); - file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { - ASSIMP_LOG_ERROR_F("EmbedTexturesProcess: Unable to embed texture: ", path, "."); - return false; - } - } - } - - aiTexel* imageContent = new aiTexel[ 1ul + static_cast( imageSize ) / sizeof(aiTexel)]; - file.seekg(0, std::ios::beg); - file.read(reinterpret_cast(imageContent), imageSize); - - // Enlarging the textures table - unsigned int textureId = pScene->mNumTextures++; - auto oldTextures = pScene->mTextures; - pScene->mTextures = new aiTexture*[pScene->mNumTextures]; - ::memmove(pScene->mTextures, oldTextures, sizeof(aiTexture*) * (pScene->mNumTextures - 1u)); - - // Add the new texture - auto pTexture = new aiTexture; - pTexture->mHeight = 0; // Means that this is still compressed - pTexture->mWidth = static_cast(imageSize); - pTexture->pcData = imageContent; - - auto extension = path.substr(path.find_last_of('.') + 1u); - std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); - if (extension == "jpeg") { - extension = "jpg"; - } - - size_t len = extension.size(); - if (len > HINTMAXTEXTURELEN -1 ) { - len = HINTMAXTEXTURELEN - 1; - } - ::strncpy(pTexture->achFormatHint, extension.c_str(), len); - pScene->mTextures[textureId] = pTexture; - - return true; -} diff --git a/thirdparty/assimp/code/EmbedTexturesProcess.h b/thirdparty/assimp/code/EmbedTexturesProcess.h deleted file mode 100644 index cdf40bef74..0000000000 --- a/thirdparty/assimp/code/EmbedTexturesProcess.h +++ /dev/null @@ -1,85 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -#pragma once - -#include "BaseProcess.h" - -#include - -struct aiNode; - -namespace Assimp { - -/** - * Force embedding of textures (using the path = "*1" convention). - * If a texture's file does not exist at the specified path - * (due, for instance, to an absolute path generated on another system), - * it will check if a file with the same name exists at the root folder - * of the imported model. And if so, it uses that. - */ -class ASSIMP_API EmbedTexturesProcess : public BaseProcess { -public: - /// The default class constructor. - EmbedTexturesProcess(); - - /// The class destructor. - virtual ~EmbedTexturesProcess(); - - /// Overwritten, @see BaseProcess - virtual bool IsActive(unsigned int pFlags) const; - - /// Overwritten, @see BaseProcess - virtual void SetupProperties(const Importer* pImp); - - /// Overwritten, @see BaseProcess - virtual void Execute(aiScene* pScene); - -private: - // Resolve the path and add the file content to the scene as a texture. - bool addTexture(aiScene* pScene, std::string path) const; - -private: - std::string mRootPath; -}; - -} // namespace Assimp diff --git a/thirdparty/assimp/code/Exporter.cpp b/thirdparty/assimp/code/Exporter.cpp deleted file mode 100644 index 8848e87f5b..0000000000 --- a/thirdparty/assimp/code/Exporter.cpp +++ /dev/null @@ -1,648 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Exporter.cpp - -Assimp export interface. While it's public interface bears many similarities -to the import interface (in fact, it is largely symmetric), the internal -implementations differs a lot. Exporters are considered stateless and are -simple callbacks which we maintain in a global list along with their -description strings. - -Here we implement only the C++ interface (Assimp::Exporter). -*/ - -#ifndef ASSIMP_BUILD_NO_EXPORT - -#include -#include -#include -#include -#include -#include -#include - -#include "DefaultProgressHandler.h" -#include "BaseProcess.h" -#include "JoinVerticesProcess.h" -#include "MakeVerboseFormat.h" -#include "ConvertToLHProcess.h" -#include "PretransformVertices.h" -#include -#include "ScenePrivate.h" - -#include - -namespace Assimp { - -// PostStepRegistry.cpp -void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); - -// ------------------------------------------------------------------------------------------------ -// Exporter worker function prototypes. Should not be necessary to #ifndef them, it's just a prototype -// do not use const, because some exporter need to convert the scene temporary -void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneXFile(const char*,IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneStep(const char*,IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneObj(const char*,IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneObjNoMtl(const char*,IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneSTL(const char*,IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*, const ExportProperties*); -void ExportScenePly(const char*,IOSystem*, const aiScene*, const ExportProperties*); -void ExportScenePlyBinary(const char*, IOSystem*, const aiScene*, const ExportProperties*); -void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*); -void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*); -void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* ); - -// ------------------------------------------------------------------------------------------------ -// global array of all export formats which Assimp supports in its current build -Exporter::ExportFormatEntry gExporters[] = -{ -#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER - Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada ), -#endif - -#ifndef ASSIMP_BUILD_NO_X_EXPORTER - Exporter::ExportFormatEntry( "x", "X Files", "x", &ExportSceneXFile, - aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs ), -#endif - -#ifndef ASSIMP_BUILD_NO_STEP_EXPORTER - Exporter::ExportFormatEntry( "stp", "Step Files", "stp", &ExportSceneStep, 0 ), -#endif - -#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER - Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj, - aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ), - Exporter::ExportFormatEntry( "objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl, - aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ), -#endif - -#ifndef ASSIMP_BUILD_NO_STL_EXPORTER - Exporter::ExportFormatEntry( "stl", "Stereolithography", "stl" , &ExportSceneSTL, - aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices - ), - Exporter::ExportFormatEntry( "stlb", "Stereolithography (binary)", "stl" , &ExportSceneSTLBinary, - aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices - ), -#endif - -#ifndef ASSIMP_BUILD_NO_PLY_EXPORTER - Exporter::ExportFormatEntry( "ply", "Stanford Polygon Library", "ply" , &ExportScenePly, - aiProcess_PreTransformVertices - ), - Exporter::ExportFormatEntry( "plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary, - aiProcess_PreTransformVertices - ), -#endif - -#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER - Exporter::ExportFormatEntry( "3ds", "Autodesk 3DS (legacy)", "3ds" , &ExportScene3DS, - aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices ), -#endif - -#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER - Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2, - aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), - Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2, - aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), - Exporter::ExportFormatEntry( "gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF, - aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), - Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB, - aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), -#endif - -#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER - Exporter::ExportFormatEntry( "assbin", "Assimp Binary", "assbin" , &ExportSceneAssbin, 0 ), -#endif - -#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER - Exporter::ExportFormatEntry( "assxml", "Assxml Document", "assxml" , &ExportSceneAssxml, 0 ), -#endif - -#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER - Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0 ), -#endif - -#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER - Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ), - Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ), -#endif - -#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER - Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ) -#endif -}; - -#define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0])) - - -class ExporterPimpl { -public: - ExporterPimpl() - : blob() - , mIOSystem(new Assimp::DefaultIOSystem()) - , mIsDefaultIOHandler(true) - , mProgressHandler( nullptr ) - , mIsDefaultProgressHandler( true ) - , mPostProcessingSteps() - , mError() - , mExporters() { - GetPostProcessingStepInstanceList(mPostProcessingSteps); - - // grab all built-in exporters - if ( 0 != ( ASSIMP_NUM_EXPORTERS ) ) { - mExporters.resize( ASSIMP_NUM_EXPORTERS ); - std::copy( gExporters, gExporters + ASSIMP_NUM_EXPORTERS, mExporters.begin() ); - } - } - - ~ExporterPimpl() { - delete blob; - - // Delete all post-processing plug-ins - for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) { - delete mPostProcessingSteps[a]; - } - delete mProgressHandler; - } - -public: - aiExportDataBlob* blob; - std::shared_ptr< Assimp::IOSystem > mIOSystem; - bool mIsDefaultIOHandler; - - /** The progress handler */ - ProgressHandler *mProgressHandler; - bool mIsDefaultProgressHandler; - - /** Post processing steps we can apply at the imported data. */ - std::vector< BaseProcess* > mPostProcessingSteps; - - /** Last fatal export error */ - std::string mError; - - /** Exporters, this includes those registered using #Assimp::Exporter::RegisterExporter */ - std::vector mExporters; -}; - -} // end of namespace Assimp - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -Exporter :: Exporter() -: pimpl(new ExporterPimpl()) { - pimpl->mProgressHandler = new DefaultProgressHandler(); -} - -// ------------------------------------------------------------------------------------------------ -Exporter::~Exporter() { - FreeBlob(); - delete pimpl; -} - -// ------------------------------------------------------------------------------------------------ -void Exporter::SetIOHandler( IOSystem* pIOHandler) { - pimpl->mIsDefaultIOHandler = !pIOHandler; - pimpl->mIOSystem.reset(pIOHandler); -} - -// ------------------------------------------------------------------------------------------------ -IOSystem* Exporter::GetIOHandler() const { - return pimpl->mIOSystem.get(); -} - -// ------------------------------------------------------------------------------------------------ -bool Exporter::IsDefaultIOHandler() const { - return pimpl->mIsDefaultIOHandler; -} - -// ------------------------------------------------------------------------------------------------ -void Exporter::SetProgressHandler(ProgressHandler* pHandler) { - ai_assert(nullptr != pimpl); - - if ( nullptr == pHandler) { - // Release pointer in the possession of the caller - pimpl->mProgressHandler = new DefaultProgressHandler(); - pimpl->mIsDefaultProgressHandler = true; - return; - } - - if (pimpl->mProgressHandler == pHandler) { - return; - } - - delete pimpl->mProgressHandler; - pimpl->mProgressHandler = pHandler; - pimpl->mIsDefaultProgressHandler = false; -} - -// ------------------------------------------------------------------------------------------------ -const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId, - unsigned int, const ExportProperties* /*pProperties*/ ) { - if (pimpl->blob) { - delete pimpl->blob; - pimpl->blob = nullptr; - } - - std::shared_ptr old = pimpl->mIOSystem; - BlobIOSystem* blobio = new BlobIOSystem(); - pimpl->mIOSystem = std::shared_ptr( blobio ); - - if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName())) { - pimpl->mIOSystem = old; - return nullptr; - } - - pimpl->blob = blobio->GetBlobChain(); - pimpl->mIOSystem = old; - - return pimpl->blob; -} - -// ------------------------------------------------------------------------------------------------ -bool IsVerboseFormat(const aiMesh* mesh) { - // avoid slow vector specialization - std::vector seen(mesh->mNumVertices,0); - for(unsigned int i = 0; i < mesh->mNumFaces; ++i) { - const aiFace& f = mesh->mFaces[i]; - for(unsigned int j = 0; j < f.mNumIndices; ++j) { - if(++seen[f.mIndices[j]] == 2) { - // found a duplicate index - return false; - } - } - } - - return true; -} - -// ------------------------------------------------------------------------------------------------ -bool IsVerboseFormat(const aiScene* pScene) { - for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { - if(!IsVerboseFormat(pScene->mMeshes[i])) { - return false; - } - } - - return true; -} - -// ------------------------------------------------------------------------------------------------ -aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath, - unsigned int pPreprocessing, const ExportProperties* pProperties) { - ASSIMP_BEGIN_EXCEPTION_REGION(); - - // when they create scenes from scratch, users will likely create them not in verbose - // format. They will likely not be aware that there is a flag in the scene to indicate - // this, however. To avoid surprises and bug reports, we check for duplicates in - // meshes upfront. - const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || IsVerboseFormat(pScene); - - pimpl->mProgressHandler->UpdateFileWrite(0, 4); - - pimpl->mError = ""; - for (size_t i = 0; i < pimpl->mExporters.size(); ++i) { - const Exporter::ExportFormatEntry& exp = pimpl->mExporters[i]; - if (!strcmp(exp.mDescription.id,pFormatId)) { - try { - // Always create a full copy of the scene. We might optimize this one day, - // but for now it is the most pragmatic way. - aiScene* scenecopy_tmp = nullptr; - SceneCombiner::CopyScene(&scenecopy_tmp,pScene); - - pimpl->mProgressHandler->UpdateFileWrite(1, 4); - - std::unique_ptr scenecopy(scenecopy_tmp); - const ScenePrivateData* const priv = ScenePriv(pScene); - - // steps that are not idempotent, i.e. we might need to run them again, usually to get back to the - // original state before the step was applied first. When checking which steps we don't need - // to run, those are excluded. - const unsigned int nonIdempotentSteps = aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_MakeLeftHanded; - - // Erase all pp steps that were already applied to this scene - const unsigned int pp = (exp.mEnforcePP | pPreprocessing) & ~(priv && !priv->mIsCopy - ? (priv->mPPStepsApplied & ~nonIdempotentSteps) - : 0u); - - // If no extra post-processing was specified, and we obtained this scene from an - // Assimp importer, apply the reverse steps automatically. - // TODO: either drop this, or document it. Otherwise it is just a bad surprise. - //if (!pPreprocessing && priv) { - // pp |= (nonIdempotentSteps & priv->mPPStepsApplied); - //} - - // If the input scene is not in verbose format, but there is at least post-processing step that relies on it, - // we need to run the MakeVerboseFormat step first. - bool must_join_again = false; - if (!is_verbose_format) { - bool verbosify = false; - for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { - BaseProcess* const p = pimpl->mPostProcessingSteps[a]; - - if (p->IsActive(pp) && p->RequireVerboseFormat()) { - verbosify = true; - break; - } - } - - if (verbosify || (exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { - ASSIMP_LOG_DEBUG("export: Scene data not in verbose format, applying MakeVerboseFormat step first"); - - MakeVerboseFormatProcess proc; - proc.Execute(scenecopy.get()); - - if(!(exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { - must_join_again = true; - } - } - } - - pimpl->mProgressHandler->UpdateFileWrite(2, 4); - - if (pp) { - // the three 'conversion' steps need to be executed first because all other steps rely on the standard data layout - { - FlipWindingOrderProcess step; - if (step.IsActive(pp)) { - step.Execute(scenecopy.get()); - } - } - - { - FlipUVsProcess step; - if (step.IsActive(pp)) { - step.Execute(scenecopy.get()); - } - } - - { - MakeLeftHandedProcess step; - if (step.IsActive(pp)) { - step.Execute(scenecopy.get()); - } - } - - bool exportPointCloud(false); - if (nullptr != pProperties) { - exportPointCloud = pProperties->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); - } - - // dispatch other processes - for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { - BaseProcess* const p = pimpl->mPostProcessingSteps[a]; - - if (p->IsActive(pp) - && !dynamic_cast(p) - && !dynamic_cast(p) - && !dynamic_cast(p)) { - if (dynamic_cast(p) && exportPointCloud) { - continue; - } - p->Execute(scenecopy.get()); - } - } - ScenePrivateData* const privOut = ScenePriv(scenecopy.get()); - ai_assert(nullptr != privOut); - - privOut->mPPStepsApplied |= pp; - } - - pimpl->mProgressHandler->UpdateFileWrite(3, 4); - - if(must_join_again) { - JoinVerticesProcess proc; - proc.Execute(scenecopy.get()); - } - - ExportProperties emptyProperties; // Never pass NULL ExportProperties so Exporters don't have to worry. - exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties ? pProperties : &emptyProperties); - - pimpl->mProgressHandler->UpdateFileWrite(4, 4); - } catch (DeadlyExportError& err) { - pimpl->mError = err.what(); - return AI_FAILURE; - } - return AI_SUCCESS; - } - } - - pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId; - ASSIMP_END_EXCEPTION_REGION(aiReturn); - - return AI_FAILURE; -} - -// ------------------------------------------------------------------------------------------------ -const char* Exporter::GetErrorString() const { - return pimpl->mError.c_str(); -} - -// ------------------------------------------------------------------------------------------------ -void Exporter::FreeBlob() { - delete pimpl->blob; - pimpl->blob = nullptr; - - pimpl->mError = ""; -} - -// ------------------------------------------------------------------------------------------------ -const aiExportDataBlob* Exporter::GetBlob() const { - return pimpl->blob; -} - -// ------------------------------------------------------------------------------------------------ -const aiExportDataBlob* Exporter::GetOrphanedBlob() const { - const aiExportDataBlob* tmp = pimpl->blob; - pimpl->blob = nullptr; - return tmp; -} - -// ------------------------------------------------------------------------------------------------ -size_t Exporter::GetExportFormatCount() const { - return pimpl->mExporters.size(); -} - -// ------------------------------------------------------------------------------------------------ -const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const { - if (index >= GetExportFormatCount()) { - return nullptr; - } - - // Return from static storage if the requested index is built-in. - if (index < sizeof(gExporters) / sizeof(gExporters[0])) { - return &gExporters[index].mDescription; - } - - return &pimpl->mExporters[index].mDescription; -} - -// ------------------------------------------------------------------------------------------------ -aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) { - for(const ExportFormatEntry& e : pimpl->mExporters) { - if (!strcmp(e.mDescription.id,desc.mDescription.id)) { - return aiReturn_FAILURE; - } - } - - pimpl->mExporters.push_back(desc); - return aiReturn_SUCCESS; -} - -// ------------------------------------------------------------------------------------------------ -void Exporter::UnregisterExporter(const char* id) { - for(std::vector::iterator it = pimpl->mExporters.begin(); - it != pimpl->mExporters.end(); ++it) { - if (!strcmp((*it).mDescription.id,id)) { - pimpl->mExporters.erase(it); - break; - } - } -} - -// ------------------------------------------------------------------------------------------------ -ExportProperties::ExportProperties() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -ExportProperties::ExportProperties(const ExportProperties &other) -: mIntProperties(other.mIntProperties) -, mFloatProperties(other.mFloatProperties) -, mStringProperties(other.mStringProperties) -, mMatrixProperties(other.mMatrixProperties) { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Set a configuration property -bool ExportProperties::SetPropertyInteger(const char* szName, int iValue) { - return SetGenericProperty(mIntProperties, szName,iValue); -} - -// ------------------------------------------------------------------------------------------------ -// Set a configuration property -bool ExportProperties::SetPropertyFloat(const char* szName, ai_real iValue) { - return SetGenericProperty(mFloatProperties, szName,iValue); -} - -// ------------------------------------------------------------------------------------------------ -// Set a configuration property -bool ExportProperties::SetPropertyString(const char* szName, const std::string& value) { - return SetGenericProperty(mStringProperties, szName,value); -} - -// ------------------------------------------------------------------------------------------------ -// Set a configuration property -bool ExportProperties::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { - return SetGenericProperty(mMatrixProperties, szName,value); -} - -// ------------------------------------------------------------------------------------------------ -// Get a configuration property -int ExportProperties::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const { - return GetGenericProperty(mIntProperties,szName,iErrorReturn); -} - -// ------------------------------------------------------------------------------------------------ -// Get a configuration property -ai_real ExportProperties::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const { - return GetGenericProperty(mFloatProperties,szName,iErrorReturn); -} - -// ------------------------------------------------------------------------------------------------ -// Get a configuration property -const std::string ExportProperties::GetPropertyString(const char* szName, - const std::string& iErrorReturn /*= ""*/) const { - return GetGenericProperty(mStringProperties,szName,iErrorReturn); -} - -// ------------------------------------------------------------------------------------------------ -// Has a configuration property -const aiMatrix4x4 ExportProperties::GetPropertyMatrix(const char* szName, - const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const { - return GetGenericProperty(mMatrixProperties,szName,iErrorReturn); -} - -// ------------------------------------------------------------------------------------------------ -// Has a configuration property -bool ExportProperties::HasPropertyInteger(const char* szName) const { - return HasGenericProperty(mIntProperties, szName); -} - -// ------------------------------------------------------------------------------------------------ -// Has a configuration property -bool ExportProperties::HasPropertyBool(const char* szName) const { - return HasGenericProperty(mIntProperties, szName); -} - -// ------------------------------------------------------------------------------------------------ -// Has a configuration property -bool ExportProperties::HasPropertyFloat(const char* szName) const { - return HasGenericProperty(mFloatProperties, szName); -} - -// ------------------------------------------------------------------------------------------------ -// Has a configuration property -bool ExportProperties::HasPropertyString(const char* szName) const { - return HasGenericProperty(mStringProperties, szName); -} - -// ------------------------------------------------------------------------------------------------ -// Has a configuration property -bool ExportProperties::HasPropertyMatrix(const char* szName) const { - return HasGenericProperty(mMatrixProperties, szName); -} - - -#endif // !ASSIMP_BUILD_NO_EXPORT diff --git a/thirdparty/assimp/code/FBX/FBXAnimation.cpp b/thirdparty/assimp/code/FBX/FBXAnimation.cpp new file mode 100644 index 0000000000..874914431b --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXAnimation.cpp @@ -0,0 +1,305 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXAnimation.cpp + * @brief Assimp::FBX::AnimationCurve, Assimp::FBX::AnimationCurveNode, + * Assimp::FBX::AnimationLayer, Assimp::FBX::AnimationStack + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXParser.h" +#include "FBXDocument.h" +#include "FBXImporter.h" +#include "FBXDocumentUtil.h" + +namespace Assimp { +namespace FBX { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& /*doc*/) +: Object(id, element, name) +{ + const Scope& sc = GetRequiredScope(element); + const Element& KeyTime = GetRequiredElement(sc,"KeyTime"); + const Element& KeyValueFloat = GetRequiredElement(sc,"KeyValueFloat"); + + ParseVectorDataArray(keys, KeyTime); + ParseVectorDataArray(values, KeyValueFloat); + + if(keys.size() != values.size()) { + DOMError("the number of key times does not match the number of keyframe values",&KeyTime); + } + + // check if the key times are well-ordered + if(!std::equal(keys.begin(), keys.end() - 1, keys.begin() + 1, std::less())) { + DOMError("the keyframes are not in ascending order",&KeyTime); + } + + const Element* KeyAttrDataFloat = sc["KeyAttrDataFloat"]; + if(KeyAttrDataFloat) { + ParseVectorDataArray(attributes, *KeyAttrDataFloat); + } + + const Element* KeyAttrFlags = sc["KeyAttrFlags"]; + if(KeyAttrFlags) { + ParseVectorDataArray(flags, *KeyAttrFlags); + } +} + +// ------------------------------------------------------------------------------------------------ +AnimationCurve::~AnimationCurve() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, + const Document& doc, const char* const * target_prop_whitelist /*= NULL*/, + size_t whitelist_size /*= 0*/) +: Object(id, element, name) +, target() +, doc(doc) +{ + const Scope& sc = GetRequiredScope(element); + + // find target node + const char* whitelist[] = {"Model","NodeAttribute","Deformer"}; + const std::vector& conns = doc.GetConnectionsBySourceSequenced(ID(),whitelist,3); + + for(const Connection* con : conns) { + + // link should go for a property + if (!con->PropertyName().length()) { + continue; + } + + if(target_prop_whitelist) { + const char* const s = con->PropertyName().c_str(); + bool ok = false; + for (size_t i = 0; i < whitelist_size; ++i) { + if (!strcmp(s, target_prop_whitelist[i])) { + ok = true; + break; + } + } + + if (!ok) { + throw std::range_error("AnimationCurveNode target property is not in whitelist"); + } + } + + const Object* const ob = con->DestinationObject(); + if(!ob) { + DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring",&element); + continue; + } + + // XXX support constraints as DOM class + //ai_assert(dynamic_cast(ob) || dynamic_cast(ob)); + target = ob; + if(!target) { + continue; + } + + prop = con->PropertyName(); + break; + } + + if(!target) { + DOMWarning("failed to resolve target Model/NodeAttribute/Constraint for AnimationCurveNode",&element); + } + + props = GetPropertyTable(doc,"AnimationCurveNode.FbxAnimCurveNode",element,sc,false); +} + +// ------------------------------------------------------------------------------------------------ +AnimationCurveNode::~AnimationCurveNode() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +const AnimationCurveMap& AnimationCurveNode::Curves() const +{ + if ( curves.empty() ) { + // resolve attached animation curves + const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurve"); + + for(const Connection* con : conns) { + + // link should go for a property + if (!con->PropertyName().length()) { + continue; + } + + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring",&element); + continue; + } + + const AnimationCurve* const anim = dynamic_cast(ob); + if(!anim) { + DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve",&element); + continue; + } + + curves[con->PropertyName()] = anim; + } + } + + return curves; +} + +// ------------------------------------------------------------------------------------------------ +AnimationLayer::AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc) +: Object(id, element, name) +, doc(doc) +{ + const Scope& sc = GetRequiredScope(element); + + // note: the props table here bears little importance and is usually absent + props = GetPropertyTable(doc,"AnimationLayer.FbxAnimLayer",element,sc, true); +} + +// ------------------------------------------------------------------------------------------------ +AnimationLayer::~AnimationLayer() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +AnimationCurveNodeList AnimationLayer::Nodes(const char* const * target_prop_whitelist /*= NULL*/, + size_t whitelist_size /*= 0*/) const +{ + AnimationCurveNodeList nodes; + + // resolve attached animation nodes + const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurveNode"); + nodes.reserve(conns.size()); + + for(const Connection* con : conns) { + + // link should not go to a property + if (con->PropertyName().length()) { + continue; + } + + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring",&element); + continue; + } + + const AnimationCurveNode* const anim = dynamic_cast(ob); + if(!anim) { + DOMWarning("source object for ->AnimationLayer link is not an AnimationCurveNode",&element); + continue; + } + + if(target_prop_whitelist) { + const char* s = anim->TargetProperty().c_str(); + bool ok = false; + for (size_t i = 0; i < whitelist_size; ++i) { + if (!strcmp(s, target_prop_whitelist[i])) { + ok = true; + break; + } + } + if(!ok) { + continue; + } + } + nodes.push_back(anim); + } + + return nodes; // pray for NRVO +} + +// ------------------------------------------------------------------------------------------------ +AnimationStack::AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc) +: Object(id, element, name) +{ + const Scope& sc = GetRequiredScope(element); + + // note: we don't currently use any of these properties so we shouldn't bother if it is missing + props = GetPropertyTable(doc,"AnimationStack.FbxAnimStack",element,sc, true); + + // resolve attached animation layers + const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationLayer"); + layers.reserve(conns.size()); + + for(const Connection* con : conns) { + + // link should not go to a property + if (con->PropertyName().length()) { + continue; + } + + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring",&element); + continue; + } + + const AnimationLayer* const anim = dynamic_cast(ob); + if(!anim) { + DOMWarning("source object for ->AnimationStack link is not an AnimationLayer",&element); + continue; + } + layers.push_back(anim); + } +} + +// ------------------------------------------------------------------------------------------------ +AnimationStack::~AnimationStack() +{ + // empty +} + +} //!FBX +} //!Assimp + +#endif // ASSIMP_BUILD_NO_FBX_IMPORTER diff --git a/thirdparty/assimp/code/FBX/FBXBinaryTokenizer.cpp b/thirdparty/assimp/code/FBX/FBXBinaryTokenizer.cpp new file mode 100644 index 0000000000..a4a2bc8e79 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXBinaryTokenizer.cpp @@ -0,0 +1,466 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/** @file FBXBinaryTokenizer.cpp + * @brief Implementation of a fake lexer for binary fbx files - + * we emit tokens so the parser needs almost no special handling + * for binary files. + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXTokenizer.h" +#include "FBXUtil.h" +#include +#include +#include +#include + +namespace Assimp { +namespace FBX { + +//enum Flag +//{ +// e_unknown_0 = 1 << 0, +// e_unknown_1 = 1 << 1, +// e_unknown_2 = 1 << 2, +// e_unknown_3 = 1 << 3, +// e_unknown_4 = 1 << 4, +// e_unknown_5 = 1 << 5, +// e_unknown_6 = 1 << 6, +// e_unknown_7 = 1 << 7, +// e_unknown_8 = 1 << 8, +// e_unknown_9 = 1 << 9, +// e_unknown_10 = 1 << 10, +// e_unknown_11 = 1 << 11, +// e_unknown_12 = 1 << 12, +// e_unknown_13 = 1 << 13, +// e_unknown_14 = 1 << 14, +// e_unknown_15 = 1 << 15, +// e_unknown_16 = 1 << 16, +// e_unknown_17 = 1 << 17, +// e_unknown_18 = 1 << 18, +// e_unknown_19 = 1 << 19, +// e_unknown_20 = 1 << 20, +// e_unknown_21 = 1 << 21, +// e_unknown_22 = 1 << 22, +// e_unknown_23 = 1 << 23, +// e_flag_field_size_64_bit = 1 << 24, // Not sure what is +// e_unknown_25 = 1 << 25, +// e_unknown_26 = 1 << 26, +// e_unknown_27 = 1 << 27, +// e_unknown_28 = 1 << 28, +// e_unknown_29 = 1 << 29, +// e_unknown_30 = 1 << 30, +// e_unknown_31 = 1 << 31 +//}; +// +//bool check_flag(uint32_t flags, Flag to_check) +//{ +// return (flags & to_check) != 0; +//} +// ------------------------------------------------------------------------------------------------ +Token::Token(const char* sbegin, const char* send, TokenType type, size_t offset) + : + #ifdef DEBUG + contents(sbegin, static_cast(send-sbegin)), + #endif + sbegin(sbegin) + , send(send) + , type(type) + , line(offset) + , column(BINARY_MARKER) +{ + ai_assert(sbegin); + ai_assert(send); + + // binary tokens may have zero length because they are sometimes dummies + // inserted by TokenizeBinary() + ai_assert(send >= sbegin); +} + + +namespace { + +// ------------------------------------------------------------------------------------------------ +// signal tokenization error, this is always unrecoverable. Throws DeadlyImportError. +AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset) AI_WONT_RETURN_SUFFIX; +AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset) +{ + throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset)); +} + + +// ------------------------------------------------------------------------------------------------ +size_t Offset(const char* begin, const char* cursor) { + ai_assert(begin <= cursor); + + return cursor - begin; +} + +// ------------------------------------------------------------------------------------------------ +void TokenizeError(const std::string& message, const char* begin, const char* cursor) { + TokenizeError(message, Offset(begin, cursor)); +} + +// ------------------------------------------------------------------------------------------------ +uint32_t ReadWord(const char* input, const char*& cursor, const char* end) { + const size_t k_to_read = sizeof( uint32_t ); + if(Offset(cursor, end) < k_to_read ) { + TokenizeError("cannot ReadWord, out of bounds",input, cursor); + } + + uint32_t word; + ::memcpy(&word, cursor, 4); + AI_SWAP4(word); + + cursor += k_to_read; + + return word; +} + +// ------------------------------------------------------------------------------------------------ +uint64_t ReadDoubleWord(const char* input, const char*& cursor, const char* end) { + const size_t k_to_read = sizeof(uint64_t); + if(Offset(cursor, end) < k_to_read) { + TokenizeError("cannot ReadDoubleWord, out of bounds",input, cursor); + } + + uint64_t dword /*= *reinterpret_cast(cursor)*/; + ::memcpy( &dword, cursor, sizeof( uint64_t ) ); + AI_SWAP8(dword); + + cursor += k_to_read; + + return dword; +} + +// ------------------------------------------------------------------------------------------------ +uint8_t ReadByte(const char* input, const char*& cursor, const char* end) { + if(Offset(cursor, end) < sizeof( uint8_t ) ) { + TokenizeError("cannot ReadByte, out of bounds",input, cursor); + } + + uint8_t word;/* = *reinterpret_cast< const uint8_t* >( cursor )*/ + ::memcpy( &word, cursor, sizeof( uint8_t ) ); + ++cursor; + + return word; +} + +// ------------------------------------------------------------------------------------------------ +unsigned int ReadString(const char*& sbegin_out, const char*& send_out, const char* input, + const char*& cursor, const char* end, bool long_length = false, bool allow_null = false) { + const uint32_t len_len = long_length ? 4 : 1; + if(Offset(cursor, end) < len_len) { + TokenizeError("cannot ReadString, out of bounds reading length",input, cursor); + } + + const uint32_t length = long_length ? ReadWord(input, cursor, end) : ReadByte(input, cursor, end); + + if (Offset(cursor, end) < length) { + TokenizeError("cannot ReadString, length is out of bounds",input, cursor); + } + + sbegin_out = cursor; + cursor += length; + + send_out = cursor; + + if(!allow_null) { + for (unsigned int i = 0; i < length; ++i) { + if(sbegin_out[i] == '\0') { + TokenizeError("failed ReadString, unexpected NUL character in string",input, cursor); + } + } + } + + return length; +} + +// ------------------------------------------------------------------------------------------------ +void ReadData(const char*& sbegin_out, const char*& send_out, const char* input, const char*& cursor, const char* end) { + if(Offset(cursor, end) < 1) { + TokenizeError("cannot ReadData, out of bounds reading length",input, cursor); + } + + const char type = *cursor; + sbegin_out = cursor++; + + switch(type) + { + // 16 bit int + case 'Y': + cursor += 2; + break; + + // 1 bit bool flag (yes/no) + case 'C': + cursor += 1; + break; + + // 32 bit int + case 'I': + // <- fall through + + // float + case 'F': + cursor += 4; + break; + + // double + case 'D': + cursor += 8; + break; + + // 64 bit int + case 'L': + cursor += 8; + break; + + // note: do not write cursor += ReadWord(...cursor) as this would be UB + + // raw binary data + case 'R': + { + const uint32_t length = ReadWord(input, cursor, end); + cursor += length; + break; + } + + case 'b': + // TODO: what is the 'b' type code? Right now we just skip over it / + // take the full range we could get + cursor = end; + break; + + // array of * + case 'f': + case 'd': + case 'l': + case 'i': + case 'c': { + const uint32_t length = ReadWord(input, cursor, end); + const uint32_t encoding = ReadWord(input, cursor, end); + + const uint32_t comp_len = ReadWord(input, cursor, end); + + // compute length based on type and check against the stored value + if(encoding == 0) { + uint32_t stride = 0; + switch(type) + { + case 'f': + case 'i': + stride = 4; + break; + + case 'd': + case 'l': + stride = 8; + break; + + case 'c': + stride = 1; + break; + + default: + ai_assert(false); + }; + ai_assert(stride > 0); + if(length * stride != comp_len) { + TokenizeError("cannot ReadData, calculated data stride differs from what the file claims",input, cursor); + } + } + // zip/deflate algorithm (encoding==1)? take given length. anything else? die + else if (encoding != 1) { + TokenizeError("cannot ReadData, unknown encoding",input, cursor); + } + cursor += comp_len; + break; + } + + // string + case 'S': { + const char* sb, *se; + // 0 characters can legally happen in such strings + ReadString(sb, se, input, cursor, end, true, true); + break; + } + default: + TokenizeError("cannot ReadData, unexpected type code: " + std::string(&type, 1),input, cursor); + } + + if(cursor > end) { + TokenizeError("cannot ReadData, the remaining size is too small for the data type: " + std::string(&type, 1),input, cursor); + } + + // the type code is contained in the returned range + send_out = cursor; +} + + +// ------------------------------------------------------------------------------------------------ +bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor, const char* end, bool const is64bits) +{ + // the first word contains the offset at which this block ends + const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); + + // we may get 0 if reading reached the end of the file - + // fbx files have a mysterious extra footer which I don't know + // how to extract any information from, but at least it always + // starts with a 0. + if(!end_offset) { + return false; + } + + if(end_offset > Offset(input, end)) { + TokenizeError("block offset is out of range",input, cursor); + } + else if(end_offset < Offset(input, cursor)) { + TokenizeError("block offset is negative out of range",input, cursor); + } + + // the second data word contains the number of properties in the scope + const uint64_t prop_count = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); + + // the third data word contains the length of the property list + const uint64_t prop_length = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); + + // now comes the name of the scope/key + const char* sbeg, *send; + ReadString(sbeg, send, input, cursor, end); + + output_tokens.push_back(new_Token(sbeg, send, TokenType_KEY, Offset(input, cursor) )); + + // now come the individual properties + const char* begin_cursor = cursor; + for (unsigned int i = 0; i < prop_count; ++i) { + ReadData(sbeg, send, input, cursor, begin_cursor + prop_length); + + output_tokens.push_back(new_Token(sbeg, send, TokenType_DATA, Offset(input, cursor) )); + + if(i != prop_count-1) { + output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_COMMA, Offset(input, cursor) )); + } + } + + if (Offset(begin_cursor, cursor) != prop_length) { + TokenizeError("property length not reached, something is wrong",input, cursor); + } + + // at the end of each nested block, there is a NUL record to indicate + // that the sub-scope exists (i.e. to distinguish between P: and P : {}) + // this NUL record is 13 bytes long on 32 bit version and 25 bytes long on 64 bit. + const size_t sentinel_block_length = is64bits ? (sizeof(uint64_t)* 3 + 1) : (sizeof(uint32_t)* 3 + 1); + + if (Offset(input, cursor) < end_offset) { + if (end_offset - Offset(input, cursor) < sentinel_block_length) { + TokenizeError("insufficient padding bytes at block end",input, cursor); + } + + output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_OPEN_BRACKET, Offset(input, cursor) )); + + // XXX this is vulnerable to stack overflowing .. + while(Offset(input, cursor) < end_offset - sentinel_block_length) { + ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits); + } + output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor) )); + + for (unsigned int i = 0; i < sentinel_block_length; ++i) { + if(cursor[i] != '\0') { + TokenizeError("failed to read nested block sentinel, expected all bytes to be 0",input, cursor); + } + } + cursor += sentinel_block_length; + } + + if (Offset(input, cursor) != end_offset) { + TokenizeError("scope length not reached, something is wrong",input, cursor); + } + + return true; +} + +} // anonymous namespace + +// ------------------------------------------------------------------------------------------------ +// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent +void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length) +{ + ai_assert(input); + + if(length < 0x1b) { + TokenizeError("file is too short",0); + } + + //uint32_t offset = 0x15; +/* const char* cursor = input + 0x15; + + const uint32_t flags = ReadWord(input, cursor, input + length); + + const uint8_t padding_0 = ReadByte(input, cursor, input + length); // unused + const uint8_t padding_1 = ReadByte(input, cursor, input + length); // unused*/ + + if (strncmp(input,"Kaydara FBX Binary",18)) { + TokenizeError("magic bytes not found",0); + } + + const char* cursor = input + 18; + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + const uint32_t version = ReadWord(input, cursor, input + length); + const bool is64bits = version >= 7500; + const char *end = input + length; + while (cursor < end ) { + if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) { + break; + } + } +} + +} // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBX/FBXCommon.h b/thirdparty/assimp/code/FBX/FBXCommon.h new file mode 100644 index 0000000000..e516449130 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXCommon.h @@ -0,0 +1,86 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXCommon.h +* Some useful constants and enums for dealing with FBX files. +*/ +#ifndef AI_FBXCOMMON_H_INC +#define AI_FBXCOMMON_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +namespace Assimp { +namespace FBX +{ + const std::string NULL_RECORD = { // 13 null bytes + '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0' + }; // who knows why + const std::string SEPARATOR = {'\x00', '\x01'}; // for use inside strings + const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import + const int64_t SECOND = 46186158000; // FBX's kTime unit + + // rotation order. We'll probably use EulerXYZ for everything + enum RotOrder { + RotOrder_EulerXYZ = 0, + RotOrder_EulerXZY, + RotOrder_EulerYZX, + RotOrder_EulerYXZ, + RotOrder_EulerZXY, + RotOrder_EulerZYX, + + RotOrder_SphericXYZ, + + RotOrder_MAX // end-of-enum sentinel + }; + + // transformation inheritance method. Most of the time RSrs + enum TransformInheritance { + TransformInheritance_RrSs = 0, + TransformInheritance_RSrs, + TransformInheritance_Rrs, + + TransformInheritance_MAX // end-of-enum sentinel + }; +} +} +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXCOMMON_H_INC diff --git a/thirdparty/assimp/code/FBX/FBXCompileConfig.h b/thirdparty/assimp/code/FBX/FBXCompileConfig.h new file mode 100644 index 0000000000..3a3841fa5b --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXCompileConfig.h @@ -0,0 +1,70 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXCompileConfig.h + * @brief FBX importer compile-time switches + */ +#ifndef INCLUDED_AI_FBX_COMPILECONFIG_H +#define INCLUDED_AI_FBX_COMPILECONFIG_H + +#include + +// +#if _MSC_VER > 1500 || (defined __GNUC___) +# define ASSIMP_FBX_USE_UNORDERED_MULTIMAP +# else +# define fbx_unordered_map map +# define fbx_unordered_multimap multimap +#endif + +#ifdef ASSIMP_FBX_USE_UNORDERED_MULTIMAP +# include +# if _MSC_VER > 1600 +# define fbx_unordered_map unordered_map +# define fbx_unordered_multimap unordered_multimap +# else +# define fbx_unordered_map tr1::unordered_map +# define fbx_unordered_multimap tr1::unordered_multimap +# endif +#endif + +#endif // INCLUDED_AI_FBX_COMPILECONFIG_H diff --git a/thirdparty/assimp/code/FBX/FBXConverter.cpp b/thirdparty/assimp/code/FBX/FBXConverter.cpp new file mode 100644 index 0000000000..9f940d3226 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXConverter.cpp @@ -0,0 +1,3636 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXConverter.cpp + * @brief Implementation of the FBX DOM -> aiScene converter + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXConverter.h" +#include "FBXParser.h" +#include "FBXMeshGeometry.h" +#include "FBXDocument.h" +#include "FBXUtil.h" +#include "FBXProperties.h" +#include "FBXImporter.h" + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + + +namespace Assimp { + namespace FBX { + + using namespace Util; + +#define MAGIC_NODE_TAG "_$AssimpFbx$" + +#define CONVERT_FBX_TIME(time) static_cast(time) / 46186158000L + + FBXConverter::FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit ) + : defaultMaterialIndex() + , lights() + , cameras() + , textures() + , materials_converted() + , textures_converted() + , meshes_converted() + , node_anim_chain_bits() + , mNodeNames() + , anim_fps() + , out(out) + , doc(doc) + , mRemoveEmptyBones( removeEmptyBones ) + , mCurrentUnit(FbxUnit::cm) { + // animations need to be converted first since this will + // populate the node_anim_chain_bits map, which is needed + // to determine which nodes need to be generated. + ConvertAnimations(); + ConvertRootNode(); + + if (doc.Settings().readAllMaterials) { + // unfortunately this means we have to evaluate all objects + for (const ObjectMap::value_type& v : doc.Objects()) { + + const Object* ob = v.second->Get(); + if (!ob) { + continue; + } + + const Material* mat = dynamic_cast(ob); + if (mat) { + + if (materials_converted.find(mat) == materials_converted.end()) { + ConvertMaterial(*mat, 0); + } + } + } + } + + ConvertGlobalSettings(); + TransferDataToScene(); + ConvertToUnitScale(unit); + + // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE + // to make sure the scene passes assimp's validation. FBX files + // need not contain geometry (i.e. camera animations, raw armatures). + if (out->mNumMeshes == 0) { + out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + } + } + + + FBXConverter::~FBXConverter() { + std::for_each(meshes.begin(), meshes.end(), Util::delete_fun()); + std::for_each(materials.begin(), materials.end(), Util::delete_fun()); + std::for_each(animations.begin(), animations.end(), Util::delete_fun()); + std::for_each(lights.begin(), lights.end(), Util::delete_fun()); + std::for_each(cameras.begin(), cameras.end(), Util::delete_fun()); + std::for_each(textures.begin(), textures.end(), Util::delete_fun()); + } + + void FBXConverter::ConvertRootNode() { + out->mRootNode = new aiNode(); + std::string unique_name; + GetUniqueName("RootNode", unique_name); + out->mRootNode->mName.Set(unique_name); + + // root has ID 0 + ConvertNodes(0L, *out->mRootNode); + } + + static std::string getAncestorBaseName(const aiNode* node) + { + const char* nodeName = nullptr; + size_t length = 0; + while (node && (!nodeName || length == 0)) + { + nodeName = node->mName.C_Str(); + length = node->mName.length; + node = node->mParent; + } + + if (!nodeName || length == 0) + { + return {}; + } + // could be std::string_view if c++17 available + return std::string(nodeName, length); + } + + // Make unique name + std::string FBXConverter::MakeUniqueNodeName(const Model* const model, const aiNode& parent) + { + std::string original_name = FixNodeName(model->Name()); + if (original_name.empty()) + { + original_name = getAncestorBaseName(&parent); + } + std::string unique_name; + GetUniqueName(original_name, unique_name); + return unique_name; + } + + void FBXConverter::ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform) { + const std::vector& conns = doc.GetConnectionsByDestinationSequenced(id, "Model"); + + std::vector nodes; + nodes.reserve(conns.size()); + + std::vector nodes_chain; + std::vector post_nodes_chain; + + try { + for (const Connection* con : conns) { + + // ignore object-property links + if (con->PropertyName().length()) { + continue; + } + + const Object* const object = con->SourceObject(); + if (nullptr == object) { + FBXImporter::LogWarn("failed to convert source object for Model link"); + continue; + } + + const Model* const model = dynamic_cast(object); + + if (nullptr != model) { + nodes_chain.clear(); + post_nodes_chain.clear(); + + aiMatrix4x4 new_abs_transform = parent_transform; + + std::string unique_name = MakeUniqueNodeName(model, parent); + + // even though there is only a single input node, the design of + // assimp (or rather: the complicated transformation chain that + // is employed by fbx) means that we may need multiple aiNode's + // to represent a fbx node's transformation. + const bool need_additional_node = GenerateTransformationNodeChain(*model, unique_name, nodes_chain, post_nodes_chain); + + ai_assert(nodes_chain.size()); + + if (need_additional_node) { + nodes_chain.push_back(new aiNode(unique_name)); + } + + //setup metadata on newest node + SetupNodeMetadata(*model, *nodes_chain.back()); + + // link all nodes in a row + aiNode* last_parent = &parent; + for (aiNode* prenode : nodes_chain) { + ai_assert(prenode); + + if (last_parent != &parent) { + last_parent->mNumChildren = 1; + last_parent->mChildren = new aiNode*[1]; + last_parent->mChildren[0] = prenode; + } + + prenode->mParent = last_parent; + last_parent = prenode; + + new_abs_transform *= prenode->mTransformation; + } + + // attach geometry + ConvertModel(*model, *nodes_chain.back(), new_abs_transform); + + // check if there will be any child nodes + const std::vector& child_conns + = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model"); + + // if so, link the geometric transform inverse nodes + // before we attach any child nodes + if (child_conns.size()) { + for (aiNode* postnode : post_nodes_chain) { + ai_assert(postnode); + + if (last_parent != &parent) { + last_parent->mNumChildren = 1; + last_parent->mChildren = new aiNode*[1]; + last_parent->mChildren[0] = postnode; + } + + postnode->mParent = last_parent; + last_parent = postnode; + + new_abs_transform *= postnode->mTransformation; + } + } + else { + // free the nodes we allocated as we don't need them + Util::delete_fun deleter; + std::for_each( + post_nodes_chain.begin(), + post_nodes_chain.end(), + deleter + ); + } + + // attach sub-nodes (if any) + ConvertNodes(model->ID(), *last_parent, new_abs_transform); + + if (doc.Settings().readLights) { + ConvertLights(*model, unique_name); + } + + if (doc.Settings().readCameras) { + ConvertCameras(*model, unique_name); + } + + nodes.push_back(nodes_chain.front()); + nodes_chain.clear(); + } + } + + if (nodes.size()) { + parent.mChildren = new aiNode*[nodes.size()](); + parent.mNumChildren = static_cast(nodes.size()); + + std::swap_ranges(nodes.begin(), nodes.end(), parent.mChildren); + } + } + catch (std::exception&) { + Util::delete_fun deleter; + std::for_each(nodes.begin(), nodes.end(), deleter); + std::for_each(nodes_chain.begin(), nodes_chain.end(), deleter); + std::for_each(post_nodes_chain.begin(), post_nodes_chain.end(), deleter); + } + } + + + void FBXConverter::ConvertLights(const Model& model, const std::string &orig_name) { + const std::vector& node_attrs = model.GetAttributes(); + for (const NodeAttribute* attr : node_attrs) { + const Light* const light = dynamic_cast(attr); + if (light) { + ConvertLight(*light, orig_name); + } + } + } + + void FBXConverter::ConvertCameras(const Model& model, const std::string &orig_name) { + const std::vector& node_attrs = model.GetAttributes(); + for (const NodeAttribute* attr : node_attrs) { + const Camera* const cam = dynamic_cast(attr); + if (cam) { + ConvertCamera(*cam, orig_name); + } + } + } + + void FBXConverter::ConvertLight(const Light& light, const std::string &orig_name) { + lights.push_back(new aiLight()); + aiLight* const out_light = lights.back(); + + out_light->mName.Set(orig_name); + + const float intensity = light.Intensity() / 100.0f; + const aiVector3D& col = light.Color(); + + out_light->mColorDiffuse = aiColor3D(col.x, col.y, col.z); + out_light->mColorDiffuse.r *= intensity; + out_light->mColorDiffuse.g *= intensity; + out_light->mColorDiffuse.b *= intensity; + + out_light->mColorSpecular = out_light->mColorDiffuse; + + //lights are defined along negative y direction + out_light->mPosition = aiVector3D(0.0f); + out_light->mDirection = aiVector3D(0.0f, -1.0f, 0.0f); + out_light->mUp = aiVector3D(0.0f, 0.0f, -1.0f); + + switch (light.LightType()) + { + case Light::Type_Point: + out_light->mType = aiLightSource_POINT; + break; + + case Light::Type_Directional: + out_light->mType = aiLightSource_DIRECTIONAL; + break; + + case Light::Type_Spot: + out_light->mType = aiLightSource_SPOT; + out_light->mAngleOuterCone = AI_DEG_TO_RAD(light.OuterAngle()); + out_light->mAngleInnerCone = AI_DEG_TO_RAD(light.InnerAngle()); + break; + + case Light::Type_Area: + FBXImporter::LogWarn("cannot represent area light, set to UNDEFINED"); + out_light->mType = aiLightSource_UNDEFINED; + break; + + case Light::Type_Volume: + FBXImporter::LogWarn("cannot represent volume light, set to UNDEFINED"); + out_light->mType = aiLightSource_UNDEFINED; + break; + default: + ai_assert(false); + } + + float decay = light.DecayStart(); + switch (light.DecayType()) + { + case Light::Decay_None: + out_light->mAttenuationConstant = decay; + out_light->mAttenuationLinear = 0.0f; + out_light->mAttenuationQuadratic = 0.0f; + break; + case Light::Decay_Linear: + out_light->mAttenuationConstant = 0.0f; + out_light->mAttenuationLinear = 2.0f / decay; + out_light->mAttenuationQuadratic = 0.0f; + break; + case Light::Decay_Quadratic: + out_light->mAttenuationConstant = 0.0f; + out_light->mAttenuationLinear = 0.0f; + out_light->mAttenuationQuadratic = 2.0f / (decay * decay); + break; + case Light::Decay_Cubic: + FBXImporter::LogWarn("cannot represent cubic attenuation, set to Quadratic"); + out_light->mAttenuationQuadratic = 1.0f; + break; + default: + ai_assert(false); + break; + } + } + + void FBXConverter::ConvertCamera(const Camera& cam, const std::string &orig_name) + { + cameras.push_back(new aiCamera()); + aiCamera* const out_camera = cameras.back(); + + out_camera->mName.Set(orig_name); + + out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight(); + + out_camera->mPosition = aiVector3D(0.0f); + out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f); + out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f); + + out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView()); + + out_camera->mClipPlaneNear = cam.NearPlane(); + out_camera->mClipPlaneFar = cam.FarPlane(); + + out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView()); + out_camera->mClipPlaneNear = cam.NearPlane(); + out_camera->mClipPlaneFar = cam.FarPlane(); + } + + void FBXConverter::GetUniqueName(const std::string &name, std::string &uniqueName) + { + uniqueName = name; + auto it_pair = mNodeNames.insert({ name, 0 }); // duplicate node name instance count + unsigned int& i = it_pair.first->second; + while (!it_pair.second) + { + i++; + std::ostringstream ext; + ext << name << std::setfill('0') << std::setw(3) << i; + uniqueName = ext.str(); + it_pair = mNodeNames.insert({ uniqueName, 0 }); + } + } + + const char* FBXConverter::NameTransformationComp(TransformationComp comp) { + switch (comp) { + case TransformationComp_Translation: + return "Translation"; + case TransformationComp_RotationOffset: + return "RotationOffset"; + case TransformationComp_RotationPivot: + return "RotationPivot"; + case TransformationComp_PreRotation: + return "PreRotation"; + case TransformationComp_Rotation: + return "Rotation"; + case TransformationComp_PostRotation: + return "PostRotation"; + case TransformationComp_RotationPivotInverse: + return "RotationPivotInverse"; + case TransformationComp_ScalingOffset: + return "ScalingOffset"; + case TransformationComp_ScalingPivot: + return "ScalingPivot"; + case TransformationComp_Scaling: + return "Scaling"; + case TransformationComp_ScalingPivotInverse: + return "ScalingPivotInverse"; + case TransformationComp_GeometricScaling: + return "GeometricScaling"; + case TransformationComp_GeometricRotation: + return "GeometricRotation"; + case TransformationComp_GeometricTranslation: + return "GeometricTranslation"; + case TransformationComp_GeometricScalingInverse: + return "GeometricScalingInverse"; + case TransformationComp_GeometricRotationInverse: + return "GeometricRotationInverse"; + case TransformationComp_GeometricTranslationInverse: + return "GeometricTranslationInverse"; + case TransformationComp_MAXIMUM: // this is to silence compiler warnings + default: + break; + } + + ai_assert(false); + + return nullptr; + } + + const char* FBXConverter::NameTransformationCompProperty(TransformationComp comp) { + switch (comp) { + case TransformationComp_Translation: + return "Lcl Translation"; + case TransformationComp_RotationOffset: + return "RotationOffset"; + case TransformationComp_RotationPivot: + return "RotationPivot"; + case TransformationComp_PreRotation: + return "PreRotation"; + case TransformationComp_Rotation: + return "Lcl Rotation"; + case TransformationComp_PostRotation: + return "PostRotation"; + case TransformationComp_RotationPivotInverse: + return "RotationPivotInverse"; + case TransformationComp_ScalingOffset: + return "ScalingOffset"; + case TransformationComp_ScalingPivot: + return "ScalingPivot"; + case TransformationComp_Scaling: + return "Lcl Scaling"; + case TransformationComp_ScalingPivotInverse: + return "ScalingPivotInverse"; + case TransformationComp_GeometricScaling: + return "GeometricScaling"; + case TransformationComp_GeometricRotation: + return "GeometricRotation"; + case TransformationComp_GeometricTranslation: + return "GeometricTranslation"; + case TransformationComp_GeometricScalingInverse: + return "GeometricScalingInverse"; + case TransformationComp_GeometricRotationInverse: + return "GeometricRotationInverse"; + case TransformationComp_GeometricTranslationInverse: + return "GeometricTranslationInverse"; + case TransformationComp_MAXIMUM: // this is to silence compiler warnings + break; + } + + ai_assert(false); + + return nullptr; + } + + aiVector3D FBXConverter::TransformationCompDefaultValue(TransformationComp comp) + { + // XXX a neat way to solve the never-ending special cases for scaling + // would be to do everything in log space! + return comp == TransformationComp_Scaling ? aiVector3D(1.f, 1.f, 1.f) : aiVector3D(); + } + + void FBXConverter::GetRotationMatrix(Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out) + { + if (mode == Model::RotOrder_SphericXYZ) { + FBXImporter::LogError("Unsupported RotationMode: SphericXYZ"); + out = aiMatrix4x4(); + return; + } + + const float angle_epsilon = 1e-6f; + + out = aiMatrix4x4(); + + bool is_id[3] = { true, true, true }; + + aiMatrix4x4 temp[3]; + if (std::fabs(rotation.z) > angle_epsilon) { + aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(rotation.z), temp[2]); + is_id[2] = false; + } + if (std::fabs(rotation.y) > angle_epsilon) { + aiMatrix4x4::RotationY(AI_DEG_TO_RAD(rotation.y), temp[1]); + is_id[1] = false; + } + if (std::fabs(rotation.x) > angle_epsilon) { + aiMatrix4x4::RotationX(AI_DEG_TO_RAD(rotation.x), temp[0]); + is_id[0] = false; + } + + int order[3] = { -1, -1, -1 }; + + // note: rotation order is inverted since we're left multiplying as is usual in assimp + switch (mode) + { + case Model::RotOrder_EulerXYZ: + order[0] = 2; + order[1] = 1; + order[2] = 0; + break; + + case Model::RotOrder_EulerXZY: + order[0] = 1; + order[1] = 2; + order[2] = 0; + break; + + case Model::RotOrder_EulerYZX: + order[0] = 0; + order[1] = 2; + order[2] = 1; + break; + + case Model::RotOrder_EulerYXZ: + order[0] = 2; + order[1] = 0; + order[2] = 1; + break; + + case Model::RotOrder_EulerZXY: + order[0] = 1; + order[1] = 0; + order[2] = 2; + break; + + case Model::RotOrder_EulerZYX: + order[0] = 0; + order[1] = 1; + order[2] = 2; + break; + + default: + ai_assert(false); + break; + } + + ai_assert(order[0] >= 0); + ai_assert(order[0] <= 2); + ai_assert(order[1] >= 0); + ai_assert(order[1] <= 2); + ai_assert(order[2] >= 0); + ai_assert(order[2] <= 2); + + if (!is_id[order[0]]) { + out = temp[order[0]]; + } + + if (!is_id[order[1]]) { + out = out * temp[order[1]]; + } + + if (!is_id[order[2]]) { + out = out * temp[order[2]]; + } + } + + bool FBXConverter::NeedsComplexTransformationChain(const Model& model) + { + const PropertyTable& props = model.Props(); + bool ok; + + const float zero_epsilon = 1e-6f; + const aiVector3D all_ones(1.0f, 1.0f, 1.0f); + for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) { + const TransformationComp comp = static_cast(i); + + if (comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation) { + continue; + } + + bool scale_compare = (comp == TransformationComp_GeometricScaling || comp == TransformationComp_Scaling); + + const aiVector3D& v = PropertyGet(props, NameTransformationCompProperty(comp), ok); + if (ok && scale_compare) { + if ((v - all_ones).SquareLength() > zero_epsilon) { + return true; + } + } else if (ok) { + if (v.SquareLength() > zero_epsilon) { + return true; + } + } + } + + return false; + } + + std::string FBXConverter::NameTransformationChainNode(const std::string& name, TransformationComp comp) + { + return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp); + } + + bool FBXConverter::GenerateTransformationNodeChain(const Model& model, const std::string& name, std::vector& output_nodes, + std::vector& post_output_nodes) { + const PropertyTable& props = model.Props(); + const Model::RotOrder rot = model.RotationOrder(); + + bool ok; + + aiMatrix4x4 chain[TransformationComp_MAXIMUM]; + std::fill_n(chain, static_cast(TransformationComp_MAXIMUM), aiMatrix4x4()); + + // generate transformation matrices for all the different transformation components + const float zero_epsilon = 1e-6f; + const aiVector3D all_ones(1.0f, 1.0f, 1.0f); + bool is_complex = false; + + const aiVector3D& PreRotation = PropertyGet(props, "PreRotation", ok); + if (ok && PreRotation.SquareLength() > zero_epsilon) { + is_complex = true; + + GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PreRotation, chain[TransformationComp_PreRotation]); + } + + const aiVector3D& PostRotation = PropertyGet(props, "PostRotation", ok); + if (ok && PostRotation.SquareLength() > zero_epsilon) { + is_complex = true; + + GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PostRotation, chain[TransformationComp_PostRotation]); + } + + const aiVector3D& RotationPivot = PropertyGet(props, "RotationPivot", ok); + if (ok && RotationPivot.SquareLength() > zero_epsilon) { + is_complex = true; + + aiMatrix4x4::Translation(RotationPivot, chain[TransformationComp_RotationPivot]); + aiMatrix4x4::Translation(-RotationPivot, chain[TransformationComp_RotationPivotInverse]); + } + + const aiVector3D& RotationOffset = PropertyGet(props, "RotationOffset", ok); + if (ok && RotationOffset.SquareLength() > zero_epsilon) { + is_complex = true; + + aiMatrix4x4::Translation(RotationOffset, chain[TransformationComp_RotationOffset]); + } + + const aiVector3D& ScalingOffset = PropertyGet(props, "ScalingOffset", ok); + if (ok && ScalingOffset.SquareLength() > zero_epsilon) { + is_complex = true; + + aiMatrix4x4::Translation(ScalingOffset, chain[TransformationComp_ScalingOffset]); + } + + const aiVector3D& ScalingPivot = PropertyGet(props, "ScalingPivot", ok); + if (ok && ScalingPivot.SquareLength() > zero_epsilon) { + is_complex = true; + + aiMatrix4x4::Translation(ScalingPivot, chain[TransformationComp_ScalingPivot]); + aiMatrix4x4::Translation(-ScalingPivot, chain[TransformationComp_ScalingPivotInverse]); + } + + const aiVector3D& Translation = PropertyGet(props, "Lcl Translation", ok); + if (ok && Translation.SquareLength() > zero_epsilon) { + aiMatrix4x4::Translation(Translation, chain[TransformationComp_Translation]); + } + + const aiVector3D& Scaling = PropertyGet(props, "Lcl Scaling", ok); + if (ok && (Scaling - all_ones).SquareLength() > zero_epsilon) { + aiMatrix4x4::Scaling(Scaling, chain[TransformationComp_Scaling]); + } + + const aiVector3D& Rotation = PropertyGet(props, "Lcl Rotation", ok); + if (ok && Rotation.SquareLength() > zero_epsilon) { + GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]); + } + + const aiVector3D& GeometricScaling = PropertyGet(props, "GeometricScaling", ok); + if (ok && (GeometricScaling - all_ones).SquareLength() > zero_epsilon) { + is_complex = true; + aiMatrix4x4::Scaling(GeometricScaling, chain[TransformationComp_GeometricScaling]); + aiVector3D GeometricScalingInverse = GeometricScaling; + bool canscale = true; + for (unsigned int i = 0; i < 3; ++i) { + if (std::fabs(GeometricScalingInverse[i]) > zero_epsilon) { + GeometricScalingInverse[i] = 1.0f / GeometricScaling[i]; + } + else { + FBXImporter::LogError("cannot invert geometric scaling matrix with a 0.0 scale component"); + canscale = false; + break; + } + } + if (canscale) { + aiMatrix4x4::Scaling(GeometricScalingInverse, chain[TransformationComp_GeometricScalingInverse]); + } + } + + const aiVector3D& GeometricRotation = PropertyGet(props, "GeometricRotation", ok); + if (ok && GeometricRotation.SquareLength() > zero_epsilon) { + is_complex = true; + GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotation]); + GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotationInverse]); + chain[TransformationComp_GeometricRotationInverse].Inverse(); + } + + const aiVector3D& GeometricTranslation = PropertyGet(props, "GeometricTranslation", ok); + if (ok && GeometricTranslation.SquareLength() > zero_epsilon) { + is_complex = true; + aiMatrix4x4::Translation(GeometricTranslation, chain[TransformationComp_GeometricTranslation]); + aiMatrix4x4::Translation(-GeometricTranslation, chain[TransformationComp_GeometricTranslationInverse]); + } + + // is_complex needs to be consistent with NeedsComplexTransformationChain() + // or the interplay between this code and the animation converter would + // not be guaranteed. + ai_assert(NeedsComplexTransformationChain(model) == is_complex); + + // now, if we have more than just Translation, Scaling and Rotation, + // we need to generate a full node chain to accommodate for assimp's + // lack to express pivots and offsets. + if (is_complex && doc.Settings().preservePivots) { + FBXImporter::LogInfo("generating full transformation chain for node: " + name); + + // query the anim_chain_bits dictionary to find out which chain elements + // have associated node animation channels. These can not be dropped + // even if they have identity transform in bind pose. + NodeAnimBitMap::const_iterator it = node_anim_chain_bits.find(name); + const unsigned int anim_chain_bitmask = (it == node_anim_chain_bits.end() ? 0 : (*it).second); + + unsigned int bit = 0x1; + for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) { + const TransformationComp comp = static_cast(i); + + if (chain[i].IsIdentity() && (anim_chain_bitmask & bit) == 0) { + continue; + } + + if (comp == TransformationComp_PostRotation) { + chain[i] = chain[i].Inverse(); + } + + aiNode* nd = new aiNode(); + nd->mName.Set(NameTransformationChainNode(name, comp)); + nd->mTransformation = chain[i]; + + // geometric inverses go in a post-node chain + if (comp == TransformationComp_GeometricScalingInverse || + comp == TransformationComp_GeometricRotationInverse || + comp == TransformationComp_GeometricTranslationInverse + ) { + post_output_nodes.push_back(nd); + } + else { + output_nodes.push_back(nd); + } + } + + ai_assert(output_nodes.size()); + return true; + } + + // else, we can just multiply the matrices together + aiNode* nd = new aiNode(); + output_nodes.push_back(nd); + + // name passed to the method is already unique + nd->mName.Set(name); + + for (const auto &transform : chain) { + nd->mTransformation = nd->mTransformation * transform; + } + return false; + } + + void FBXConverter::SetupNodeMetadata(const Model& model, aiNode& nd) + { + const PropertyTable& props = model.Props(); + DirectPropertyMap unparsedProperties = props.GetUnparsedProperties(); + + // create metadata on node + const std::size_t numStaticMetaData = 2; + aiMetadata* data = aiMetadata::Alloc(static_cast(unparsedProperties.size() + numStaticMetaData)); + nd.mMetaData = data; + int index = 0; + + // find user defined properties (3ds Max) + data->Set(index++, "UserProperties", aiString(PropertyGet(props, "UDP3DSMAX", ""))); + // preserve the info that a node was marked as Null node in the original file. + data->Set(index++, "IsNull", model.IsNull() ? true : false); + + // add unparsed properties to the node's metadata + for (const DirectPropertyMap::value_type& prop : unparsedProperties) { + // Interpret the property as a concrete type + if (const TypedProperty* interpreted = prop.second->As >()) { + data->Set(index++, prop.first, interpreted->Value()); + } + else if (const TypedProperty* interpreted = prop.second->As >()) { + data->Set(index++, prop.first, interpreted->Value()); + } + else if (const TypedProperty* interpreted = prop.second->As >()) { + data->Set(index++, prop.first, interpreted->Value()); + } + else if (const TypedProperty* interpreted = prop.second->As >()) { + data->Set(index++, prop.first, interpreted->Value()); + } + else if (const TypedProperty* interpreted = prop.second->As >()) { + data->Set(index++, prop.first, aiString(interpreted->Value())); + } + else if (const TypedProperty* interpreted = prop.second->As >()) { + data->Set(index++, prop.first, interpreted->Value()); + } + else { + ai_assert(false); + } + } + } + + void FBXConverter::ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform) + { + const std::vector& geos = model.GetGeometry(); + + std::vector meshes; + meshes.reserve(geos.size()); + + for (const Geometry* geo : geos) { + + const MeshGeometry* const mesh = dynamic_cast(geo); + const LineGeometry* const line = dynamic_cast(geo); + if (mesh) { + const std::vector& indices = ConvertMesh(*mesh, model, node_global_transform, nd); + std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); + } + else if (line) { + const std::vector& indices = ConvertLine(*line, model, node_global_transform, nd); + std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); + } + else { + FBXImporter::LogWarn("ignoring unrecognized geometry: " + geo->Name()); + } + } + + if (meshes.size()) { + nd.mMeshes = new unsigned int[meshes.size()](); + nd.mNumMeshes = static_cast(meshes.size()); + + std::swap_ranges(meshes.begin(), meshes.end(), nd.mMeshes); + } + } + + std::vector FBXConverter::ConvertMesh(const MeshGeometry& mesh, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd) + { + std::vector temp; + + MeshMap::const_iterator it = meshes_converted.find(&mesh); + if (it != meshes_converted.end()) { + std::copy((*it).second.begin(), (*it).second.end(), std::back_inserter(temp)); + return temp; + } + + const std::vector& vertices = mesh.GetVertices(); + const std::vector& faces = mesh.GetFaceIndexCounts(); + if (vertices.empty() || faces.empty()) { + FBXImporter::LogWarn("ignoring empty geometry: " + mesh.Name()); + return temp; + } + + // one material per mesh maps easily to aiMesh. Multiple material + // meshes need to be split. + const MatIndexArray& mindices = mesh.GetMaterialIndices(); + if (doc.Settings().readMaterials && !mindices.empty()) { + const MatIndexArray::value_type base = mindices[0]; + for (MatIndexArray::value_type index : mindices) { + if (index != base) { + return ConvertMeshMultiMaterial(mesh, model, node_global_transform, nd); + } + } + } + + // faster code-path, just copy the data + temp.push_back(ConvertMeshSingleMaterial(mesh, model, node_global_transform, nd)); + return temp; + } + + std::vector FBXConverter::ConvertLine(const LineGeometry& line, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd) + { + std::vector temp; + + const std::vector& vertices = line.GetVertices(); + const std::vector& indices = line.GetIndices(); + if (vertices.empty() || indices.empty()) { + FBXImporter::LogWarn("ignoring empty line: " + line.Name()); + return temp; + } + + aiMesh* const out_mesh = SetupEmptyMesh(line, nd); + out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + + // copy vertices + out_mesh->mNumVertices = static_cast(vertices.size()); + out_mesh->mVertices = new aiVector3D[out_mesh->mNumVertices]; + std::copy(vertices.begin(), vertices.end(), out_mesh->mVertices); + + //Number of line segments (faces) is "Number of Points - Number of Endpoints" + //N.B.: Endpoints in FbxLine are denoted by negative indices. + //If such an Index is encountered, add 1 and multiply by -1 to get the real index. + unsigned int epcount = 0; + for (unsigned i = 0; i < indices.size(); i++) + { + if (indices[i] < 0) { + epcount++; + } + } + unsigned int pcount = static_cast( indices.size() ); + unsigned int scount = out_mesh->mNumFaces = pcount - epcount; + + aiFace* fac = out_mesh->mFaces = new aiFace[scount](); + for (unsigned int i = 0; i < pcount; ++i) { + if (indices[i] < 0) continue; + aiFace& f = *fac++; + f.mNumIndices = 2; //2 == aiPrimitiveType_LINE + f.mIndices = new unsigned int[2]; + f.mIndices[0] = indices[i]; + int segid = indices[(i + 1 == pcount ? 0 : i + 1)]; //If we have reached he last point, wrap around + f.mIndices[1] = (segid < 0 ? (segid + 1)*-1 : segid); //Convert EndPoint Index to normal Index + } + temp.push_back(static_cast(meshes.size() - 1)); + return temp; + } + + aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode& nd) + { + aiMesh* const out_mesh = new aiMesh(); + meshes.push_back(out_mesh); + meshes_converted[&mesh].push_back(static_cast(meshes.size() - 1)); + + // set name + std::string name = mesh.Name(); + if (name.substr(0, 10) == "Geometry::") { + name = name.substr(10); + } + + if (name.length()) { + out_mesh->mName.Set(name); + } + else + { + out_mesh->mName = nd.mName; + } + + return out_mesh; + } + + unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd) + { + const MatIndexArray& mindices = mesh.GetMaterialIndices(); + aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); + + const std::vector& vertices = mesh.GetVertices(); + const std::vector& faces = mesh.GetFaceIndexCounts(); + + // copy vertices + out_mesh->mNumVertices = static_cast(vertices.size()); + out_mesh->mVertices = new aiVector3D[vertices.size()]; + + std::copy(vertices.begin(), vertices.end(), out_mesh->mVertices); + + // generate dummy faces + out_mesh->mNumFaces = static_cast(faces.size()); + aiFace* fac = out_mesh->mFaces = new aiFace[faces.size()](); + + unsigned int cursor = 0; + for (unsigned int pcount : faces) { + aiFace& f = *fac++; + f.mNumIndices = pcount; + f.mIndices = new unsigned int[pcount]; + switch (pcount) + { + case 1: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + case 2: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + case 3: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + default: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + break; + } + for (unsigned int i = 0; i < pcount; ++i) { + f.mIndices[i] = cursor++; + } + } + + // copy normals + const std::vector& normals = mesh.GetNormals(); + if (normals.size()) { + ai_assert(normals.size() == vertices.size()); + + out_mesh->mNormals = new aiVector3D[vertices.size()]; + std::copy(normals.begin(), normals.end(), out_mesh->mNormals); + } + + // copy tangents - assimp requires both tangents and bitangents (binormals) + // to be present, or neither of them. Compute binormals from normals + // and tangents if needed. + const std::vector& tangents = mesh.GetTangents(); + const std::vector* binormals = &mesh.GetBinormals(); + + if (tangents.size()) { + std::vector tempBinormals; + if (!binormals->size()) { + if (normals.size()) { + tempBinormals.resize(normals.size()); + for (unsigned int i = 0; i < tangents.size(); ++i) { + tempBinormals[i] = normals[i] ^ tangents[i]; + } + + binormals = &tempBinormals; + } + else { + binormals = NULL; + } + } + + if (binormals) { + ai_assert(tangents.size() == vertices.size()); + ai_assert(binormals->size() == vertices.size()); + + out_mesh->mTangents = new aiVector3D[vertices.size()]; + std::copy(tangents.begin(), tangents.end(), out_mesh->mTangents); + + out_mesh->mBitangents = new aiVector3D[vertices.size()]; + std::copy(binormals->begin(), binormals->end(), out_mesh->mBitangents); + } + } + + // copy texture coords + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + const std::vector& uvs = mesh.GetTextureCoords(i); + if (uvs.empty()) { + break; + } + + aiVector3D* out_uv = out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()]; + for (const aiVector2D& v : uvs) { + *out_uv++ = aiVector3D(v.x, v.y, 0.0f); + } + + out_mesh->mNumUVComponents[i] = 2; + } + + // copy vertex colors + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) { + const std::vector& colors = mesh.GetVertexColors(i); + if (colors.empty()) { + break; + } + + out_mesh->mColors[i] = new aiColor4D[vertices.size()]; + std::copy(colors.begin(), colors.end(), out_mesh->mColors[i]); + } + + if (!doc.Settings().readMaterials || mindices.empty()) { + FBXImporter::LogError("no material assigned to mesh, setting default material"); + out_mesh->mMaterialIndex = GetDefaultMaterial(); + } + else { + ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]); + } + + if (doc.Settings().readWeights && mesh.DeformerSkin() != NULL) { + ConvertWeights(out_mesh, model, mesh, node_global_transform, NO_MATERIAL_SEPARATION); + } + + std::vector animMeshes; + for (const BlendShape* blendShape : mesh.GetBlendShapes()) { + for (const BlendShapeChannel* blendShapeChannel : blendShape->BlendShapeChannels()) { + const std::vector& shapeGeometries = blendShapeChannel->GetShapeGeometries(); + for (size_t i = 0; i < shapeGeometries.size(); i++) { + aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh); + const ShapeGeometry* shapeGeometry = shapeGeometries.at(i); + const std::vector& vertices = shapeGeometry->GetVertices(); + const std::vector& normals = shapeGeometry->GetNormals(); + const std::vector& indices = shapeGeometry->GetIndices(); + animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name())); + for (size_t j = 0; j < indices.size(); j++) { + unsigned int index = indices.at(j); + aiVector3D vertex = vertices.at(j); + aiVector3D normal = normals.at(j); + unsigned int count = 0; + const unsigned int* outIndices = mesh.ToOutputVertexIndex(index, count); + for (unsigned int k = 0; k < count; k++) { + unsigned int index = outIndices[k]; + animMesh->mVertices[index] += vertex; + if (animMesh->mNormals != nullptr) { + animMesh->mNormals[index] += normal; + animMesh->mNormals[index].NormalizeSafe(); + } + } + } + animMesh->mWeight = shapeGeometries.size() > 1 ? blendShapeChannel->DeformPercent() / 100.0f : 1.0f; + animMeshes.push_back(animMesh); + } + } + } + const size_t numAnimMeshes = animMeshes.size(); + if (numAnimMeshes > 0) { + out_mesh->mNumAnimMeshes = static_cast(numAnimMeshes); + out_mesh->mAnimMeshes = new aiAnimMesh*[numAnimMeshes]; + for (size_t i = 0; i < numAnimMeshes; i++) { + out_mesh->mAnimMeshes[i] = animMeshes.at(i); + } + } + return static_cast(meshes.size() - 1); + } + + std::vector FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd) + { + const MatIndexArray& mindices = mesh.GetMaterialIndices(); + ai_assert(mindices.size()); + + std::set had; + std::vector indices; + + for (MatIndexArray::value_type index : mindices) { + if (had.find(index) == had.end()) { + + indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, node_global_transform, nd)); + had.insert(index); + } + } + + return indices; + } + + unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, + MatIndexArray::value_type index, + const aiMatrix4x4& node_global_transform, + aiNode& nd) + { + aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); + + const MatIndexArray& mindices = mesh.GetMaterialIndices(); + const std::vector& vertices = mesh.GetVertices(); + const std::vector& faces = mesh.GetFaceIndexCounts(); + + const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != NULL; + + unsigned int count_faces = 0; + unsigned int count_vertices = 0; + + // count faces + std::vector::const_iterator itf = faces.begin(); + for (MatIndexArray::const_iterator it = mindices.begin(), + end = mindices.end(); it != end; ++it, ++itf) + { + if ((*it) != index) { + continue; + } + ++count_faces; + count_vertices += *itf; + } + + ai_assert(count_faces); + ai_assert(count_vertices); + + // mapping from output indices to DOM indexing, needed to resolve weights or blendshapes + std::vector reverseMapping; + std::map translateIndexMap; + if (process_weights || mesh.GetBlendShapes().size() > 0) { + reverseMapping.resize(count_vertices); + } + + // allocate output data arrays, but don't fill them yet + out_mesh->mNumVertices = count_vertices; + out_mesh->mVertices = new aiVector3D[count_vertices]; + + out_mesh->mNumFaces = count_faces; + aiFace* fac = out_mesh->mFaces = new aiFace[count_faces](); + + + // allocate normals + const std::vector& normals = mesh.GetNormals(); + if (normals.size()) { + ai_assert(normals.size() == vertices.size()); + out_mesh->mNormals = new aiVector3D[vertices.size()]; + } + + // allocate tangents, binormals. + const std::vector& tangents = mesh.GetTangents(); + const std::vector* binormals = &mesh.GetBinormals(); + std::vector tempBinormals; + + if (tangents.size()) { + if (!binormals->size()) { + if (normals.size()) { + // XXX this computes the binormals for the entire mesh, not only + // the part for which we need them. + tempBinormals.resize(normals.size()); + for (unsigned int i = 0; i < tangents.size(); ++i) { + tempBinormals[i] = normals[i] ^ tangents[i]; + } + + binormals = &tempBinormals; + } + else { + binormals = NULL; + } + } + + if (binormals) { + ai_assert(tangents.size() == vertices.size() && binormals->size() == vertices.size()); + + out_mesh->mTangents = new aiVector3D[vertices.size()]; + out_mesh->mBitangents = new aiVector3D[vertices.size()]; + } + } + + // allocate texture coords + unsigned int num_uvs = 0; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i, ++num_uvs) { + const std::vector& uvs = mesh.GetTextureCoords(i); + if (uvs.empty()) { + break; + } + + out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()]; + out_mesh->mNumUVComponents[i] = 2; + } + + // allocate vertex colors + unsigned int num_vcs = 0; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i, ++num_vcs) { + const std::vector& colors = mesh.GetVertexColors(i); + if (colors.empty()) { + break; + } + + out_mesh->mColors[i] = new aiColor4D[vertices.size()]; + } + + unsigned int cursor = 0, in_cursor = 0; + + itf = faces.begin(); + for (MatIndexArray::const_iterator it = mindices.begin(), end = mindices.end(); it != end; ++it, ++itf) + { + const unsigned int pcount = *itf; + if ((*it) != index) { + in_cursor += pcount; + continue; + } + + aiFace& f = *fac++; + + f.mNumIndices = pcount; + f.mIndices = new unsigned int[pcount]; + switch (pcount) + { + case 1: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + case 2: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + case 3: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + default: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + break; + } + for (unsigned int i = 0; i < pcount; ++i, ++cursor, ++in_cursor) { + f.mIndices[i] = cursor; + + if (reverseMapping.size()) { + reverseMapping[cursor] = in_cursor; + translateIndexMap[in_cursor] = cursor; + } + + out_mesh->mVertices[cursor] = vertices[in_cursor]; + + if (out_mesh->mNormals) { + out_mesh->mNormals[cursor] = normals[in_cursor]; + } + + if (out_mesh->mTangents) { + out_mesh->mTangents[cursor] = tangents[in_cursor]; + out_mesh->mBitangents[cursor] = (*binormals)[in_cursor]; + } + + for (unsigned int j = 0; j < num_uvs; ++j) { + const std::vector& uvs = mesh.GetTextureCoords(j); + out_mesh->mTextureCoords[j][cursor] = aiVector3D(uvs[in_cursor].x, uvs[in_cursor].y, 0.0f); + } + + for (unsigned int j = 0; j < num_vcs; ++j) { + const std::vector& cols = mesh.GetVertexColors(j); + out_mesh->mColors[j][cursor] = cols[in_cursor]; + } + } + } + + ConvertMaterialForMesh(out_mesh, model, mesh, index); + + if (process_weights) { + ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping); + } + + std::vector animMeshes; + for (const BlendShape* blendShape : mesh.GetBlendShapes()) { + for (const BlendShapeChannel* blendShapeChannel : blendShape->BlendShapeChannels()) { + const std::vector& shapeGeometries = blendShapeChannel->GetShapeGeometries(); + for (size_t i = 0; i < shapeGeometries.size(); i++) { + aiAnimMesh* animMesh = aiCreateAnimMesh(out_mesh); + const ShapeGeometry* shapeGeometry = shapeGeometries.at(i); + const std::vector& vertices = shapeGeometry->GetVertices(); + const std::vector& normals = shapeGeometry->GetNormals(); + const std::vector& indices = shapeGeometry->GetIndices(); + animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name())); + for (size_t j = 0; j < indices.size(); j++) { + unsigned int index = indices.at(j); + aiVector3D vertex = vertices.at(j); + aiVector3D normal = normals.at(j); + unsigned int count = 0; + const unsigned int* outIndices = mesh.ToOutputVertexIndex(index, count); + for (unsigned int k = 0; k < count; k++) { + unsigned int outIndex = outIndices[k]; + if (translateIndexMap.find(outIndex) == translateIndexMap.end()) + continue; + unsigned int index = translateIndexMap[outIndex]; + animMesh->mVertices[index] += vertex; + if (animMesh->mNormals != nullptr) { + animMesh->mNormals[index] += normal; + animMesh->mNormals[index].NormalizeSafe(); + } + } + } + animMesh->mWeight = shapeGeometries.size() > 1 ? blendShapeChannel->DeformPercent() / 100.0f : 1.0f; + animMeshes.push_back(animMesh); + } + } + } + + const size_t numAnimMeshes = animMeshes.size(); + if (numAnimMeshes > 0) { + out_mesh->mNumAnimMeshes = static_cast(numAnimMeshes); + out_mesh->mAnimMeshes = new aiAnimMesh*[numAnimMeshes]; + for (size_t i = 0; i < numAnimMeshes; i++) { + out_mesh->mAnimMeshes[i] = animMeshes.at(i); + } + } + + return static_cast(meshes.size() - 1); + } + + void FBXConverter::ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, + const aiMatrix4x4& node_global_transform, + unsigned int materialIndex, + std::vector* outputVertStartIndices) + { + ai_assert(geo.DeformerSkin()); + + std::vector out_indices; + std::vector index_out_indices; + std::vector count_out_indices; + + const Skin& sk = *geo.DeformerSkin(); + + std::vector bones; + bones.reserve(sk.Clusters().size()); + + const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION; + ai_assert(no_mat_check || outputVertStartIndices); + + try { + + for (const Cluster* cluster : sk.Clusters()) { + ai_assert(cluster); + + const WeightIndexArray& indices = cluster->GetIndices(); + + if (indices.empty() && mRemoveEmptyBones ) { + continue; + } + + const MatIndexArray& mats = geo.GetMaterialIndices(); + + bool ok = false; + + const size_t no_index_sentinel = std::numeric_limits::max(); + + count_out_indices.clear(); + index_out_indices.clear(); + out_indices.clear(); + + // now check if *any* of these weights is contained in the output mesh, + // taking notes so we don't need to do it twice. + for (WeightIndexArray::value_type index : indices) { + + unsigned int count = 0; + const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count); + // ToOutputVertexIndex only returns NULL if index is out of bounds + // which should never happen + ai_assert(out_idx != NULL); + + index_out_indices.push_back(no_index_sentinel); + count_out_indices.push_back(0); + + for (unsigned int i = 0; i < count; ++i) { + if (no_mat_check || static_cast(mats[geo.FaceForVertexIndex(out_idx[i])]) == materialIndex) { + + if (index_out_indices.back() == no_index_sentinel) { + index_out_indices.back() = out_indices.size(); + } + + if (no_mat_check) { + out_indices.push_back(out_idx[i]); + } else { + // this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn) + const std::vector::iterator it = std::lower_bound( + outputVertStartIndices->begin(), + outputVertStartIndices->end(), + out_idx[i] + ); + + out_indices.push_back(std::distance(outputVertStartIndices->begin(), it)); + } + + ++count_out_indices.back(); + ok = true; + } + } + } + + // if we found at least one, generate the output bones + // XXX this could be heavily simplified by collecting the bone + // data in a single step. + if (ok && mRemoveEmptyBones) { + ConvertCluster(bones, model, *cluster, out_indices, index_out_indices, + count_out_indices, node_global_transform); + } + } + } + catch (std::exception&) { + std::for_each(bones.begin(), bones.end(), Util::delete_fun()); + throw; + } + + if (bones.empty()) { + return; + } + + out->mBones = new aiBone*[bones.size()](); + out->mNumBones = static_cast(bones.size()); + + std::swap_ranges(bones.begin(), bones.end(), out->mBones); + } + + void FBXConverter::ConvertCluster(std::vector& bones, const Model& /*model*/, const Cluster& cl, + std::vector& out_indices, + std::vector& index_out_indices, + std::vector& count_out_indices, + const aiMatrix4x4& node_global_transform) + { + + aiBone* const bone = new aiBone(); + bones.push_back(bone); + + bone->mName = FixNodeName(cl.TargetNode()->Name()); + + bone->mOffsetMatrix = cl.TransformLink(); + bone->mOffsetMatrix.Inverse(); + + bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform; + + bone->mNumWeights = static_cast(out_indices.size()); + aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()]; + + const size_t no_index_sentinel = std::numeric_limits::max(); + const WeightArray& weights = cl.GetWeights(); + + const size_t c = index_out_indices.size(); + for (size_t i = 0; i < c; ++i) { + const size_t index_index = index_out_indices[i]; + + if (index_index == no_index_sentinel) { + continue; + } + + const size_t cc = count_out_indices[i]; + for (size_t j = 0; j < cc; ++j) { + aiVertexWeight& out_weight = *cursor++; + + out_weight.mVertexId = static_cast(out_indices[index_index + j]); + out_weight.mWeight = weights[i]; + } + } + } + + void FBXConverter::ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, + MatIndexArray::value_type materialIndex) + { + // locate source materials for this mesh + const std::vector& mats = model.GetMaterials(); + if (static_cast(materialIndex) >= mats.size() || materialIndex < 0) { + FBXImporter::LogError("material index out of bounds, setting default material"); + out->mMaterialIndex = GetDefaultMaterial(); + return; + } + + const Material* const mat = mats[materialIndex]; + MaterialMap::const_iterator it = materials_converted.find(mat); + if (it != materials_converted.end()) { + out->mMaterialIndex = (*it).second; + return; + } + + out->mMaterialIndex = ConvertMaterial(*mat, &geo); + materials_converted[mat] = out->mMaterialIndex; + } + + unsigned int FBXConverter::GetDefaultMaterial() + { + if (defaultMaterialIndex) { + return defaultMaterialIndex - 1; + } + + aiMaterial* out_mat = new aiMaterial(); + materials.push_back(out_mat); + + const aiColor3D diffuse = aiColor3D(0.8f, 0.8f, 0.8f); + out_mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + + aiString s; + s.Set(AI_DEFAULT_MATERIAL_NAME); + + out_mat->AddProperty(&s, AI_MATKEY_NAME); + + defaultMaterialIndex = static_cast(materials.size()); + return defaultMaterialIndex - 1; + } + + + unsigned int FBXConverter::ConvertMaterial(const Material& material, const MeshGeometry* const mesh) + { + const PropertyTable& props = material.Props(); + + // generate empty output material + aiMaterial* out_mat = new aiMaterial(); + materials_converted[&material] = static_cast(materials.size()); + + materials.push_back(out_mat); + + aiString str; + + // strip Material:: prefix + std::string name = material.Name(); + if (name.substr(0, 10) == "Material::") { + name = name.substr(10); + } + + // set material name if not empty - this could happen + // and there should be no key for it in this case. + if (name.length()) { + str.Set(name); + out_mat->AddProperty(&str, AI_MATKEY_NAME); + } + + // Set the shading mode as best we can: The FBX specification only mentions Lambert and Phong, and only Phong is mentioned in Assimp's aiShadingMode enum. + if (material.GetShadingModel() == "phong") + { + aiShadingMode shadingMode = aiShadingMode_Phong; + out_mat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); + } + + // shading stuff and colors + SetShadingPropertiesCommon(out_mat, props); + SetShadingPropertiesRaw( out_mat, props, material.Textures(), mesh ); + + // texture assignments + SetTextureProperties(out_mat, material.Textures(), mesh); + SetTextureProperties(out_mat, material.LayeredTextures(), mesh); + + return static_cast(materials.size() - 1); + } + + unsigned int FBXConverter::ConvertVideo(const Video& video) + { + // generate empty output texture + aiTexture* out_tex = new aiTexture(); + textures.push_back(out_tex); + + // assuming the texture is compressed + out_tex->mWidth = static_cast(video.ContentLength()); // total data size + out_tex->mHeight = 0; // fixed to 0 + + // steal the data from the Video to avoid an additional copy + out_tex->pcData = reinterpret_cast(const_cast(video).RelinquishContent()); + + // try to extract a hint from the file extension + const std::string& filename = video.RelativeFilename().empty() ? video.FileName() : video.RelativeFilename(); + std::string ext = BaseImporter::GetExtension(filename); + + if (ext == "jpeg") { + ext = "jpg"; + } + + if (ext.size() <= 3) { + memcpy(out_tex->achFormatHint, ext.c_str(), ext.size()); + } + + out_tex->mFilename.Set(filename.c_str()); + + return static_cast(textures.size() - 1); + } + + aiString FBXConverter::GetTexturePath(const Texture* tex) + { + aiString path; + path.Set(tex->RelativeFilename()); + + const Video* media = tex->Media(); + if (media != nullptr) { + bool textureReady = false; //tells if our texture is ready (if it was loaded or if it was found) + unsigned int index; + + VideoMap::const_iterator it = textures_converted.find(media); + if (it != textures_converted.end()) { + index = (*it).second; + textureReady = true; + } + else { + if (media->ContentLength() > 0) { + index = ConvertVideo(*media); + textures_converted[media] = index; + textureReady = true; + } + } + + // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture), if the texture is ready + if (doc.Settings().useLegacyEmbeddedTextureNaming) { + if (textureReady) { + // TODO: check the possibility of using the flag "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING" + // In FBX files textures are now stored internally by Assimp with their filename included + // Now Assimp can lookup through the loaded textures after all data is processed + // We need to load all textures before referencing them, as FBX file format order may reference a texture before loading it + // This may occur on this case too, it has to be studied + path.data[0] = '*'; + path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index); + } + } + } + + return path; + } + + void FBXConverter::TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, + const std::string& propName, + aiTextureType target, const MeshGeometry* const mesh) { + TextureMap::const_iterator it = textures.find(propName); + if (it == textures.end()) { + return; + } + + const Texture* const tex = (*it).second; + if (tex != 0) + { + aiString path = GetTexturePath(tex); + out_mat->AddProperty(&path, _AI_MATKEY_TEXTURE_BASE, target, 0); + + aiUVTransform uvTrafo; + // XXX handle all kinds of UV transformations + uvTrafo.mScaling = tex->UVScaling(); + uvTrafo.mTranslation = tex->UVTranslation(); + out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, 0); + + const PropertyTable& props = tex->Props(); + + int uvIndex = 0; + + bool ok; + const std::string& uvSet = PropertyGet(props, "UVSet", ok); + if (ok) { + // "default" is the name which usually appears in the FbxFileTexture template + if (uvSet != "default" && uvSet.length()) { + // this is a bit awkward - we need to find a mesh that uses this + // material and scan its UV channels for the given UV name because + // assimp references UV channels by index, not by name. + + // XXX: the case that UV channels may appear in different orders + // in meshes is unhandled. A possible solution would be to sort + // the UV channels alphabetically, but this would have the side + // effect that the primary (first) UV channel would sometimes + // be moved, causing trouble when users read only the first + // UV channel and ignore UV channel assignments altogether. + + const unsigned int matIndex = static_cast(std::distance(materials.begin(), + std::find(materials.begin(), materials.end(), out_mat) + )); + + + uvIndex = -1; + if (!mesh) + { + for (const MeshMap::value_type& v : meshes_converted) { + const MeshGeometry* const meshGeom = dynamic_cast (v.first); + if (!meshGeom) { + continue; + } + + const MatIndexArray& mats = meshGeom->GetMaterialIndices(); + if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) { + continue; + } + + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (meshGeom->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = meshGeom->GetTextureCoordChannelName(i); + if (name == uvSet) { + index = static_cast(i); + break; + } + } + if (index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + continue; + } + + if (uvIndex == -1) { + uvIndex = index; + } + else { + FBXImporter::LogWarn("the UV channel named " + uvSet + + " appears at different positions in meshes, results will be wrong"); + } + } + } + else + { + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (mesh->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = mesh->GetTextureCoordChannelName(i); + if (name == uvSet) { + index = static_cast(i); + break; + } + } + if (index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + } + + if (uvIndex == -1) { + uvIndex = index; + } + } + + if (uvIndex == -1) { + FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel"); + uvIndex = 0; + } + } + } + + out_mat->AddProperty(&uvIndex, 1, _AI_MATKEY_UVWSRC_BASE, target, 0); + } + } + + void FBXConverter::TrySetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, + const std::string& propName, + aiTextureType target, const MeshGeometry* const mesh) { + LayeredTextureMap::const_iterator it = layeredTextures.find(propName); + if (it == layeredTextures.end()) { + return; + } + + int texCount = (*it).second->textureCount(); + + // Set the blend mode for layered textures + int blendmode = (*it).second->GetBlendMode(); + out_mat->AddProperty(&blendmode, 1, _AI_MATKEY_TEXOP_BASE, target, 0); + + for (int texIndex = 0; texIndex < texCount; texIndex++) { + + const Texture* const tex = (*it).second->getTexture(texIndex); + + aiString path = GetTexturePath(tex); + out_mat->AddProperty(&path, _AI_MATKEY_TEXTURE_BASE, target, texIndex); + + aiUVTransform uvTrafo; + // XXX handle all kinds of UV transformations + uvTrafo.mScaling = tex->UVScaling(); + uvTrafo.mTranslation = tex->UVTranslation(); + out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, texIndex); + + const PropertyTable& props = tex->Props(); + + int uvIndex = 0; + + bool ok; + const std::string& uvSet = PropertyGet(props, "UVSet", ok); + if (ok) { + // "default" is the name which usually appears in the FbxFileTexture template + if (uvSet != "default" && uvSet.length()) { + // this is a bit awkward - we need to find a mesh that uses this + // material and scan its UV channels for the given UV name because + // assimp references UV channels by index, not by name. + + // XXX: the case that UV channels may appear in different orders + // in meshes is unhandled. A possible solution would be to sort + // the UV channels alphabetically, but this would have the side + // effect that the primary (first) UV channel would sometimes + // be moved, causing trouble when users read only the first + // UV channel and ignore UV channel assignments altogether. + + const unsigned int matIndex = static_cast(std::distance(materials.begin(), + std::find(materials.begin(), materials.end(), out_mat) + )); + + uvIndex = -1; + if (!mesh) + { + for (const MeshMap::value_type& v : meshes_converted) { + const MeshGeometry* const meshGeom = dynamic_cast (v.first); + if (!meshGeom) { + continue; + } + + const MatIndexArray& mats = meshGeom->GetMaterialIndices(); + if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) { + continue; + } + + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (meshGeom->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = meshGeom->GetTextureCoordChannelName(i); + if (name == uvSet) { + index = static_cast(i); + break; + } + } + if (index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + continue; + } + + if (uvIndex == -1) { + uvIndex = index; + } + else { + FBXImporter::LogWarn("the UV channel named " + uvSet + + " appears at different positions in meshes, results will be wrong"); + } + } + } + else + { + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (mesh->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = mesh->GetTextureCoordChannelName(i); + if (name == uvSet) { + index = static_cast(i); + break; + } + } + if (index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + } + + if (uvIndex == -1) { + uvIndex = index; + } + } + + if (uvIndex == -1) { + FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel"); + uvIndex = 0; + } + } + } + + out_mat->AddProperty(&uvIndex, 1, _AI_MATKEY_UVWSRC_BASE, target, texIndex); + } + } + + void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, const MeshGeometry* const mesh) + { + TrySetTextureProperties(out_mat, textures, "DiffuseColor", aiTextureType_DIFFUSE, mesh); + TrySetTextureProperties(out_mat, textures, "AmbientColor", aiTextureType_AMBIENT, mesh); + TrySetTextureProperties(out_mat, textures, "EmissiveColor", aiTextureType_EMISSIVE, mesh); + TrySetTextureProperties(out_mat, textures, "SpecularColor", aiTextureType_SPECULAR, mesh); + TrySetTextureProperties(out_mat, textures, "SpecularFactor", aiTextureType_SPECULAR, mesh); + TrySetTextureProperties(out_mat, textures, "TransparentColor", aiTextureType_OPACITY, mesh); + TrySetTextureProperties(out_mat, textures, "ReflectionColor", aiTextureType_REFLECTION, mesh); + TrySetTextureProperties(out_mat, textures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh); + TrySetTextureProperties(out_mat, textures, "NormalMap", aiTextureType_NORMALS, mesh); + TrySetTextureProperties(out_mat, textures, "Bump", aiTextureType_HEIGHT, mesh); + TrySetTextureProperties(out_mat, textures, "ShininessExponent", aiTextureType_SHININESS, mesh); + TrySetTextureProperties( out_mat, textures, "TransparencyFactor", aiTextureType_OPACITY, mesh ); + TrySetTextureProperties( out_mat, textures, "EmissiveFactor", aiTextureType_EMISSIVE, mesh ); + //Maya counterparts + TrySetTextureProperties(out_mat, textures, "Maya|DiffuseTexture", aiTextureType_DIFFUSE, mesh); + TrySetTextureProperties(out_mat, textures, "Maya|NormalTexture", aiTextureType_NORMALS, mesh); + TrySetTextureProperties(out_mat, textures, "Maya|SpecularTexture", aiTextureType_SPECULAR, mesh); + TrySetTextureProperties(out_mat, textures, "Maya|FalloffTexture", aiTextureType_OPACITY, mesh); + TrySetTextureProperties(out_mat, textures, "Maya|ReflectionMapTexture", aiTextureType_REFLECTION, mesh); + } + + void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh) + { + TrySetTextureProperties(out_mat, layeredTextures, "DiffuseColor", aiTextureType_DIFFUSE, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "AmbientColor", aiTextureType_AMBIENT, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "EmissiveColor", aiTextureType_EMISSIVE, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "SpecularColor", aiTextureType_SPECULAR, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "SpecularFactor", aiTextureType_SPECULAR, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "TransparentColor", aiTextureType_OPACITY, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "ReflectionColor", aiTextureType_REFLECTION, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "NormalMap", aiTextureType_NORMALS, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "Bump", aiTextureType_HEIGHT, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "ShininessExponent", aiTextureType_SHININESS, mesh); + TrySetTextureProperties( out_mat, layeredTextures, "EmissiveFactor", aiTextureType_EMISSIVE, mesh ); + TrySetTextureProperties( out_mat, layeredTextures, "TransparencyFactor", aiTextureType_OPACITY, mesh ); + } + + aiColor3D FBXConverter::GetColorPropertyFactored(const PropertyTable& props, const std::string& colorName, + const std::string& factorName, bool& result, bool useTemplate) + { + result = true; + + bool ok; + aiVector3D BaseColor = PropertyGet(props, colorName, ok, useTemplate); + if (!ok) { + result = false; + return aiColor3D(0.0f, 0.0f, 0.0f); + } + + // if no factor name, return the colour as is + if (factorName.empty()) { + return aiColor3D(BaseColor.x, BaseColor.y, BaseColor.z); + } + + // otherwise it should be multiplied by the factor, if found. + float factor = PropertyGet(props, factorName, ok, useTemplate); + if (ok) { + BaseColor *= factor; + } + return aiColor3D(BaseColor.x, BaseColor.y, BaseColor.z); + } + + aiColor3D FBXConverter::GetColorPropertyFromMaterial(const PropertyTable& props, const std::string& baseName, + bool& result) + { + return GetColorPropertyFactored(props, baseName + "Color", baseName + "Factor", result, true); + } + + aiColor3D FBXConverter::GetColorProperty(const PropertyTable& props, const std::string& colorName, + bool& result, bool useTemplate) + { + result = true; + bool ok; + const aiVector3D& ColorVec = PropertyGet(props, colorName, ok, useTemplate); + if (!ok) { + result = false; + return aiColor3D(0.0f, 0.0f, 0.0f); + } + return aiColor3D(ColorVec.x, ColorVec.y, ColorVec.z); + } + + void FBXConverter::SetShadingPropertiesCommon(aiMaterial* out_mat, const PropertyTable& props) + { + // Set shading properties. + // Modern FBX Files have two separate systems for defining these, + // with only the more comprehensive one described in the property template. + // Likely the other values are a legacy system, + // which is still always exported by the official FBX SDK. + // + // Blender's FBX import and export mostly ignore this legacy system, + // and as we only support recent versions of FBX anyway, we can do the same. + bool ok; + + const aiColor3D& Diffuse = GetColorPropertyFromMaterial(props, "Diffuse", ok); + if (ok) { + out_mat->AddProperty(&Diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + } + + const aiColor3D& Emissive = GetColorPropertyFromMaterial(props, "Emissive", ok); + if (ok) { + out_mat->AddProperty(&Emissive, 1, AI_MATKEY_COLOR_EMISSIVE); + } + + const aiColor3D& Ambient = GetColorPropertyFromMaterial(props, "Ambient", ok); + if (ok) { + out_mat->AddProperty(&Ambient, 1, AI_MATKEY_COLOR_AMBIENT); + } + + // we store specular factor as SHININESS_STRENGTH, so just get the color + const aiColor3D& Specular = GetColorProperty(props, "SpecularColor", ok, true); + if (ok) { + out_mat->AddProperty(&Specular, 1, AI_MATKEY_COLOR_SPECULAR); + } + + // and also try to get SHININESS_STRENGTH + const float SpecularFactor = PropertyGet(props, "SpecularFactor", ok, true); + if (ok) { + out_mat->AddProperty(&SpecularFactor, 1, AI_MATKEY_SHININESS_STRENGTH); + } + + // and the specular exponent + const float ShininessExponent = PropertyGet(props, "ShininessExponent", ok); + if (ok) { + out_mat->AddProperty(&ShininessExponent, 1, AI_MATKEY_SHININESS); + } + + // TransparentColor / TransparencyFactor... gee thanks FBX :rolleyes: + const aiColor3D& Transparent = GetColorPropertyFactored(props, "TransparentColor", "TransparencyFactor", ok); + float CalculatedOpacity = 1.0f; + if (ok) { + out_mat->AddProperty(&Transparent, 1, AI_MATKEY_COLOR_TRANSPARENT); + // as calculated by FBX SDK 2017: + CalculatedOpacity = 1.0f - ((Transparent.r + Transparent.g + Transparent.b) / 3.0f); + } + + // try to get the transparency factor + const float TransparencyFactor = PropertyGet(props, "TransparencyFactor", ok); + if (ok) { + out_mat->AddProperty(&TransparencyFactor, 1, AI_MATKEY_TRANSPARENCYFACTOR); + } + + // use of TransparencyFactor is inconsistent. + // Maya always stores it as 1.0, + // so we can't use it to set AI_MATKEY_OPACITY. + // Blender is more sensible and stores it as the alpha value. + // However both the FBX SDK and Blender always write an additional + // legacy "Opacity" field, so we can try to use that. + // + // If we can't find it, + // we can fall back to the value which the FBX SDK calculates + // from transparency colour (RGB) and factor (F) as + // 1.0 - F*((R+G+B)/3). + // + // There's no consistent way to interpret this opacity value, + // so it's up to clients to do the correct thing. + const float Opacity = PropertyGet(props, "Opacity", ok); + if (ok) { + out_mat->AddProperty(&Opacity, 1, AI_MATKEY_OPACITY); + } + else if (CalculatedOpacity != 1.0) { + out_mat->AddProperty(&CalculatedOpacity, 1, AI_MATKEY_OPACITY); + } + + // reflection color and factor are stored separately + const aiColor3D& Reflection = GetColorProperty(props, "ReflectionColor", ok, true); + if (ok) { + out_mat->AddProperty(&Reflection, 1, AI_MATKEY_COLOR_REFLECTIVE); + } + + float ReflectionFactor = PropertyGet(props, "ReflectionFactor", ok, true); + if (ok) { + out_mat->AddProperty(&ReflectionFactor, 1, AI_MATKEY_REFLECTIVITY); + } + + const float BumpFactor = PropertyGet(props, "BumpFactor", ok); + if (ok) { + out_mat->AddProperty(&BumpFactor, 1, AI_MATKEY_BUMPSCALING); + } + + const float DispFactor = PropertyGet(props, "DisplacementFactor", ok); + if (ok) { + out_mat->AddProperty(&DispFactor, 1, "$mat.displacementscaling", 0, 0); + } +} + + +void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTable& props, const TextureMap& textures, const MeshGeometry* const mesh) +{ + // Add all the unparsed properties with a "$raw." prefix + + const std::string prefix = "$raw."; + + for (const DirectPropertyMap::value_type& prop : props.GetUnparsedProperties()) { + + std::string name = prefix + prop.first; + + if (const TypedProperty* interpreted = prop.second->As >()) + { + out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); + } + else if (const TypedProperty* interpreted = prop.second->As >()) + { + out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); + } + else if (const TypedProperty* interpreted = prop.second->As >()) + { + out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); + } + else if (const TypedProperty* interpreted = prop.second->As >()) + { + out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); + } + else if (const TypedProperty* interpreted = prop.second->As >()) + { + out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); + } + else if (const TypedProperty* interpreted = prop.second->As >()) + { + int value = interpreted->Value() ? 1 : 0; + out_mat->AddProperty(&value, 1, name.c_str(), 0, 0); + } + else if (const TypedProperty* interpreted = prop.second->As >()) + { + const aiString value = aiString(interpreted->Value()); + out_mat->AddProperty(&value, name.c_str(), 0, 0); + } + } + + // Add the textures' properties + + for (TextureMap::const_iterator it = textures.begin(); it != textures.end(); it++) { + + std::string name = prefix + it->first; + + const Texture* const tex = (*it).second; + if (tex != nullptr) + { + aiString path; + path.Set(tex->RelativeFilename()); + + const Video* media = tex->Media(); + if (media != nullptr && media->ContentLength() > 0) { + unsigned int index; + + VideoMap::const_iterator it = textures_converted.find(media); + if (it != textures_converted.end()) { + index = (*it).second; + } + else { + index = ConvertVideo(*media); + textures_converted[media] = index; + } + + // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture) + path.data[0] = '*'; + path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index); + } + + out_mat->AddProperty(&path, (name + "|file").c_str(), aiTextureType_UNKNOWN, 0); + + aiUVTransform uvTrafo; + // XXX handle all kinds of UV transformations + uvTrafo.mScaling = tex->UVScaling(); + uvTrafo.mTranslation = tex->UVTranslation(); + out_mat->AddProperty(&uvTrafo, 1, (name + "|uvtrafo").c_str(), aiTextureType_UNKNOWN, 0); + + int uvIndex = 0; + + bool uvFound = false; + const std::string& uvSet = PropertyGet(tex->Props(), "UVSet", uvFound); + if (uvFound) { + // "default" is the name which usually appears in the FbxFileTexture template + if (uvSet != "default" && uvSet.length()) { + // this is a bit awkward - we need to find a mesh that uses this + // material and scan its UV channels for the given UV name because + // assimp references UV channels by index, not by name. + + // XXX: the case that UV channels may appear in different orders + // in meshes is unhandled. A possible solution would be to sort + // the UV channels alphabetically, but this would have the side + // effect that the primary (first) UV channel would sometimes + // be moved, causing trouble when users read only the first + // UV channel and ignore UV channel assignments altogether. + + std::vector::iterator materialIt = std::find(materials.begin(), materials.end(), out_mat); + const unsigned int matIndex = static_cast(std::distance(materials.begin(), materialIt)); + + uvIndex = -1; + if (!mesh) + { + for (const MeshMap::value_type& v : meshes_converted) { + const MeshGeometry* const meshGeom = dynamic_cast(v.first); + if (!meshGeom) { + continue; + } + + const MatIndexArray& mats = meshGeom->GetMaterialIndices(); + if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) { + continue; + } + + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (meshGeom->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = meshGeom->GetTextureCoordChannelName(i); + if (name == uvSet) { + index = static_cast(i); + break; + } + } + if (index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + continue; + } + + if (uvIndex == -1) { + uvIndex = index; + } + else { + FBXImporter::LogWarn("the UV channel named " + uvSet + " appears at different positions in meshes, results will be wrong"); + } + } + } + else + { + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (mesh->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = mesh->GetTextureCoordChannelName(i); + if (name == uvSet) { + index = static_cast(i); + break; + } + } + if (index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + } + + if (uvIndex == -1) { + uvIndex = index; + } + } + + if (uvIndex == -1) { + FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel"); + uvIndex = 0; + } + } + } + + out_mat->AddProperty(&uvIndex, 1, (name + "|uvwsrc").c_str(), aiTextureType_UNKNOWN, 0); + } + } + } + + + double FBXConverter::FrameRateToDouble(FileGlobalSettings::FrameRate fp, double customFPSVal) { + switch (fp) { + case FileGlobalSettings::FrameRate_DEFAULT: + return 1.0; + + case FileGlobalSettings::FrameRate_120: + return 120.0; + + case FileGlobalSettings::FrameRate_100: + return 100.0; + + case FileGlobalSettings::FrameRate_60: + return 60.0; + + case FileGlobalSettings::FrameRate_50: + return 50.0; + + case FileGlobalSettings::FrameRate_48: + return 48.0; + + case FileGlobalSettings::FrameRate_30: + case FileGlobalSettings::FrameRate_30_DROP: + return 30.0; + + case FileGlobalSettings::FrameRate_NTSC_DROP_FRAME: + case FileGlobalSettings::FrameRate_NTSC_FULL_FRAME: + return 29.9700262; + + case FileGlobalSettings::FrameRate_PAL: + return 25.0; + + case FileGlobalSettings::FrameRate_CINEMA: + return 24.0; + + case FileGlobalSettings::FrameRate_1000: + return 1000.0; + + case FileGlobalSettings::FrameRate_CINEMA_ND: + return 23.976; + + case FileGlobalSettings::FrameRate_CUSTOM: + return customFPSVal; + + case FileGlobalSettings::FrameRate_MAX: // this is to silence compiler warnings + break; + } + + ai_assert(false); + + return -1.0f; + } + + + void FBXConverter::ConvertAnimations() + { + // first of all determine framerate + const FileGlobalSettings::FrameRate fps = doc.GlobalSettings().TimeMode(); + const float custom = doc.GlobalSettings().CustomFrameRate(); + anim_fps = FrameRateToDouble(fps, custom); + + const std::vector& animations = doc.AnimationStacks(); + for (const AnimationStack* stack : animations) { + ConvertAnimationStack(*stack); + } + } + + std::string FBXConverter::FixNodeName(const std::string& name) { + // strip Model:: prefix, avoiding ambiguities (i.e. don't strip if + // this causes ambiguities, well possible between empty identifiers, + // such as "Model::" and ""). Make sure the behaviour is consistent + // across multiple calls to FixNodeName(). + if (name.substr(0, 7) == "Model::") { + std::string temp = name.substr(7); + return temp; + } + + return name; + } + + std::string FBXConverter::FixAnimMeshName(const std::string& name) { + if (name.length()) { + size_t indexOf = name.find_first_of("::"); + if (indexOf != std::string::npos && indexOf < name.size() - 2) { + return name.substr(indexOf + 2); + } + } + return name.length() ? name : "AnimMesh"; + } + + void FBXConverter::ConvertAnimationStack(const AnimationStack& st) + { + const AnimationLayerList& layers = st.Layers(); + if (layers.empty()) { + return; + } + + aiAnimation* const anim = new aiAnimation(); + animations.push_back(anim); + + // strip AnimationStack:: prefix + std::string name = st.Name(); + if (name.substr(0, 16) == "AnimationStack::") { + name = name.substr(16); + } + else if (name.substr(0, 11) == "AnimStack::") { + name = name.substr(11); + } + + anim->mName.Set(name); + + // need to find all nodes for which we need to generate node animations - + // it may happen that we need to merge multiple layers, though. + NodeMap node_map; + + // reverse mapping from curves to layers, much faster than querying + // the FBX DOM for it. + LayerMap layer_map; + + const char* prop_whitelist[] = { + "Lcl Scaling", + "Lcl Rotation", + "Lcl Translation", + "DeformPercent" + }; + + std::map morphAnimDatas; + + for (const AnimationLayer* layer : layers) { + ai_assert(layer); + const AnimationCurveNodeList& nodes = layer->Nodes(prop_whitelist, 4); + for (const AnimationCurveNode* node : nodes) { + ai_assert(node); + const Model* const model = dynamic_cast(node->Target()); + if (model) { + const std::string& name = FixNodeName(model->Name()); + node_map[name].push_back(node); + layer_map[node] = layer; + continue; + } + const BlendShapeChannel* const bsc = dynamic_cast(node->Target()); + if (bsc) { + ProcessMorphAnimDatas(&morphAnimDatas, bsc, node); + } + } + } + + // generate node animations + std::vector node_anims; + + double min_time = 1e10; + double max_time = -1e10; + + int64_t start_time = st.LocalStart(); + int64_t stop_time = st.LocalStop(); + bool has_local_startstop = start_time != 0 || stop_time != 0; + if (!has_local_startstop) { + // no time range given, so accept every keyframe and use the actual min/max time + // the numbers are INT64_MIN/MAX, the 20000 is for safety because GenerateNodeAnimations uses an epsilon of 10000 + start_time = -9223372036854775807ll + 20000; + stop_time = 9223372036854775807ll - 20000; + } + + try { + for (const NodeMap::value_type& kv : node_map) { + GenerateNodeAnimations(node_anims, + kv.first, + kv.second, + layer_map, + start_time, stop_time, + max_time, + min_time); + } + } + catch (std::exception&) { + std::for_each(node_anims.begin(), node_anims.end(), Util::delete_fun()); + throw; + } + + if (node_anims.size() || morphAnimDatas.size()) { + if (node_anims.size()) { + anim->mChannels = new aiNodeAnim*[node_anims.size()](); + anim->mNumChannels = static_cast(node_anims.size()); + std::swap_ranges(node_anims.begin(), node_anims.end(), anim->mChannels); + } + if (morphAnimDatas.size()) { + unsigned int numMorphMeshChannels = static_cast(morphAnimDatas.size()); + anim->mMorphMeshChannels = new aiMeshMorphAnim*[numMorphMeshChannels]; + anim->mNumMorphMeshChannels = numMorphMeshChannels; + unsigned int i = 0; + for (auto morphAnimIt : morphAnimDatas) { + morphAnimData* animData = morphAnimIt.second; + unsigned int numKeys = static_cast(animData->size()); + aiMeshMorphAnim* meshMorphAnim = new aiMeshMorphAnim(); + meshMorphAnim->mName.Set(morphAnimIt.first); + meshMorphAnim->mNumKeys = numKeys; + meshMorphAnim->mKeys = new aiMeshMorphKey[numKeys]; + unsigned int j = 0; + for (auto animIt : *animData) { + morphKeyData* keyData = animIt.second; + unsigned int numValuesAndWeights = static_cast(keyData->values.size()); + meshMorphAnim->mKeys[j].mNumValuesAndWeights = numValuesAndWeights; + meshMorphAnim->mKeys[j].mValues = new unsigned int[numValuesAndWeights]; + meshMorphAnim->mKeys[j].mWeights = new double[numValuesAndWeights]; + meshMorphAnim->mKeys[j].mTime = CONVERT_FBX_TIME(animIt.first) * anim_fps; + for (unsigned int k = 0; k < numValuesAndWeights; k++) { + meshMorphAnim->mKeys[j].mValues[k] = keyData->values.at(k); + meshMorphAnim->mKeys[j].mWeights[k] = keyData->weights.at(k); + } + j++; + } + anim->mMorphMeshChannels[i++] = meshMorphAnim; + } + } + } + else { + // empty animations would fail validation, so drop them + delete anim; + animations.pop_back(); + FBXImporter::LogInfo("ignoring empty AnimationStack (using IK?): " + name); + return; + } + + double start_time_fps = has_local_startstop ? (CONVERT_FBX_TIME(start_time) * anim_fps) : min_time; + double stop_time_fps = has_local_startstop ? (CONVERT_FBX_TIME(stop_time) * anim_fps) : max_time; + + // adjust relative timing for animation + for (unsigned int c = 0; c < anim->mNumChannels; c++) { + aiNodeAnim* channel = anim->mChannels[c]; + for (uint32_t i = 0; i < channel->mNumPositionKeys; i++) { + channel->mPositionKeys[i].mTime -= start_time_fps; + } + for (uint32_t i = 0; i < channel->mNumRotationKeys; i++) { + channel->mRotationKeys[i].mTime -= start_time_fps; + } + for (uint32_t i = 0; i < channel->mNumScalingKeys; i++) { + channel->mScalingKeys[i].mTime -= start_time_fps; + } + } + for (unsigned int c = 0; c < anim->mNumMorphMeshChannels; c++) { + aiMeshMorphAnim* channel = anim->mMorphMeshChannels[c]; + for (uint32_t i = 0; i < channel->mNumKeys; i++) { + channel->mKeys[i].mTime -= start_time_fps; + } + } + + // for some mysterious reason, mDuration is simply the maximum key -- the + // validator always assumes animations to start at zero. + anim->mDuration = stop_time_fps - start_time_fps; + anim->mTicksPerSecond = anim_fps; + } + + // ------------------------------------------------------------------------------------------------ + void FBXConverter::ProcessMorphAnimDatas(std::map* morphAnimDatas, const BlendShapeChannel* bsc, const AnimationCurveNode* node) { + std::vector bscConnections = doc.GetConnectionsBySourceSequenced(bsc->ID(), "Deformer"); + for (const Connection* bscConnection : bscConnections) { + auto bs = dynamic_cast(bscConnection->DestinationObject()); + if (bs) { + auto channelIt = std::find(bs->BlendShapeChannels().begin(), bs->BlendShapeChannels().end(), bsc); + if (channelIt != bs->BlendShapeChannels().end()) { + auto channelIndex = static_cast(std::distance(bs->BlendShapeChannels().begin(), channelIt)); + std::vector bsConnections = doc.GetConnectionsBySourceSequenced(bs->ID(), "Geometry"); + for (const Connection* bsConnection : bsConnections) { + auto geo = dynamic_cast(bsConnection->DestinationObject()); + if (geo) { + std::vector geoConnections = doc.GetConnectionsBySourceSequenced(geo->ID(), "Model"); + for (const Connection* geoConnection : geoConnections) { + auto model = dynamic_cast(geoConnection->DestinationObject()); + if (model) { + auto geoIt = std::find(model->GetGeometry().begin(), model->GetGeometry().end(), geo); + auto geoIndex = static_cast(std::distance(model->GetGeometry().begin(), geoIt)); + auto name = aiString(FixNodeName(model->Name() + "*")); + name.length = 1 + ASSIMP_itoa10(name.data + name.length, MAXLEN - 1, geoIndex); + morphAnimData* animData; + auto animIt = morphAnimDatas->find(name.C_Str()); + if (animIt == morphAnimDatas->end()) { + animData = new morphAnimData(); + morphAnimDatas->insert(std::make_pair(name.C_Str(), animData)); + } + else { + animData = animIt->second; + } + for (std::pair curvesIt : node->Curves()) { + if (curvesIt.first == "d|DeformPercent") { + const AnimationCurve* animationCurve = curvesIt.second; + const KeyTimeList& keys = animationCurve->GetKeys(); + const KeyValueList& values = animationCurve->GetValues(); + unsigned int k = 0; + for (auto key : keys) { + morphKeyData* keyData; + auto keyIt = animData->find(key); + if (keyIt == animData->end()) { + keyData = new morphKeyData(); + animData->insert(std::make_pair(key, keyData)); + } + else { + keyData = keyIt->second; + } + keyData->values.push_back(channelIndex); + keyData->weights.push_back(values.at(k) / 100.0f); + k++; + } + } + } + } + } + } + } + } + } + } + } + + // ------------------------------------------------------------------------------------------------ +#ifdef ASSIMP_BUILD_DEBUG + // ------------------------------------------------------------------------------------------------ + // sanity check whether the input is ok + static void validateAnimCurveNodes(const std::vector& curves, + bool strictMode) { + const Object* target(NULL); + for (const AnimationCurveNode* node : curves) { + if (!target) { + target = node->Target(); + } + if (node->Target() != target) { + FBXImporter::LogWarn("Node target is nullptr type."); + } + if (strictMode) { + ai_assert(node->Target() == target); + } + } + } +#endif // ASSIMP_BUILD_DEBUG + + // ------------------------------------------------------------------------------------------------ + void FBXConverter::GenerateNodeAnimations(std::vector& node_anims, + const std::string& fixed_name, + const std::vector& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time) + { + + NodeMap node_property_map; + ai_assert(curves.size()); + +#ifdef ASSIMP_BUILD_DEBUG + validateAnimCurveNodes(curves, doc.Settings().strictMode); +#endif + const AnimationCurveNode* curve_node = NULL; + for (const AnimationCurveNode* node : curves) { + ai_assert(node); + + if (node->TargetProperty().empty()) { + FBXImporter::LogWarn("target property for animation curve not set: " + node->Name()); + continue; + } + + curve_node = node; + if (node->Curves().empty()) { + FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode: " + node->Name()); + continue; + } + + node_property_map[node->TargetProperty()].push_back(node); + } + + ai_assert(curve_node); + ai_assert(curve_node->TargetAsModel()); + + const Model& target = *curve_node->TargetAsModel(); + + // check for all possible transformation components + NodeMap::const_iterator chain[TransformationComp_MAXIMUM]; + + bool has_any = false; + bool has_complex = false; + + for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) { + const TransformationComp comp = static_cast(i); + + // inverse pivots don't exist in the input, we just generate them + if (comp == TransformationComp_RotationPivotInverse || comp == TransformationComp_ScalingPivotInverse) { + chain[i] = node_property_map.end(); + continue; + } + + chain[i] = node_property_map.find(NameTransformationCompProperty(comp)); + if (chain[i] != node_property_map.end()) { + + // check if this curves contains redundant information by looking + // up the corresponding node's transformation chain. + if (doc.Settings().optimizeEmptyAnimationCurves && + IsRedundantAnimationData(target, comp, (*chain[i]).second)) { + + FBXImporter::LogDebug("dropping redundant animation channel for node " + target.Name()); + continue; + } + + has_any = true; + + if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling && comp != TransformationComp_Translation) + { + has_complex = true; + } + } + } + + if (!has_any) { + FBXImporter::LogWarn("ignoring node animation, did not find any transformation key frames"); + return; + } + + // this needs to play nicely with GenerateTransformationNodeChain() which will + // be invoked _later_ (animations come first). If this node has only rotation, + // scaling and translation _and_ there are no animated other components either, + // we can use a single node and also a single node animation channel. + if (!has_complex && !NeedsComplexTransformationChain(target)) { + + aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain, + node_property_map.end(), + layer_map, + start, stop, + max_time, + min_time, + true // input is TRS order, assimp is SRT + ); + + ai_assert(nd); + if (nd->mNumPositionKeys == 0 && nd->mNumRotationKeys == 0 && nd->mNumScalingKeys == 0) { + delete nd; + } + else { + node_anims.push_back(nd); + } + return; + } + + // otherwise, things get gruesome and we need separate animation channels + // for each part of the transformation chain. Remember which channels + // we generated and pass this information to the node conversion + // code to avoid nodes that have identity transform, but non-identity + // animations, being dropped. + unsigned int flags = 0, bit = 0x1; + for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) { + const TransformationComp comp = static_cast(i); + + if (chain[i] != node_property_map.end()) { + flags |= bit; + + ai_assert(comp != TransformationComp_RotationPivotInverse); + ai_assert(comp != TransformationComp_ScalingPivotInverse); + + const std::string& chain_name = NameTransformationChainNode(fixed_name, comp); + + aiNodeAnim* na = nullptr; + switch (comp) + { + case TransformationComp_Rotation: + case TransformationComp_PreRotation: + case TransformationComp_PostRotation: + case TransformationComp_GeometricRotation: + na = GenerateRotationNodeAnim(chain_name, + target, + (*chain[i]).second, + layer_map, + start, stop, + max_time, + min_time); + + break; + + case TransformationComp_RotationOffset: + case TransformationComp_RotationPivot: + case TransformationComp_ScalingOffset: + case TransformationComp_ScalingPivot: + case TransformationComp_Translation: + case TransformationComp_GeometricTranslation: + na = GenerateTranslationNodeAnim(chain_name, + target, + (*chain[i]).second, + layer_map, + start, stop, + max_time, + min_time); + + // pivoting requires us to generate an implicit inverse channel to undo the pivot translation + if (comp == TransformationComp_RotationPivot) { + const std::string& invName = NameTransformationChainNode(fixed_name, + TransformationComp_RotationPivotInverse); + + aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName, + target, + (*chain[i]).second, + layer_map, + start, stop, + max_time, + min_time, + true); + + ai_assert(inv); + if (inv->mNumPositionKeys == 0 && inv->mNumRotationKeys == 0 && inv->mNumScalingKeys == 0) { + delete inv; + } + else { + node_anims.push_back(inv); + } + + ai_assert(TransformationComp_RotationPivotInverse > i); + flags |= bit << (TransformationComp_RotationPivotInverse - i); + } + else if (comp == TransformationComp_ScalingPivot) { + const std::string& invName = NameTransformationChainNode(fixed_name, + TransformationComp_ScalingPivotInverse); + + aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName, + target, + (*chain[i]).second, + layer_map, + start, stop, + max_time, + min_time, + true); + + ai_assert(inv); + if (inv->mNumPositionKeys == 0 && inv->mNumRotationKeys == 0 && inv->mNumScalingKeys == 0) { + delete inv; + } + else { + node_anims.push_back(inv); + } + + ai_assert(TransformationComp_RotationPivotInverse > i); + flags |= bit << (TransformationComp_RotationPivotInverse - i); + } + + break; + + case TransformationComp_Scaling: + case TransformationComp_GeometricScaling: + na = GenerateScalingNodeAnim(chain_name, + target, + (*chain[i]).second, + layer_map, + start, stop, + max_time, + min_time); + + break; + + default: + ai_assert(false); + } + + ai_assert(na); + if (na->mNumPositionKeys == 0 && na->mNumRotationKeys == 0 && na->mNumScalingKeys == 0) { + delete na; + } + else { + node_anims.push_back(na); + } + continue; + } + } + + node_anim_chain_bits[fixed_name] = flags; + } + + + bool FBXConverter::IsRedundantAnimationData(const Model& target, + TransformationComp comp, + const std::vector& curves) { + ai_assert(curves.size()); + + // look for animation nodes with + // * sub channels for all relevant components set + // * one key/value pair per component + // * combined values match up the corresponding value in the bind pose node transformation + // only such nodes are 'redundant' for this function. + + if (curves.size() > 1) { + return false; + } + + const AnimationCurveNode& nd = *curves.front(); + const AnimationCurveMap& sub_curves = nd.Curves(); + + const AnimationCurveMap::const_iterator dx = sub_curves.find("d|X"); + const AnimationCurveMap::const_iterator dy = sub_curves.find("d|Y"); + const AnimationCurveMap::const_iterator dz = sub_curves.find("d|Z"); + + if (dx == sub_curves.end() || dy == sub_curves.end() || dz == sub_curves.end()) { + return false; + } + + const KeyValueList& vx = (*dx).second->GetValues(); + const KeyValueList& vy = (*dy).second->GetValues(); + const KeyValueList& vz = (*dz).second->GetValues(); + + if (vx.size() != 1 || vy.size() != 1 || vz.size() != 1) { + return false; + } + + const aiVector3D dyn_val = aiVector3D(vx[0], vy[0], vz[0]); + const aiVector3D& static_val = PropertyGet(target.Props(), + NameTransformationCompProperty(comp), + TransformationCompDefaultValue(comp) + ); + + const float epsilon = 1e-6f; + return (dyn_val - static_val).SquareLength() < epsilon; + } + + + aiNodeAnim* FBXConverter::GenerateRotationNodeAnim(const std::string& name, + const Model& target, + const std::vector& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time) + { + std::unique_ptr na(new aiNodeAnim()); + na->mNodeName.Set(name); + + ConvertRotationKeys(na.get(), curves, layer_map, start, stop, max_time, min_time, target.RotationOrder()); + + // dummy scaling key + na->mScalingKeys = new aiVectorKey[1]; + na->mNumScalingKeys = 1; + + na->mScalingKeys[0].mTime = 0.; + na->mScalingKeys[0].mValue = aiVector3D(1.0f, 1.0f, 1.0f); + + // dummy position key + na->mPositionKeys = new aiVectorKey[1]; + na->mNumPositionKeys = 1; + + na->mPositionKeys[0].mTime = 0.; + na->mPositionKeys[0].mValue = aiVector3D(); + + return na.release(); + } + + aiNodeAnim* FBXConverter::GenerateScalingNodeAnim(const std::string& name, + const Model& /*target*/, + const std::vector& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time) + { + std::unique_ptr na(new aiNodeAnim()); + na->mNodeName.Set(name); + + ConvertScaleKeys(na.get(), curves, layer_map, start, stop, max_time, min_time); + + // dummy rotation key + na->mRotationKeys = new aiQuatKey[1]; + na->mNumRotationKeys = 1; + + na->mRotationKeys[0].mTime = 0.; + na->mRotationKeys[0].mValue = aiQuaternion(); + + // dummy position key + na->mPositionKeys = new aiVectorKey[1]; + na->mNumPositionKeys = 1; + + na->mPositionKeys[0].mTime = 0.; + na->mPositionKeys[0].mValue = aiVector3D(); + + return na.release(); + } + + aiNodeAnim* FBXConverter::GenerateTranslationNodeAnim(const std::string& name, + const Model& /*target*/, + const std::vector& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time, + bool inverse) { + std::unique_ptr na(new aiNodeAnim()); + na->mNodeName.Set(name); + + ConvertTranslationKeys(na.get(), curves, layer_map, start, stop, max_time, min_time); + + if (inverse) { + for (unsigned int i = 0; i < na->mNumPositionKeys; ++i) { + na->mPositionKeys[i].mValue *= -1.0f; + } + } + + // dummy scaling key + na->mScalingKeys = new aiVectorKey[1]; + na->mNumScalingKeys = 1; + + na->mScalingKeys[0].mTime = 0.; + na->mScalingKeys[0].mValue = aiVector3D(1.0f, 1.0f, 1.0f); + + // dummy rotation key + na->mRotationKeys = new aiQuatKey[1]; + na->mNumRotationKeys = 1; + + na->mRotationKeys[0].mTime = 0.; + na->mRotationKeys[0].mValue = aiQuaternion(); + + return na.release(); + } + + aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name, + const Model& target, + NodeMap::const_iterator chain[TransformationComp_MAXIMUM], + NodeMap::const_iterator iter_end, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time, + bool reverse_order) + + { + std::unique_ptr na(new aiNodeAnim()); + na->mNodeName.Set(name); + + const PropertyTable& props = target.Props(); + + // need to convert from TRS order to SRT? + if (reverse_order) { + + aiVector3D def_scale = PropertyGet(props, "Lcl Scaling", aiVector3D(1.f, 1.f, 1.f)); + aiVector3D def_translate = PropertyGet(props, "Lcl Translation", aiVector3D(0.f, 0.f, 0.f)); + aiVector3D def_rot = PropertyGet(props, "Lcl Rotation", aiVector3D(0.f, 0.f, 0.f)); + + KeyFrameListList scaling; + KeyFrameListList translation; + KeyFrameListList rotation; + + if (chain[TransformationComp_Scaling] != iter_end) { + scaling = GetKeyframeList((*chain[TransformationComp_Scaling]).second, start, stop); + } + + if (chain[TransformationComp_Translation] != iter_end) { + translation = GetKeyframeList((*chain[TransformationComp_Translation]).second, start, stop); + } + + if (chain[TransformationComp_Rotation] != iter_end) { + rotation = GetKeyframeList((*chain[TransformationComp_Rotation]).second, start, stop); + } + + KeyFrameListList joined; + joined.insert(joined.end(), scaling.begin(), scaling.end()); + joined.insert(joined.end(), translation.begin(), translation.end()); + joined.insert(joined.end(), rotation.begin(), rotation.end()); + + const KeyTimeList& times = GetKeyTimeList(joined); + + aiQuatKey* out_quat = new aiQuatKey[times.size()]; + aiVectorKey* out_scale = new aiVectorKey[times.size()]; + aiVectorKey* out_translation = new aiVectorKey[times.size()]; + + if (times.size()) + { + ConvertTransformOrder_TRStoSRT(out_quat, out_scale, out_translation, + scaling, + translation, + rotation, + times, + max_time, + min_time, + target.RotationOrder(), + def_scale, + def_translate, + def_rot); + } + + // XXX remove duplicates / redundant keys which this operation did + // likely produce if not all three channels were equally dense. + + na->mNumScalingKeys = static_cast(times.size()); + na->mNumRotationKeys = na->mNumScalingKeys; + na->mNumPositionKeys = na->mNumScalingKeys; + + na->mScalingKeys = out_scale; + na->mRotationKeys = out_quat; + na->mPositionKeys = out_translation; + } + else { + + // if a particular transformation is not given, grab it from + // the corresponding node to meet the semantics of aiNodeAnim, + // which requires all of rotation, scaling and translation + // to be set. + if (chain[TransformationComp_Scaling] != iter_end) { + ConvertScaleKeys(na.get(), (*chain[TransformationComp_Scaling]).second, + layer_map, + start, stop, + max_time, + min_time); + } + else { + na->mScalingKeys = new aiVectorKey[1]; + na->mNumScalingKeys = 1; + + na->mScalingKeys[0].mTime = 0.; + na->mScalingKeys[0].mValue = PropertyGet(props, "Lcl Scaling", + aiVector3D(1.f, 1.f, 1.f)); + } + + if (chain[TransformationComp_Rotation] != iter_end) { + ConvertRotationKeys(na.get(), (*chain[TransformationComp_Rotation]).second, + layer_map, + start, stop, + max_time, + min_time, + target.RotationOrder()); + } + else { + na->mRotationKeys = new aiQuatKey[1]; + na->mNumRotationKeys = 1; + + na->mRotationKeys[0].mTime = 0.; + na->mRotationKeys[0].mValue = EulerToQuaternion( + PropertyGet(props, "Lcl Rotation", aiVector3D(0.f, 0.f, 0.f)), + target.RotationOrder()); + } + + if (chain[TransformationComp_Translation] != iter_end) { + ConvertTranslationKeys(na.get(), (*chain[TransformationComp_Translation]).second, + layer_map, + start, stop, + max_time, + min_time); + } + else { + na->mPositionKeys = new aiVectorKey[1]; + na->mNumPositionKeys = 1; + + na->mPositionKeys[0].mTime = 0.; + na->mPositionKeys[0].mValue = PropertyGet(props, "Lcl Translation", + aiVector3D(0.f, 0.f, 0.f)); + } + + } + return na.release(); + } + + FBXConverter::KeyFrameListList FBXConverter::GetKeyframeList(const std::vector& nodes, int64_t start, int64_t stop) + { + KeyFrameListList inputs; + inputs.reserve(nodes.size() * 3); + + //give some breathing room for rounding errors + int64_t adj_start = start - 10000; + int64_t adj_stop = stop + 10000; + + for (const AnimationCurveNode* node : nodes) { + ai_assert(node); + + const AnimationCurveMap& curves = node->Curves(); + for (const AnimationCurveMap::value_type& kv : curves) { + + unsigned int mapto; + if (kv.first == "d|X") { + mapto = 0; + } + else if (kv.first == "d|Y") { + mapto = 1; + } + else if (kv.first == "d|Z") { + mapto = 2; + } + else { + FBXImporter::LogWarn("ignoring scale animation curve, did not recognize target component"); + continue; + } + + const AnimationCurve* const curve = kv.second; + ai_assert(curve->GetKeys().size() == curve->GetValues().size() && curve->GetKeys().size()); + + //get values within the start/stop time window + std::shared_ptr Keys(new KeyTimeList()); + std::shared_ptr Values(new KeyValueList()); + const size_t count = curve->GetKeys().size(); + Keys->reserve(count); + Values->reserve(count); + for (size_t n = 0; n < count; n++) + { + int64_t k = curve->GetKeys().at(n); + if (k >= adj_start && k <= adj_stop) + { + Keys->push_back(k); + Values->push_back(curve->GetValues().at(n)); + } + } + + inputs.push_back(std::make_tuple(Keys, Values, mapto)); + } + } + return inputs; // pray for NRVO :-) + } + + + KeyTimeList FBXConverter::GetKeyTimeList(const KeyFrameListList& inputs) { + ai_assert(!inputs.empty()); + + // reserve some space upfront - it is likely that the key-frame lists + // have matching time values, so max(of all key-frame lists) should + // be a good estimate. + KeyTimeList keys; + + size_t estimate = 0; + for (const KeyFrameList& kfl : inputs) { + estimate = std::max(estimate, std::get<0>(kfl)->size()); + } + + keys.reserve(estimate); + + std::vector next_pos; + next_pos.resize(inputs.size(), 0); + + const size_t count = inputs.size(); + while (true) { + + int64_t min_tick = std::numeric_limits::max(); + for (size_t i = 0; i < count; ++i) { + const KeyFrameList& kfl = inputs[i]; + + if (std::get<0>(kfl)->size() > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) < min_tick) { + min_tick = std::get<0>(kfl)->at(next_pos[i]); + } + } + + if (min_tick == std::numeric_limits::max()) { + break; + } + keys.push_back(min_tick); + + for (size_t i = 0; i < count; ++i) { + const KeyFrameList& kfl = inputs[i]; + + + while (std::get<0>(kfl)->size() > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) == min_tick) { + ++next_pos[i]; + } + } + } + + return keys; + } + + void FBXConverter::InterpolateKeys(aiVectorKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs, + const aiVector3D& def_value, + double& max_time, + double& min_time) { + ai_assert(!keys.empty()); + ai_assert(nullptr != valOut); + + std::vector next_pos; + const size_t count(inputs.size()); + + next_pos.resize(inputs.size(), 0); + + for (KeyTimeList::value_type time : keys) { + ai_real result[3] = { def_value.x, def_value.y, def_value.z }; + + for (size_t i = 0; i < count; ++i) { + const KeyFrameList& kfl = inputs[i]; + + const size_t ksize = std::get<0>(kfl)->size(); + if (ksize == 0) { + continue; + } + if (ksize > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) == time) { + ++next_pos[i]; + } + + const size_t id0 = next_pos[i] > 0 ? next_pos[i] - 1 : 0; + const size_t id1 = next_pos[i] == ksize ? ksize - 1 : next_pos[i]; + + // use lerp for interpolation + const KeyValueList::value_type valueA = std::get<1>(kfl)->at(id0); + const KeyValueList::value_type valueB = std::get<1>(kfl)->at(id1); + + const KeyTimeList::value_type timeA = std::get<0>(kfl)->at(id0); + const KeyTimeList::value_type timeB = std::get<0>(kfl)->at(id1); + + const ai_real factor = timeB == timeA ? ai_real(0.) : static_cast((time - timeA)) / (timeB - timeA); + const ai_real interpValue = static_cast(valueA + (valueB - valueA) * factor); + + result[std::get<2>(kfl)] = interpValue; + } + + // magic value to convert fbx times to seconds + valOut->mTime = CONVERT_FBX_TIME(time) * anim_fps; + + min_time = std::min(min_time, valOut->mTime); + max_time = std::max(max_time, valOut->mTime); + + valOut->mValue.x = result[0]; + valOut->mValue.y = result[1]; + valOut->mValue.z = result[2]; + + ++valOut; + } + } + + void FBXConverter::InterpolateKeys(aiQuatKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs, + const aiVector3D& def_value, + double& maxTime, + double& minTime, + Model::RotOrder order) + { + ai_assert(!keys.empty()); + ai_assert(nullptr != valOut); + + std::unique_ptr temp(new aiVectorKey[keys.size()]); + InterpolateKeys(temp.get(), keys, inputs, def_value, maxTime, minTime); + + aiMatrix4x4 m; + + aiQuaternion lastq; + + for (size_t i = 0, c = keys.size(); i < c; ++i) { + + valOut[i].mTime = temp[i].mTime; + + GetRotationMatrix(order, temp[i].mValue, m); + aiQuaternion quat = aiQuaternion(aiMatrix3x3(m)); + + // take shortest path by checking the inner product + // http://www.3dkingdoms.com/weekly/weekly.php?a=36 + if (quat.x * lastq.x + quat.y * lastq.y + quat.z * lastq.z + quat.w * lastq.w < 0) + { + quat.x = -quat.x; + quat.y = -quat.y; + quat.z = -quat.z; + quat.w = -quat.w; + } + lastq = quat; + + valOut[i].mValue = quat; + } + } + + void FBXConverter::ConvertTransformOrder_TRStoSRT(aiQuatKey* out_quat, aiVectorKey* out_scale, + aiVectorKey* out_translation, + const KeyFrameListList& scaling, + const KeyFrameListList& translation, + const KeyFrameListList& rotation, + const KeyTimeList& times, + double& maxTime, + double& minTime, + Model::RotOrder order, + const aiVector3D& def_scale, + const aiVector3D& def_translate, + const aiVector3D& def_rotation) + { + if (rotation.size()) { + InterpolateKeys(out_quat, times, rotation, def_rotation, maxTime, minTime, order); + } + else { + for (size_t i = 0; i < times.size(); ++i) { + out_quat[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps; + out_quat[i].mValue = EulerToQuaternion(def_rotation, order); + } + } + + if (scaling.size()) { + InterpolateKeys(out_scale, times, scaling, def_scale, maxTime, minTime); + } + else { + for (size_t i = 0; i < times.size(); ++i) { + out_scale[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps; + out_scale[i].mValue = def_scale; + } + } + + if (translation.size()) { + InterpolateKeys(out_translation, times, translation, def_translate, maxTime, minTime); + } + else { + for (size_t i = 0; i < times.size(); ++i) { + out_translation[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps; + out_translation[i].mValue = def_translate; + } + } + + const size_t count = times.size(); + for (size_t i = 0; i < count; ++i) { + aiQuaternion& r = out_quat[i].mValue; + aiVector3D& s = out_scale[i].mValue; + aiVector3D& t = out_translation[i].mValue; + + aiMatrix4x4 mat, temp; + aiMatrix4x4::Translation(t, mat); + mat *= aiMatrix4x4(r.GetMatrix()); + mat *= aiMatrix4x4::Scaling(s, temp); + + mat.Decompose(s, r, t); + } + } + + aiQuaternion FBXConverter::EulerToQuaternion(const aiVector3D& rot, Model::RotOrder order) + { + aiMatrix4x4 m; + GetRotationMatrix(order, rot, m); + + return aiQuaternion(aiMatrix3x3(m)); + } + + void FBXConverter::ConvertScaleKeys(aiNodeAnim* na, const std::vector& nodes, const LayerMap& /*layers*/, + int64_t start, int64_t stop, + double& maxTime, + double& minTime) + { + ai_assert(nodes.size()); + + // XXX for now, assume scale should be blended geometrically (i.e. two + // layers should be multiplied with each other). There is a FBX + // property in the layer to specify the behaviour, though. + + const KeyFrameListList& inputs = GetKeyframeList(nodes, start, stop); + const KeyTimeList& keys = GetKeyTimeList(inputs); + + na->mNumScalingKeys = static_cast(keys.size()); + na->mScalingKeys = new aiVectorKey[keys.size()]; + if (keys.size() > 0) { + InterpolateKeys(na->mScalingKeys, keys, inputs, aiVector3D(1.0f, 1.0f, 1.0f), maxTime, minTime); + } + } + + void FBXConverter::ConvertTranslationKeys(aiNodeAnim* na, const std::vector& nodes, + const LayerMap& /*layers*/, + int64_t start, int64_t stop, + double& maxTime, + double& minTime) + { + ai_assert(nodes.size()); + + // XXX see notes in ConvertScaleKeys() + const KeyFrameListList& inputs = GetKeyframeList(nodes, start, stop); + const KeyTimeList& keys = GetKeyTimeList(inputs); + + na->mNumPositionKeys = static_cast(keys.size()); + na->mPositionKeys = new aiVectorKey[keys.size()]; + if (keys.size() > 0) + InterpolateKeys(na->mPositionKeys, keys, inputs, aiVector3D(0.0f, 0.0f, 0.0f), maxTime, minTime); + } + + void FBXConverter::ConvertRotationKeys(aiNodeAnim* na, const std::vector& nodes, + const LayerMap& /*layers*/, + int64_t start, int64_t stop, + double& maxTime, + double& minTime, + Model::RotOrder order) + { + ai_assert(nodes.size()); + + // XXX see notes in ConvertScaleKeys() + const std::vector< KeyFrameList >& inputs = GetKeyframeList(nodes, start, stop); + const KeyTimeList& keys = GetKeyTimeList(inputs); + + na->mNumRotationKeys = static_cast(keys.size()); + na->mRotationKeys = new aiQuatKey[keys.size()]; + if (!keys.empty()) { + InterpolateKeys(na->mRotationKeys, keys, inputs, aiVector3D(0.0f, 0.0f, 0.0f), maxTime, minTime, order); + } + } + + void FBXConverter::ConvertGlobalSettings() { + if (nullptr == out) { + return; + } + + out->mMetaData = aiMetadata::Alloc(15); + out->mMetaData->Set(0, "UpAxis", doc.GlobalSettings().UpAxis()); + out->mMetaData->Set(1, "UpAxisSign", doc.GlobalSettings().UpAxisSign()); + out->mMetaData->Set(2, "FrontAxis", doc.GlobalSettings().FrontAxis()); + out->mMetaData->Set(3, "FrontAxisSign", doc.GlobalSettings().FrontAxisSign()); + out->mMetaData->Set(4, "CoordAxis", doc.GlobalSettings().CoordAxis()); + out->mMetaData->Set(5, "CoordAxisSign", doc.GlobalSettings().CoordAxisSign()); + out->mMetaData->Set(6, "OriginalUpAxis", doc.GlobalSettings().OriginalUpAxis()); + out->mMetaData->Set(7, "OriginalUpAxisSign", doc.GlobalSettings().OriginalUpAxisSign()); + out->mMetaData->Set(8, "UnitScaleFactor", (double)doc.GlobalSettings().UnitScaleFactor()); + out->mMetaData->Set(9, "OriginalUnitScaleFactor", doc.GlobalSettings().OriginalUnitScaleFactor()); + out->mMetaData->Set(10, "AmbientColor", doc.GlobalSettings().AmbientColor()); + out->mMetaData->Set(11, "FrameRate", (int)doc.GlobalSettings().TimeMode()); + out->mMetaData->Set(12, "TimeSpanStart", doc.GlobalSettings().TimeSpanStart()); + out->mMetaData->Set(13, "TimeSpanStop", doc.GlobalSettings().TimeSpanStop()); + out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate()); + } + + void FBXConverter::ConvertToUnitScale( FbxUnit unit ) { + if (mCurrentUnit == unit) { + return; + } + + ai_real scale = 1.0; + if (mCurrentUnit == FbxUnit::cm) { + if (unit == FbxUnit::m) { + scale = (ai_real)0.01; + } else if (unit == FbxUnit::km) { + scale = (ai_real)0.00001; + } + } else if (mCurrentUnit == FbxUnit::m) { + if (unit == FbxUnit::cm) { + scale = (ai_real)100.0; + } else if (unit == FbxUnit::km) { + scale = (ai_real)0.001; + } + } else if (mCurrentUnit == FbxUnit::km) { + if (unit == FbxUnit::cm) { + scale = (ai_real)100000.0; + } else if (unit == FbxUnit::m) { + scale = (ai_real)1000.0; + } + } + + for (auto mesh : meshes) { + if (nullptr == mesh) { + continue; + } + + if (mesh->HasPositions()) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + aiVector3D &pos = mesh->mVertices[i]; + pos *= scale; + } + } + } + } + + void FBXConverter::TransferDataToScene() + { + ai_assert(!out->mMeshes); + ai_assert(!out->mNumMeshes); + + // note: the trailing () ensures initialization with NULL - not + // many C++ users seem to know this, so pointing it out to avoid + // confusion why this code works. + + if (meshes.size()) { + out->mMeshes = new aiMesh*[meshes.size()](); + out->mNumMeshes = static_cast(meshes.size()); + + std::swap_ranges(meshes.begin(), meshes.end(), out->mMeshes); + } + + if (materials.size()) { + out->mMaterials = new aiMaterial*[materials.size()](); + out->mNumMaterials = static_cast(materials.size()); + + std::swap_ranges(materials.begin(), materials.end(), out->mMaterials); + } + + if (animations.size()) { + out->mAnimations = new aiAnimation*[animations.size()](); + out->mNumAnimations = static_cast(animations.size()); + + std::swap_ranges(animations.begin(), animations.end(), out->mAnimations); + } + + if (lights.size()) { + out->mLights = new aiLight*[lights.size()](); + out->mNumLights = static_cast(lights.size()); + + std::swap_ranges(lights.begin(), lights.end(), out->mLights); + } + + if (cameras.size()) { + out->mCameras = new aiCamera*[cameras.size()](); + out->mNumCameras = static_cast(cameras.size()); + + std::swap_ranges(cameras.begin(), cameras.end(), out->mCameras); + } + + if (textures.size()) { + out->mTextures = new aiTexture*[textures.size()](); + out->mNumTextures = static_cast(textures.size()); + + std::swap_ranges(textures.begin(), textures.end(), out->mTextures); + } + } + + // ------------------------------------------------------------------------------------------------ + void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit) + { + FBXConverter converter(out, doc, removeEmptyBones, unit); + } + + } // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBX/FBXConverter.h b/thirdparty/assimp/code/FBX/FBXConverter.h new file mode 100644 index 0000000000..17a7bc56b7 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXConverter.h @@ -0,0 +1,482 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXDConverter.h + * @brief FBX DOM to aiScene conversion + */ +#ifndef INCLUDED_AI_FBX_CONVERTER_H +#define INCLUDED_AI_FBX_CONVERTER_H + +#include "FBXParser.h" +#include "FBXMeshGeometry.h" +#include "FBXDocument.h" +#include "FBXUtil.h" +#include "FBXProperties.h" +#include "FBXImporter.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +struct aiScene; +struct aiNode; +struct aiMaterial; + +struct morphKeyData { + std::vector values; + std::vector weights; +}; +typedef std::map morphAnimData; + +namespace Assimp { +namespace FBX { + +class Document; + +enum class FbxUnit { + cm = 0, + m, + km, + NumUnits, + + Undefined +}; + +/** + * Convert a FBX #Document to #aiScene + * @param out Empty scene to be populated + * @param doc Parsed FBX document + * @param removeEmptyBones Will remove bones, which do not have any references to vertices. + */ +void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit); + +/** Dummy class to encapsulate the conversion process */ +class FBXConverter { +public: + /** + * The different parts that make up the final local transformation of a fbx-node + */ + enum TransformationComp { + TransformationComp_GeometricScalingInverse = 0, + TransformationComp_GeometricRotationInverse, + TransformationComp_GeometricTranslationInverse, + TransformationComp_Translation, + TransformationComp_RotationOffset, + TransformationComp_RotationPivot, + TransformationComp_PreRotation, + TransformationComp_Rotation, + TransformationComp_PostRotation, + TransformationComp_RotationPivotInverse, + TransformationComp_ScalingOffset, + TransformationComp_ScalingPivot, + TransformationComp_Scaling, + TransformationComp_ScalingPivotInverse, + TransformationComp_GeometricTranslation, + TransformationComp_GeometricRotation, + TransformationComp_GeometricScaling, + + TransformationComp_MAXIMUM + }; + +public: + FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit); + ~FBXConverter(); + +private: + // ------------------------------------------------------------------------------------------------ + // find scene root and trigger recursive scene conversion + void ConvertRootNode(); + + // ------------------------------------------------------------------------------------------------ + // collect and assign child nodes + void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4()); + + // ------------------------------------------------------------------------------------------------ + void ConvertLights(const Model& model, const std::string &orig_name ); + + // ------------------------------------------------------------------------------------------------ + void ConvertCameras(const Model& model, const std::string &orig_name ); + + // ------------------------------------------------------------------------------------------------ + void ConvertLight( const Light& light, const std::string &orig_name ); + + // ------------------------------------------------------------------------------------------------ + void ConvertCamera( const Camera& cam, const std::string &orig_name ); + + // ------------------------------------------------------------------------------------------------ + void GetUniqueName( const std::string &name, std::string& uniqueName ); + + // ------------------------------------------------------------------------------------------------ + // this returns unified names usable within assimp identifiers (i.e. no space characters - + // while these would be allowed, they are a potential trouble spot so better not use them). + const char* NameTransformationComp(TransformationComp comp); + + // ------------------------------------------------------------------------------------------------ + // Returns an unique name for a node or traverses up a hierarchy until a non-empty name is found and + // then makes this name unique + std::string MakeUniqueNodeName(const Model* const model, const aiNode& parent); + + // ------------------------------------------------------------------------------------------------ + // note: this returns the REAL fbx property names + const char* NameTransformationCompProperty(TransformationComp comp); + + // ------------------------------------------------------------------------------------------------ + aiVector3D TransformationCompDefaultValue(TransformationComp comp); + + // ------------------------------------------------------------------------------------------------ + void GetRotationMatrix(Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out); + // ------------------------------------------------------------------------------------------------ + /** + * checks if a node has more than just scaling, rotation and translation components + */ + bool NeedsComplexTransformationChain(const Model& model); + + // ------------------------------------------------------------------------------------------------ + // note: name must be a FixNodeName() result + std::string NameTransformationChainNode(const std::string& name, TransformationComp comp); + + // ------------------------------------------------------------------------------------------------ + /** + * note: memory for output_nodes will be managed by the caller + */ + bool GenerateTransformationNodeChain(const Model& model, const std::string& name, std::vector& output_nodes, std::vector& post_output_nodes); + + // ------------------------------------------------------------------------------------------------ + void SetupNodeMetadata(const Model& model, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + void ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform); + + // ------------------------------------------------------------------------------------------------ + // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed + std::vector ConvertMesh(const MeshGeometry& mesh, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + std::vector ConvertLine(const LineGeometry& line, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + std::vector ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, + MatIndexArray::value_type index, + const aiMatrix4x4& node_global_transform, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits::max() */ + static_cast(-1); + + // ------------------------------------------------------------------------------------------------ + /** + * - if materialIndex == NO_MATERIAL_SEPARATION, materials are not taken into + * account when determining which weights to include. + * - outputVertStartIndices is only used when a material index is specified, it gives for + * each output vertex the DOM index it maps to. + */ + void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, + const aiMatrix4x4& node_global_transform = aiMatrix4x4(), + unsigned int materialIndex = NO_MATERIAL_SEPARATION, + std::vector* outputVertStartIndices = NULL); + + // ------------------------------------------------------------------------------------------------ + void ConvertCluster(std::vector& bones, const Model& /*model*/, const Cluster& cl, + std::vector& out_indices, + std::vector& index_out_indices, + std::vector& count_out_indices, + const aiMatrix4x4& node_global_transform); + + // ------------------------------------------------------------------------------------------------ + void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, + MatIndexArray::value_type materialIndex); + + // ------------------------------------------------------------------------------------------------ + unsigned int GetDefaultMaterial(); + + // ------------------------------------------------------------------------------------------------ + // Material -> aiMaterial + unsigned int ConvertMaterial(const Material& material, const MeshGeometry* const mesh); + + // ------------------------------------------------------------------------------------------------ + // Video -> aiTexture + unsigned int ConvertVideo(const Video& video); + + // ------------------------------------------------------------------------------------------------ + // convert embedded texture if necessary and return actual texture path + aiString GetTexturePath(const Texture* tex); + + // ------------------------------------------------------------------------------------------------ + void TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, + const std::string& propName, + aiTextureType target, const MeshGeometry* const mesh); + + // ------------------------------------------------------------------------------------------------ + void TrySetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, + const std::string& propName, + aiTextureType target, const MeshGeometry* const mesh); + + // ------------------------------------------------------------------------------------------------ + void SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, const MeshGeometry* const mesh); + + // ------------------------------------------------------------------------------------------------ + void SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh); + + // ------------------------------------------------------------------------------------------------ + aiColor3D GetColorPropertyFromMaterial(const PropertyTable& props, const std::string& baseName, + bool& result); + aiColor3D GetColorPropertyFactored(const PropertyTable& props, const std::string& colorName, + const std::string& factorName, bool& result, bool useTemplate = true); + aiColor3D GetColorProperty(const PropertyTable& props, const std::string& colorName, + bool& result, bool useTemplate = true); + + // ------------------------------------------------------------------------------------------------ + void SetShadingPropertiesCommon(aiMaterial* out_mat, const PropertyTable& props); + void SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTable& props, const TextureMap& textures, const MeshGeometry* const mesh); + + // ------------------------------------------------------------------------------------------------ + // get the number of fps for a FrameRate enumerated value + static double FrameRateToDouble(FileGlobalSettings::FrameRate fp, double customFPSVal = -1.0); + + // ------------------------------------------------------------------------------------------------ + // convert animation data to aiAnimation et al + void ConvertAnimations(); + + // ------------------------------------------------------------------------------------------------ + // takes a fbx node name and returns the identifier to be used in the assimp output scene. + // the function is guaranteed to provide consistent results over multiple invocations + // UNLESS RenameNode() is called for a particular node name. + std::string FixNodeName(const std::string& name); + std::string FixAnimMeshName(const std::string& name); + + typedef std::map LayerMap; + + // XXX: better use multi_map .. + typedef std::map > NodeMap; + + // ------------------------------------------------------------------------------------------------ + void ConvertAnimationStack(const AnimationStack& st); + + // ------------------------------------------------------------------------------------------------ + void ProcessMorphAnimDatas(std::map* morphAnimDatas, const BlendShapeChannel* bsc, const AnimationCurveNode* node); + + // ------------------------------------------------------------------------------------------------ + void GenerateNodeAnimations(std::vector& node_anims, + const std::string& fixed_name, + const std::vector& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time); + + // ------------------------------------------------------------------------------------------------ + bool IsRedundantAnimationData(const Model& target, + TransformationComp comp, + const std::vector& curves); + + // ------------------------------------------------------------------------------------------------ + aiNodeAnim* GenerateRotationNodeAnim(const std::string& name, + const Model& target, + const std::vector& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time); + + // ------------------------------------------------------------------------------------------------ + aiNodeAnim* GenerateScalingNodeAnim(const std::string& name, + const Model& /*target*/, + const std::vector& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time); + + // ------------------------------------------------------------------------------------------------ + aiNodeAnim* GenerateTranslationNodeAnim(const std::string& name, + const Model& /*target*/, + const std::vector& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time, + bool inverse = false); + + // ------------------------------------------------------------------------------------------------ + // generate node anim, extracting only Rotation, Scaling and Translation from the given chain + aiNodeAnim* GenerateSimpleNodeAnim(const std::string& name, + const Model& target, + NodeMap::const_iterator chain[TransformationComp_MAXIMUM], + NodeMap::const_iterator iter_end, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time, + bool reverse_order = false); + + // key (time), value, mapto (component index) + typedef std::tuple, std::shared_ptr, unsigned int > KeyFrameList; + typedef std::vector KeyFrameListList; + + // ------------------------------------------------------------------------------------------------ + KeyFrameListList GetKeyframeList(const std::vector& nodes, int64_t start, int64_t stop); + + // ------------------------------------------------------------------------------------------------ + KeyTimeList GetKeyTimeList(const KeyFrameListList& inputs); + + // ------------------------------------------------------------------------------------------------ + void InterpolateKeys(aiVectorKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs, + const aiVector3D& def_value, + double& max_time, + double& min_time); + + // ------------------------------------------------------------------------------------------------ + void InterpolateKeys(aiQuatKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs, + const aiVector3D& def_value, + double& maxTime, + double& minTime, + Model::RotOrder order); + + // ------------------------------------------------------------------------------------------------ + void ConvertTransformOrder_TRStoSRT(aiQuatKey* out_quat, aiVectorKey* out_scale, + aiVectorKey* out_translation, + const KeyFrameListList& scaling, + const KeyFrameListList& translation, + const KeyFrameListList& rotation, + const KeyTimeList& times, + double& maxTime, + double& minTime, + Model::RotOrder order, + const aiVector3D& def_scale, + const aiVector3D& def_translate, + const aiVector3D& def_rotation); + + // ------------------------------------------------------------------------------------------------ + // euler xyz -> quat + aiQuaternion EulerToQuaternion(const aiVector3D& rot, Model::RotOrder order); + + // ------------------------------------------------------------------------------------------------ + void ConvertScaleKeys(aiNodeAnim* na, const std::vector& nodes, const LayerMap& /*layers*/, + int64_t start, int64_t stop, + double& maxTime, + double& minTime); + + // ------------------------------------------------------------------------------------------------ + void ConvertTranslationKeys(aiNodeAnim* na, const std::vector& nodes, + const LayerMap& /*layers*/, + int64_t start, int64_t stop, + double& maxTime, + double& minTime); + + // ------------------------------------------------------------------------------------------------ + void ConvertRotationKeys(aiNodeAnim* na, const std::vector& nodes, + const LayerMap& /*layers*/, + int64_t start, int64_t stop, + double& maxTime, + double& minTime, + Model::RotOrder order); + + void ConvertGlobalSettings(); + + // ------------------------------------------------------------------------------------------------ + // Will perform the conversion from a given unit to the requested unit. + void ConvertToUnitScale(FbxUnit unit); + + // ------------------------------------------------------------------------------------------------ + // copy generated meshes, animations, lights, cameras and textures to the output scene + void TransferDataToScene(); + +private: + // 0: not assigned yet, others: index is value - 1 + unsigned int defaultMaterialIndex; + + std::vector meshes; + std::vector materials; + std::vector animations; + std::vector lights; + std::vector cameras; + std::vector textures; + + using MaterialMap = std::map; + MaterialMap materials_converted; + + using VideoMap = std::map; + VideoMap textures_converted; + + using MeshMap = std::map >; + MeshMap meshes_converted; + + // fixed node name -> which trafo chain components have animations? + using NodeAnimBitMap = std::map ; + NodeAnimBitMap node_anim_chain_bits; + + // number of nodes with the same name + using NodeNameCache = std::unordered_map; + NodeNameCache mNodeNames; + + double anim_fps; + + aiScene* const out; + const FBX::Document& doc; + + bool mRemoveEmptyBones; + + FbxUnit mCurrentUnit; +}; + +} +} + +#endif // INCLUDED_AI_FBX_CONVERTER_H diff --git a/thirdparty/assimp/code/FBX/FBXDeformer.cpp b/thirdparty/assimp/code/FBX/FBXDeformer.cpp new file mode 100644 index 0000000000..6927553450 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXDeformer.cpp @@ -0,0 +1,213 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXNoteAttribute.cpp + * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXParser.h" +#include "FBXDocument.h" +#include "FBXMeshGeometry.h" +#include "FBXImporter.h" +#include "FBXDocumentUtil.h" + +namespace Assimp { +namespace FBX { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Deformer::Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name) + : Object(id,element,name) +{ + const Scope& sc = GetRequiredScope(element); + + const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2)); + props = GetPropertyTable(doc,"Deformer.Fbx" + classname,element,sc,true); +} + + +// ------------------------------------------------------------------------------------------------ +Deformer::~Deformer() +{ + +} + + +// ------------------------------------------------------------------------------------------------ +Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: Deformer(id,element,doc,name) +, node() +{ + const Scope& sc = GetRequiredScope(element); + + const Element* const Indexes = sc["Indexes"]; + const Element* const Weights = sc["Weights"]; + + const Element& Transform = GetRequiredElement(sc,"Transform",&element); + const Element& TransformLink = GetRequiredElement(sc,"TransformLink",&element); + + transform = ReadMatrix(Transform); + transformLink = ReadMatrix(TransformLink); + + // it is actually possible that there be Deformer's with no weights + if (!!Indexes != !!Weights) { + DOMError("either Indexes or Weights are missing from Cluster",&element); + } + + if(Indexes) { + ParseVectorDataArray(indices,*Indexes); + ParseVectorDataArray(weights,*Weights); + } + + if(indices.size() != weights.size()) { + DOMError("sizes of index and weight array don't match up",&element); + } + + // read assigned node + const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Model"); + for(const Connection* con : conns) { + const Model* const mod = ProcessSimpleConnection(*con, false, "Model -> Cluster", element); + if(mod) { + node = mod; + break; + } + } + + if (!node) { + DOMError("failed to read target Node for Cluster",&element); + } +} + + +// ------------------------------------------------------------------------------------------------ +Cluster::~Cluster() +{ + +} + + +// ------------------------------------------------------------------------------------------------ +Skin::Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: Deformer(id,element,doc,name) +, accuracy( 0.0f ) { + const Scope& sc = GetRequiredScope(element); + + const Element* const Link_DeformAcuracy = sc["Link_DeformAcuracy"]; + if(Link_DeformAcuracy) { + accuracy = ParseTokenAsFloat(GetRequiredToken(*Link_DeformAcuracy,0)); + } + + // resolve assigned clusters + const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer"); + + clusters.reserve(conns.size()); + for(const Connection* con : conns) { + + const Cluster* const cluster = ProcessSimpleConnection(*con, false, "Cluster -> Skin", element); + if(cluster) { + clusters.push_back(cluster); + continue; + } + } +} + + +// ------------------------------------------------------------------------------------------------ +Skin::~Skin() +{ + +} +// ------------------------------------------------------------------------------------------------ +BlendShape::BlendShape(uint64_t id, const Element& element, const Document& doc, const std::string& name) + : Deformer(id, element, doc, name) +{ + const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer"); + blendShapeChannels.reserve(conns.size()); + for (const Connection* con : conns) { + const BlendShapeChannel* const bspc = ProcessSimpleConnection(*con, false, "BlendShapeChannel -> BlendShape", element); + if (bspc) { + blendShapeChannels.push_back(bspc); + continue; + } + } +} +// ------------------------------------------------------------------------------------------------ +BlendShape::~BlendShape() +{ + +} +// ------------------------------------------------------------------------------------------------ +BlendShapeChannel::BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name) + : Deformer(id, element, doc, name) +{ + const Scope& sc = GetRequiredScope(element); + const Element* const DeformPercent = sc["DeformPercent"]; + if (DeformPercent) { + percent = ParseTokenAsFloat(GetRequiredToken(*DeformPercent, 0)); + } + const Element* const FullWeights = sc["FullWeights"]; + if (FullWeights) { + ParseVectorDataArray(fullWeights, *FullWeights); + } + const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(), "Geometry"); + shapeGeometries.reserve(conns.size()); + for (const Connection* con : conns) { + const ShapeGeometry* const sg = ProcessSimpleConnection(*con, false, "Shape -> BlendShapeChannel", element); + if (sg) { + shapeGeometries.push_back(sg); + continue; + } + } +} +// ------------------------------------------------------------------------------------------------ +BlendShapeChannel::~BlendShapeChannel() +{ + +} +// ------------------------------------------------------------------------------------------------ +} +} +#endif + diff --git a/thirdparty/assimp/code/FBX/FBXDocument.cpp b/thirdparty/assimp/code/FBX/FBXDocument.cpp new file mode 100644 index 0000000000..1af08fe6d8 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXDocument.cpp @@ -0,0 +1,726 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the* + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXDocument.cpp + * @brief Implementation of the FBX DOM classes + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXDocument.h" +#include "FBXMeshGeometry.h" +#include "FBXParser.h" +#include "FBXUtil.h" +#include "FBXImporter.h" +#include "FBXImportSettings.h" +#include "FBXDocumentUtil.h" +#include "FBXProperties.h" + +#include +#include +#include + +namespace Assimp { +namespace FBX { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc) +: doc(doc) +, element(element) +, id(id) +, flags() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +LazyObject::~LazyObject() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +const Object* LazyObject::Get(bool dieOnError) +{ + if(IsBeingConstructed() || FailedToConstruct()) { + return nullptr; + } + + if (object.get()) { + return object.get(); + } + + // if this is the root object, we return a dummy since there + // is no root object int he fbx file - it is just referenced + // with id 0. + if(id == 0L) { + object.reset(new Object(id, element, "Model::RootNode")); + return object.get(); + } + + const Token& key = element.KeyToken(); + const TokenList& tokens = element.Tokens(); + + if(tokens.size() < 3) { + DOMError("expected at least 3 tokens: id, name and class tag",&element); + } + + const char* err; + std::string name = ParseTokenAsString(*tokens[1],err); + if (err) { + DOMError(err,&element); + } + + // small fix for binary reading: binary fbx files don't use + // prefixes such as Model:: in front of their names. The + // loading code expects this at many places, though! + // so convert the binary representation (a 0x0001) to the + // double colon notation. + if(tokens[1]->IsBinary()) { + for (size_t i = 0; i < name.length(); ++i) { + if (name[i] == 0x0 && name[i+1] == 0x1) { + name = name.substr(i+2) + "::" + name.substr(0,i); + } + } + } + + const std::string classtag = ParseTokenAsString(*tokens[2],err); + if (err) { + DOMError(err,&element); + } + + // prevent recursive calls + flags |= BEING_CONSTRUCTED; + + try { + // this needs to be relatively fast since it happens a lot, + // so avoid constructing strings all the time. + const char* obtype = key.begin(); + const size_t length = static_cast(key.end()-key.begin()); + + // For debugging + //dumpObjectClassInfo( objtype, classtag ); + + if (!strncmp(obtype,"Geometry",length)) { + if (!strcmp(classtag.c_str(),"Mesh")) { + object.reset(new MeshGeometry(id,element,name,doc)); + } + if (!strcmp(classtag.c_str(), "Shape")) { + object.reset(new ShapeGeometry(id, element, name, doc)); + } + if (!strcmp(classtag.c_str(), "Line")) { + object.reset(new LineGeometry(id, element, name, doc)); + } + } + else if (!strncmp(obtype,"NodeAttribute",length)) { + if (!strcmp(classtag.c_str(),"Camera")) { + object.reset(new Camera(id,element,doc,name)); + } + else if (!strcmp(classtag.c_str(),"CameraSwitcher")) { + object.reset(new CameraSwitcher(id,element,doc,name)); + } + else if (!strcmp(classtag.c_str(),"Light")) { + object.reset(new Light(id,element,doc,name)); + } + else if (!strcmp(classtag.c_str(),"Null")) { + object.reset(new Null(id,element,doc,name)); + } + else if (!strcmp(classtag.c_str(),"LimbNode")) { + object.reset(new LimbNode(id,element,doc,name)); + } + } + else if (!strncmp(obtype,"Deformer",length)) { + if (!strcmp(classtag.c_str(),"Cluster")) { + object.reset(new Cluster(id,element,doc,name)); + } + else if (!strcmp(classtag.c_str(),"Skin")) { + object.reset(new Skin(id,element,doc,name)); + } + else if (!strcmp(classtag.c_str(), "BlendShape")) { + object.reset(new BlendShape(id, element, doc, name)); + } + else if (!strcmp(classtag.c_str(), "BlendShapeChannel")) { + object.reset(new BlendShapeChannel(id, element, doc, name)); + } + } + else if ( !strncmp( obtype, "Model", length ) ) { + // FK and IK effectors are not supported + if ( strcmp( classtag.c_str(), "IKEffector" ) && strcmp( classtag.c_str(), "FKEffector" ) ) { + object.reset( new Model( id, element, doc, name ) ); + } + } + else if (!strncmp(obtype,"Material",length)) { + object.reset(new Material(id,element,doc,name)); + } + else if (!strncmp(obtype,"Texture",length)) { + object.reset(new Texture(id,element,doc,name)); + } + else if (!strncmp(obtype,"LayeredTexture",length)) { + object.reset(new LayeredTexture(id,element,doc,name)); + } + else if (!strncmp(obtype,"Video",length)) { + object.reset(new Video(id,element,doc,name)); + } + else if (!strncmp(obtype,"AnimationStack",length)) { + object.reset(new AnimationStack(id,element,name,doc)); + } + else if (!strncmp(obtype,"AnimationLayer",length)) { + object.reset(new AnimationLayer(id,element,name,doc)); + } + // note: order matters for these two + else if (!strncmp(obtype,"AnimationCurve",length)) { + object.reset(new AnimationCurve(id,element,name,doc)); + } + else if (!strncmp(obtype,"AnimationCurveNode",length)) { + object.reset(new AnimationCurveNode(id,element,name,doc)); + } + } + catch(std::exception& ex) { + flags &= ~BEING_CONSTRUCTED; + flags |= FAILED_TO_CONSTRUCT; + + if(dieOnError || doc.Settings().strictMode) { + throw; + } + + // note: the error message is already formatted, so raw logging is ok + if(!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_ERROR(ex.what()); + } + return NULL; + } + + if (!object.get()) { + //DOMError("failed to convert element to DOM object, class: " + classtag + ", name: " + name,&element); + } + + flags &= ~BEING_CONSTRUCTED; + return object.get(); +} + +// ------------------------------------------------------------------------------------------------ +Object::Object(uint64_t id, const Element& element, const std::string& name) +: element(element) +, name(name) +, id(id) +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +Object::~Object() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +FileGlobalSettings::FileGlobalSettings(const Document& doc, std::shared_ptr props) +: props(props) +, doc(doc) +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +FileGlobalSettings::~FileGlobalSettings() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +Document::Document(const Parser& parser, const ImportSettings& settings) +: settings(settings) +, parser(parser) +{ + // Cannot use array default initialization syntax because vc8 fails on it + for (auto &timeStamp : creationTimeStamp) { + timeStamp = 0; + } + + ReadHeader(); + ReadPropertyTemplates(); + + ReadGlobalSettings(); + + // This order is important, connections need parsed objects to check + // whether connections are ok or not. Objects may not be evaluated yet, + // though, since this may require valid connections. + ReadObjects(); + ReadConnections(); +} + +// ------------------------------------------------------------------------------------------------ +Document::~Document() +{ + for(ObjectMap::value_type& v : objects) { + delete v.second; + } + + for(ConnectionMap::value_type& v : src_connections) { + delete v.second; + } + // |dest_connections| contain the same Connection objects as the |src_connections| +} + +// ------------------------------------------------------------------------------------------------ +static const unsigned int LowerSupportedVersion = 7100; +static const unsigned int UpperSupportedVersion = 7400; + +void Document::ReadHeader() { + // Read ID objects from "Objects" section + const Scope& sc = parser.GetRootScope(); + const Element* const ehead = sc["FBXHeaderExtension"]; + if(!ehead || !ehead->Compound()) { + DOMError("no FBXHeaderExtension dictionary found"); + } + + const Scope& shead = *ehead->Compound(); + fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead,"FBXVersion",ehead),0)); + + // While we may have some success with newer files, we don't support + // the older 6.n fbx format + if(fbxVersion < LowerSupportedVersion ) { + DOMError("unsupported, old format version, supported are only FBX 2011, FBX 2012 and FBX 2013"); + } + if(fbxVersion > UpperSupportedVersion ) { + if(Settings().strictMode) { + DOMError("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013" + " (turn off strict mode to try anyhow) "); + } + else { + DOMWarning("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013," + " trying to read it nevertheless"); + } + } + + const Element* const ecreator = shead["Creator"]; + if(ecreator) { + creator = ParseTokenAsString(GetRequiredToken(*ecreator,0)); + } + + const Element* const etimestamp = shead["CreationTimeStamp"]; + if(etimestamp && etimestamp->Compound()) { + const Scope& stimestamp = *etimestamp->Compound(); + creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Year"),0)); + creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Month"),0)); + creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Day"),0)); + creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Hour"),0)); + creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Minute"),0)); + creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Second"),0)); + creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Millisecond"),0)); + } +} + +// ------------------------------------------------------------------------------------------------ +void Document::ReadGlobalSettings() +{ + const Scope& sc = parser.GetRootScope(); + const Element* const ehead = sc["GlobalSettings"]; + if ( nullptr == ehead || !ehead->Compound() ) { + DOMWarning( "no GlobalSettings dictionary found" ); + globals.reset(new FileGlobalSettings(*this, std::make_shared())); + return; + } + + std::shared_ptr props = GetPropertyTable( *this, "", *ehead, *ehead->Compound(), true ); + + //double v = PropertyGet( *props.get(), std::string("UnitScaleFactor"), 1.0 ); + + if(!props) { + DOMError("GlobalSettings dictionary contains no property table"); + } + + globals.reset(new FileGlobalSettings(*this, props)); +} + +// ------------------------------------------------------------------------------------------------ +void Document::ReadObjects() +{ + // read ID objects from "Objects" section + const Scope& sc = parser.GetRootScope(); + const Element* const eobjects = sc["Objects"]; + if(!eobjects || !eobjects->Compound()) { + DOMError("no Objects dictionary found"); + } + + // add a dummy entry to represent the Model::RootNode object (id 0), + // which is only indirectly defined in the input file + objects[0] = new LazyObject(0L, *eobjects, *this); + + const Scope& sobjects = *eobjects->Compound(); + for(const ElementMap::value_type& el : sobjects.Elements()) { + + // extract ID + const TokenList& tok = el.second->Tokens(); + + if (tok.empty()) { + DOMError("expected ID after object key",el.second); + } + + const char* err; + const uint64_t id = ParseTokenAsID(*tok[0], err); + if(err) { + DOMError(err,el.second); + } + + // id=0 is normally implicit + if(id == 0L) { + DOMError("encountered object with implicitly defined id 0",el.second); + } + + if(objects.find(id) != objects.end()) { + DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second); + } + + objects[id] = new LazyObject(id, *el.second, *this); + + // grab all animation stacks upfront since there is no listing of them + if(!strcmp(el.first.c_str(),"AnimationStack")) { + animationStacks.push_back(id); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void Document::ReadPropertyTemplates() +{ + const Scope& sc = parser.GetRootScope(); + // read property templates from "Definitions" section + const Element* const edefs = sc["Definitions"]; + if(!edefs || !edefs->Compound()) { + DOMWarning("no Definitions dictionary found"); + return; + } + + const Scope& sdefs = *edefs->Compound(); + const ElementCollection otypes = sdefs.GetCollection("ObjectType"); + for(ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) { + const Element& el = *(*it).second; + const Scope* sc = el.Compound(); + if(!sc) { + DOMWarning("expected nested scope in ObjectType, ignoring",&el); + continue; + } + + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + DOMWarning("expected name for ObjectType element, ignoring",&el); + continue; + } + + const std::string& oname = ParseTokenAsString(*tok[0]); + + const ElementCollection templs = sc->GetCollection("PropertyTemplate"); + for(ElementMap::const_iterator it = templs.first; it != templs.second; ++it) { + const Element& el = *(*it).second; + const Scope* sc = el.Compound(); + if(!sc) { + DOMWarning("expected nested scope in PropertyTemplate, ignoring",&el); + continue; + } + + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + DOMWarning("expected name for PropertyTemplate element, ignoring",&el); + continue; + } + + const std::string& pname = ParseTokenAsString(*tok[0]); + + const Element* Properties70 = (*sc)["Properties70"]; + if(Properties70) { + std::shared_ptr props = std::make_shared( + *Properties70,std::shared_ptr(static_cast(NULL)) + ); + + templates[oname+"."+pname] = props; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void Document::ReadConnections() +{ + const Scope& sc = parser.GetRootScope(); + // read property templates from "Definitions" section + const Element* const econns = sc["Connections"]; + if(!econns || !econns->Compound()) { + DOMError("no Connections dictionary found"); + } + + uint64_t insertionOrder = 0l; + const Scope& sconns = *econns->Compound(); + const ElementCollection conns = sconns.GetCollection("C"); + for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) { + const Element& el = *(*it).second; + const std::string& type = ParseTokenAsString(GetRequiredToken(el,0)); + + // PP = property-property connection, ignored for now + // (tokens: "PP", ID1, "Property1", ID2, "Property2") + if ( type == "PP" ) { + continue; + } + + const uint64_t src = ParseTokenAsID(GetRequiredToken(el,1)); + const uint64_t dest = ParseTokenAsID(GetRequiredToken(el,2)); + + // OO = object-object connection + // OP = object-property connection, in which case the destination property follows the object ID + const std::string& prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el,3)) : ""); + + if(objects.find(src) == objects.end()) { + DOMWarning("source object for connection does not exist",&el); + continue; + } + + // dest may be 0 (root node) but we added a dummy object before + if(objects.find(dest) == objects.end()) { + DOMWarning("destination object for connection does not exist",&el); + continue; + } + + // add new connection + const Connection* const c = new Connection(insertionOrder++,src,dest,prop,*this); + src_connections.insert(ConnectionMap::value_type(src,c)); + dest_connections.insert(ConnectionMap::value_type(dest,c)); + } +} + +// ------------------------------------------------------------------------------------------------ +const std::vector& Document::AnimationStacks() const +{ + if (!animationStacksResolved.empty() || animationStacks.empty()) { + return animationStacksResolved; + } + + animationStacksResolved.reserve(animationStacks.size()); + for(uint64_t id : animationStacks) { + LazyObject* const lazy = GetObject(id); + const AnimationStack* stack; + if(!lazy || !(stack = lazy->Get())) { + DOMWarning("failed to read AnimationStack object"); + continue; + } + animationStacksResolved.push_back(stack); + } + + return animationStacksResolved; +} + +// ------------------------------------------------------------------------------------------------ +LazyObject* Document::GetObject(uint64_t id) const +{ + ObjectMap::const_iterator it = objects.find(id); + return it == objects.end() ? nullptr : (*it).second; +} + +#define MAX_CLASSNAMES 6 + +// ------------------------------------------------------------------------------------------------ +std::vector Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap& conns) const +{ + std::vector temp; + + const std::pair range = + conns.equal_range(id); + + temp.reserve(std::distance(range.first,range.second)); + for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) { + temp.push_back((*it).second); + } + + std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare)); + + return temp; // NRVO should handle this +} + +// ------------------------------------------------------------------------------------------------ +std::vector Document::GetConnectionsSequenced(uint64_t id, bool is_src, + const ConnectionMap& conns, + const char* const* classnames, + size_t count) const + +{ + ai_assert(classnames); + ai_assert( count != 0 ); + ai_assert( count <= MAX_CLASSNAMES); + + size_t lengths[MAX_CLASSNAMES]; + + const size_t c = count; + for (size_t i = 0; i < c; ++i) { + lengths[ i ] = strlen(classnames[i]); + } + + std::vector temp; + const std::pair range = + conns.equal_range(id); + + temp.reserve(std::distance(range.first,range.second)); + for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) { + const Token& key = (is_src + ? (*it).second->LazyDestinationObject() + : (*it).second->LazySourceObject() + ).GetElement().KeyToken(); + + const char* obtype = key.begin(); + + for (size_t i = 0; i < c; ++i) { + ai_assert(classnames[i]); + if(static_cast(std::distance(key.begin(),key.end())) == lengths[i] && !strncmp(classnames[i],obtype,lengths[i])) { + obtype = nullptr; + break; + } + } + + if(obtype) { + continue; + } + + temp.push_back((*it).second); + } + + std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare)); + return temp; // NRVO should handle this +} + +// ------------------------------------------------------------------------------------------------ +std::vector Document::GetConnectionsBySourceSequenced(uint64_t source) const +{ + return GetConnectionsSequenced(source, ConnectionsBySource()); +} + +// ------------------------------------------------------------------------------------------------ +std::vector Document::GetConnectionsBySourceSequenced(uint64_t src, const char* classname) const +{ + const char* arr[] = {classname}; + return GetConnectionsBySourceSequenced(src, arr,1); +} + +// ------------------------------------------------------------------------------------------------ +std::vector Document::GetConnectionsBySourceSequenced(uint64_t source, + const char* const* classnames, size_t count) const +{ + return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count); +} + +// ------------------------------------------------------------------------------------------------ +std::vector Document::GetConnectionsByDestinationSequenced(uint64_t dest, + const char* classname) const +{ + const char* arr[] = {classname}; + return GetConnectionsByDestinationSequenced(dest, arr,1); +} + +// ------------------------------------------------------------------------------------------------ +std::vector Document::GetConnectionsByDestinationSequenced(uint64_t dest) const +{ + return GetConnectionsSequenced(dest, ConnectionsByDestination()); +} + +// ------------------------------------------------------------------------------------------------ +std::vector Document::GetConnectionsByDestinationSequenced(uint64_t dest, + const char* const* classnames, size_t count) const + +{ + return GetConnectionsSequenced(dest, false, ConnectionsByDestination(),classnames, count); +} + +// ------------------------------------------------------------------------------------------------ +Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop, + const Document& doc) + +: insertionOrder(insertionOrder) +, prop(prop) +, src(src) +, dest(dest) +, doc(doc) +{ + ai_assert(doc.Objects().find(src) != doc.Objects().end()); + // dest may be 0 (root node) + ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end()); +} + +// ------------------------------------------------------------------------------------------------ +Connection::~Connection() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +LazyObject& Connection::LazySourceObject() const +{ + LazyObject* const lazy = doc.GetObject(src); + ai_assert(lazy); + return *lazy; +} + +// ------------------------------------------------------------------------------------------------ +LazyObject& Connection::LazyDestinationObject() const +{ + LazyObject* const lazy = doc.GetObject(dest); + ai_assert(lazy); + return *lazy; +} + +// ------------------------------------------------------------------------------------------------ +const Object* Connection::SourceObject() const +{ + LazyObject* const lazy = doc.GetObject(src); + ai_assert(lazy); + return lazy->Get(); +} + +// ------------------------------------------------------------------------------------------------ +const Object* Connection::DestinationObject() const +{ + LazyObject* const lazy = doc.GetObject(dest); + ai_assert(lazy); + return lazy->Get(); +} + +} // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBX/FBXDocument.h b/thirdparty/assimp/code/FBX/FBXDocument.h new file mode 100644 index 0000000000..18e5c38f13 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXDocument.h @@ -0,0 +1,1180 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXDocument.h + * @brief FBX DOM + */ +#ifndef INCLUDED_AI_FBX_DOCUMENT_H +#define INCLUDED_AI_FBX_DOCUMENT_H + +#include +#include +#include +#include "FBXProperties.h" +#include "FBXParser.h" + +#define _AI_CONCAT(a,b) a ## b +#define AI_CONCAT(a,b) _AI_CONCAT(a,b) + +namespace Assimp { +namespace FBX { + +class Parser; +class Object; +struct ImportSettings; + +class PropertyTable; +class Document; +class Material; +class ShapeGeometry; +class LineGeometry; +class Geometry; + +class Video; + +class AnimationCurve; +class AnimationCurveNode; +class AnimationLayer; +class AnimationStack; + +class BlendShapeChannel; +class BlendShape; +class Skin; +class Cluster; + + +/** Represents a delay-parsed FBX objects. Many objects in the scene + * are not needed by assimp, so it makes no sense to parse them + * upfront. */ +class LazyObject { +public: + LazyObject(uint64_t id, const Element& element, const Document& doc); + + ~LazyObject(); + + const Object* Get(bool dieOnError = false); + + template + const T* Get(bool dieOnError = false) { + const Object* const ob = Get(dieOnError); + return ob ? dynamic_cast(ob) : NULL; + } + + uint64_t ID() const { + return id; + } + + bool IsBeingConstructed() const { + return (flags & BEING_CONSTRUCTED) != 0; + } + + bool FailedToConstruct() const { + return (flags & FAILED_TO_CONSTRUCT) != 0; + } + + const Element& GetElement() const { + return element; + } + + const Document& GetDocument() const { + return doc; + } + +private: + const Document& doc; + const Element& element; + std::unique_ptr object; + + const uint64_t id; + + enum Flags { + BEING_CONSTRUCTED = 0x1, + FAILED_TO_CONSTRUCT = 0x2 + }; + + unsigned int flags; +}; + +/** Base class for in-memory (DOM) representations of FBX objects */ +class Object { +public: + Object(uint64_t id, const Element& element, const std::string& name); + + virtual ~Object(); + + const Element& SourceElement() const { + return element; + } + + const std::string& Name() const { + return name; + } + + uint64_t ID() const { + return id; + } + +protected: + const Element& element; + const std::string name; + const uint64_t id; +}; + +/** DOM class for generic FBX NoteAttribute blocks. NoteAttribute's just hold a property table, + * fixed members are added by deriving classes. */ +class NodeAttribute : public Object { +public: + NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~NodeAttribute(); + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + +private: + std::shared_ptr props; +}; + +/** DOM base class for FBX camera settings attached to a node */ +class CameraSwitcher : public NodeAttribute { +public: + CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~CameraSwitcher(); + + int CameraID() const { + return cameraId; + } + + const std::string& CameraName() const { + return cameraName; + } + + const std::string& CameraIndexName() const { + return cameraIndexName; + } + +private: + int cameraId; + std::string cameraName; + std::string cameraIndexName; +}; + +#define fbx_stringize(a) #a + +#define fbx_simple_property(name, type, default_value) \ + type name() const { \ + return PropertyGet(Props(), fbx_stringize(name), (default_value)); \ + } + +// XXX improve logging +#define fbx_simple_enum_property(name, type, default_value) \ + type name() const { \ + const int ival = PropertyGet(Props(), fbx_stringize(name), static_cast(default_value)); \ + if (ival < 0 || ival >= AI_CONCAT(type, _MAX)) { \ + ai_assert(static_cast(default_value) >= 0 && static_cast(default_value) < AI_CONCAT(type, _MAX)); \ + return static_cast(default_value); \ + } \ + return static_cast(ival); \ +} + + +/** DOM base class for FBX cameras attached to a node */ +class Camera : public NodeAttribute { +public: + Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Camera(); + + fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0)) + fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0)) + fbx_simple_property(InterestPosition, aiVector3D, aiVector3D(0,0,0)) + + fbx_simple_property(AspectWidth, float, 1.0f) + fbx_simple_property(AspectHeight, float, 1.0f) + fbx_simple_property(FilmWidth, float, 1.0f) + fbx_simple_property(FilmHeight, float, 1.0f) + + fbx_simple_property(NearPlane, float, 0.1f) + fbx_simple_property(FarPlane, float, 100.0f) + + fbx_simple_property(FilmAspectRatio, float, 1.0f) + fbx_simple_property(ApertureMode, int, 0) + + fbx_simple_property(FieldOfView, float, 1.0f) + fbx_simple_property(FocalLength, float, 1.0f) +}; + +/** DOM base class for FBX null markers attached to a node */ +class Null : public NodeAttribute { +public: + Null(uint64_t id, const Element& element, const Document& doc, const std::string& name); + virtual ~Null(); +}; + +/** DOM base class for FBX limb node markers attached to a node */ +class LimbNode : public NodeAttribute { +public: + LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name); + virtual ~LimbNode(); +}; + +/** DOM base class for FBX lights attached to a node */ +class Light : public NodeAttribute { +public: + Light(uint64_t id, const Element& element, const Document& doc, const std::string& name); + virtual ~Light(); + + enum Type + { + Type_Point, + Type_Directional, + Type_Spot, + Type_Area, + Type_Volume, + + Type_MAX // end-of-enum sentinel + }; + + enum Decay + { + Decay_None, + Decay_Linear, + Decay_Quadratic, + Decay_Cubic, + + Decay_MAX // end-of-enum sentinel + }; + + fbx_simple_property(Color, aiVector3D, aiVector3D(1,1,1)) + fbx_simple_enum_property(LightType, Type, 0) + fbx_simple_property(CastLightOnObject, bool, false) + fbx_simple_property(DrawVolumetricLight, bool, true) + fbx_simple_property(DrawGroundProjection, bool, true) + fbx_simple_property(DrawFrontFacingVolumetricLight, bool, false) + fbx_simple_property(Intensity, float, 100.0f) + fbx_simple_property(InnerAngle, float, 0.0f) + fbx_simple_property(OuterAngle, float, 45.0f) + fbx_simple_property(Fog, int, 50) + fbx_simple_enum_property(DecayType, Decay, 2) + fbx_simple_property(DecayStart, float, 1.0f) + fbx_simple_property(FileName, std::string, "") + + fbx_simple_property(EnableNearAttenuation, bool, false) + fbx_simple_property(NearAttenuationStart, float, 0.0f) + fbx_simple_property(NearAttenuationEnd, float, 0.0f) + fbx_simple_property(EnableFarAttenuation, bool, false) + fbx_simple_property(FarAttenuationStart, float, 0.0f) + fbx_simple_property(FarAttenuationEnd, float, 0.0f) + + fbx_simple_property(CastShadows, bool, true) + fbx_simple_property(ShadowColor, aiVector3D, aiVector3D(0,0,0)) + + fbx_simple_property(AreaLightShape, int, 0) + + fbx_simple_property(LeftBarnDoor, float, 20.0f) + fbx_simple_property(RightBarnDoor, float, 20.0f) + fbx_simple_property(TopBarnDoor, float, 20.0f) + fbx_simple_property(BottomBarnDoor, float, 20.0f) + fbx_simple_property(EnableBarnDoor, bool, true) +}; + +/** DOM base class for FBX models (even though its semantics are more "node" than "model" */ +class Model : public Object { +public: + enum RotOrder { + RotOrder_EulerXYZ = 0, + RotOrder_EulerXZY, + RotOrder_EulerYZX, + RotOrder_EulerYXZ, + RotOrder_EulerZXY, + RotOrder_EulerZYX, + + RotOrder_SphericXYZ, + + RotOrder_MAX // end-of-enum sentinel + }; + + enum TransformInheritance { + TransformInheritance_RrSs = 0, + TransformInheritance_RSrs, + TransformInheritance_Rrs, + + TransformInheritance_MAX // end-of-enum sentinel + }; + + Model(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Model(); + + fbx_simple_property(QuaternionInterpolate, int, 0) + + fbx_simple_property(RotationOffset, aiVector3D, aiVector3D()) + fbx_simple_property(RotationPivot, aiVector3D, aiVector3D()) + fbx_simple_property(ScalingOffset, aiVector3D, aiVector3D()) + fbx_simple_property(ScalingPivot, aiVector3D, aiVector3D()) + fbx_simple_property(TranslationActive, bool, false) + + fbx_simple_property(TranslationMin, aiVector3D, aiVector3D()) + fbx_simple_property(TranslationMax, aiVector3D, aiVector3D()) + + fbx_simple_property(TranslationMinX, bool, false) + fbx_simple_property(TranslationMaxX, bool, false) + fbx_simple_property(TranslationMinY, bool, false) + fbx_simple_property(TranslationMaxY, bool, false) + fbx_simple_property(TranslationMinZ, bool, false) + fbx_simple_property(TranslationMaxZ, bool, false) + + fbx_simple_enum_property(RotationOrder, RotOrder, 0) + fbx_simple_property(RotationSpaceForLimitOnly, bool, false) + fbx_simple_property(RotationStiffnessX, float, 0.0f) + fbx_simple_property(RotationStiffnessY, float, 0.0f) + fbx_simple_property(RotationStiffnessZ, float, 0.0f) + fbx_simple_property(AxisLen, float, 0.0f) + + fbx_simple_property(PreRotation, aiVector3D, aiVector3D()) + fbx_simple_property(PostRotation, aiVector3D, aiVector3D()) + fbx_simple_property(RotationActive, bool, false) + + fbx_simple_property(RotationMin, aiVector3D, aiVector3D()) + fbx_simple_property(RotationMax, aiVector3D, aiVector3D()) + + fbx_simple_property(RotationMinX, bool, false) + fbx_simple_property(RotationMaxX, bool, false) + fbx_simple_property(RotationMinY, bool, false) + fbx_simple_property(RotationMaxY, bool, false) + fbx_simple_property(RotationMinZ, bool, false) + fbx_simple_property(RotationMaxZ, bool, false) + fbx_simple_enum_property(InheritType, TransformInheritance, 0) + + fbx_simple_property(ScalingActive, bool, false) + fbx_simple_property(ScalingMin, aiVector3D, aiVector3D()) + fbx_simple_property(ScalingMax, aiVector3D, aiVector3D(1.f,1.f,1.f)) + fbx_simple_property(ScalingMinX, bool, false) + fbx_simple_property(ScalingMaxX, bool, false) + fbx_simple_property(ScalingMinY, bool, false) + fbx_simple_property(ScalingMaxY, bool, false) + fbx_simple_property(ScalingMinZ, bool, false) + fbx_simple_property(ScalingMaxZ, bool, false) + + fbx_simple_property(GeometricTranslation, aiVector3D, aiVector3D()) + fbx_simple_property(GeometricRotation, aiVector3D, aiVector3D()) + fbx_simple_property(GeometricScaling, aiVector3D, aiVector3D(1.f, 1.f, 1.f)) + + fbx_simple_property(MinDampRangeX, float, 0.0f) + fbx_simple_property(MinDampRangeY, float, 0.0f) + fbx_simple_property(MinDampRangeZ, float, 0.0f) + fbx_simple_property(MaxDampRangeX, float, 0.0f) + fbx_simple_property(MaxDampRangeY, float, 0.0f) + fbx_simple_property(MaxDampRangeZ, float, 0.0f) + + fbx_simple_property(MinDampStrengthX, float, 0.0f) + fbx_simple_property(MinDampStrengthY, float, 0.0f) + fbx_simple_property(MinDampStrengthZ, float, 0.0f) + fbx_simple_property(MaxDampStrengthX, float, 0.0f) + fbx_simple_property(MaxDampStrengthY, float, 0.0f) + fbx_simple_property(MaxDampStrengthZ, float, 0.0f) + + fbx_simple_property(PreferredAngleX, float, 0.0f) + fbx_simple_property(PreferredAngleY, float, 0.0f) + fbx_simple_property(PreferredAngleZ, float, 0.0f) + + fbx_simple_property(Show, bool, true) + fbx_simple_property(LODBox, bool, false) + fbx_simple_property(Freeze, bool, false) + + const std::string& Shading() const { + return shading; + } + + const std::string& Culling() const { + return culling; + } + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + /** Get material links */ + const std::vector& GetMaterials() const { + return materials; + } + + /** Get geometry links */ + const std::vector& GetGeometry() const { + return geometry; + } + + /** Get node attachments */ + const std::vector& GetAttributes() const { + return attributes; + } + + /** convenience method to check if the node has a Null node marker */ + bool IsNull() const; + +private: + void ResolveLinks(const Element& element, const Document& doc); + +private: + std::vector materials; + std::vector geometry; + std::vector attributes; + + std::string shading; + std::string culling; + std::shared_ptr props; +}; + +/** DOM class for generic FBX textures */ +class Texture : public Object { +public: + Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Texture(); + + const std::string& Type() const { + return type; + } + + const std::string& FileName() const { + return fileName; + } + + const std::string& RelativeFilename() const { + return relativeFileName; + } + + const std::string& AlphaSource() const { + return alphaSource; + } + + const aiVector2D& UVTranslation() const { + return uvTrans; + } + + const aiVector2D& UVScaling() const { + return uvScaling; + } + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + // return a 4-tuple + const unsigned int* Crop() const { + return crop; + } + + const Video* Media() const { + return media; + } + +private: + aiVector2D uvTrans; + aiVector2D uvScaling; + + std::string type; + std::string relativeFileName; + std::string fileName; + std::string alphaSource; + std::shared_ptr props; + + unsigned int crop[4]; + + const Video* media; +}; + +/** DOM class for layered FBX textures */ +class LayeredTexture : public Object { +public: + LayeredTexture(uint64_t id, const Element& element, const Document& doc, const std::string& name); + virtual ~LayeredTexture(); + + // Can only be called after construction of the layered texture object due to construction flag. + void fillTexture(const Document& doc); + + enum BlendMode { + BlendMode_Translucent, + BlendMode_Additive, + BlendMode_Modulate, + BlendMode_Modulate2, + BlendMode_Over, + BlendMode_Normal, + BlendMode_Dissolve, + BlendMode_Darken, + BlendMode_ColorBurn, + BlendMode_LinearBurn, + BlendMode_DarkerColor, + BlendMode_Lighten, + BlendMode_Screen, + BlendMode_ColorDodge, + BlendMode_LinearDodge, + BlendMode_LighterColor, + BlendMode_SoftLight, + BlendMode_HardLight, + BlendMode_VividLight, + BlendMode_LinearLight, + BlendMode_PinLight, + BlendMode_HardMix, + BlendMode_Difference, + BlendMode_Exclusion, + BlendMode_Subtract, + BlendMode_Divide, + BlendMode_Hue, + BlendMode_Saturation, + BlendMode_Color, + BlendMode_Luminosity, + BlendMode_Overlay, + BlendMode_BlendModeCount + }; + + const Texture* getTexture(int index=0) const + { + return textures[index]; + + } + int textureCount() const { + return static_cast(textures.size()); + } + BlendMode GetBlendMode() const + { + return blendMode; + } + float Alpha() + { + return alpha; + } +private: + std::vector textures; + BlendMode blendMode; + float alpha; +}; + +typedef std::fbx_unordered_map TextureMap; +typedef std::fbx_unordered_map LayeredTextureMap; + + +/** DOM class for generic FBX videos */ +class Video : public Object { +public: + Video(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Video(); + + const std::string& Type() const { + return type; + } + + const std::string& FileName() const { + return fileName; + } + + const std::string& RelativeFilename() const { + return relativeFileName; + } + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + const uint8_t* Content() const { + ai_assert(content); + return content; + } + + uint64_t ContentLength() const { + return contentLength; + } + + uint8_t* RelinquishContent() { + uint8_t* ptr = content; + content = 0; + return ptr; + } + +private: + std::string type; + std::string relativeFileName; + std::string fileName; + std::shared_ptr props; + + uint64_t contentLength; + uint8_t* content; +}; + +/** DOM class for generic FBX materials */ +class Material : public Object { +public: + Material(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Material(); + + const std::string& GetShadingModel() const { + return shading; + } + + bool IsMultilayer() const { + return multilayer; + } + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + const TextureMap& Textures() const { + return textures; + } + + const LayeredTextureMap& LayeredTextures() const { + return layeredTextures; + } + +private: + std::string shading; + bool multilayer; + std::shared_ptr props; + + TextureMap textures; + LayeredTextureMap layeredTextures; +}; + +typedef std::vector KeyTimeList; +typedef std::vector KeyValueList; + +/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefor) */ +class AnimationCurve : public Object { +public: + AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& doc); + virtual ~AnimationCurve(); + + /** get list of keyframe positions (time). + * Invariant: |GetKeys()| > 0 */ + const KeyTimeList& GetKeys() const { + return keys; + } + + /** get list of keyframe values. + * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/ + const KeyValueList& GetValues() const { + return values; + } + + const std::vector& GetAttributes() const { + return attributes; + } + + const std::vector& GetFlags() const { + return flags; + } + +private: + KeyTimeList keys; + KeyValueList values; + std::vector attributes; + std::vector flags; +}; + +// property-name -> animation curve +typedef std::map AnimationCurveMap; + +/** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */ +class AnimationCurveNode : public Object { +public: + /* the optional white list specifies a list of property names for which the caller + wants animations for. If the curve node does not match one of these, std::range_error + will be thrown. */ + AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, const Document& doc, + const char* const * target_prop_whitelist = NULL, size_t whitelist_size = 0); + + virtual ~AnimationCurveNode(); + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + + const AnimationCurveMap& Curves() const; + + /** Object the curve is assigned to, this can be NULL if the + * target object has no DOM representation or could not + * be read for other reasons.*/ + const Object* Target() const { + return target; + } + + const Model* TargetAsModel() const { + return dynamic_cast(target); + } + + const NodeAttribute* TargetAsNodeAttribute() const { + return dynamic_cast(target); + } + + /** Property of Target() that is being animated*/ + const std::string& TargetProperty() const { + return prop; + } + +private: + const Object* target; + std::shared_ptr props; + mutable AnimationCurveMap curves; + + std::string prop; + const Document& doc; +}; + +typedef std::vector AnimationCurveNodeList; + +/** Represents a FBX animation layer (i.e. a list of node animations) */ +class AnimationLayer : public Object { +public: + AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc); + virtual ~AnimationLayer(); + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + /* the optional white list specifies a list of property names for which the caller + wants animations for. Curves not matching this list will not be added to the + animation layer. */ + AnimationCurveNodeList Nodes(const char* const * target_prop_whitelist = nullptr, size_t whitelist_size = 0) const; + +private: + std::shared_ptr props; + const Document& doc; +}; + +typedef std::vector AnimationLayerList; + +/** Represents a FBX animation stack (i.e. a list of animation layers) */ +class AnimationStack : public Object { +public: + AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc); + virtual ~AnimationStack(); + + fbx_simple_property(LocalStart, int64_t, 0L) + fbx_simple_property(LocalStop, int64_t, 0L) + fbx_simple_property(ReferenceStart, int64_t, 0L) + fbx_simple_property(ReferenceStop, int64_t, 0L) + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + const AnimationLayerList& Layers() const { + return layers; + } + +private: + std::shared_ptr props; + AnimationLayerList layers; +}; + + +/** DOM class for deformers */ +class Deformer : public Object { +public: + Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name); + virtual ~Deformer(); + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + +private: + std::shared_ptr props; +}; + +typedef std::vector WeightArray; +typedef std::vector WeightIndexArray; + + +/** DOM class for BlendShapeChannel deformers */ +class BlendShapeChannel : public Deformer { +public: + BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~BlendShapeChannel(); + + float DeformPercent() const { + return percent; + } + + const WeightArray& GetFullWeights() const { + return fullWeights; + } + + const std::vector& GetShapeGeometries() const { + return shapeGeometries; + } + +private: + float percent; + WeightArray fullWeights; + std::vector shapeGeometries; +}; + +/** DOM class for BlendShape deformers */ +class BlendShape : public Deformer { +public: + BlendShape(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~BlendShape(); + + const std::vector& BlendShapeChannels() const { + return blendShapeChannels; + } + +private: + std::vector blendShapeChannels; +}; + +/** DOM class for skin deformer clusters (aka sub-deformers) */ +class Cluster : public Deformer { +public: + Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Cluster(); + + /** get the list of deformer weights associated with this cluster. + * Use #GetIndices() to get the associated vertices. Both arrays + * have the same size (and may also be empty). */ + const WeightArray& GetWeights() const { + return weights; + } + + /** get indices into the vertex data of the geometry associated + * with this cluster. Use #GetWeights() to get the associated weights. + * Both arrays have the same size (and may also be empty). */ + const WeightIndexArray& GetIndices() const { + return indices; + } + + /** */ + const aiMatrix4x4& Transform() const { + return transform; + } + + const aiMatrix4x4& TransformLink() const { + return transformLink; + } + + const Model* TargetNode() const { + return node; + } + +private: + WeightArray weights; + WeightIndexArray indices; + + aiMatrix4x4 transform; + aiMatrix4x4 transformLink; + + const Model* node; +}; + +/** DOM class for skin deformers */ +class Skin : public Deformer { +public: + Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Skin(); + + float DeformAccuracy() const { + return accuracy; + } + + const std::vector& Clusters() const { + return clusters; + } + +private: + float accuracy; + std::vector clusters; +}; + +/** Represents a link between two FBX objects. */ +class Connection { +public: + Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop, const Document& doc); + + ~Connection(); + + // note: a connection ensures that the source and dest objects exist, but + // not that they have DOM representations, so the return value of one of + // these functions can still be NULL. + const Object* SourceObject() const; + const Object* DestinationObject() const; + + // these, however, are always guaranteed to be valid + LazyObject& LazySourceObject() const; + LazyObject& LazyDestinationObject() const; + + + /** return the name of the property the connection is attached to. + * this is an empty string for object to object (OO) connections. */ + const std::string& PropertyName() const { + return prop; + } + + uint64_t InsertionOrder() const { + return insertionOrder; + } + + int CompareTo(const Connection* c) const { + ai_assert( nullptr != c ); + + // note: can't subtract because this would overflow uint64_t + if(InsertionOrder() > c->InsertionOrder()) { + return 1; + } + else if(InsertionOrder() < c->InsertionOrder()) { + return -1; + } + return 0; + } + + bool Compare(const Connection* c) const { + ai_assert( nullptr != c ); + + return InsertionOrder() < c->InsertionOrder(); + } + +public: + uint64_t insertionOrder; + const std::string prop; + + uint64_t src, dest; + const Document& doc; +}; + +// XXX again, unique_ptr would be useful. shared_ptr is too +// bloated since the objects have a well-defined single owner +// during their entire lifetime (Document). FBX files have +// up to many thousands of objects (most of which we never use), +// so the memory overhead for them should be kept at a minimum. +typedef std::map ObjectMap; +typedef std::fbx_unordered_map > PropertyTemplateMap; + +typedef std::multimap ConnectionMap; + +/** DOM class for global document settings, a single instance per document can + * be accessed via Document.Globals(). */ +class FileGlobalSettings { +public: + FileGlobalSettings(const Document& doc, std::shared_ptr props); + + ~FileGlobalSettings(); + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + const Document& GetDocument() const { + return doc; + } + + fbx_simple_property(UpAxis, int, 1) + fbx_simple_property(UpAxisSign, int, 1) + fbx_simple_property(FrontAxis, int, 2) + fbx_simple_property(FrontAxisSign, int, 1) + fbx_simple_property(CoordAxis, int, 0) + fbx_simple_property(CoordAxisSign, int, 1) + fbx_simple_property(OriginalUpAxis, int, 0) + fbx_simple_property(OriginalUpAxisSign, int, 1) + fbx_simple_property(UnitScaleFactor, float, 1) + fbx_simple_property(OriginalUnitScaleFactor, float, 1) + fbx_simple_property(AmbientColor, aiVector3D, aiVector3D(0,0,0)) + fbx_simple_property(DefaultCamera, std::string, "") + + + enum FrameRate { + FrameRate_DEFAULT = 0, + FrameRate_120 = 1, + FrameRate_100 = 2, + FrameRate_60 = 3, + FrameRate_50 = 4, + FrameRate_48 = 5, + FrameRate_30 = 6, + FrameRate_30_DROP = 7, + FrameRate_NTSC_DROP_FRAME = 8, + FrameRate_NTSC_FULL_FRAME = 9, + FrameRate_PAL = 10, + FrameRate_CINEMA = 11, + FrameRate_1000 = 12, + FrameRate_CINEMA_ND = 13, + FrameRate_CUSTOM = 14, + + FrameRate_MAX// end-of-enum sentinel + }; + + fbx_simple_enum_property(TimeMode, FrameRate, FrameRate_DEFAULT) + fbx_simple_property(TimeSpanStart, uint64_t, 0L) + fbx_simple_property(TimeSpanStop, uint64_t, 0L) + fbx_simple_property(CustomFrameRate, float, -1.0f) + +private: + std::shared_ptr props; + const Document& doc; +}; + +/** DOM root for a FBX file */ +class Document { +public: + Document(const Parser& parser, const ImportSettings& settings); + + ~Document(); + + LazyObject* GetObject(uint64_t id) const; + + bool IsBinary() const { + return parser.IsBinary(); + } + + unsigned int FBXVersion() const { + return fbxVersion; + } + + const std::string& Creator() const { + return creator; + } + + // elements (in this order): Year, Month, Day, Hour, Second, Millisecond + const unsigned int* CreationTimeStamp() const { + return creationTimeStamp; + } + + const FileGlobalSettings& GlobalSettings() const { + ai_assert(globals.get()); + return *globals.get(); + } + + const PropertyTemplateMap& Templates() const { + return templates; + } + + const ObjectMap& Objects() const { + return objects; + } + + const ImportSettings& Settings() const { + return settings; + } + + const ConnectionMap& ConnectionsBySource() const { + return src_connections; + } + + const ConnectionMap& ConnectionsByDestination() const { + return dest_connections; + } + + // note: the implicit rule in all DOM classes is to always resolve + // from destination to source (since the FBX object hierarchy is, + // with very few exceptions, a DAG, this avoids cycles). In all + // cases that may involve back-facing edges in the object graph, + // use LazyObject::IsBeingConstructed() to check. + + std::vector GetConnectionsBySourceSequenced(uint64_t source) const; + std::vector GetConnectionsByDestinationSequenced(uint64_t dest) const; + + std::vector GetConnectionsBySourceSequenced(uint64_t source, const char* classname) const; + std::vector GetConnectionsByDestinationSequenced(uint64_t dest, const char* classname) const; + + std::vector GetConnectionsBySourceSequenced(uint64_t source, + const char* const* classnames, size_t count) const; + std::vector GetConnectionsByDestinationSequenced(uint64_t dest, + const char* const* classnames, + size_t count) const; + + const std::vector& AnimationStacks() const; + +private: + std::vector GetConnectionsSequenced(uint64_t id, const ConnectionMap&) const; + std::vector GetConnectionsSequenced(uint64_t id, bool is_src, + const ConnectionMap&, + const char* const* classnames, + size_t count) const; + void ReadHeader(); + void ReadObjects(); + void ReadPropertyTemplates(); + void ReadConnections(); + void ReadGlobalSettings(); + +private: + const ImportSettings& settings; + + ObjectMap objects; + const Parser& parser; + + PropertyTemplateMap templates; + ConnectionMap src_connections; + ConnectionMap dest_connections; + + unsigned int fbxVersion; + std::string creator; + unsigned int creationTimeStamp[7]; + + std::vector animationStacks; + mutable std::vector animationStacksResolved; + + std::unique_ptr globals; +}; + +} // Namespace FBX +} // Namespace Assimp + +#endif // INCLUDED_AI_FBX_DOCUMENT_H diff --git a/thirdparty/assimp/code/FBX/FBXDocumentUtil.cpp b/thirdparty/assimp/code/FBX/FBXDocumentUtil.cpp new file mode 100644 index 0000000000..f84691479a --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXDocumentUtil.cpp @@ -0,0 +1,135 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXDocumentUtil.cpp + * @brief Implementation of the FBX DOM utility functions declared in FBXDocumentUtil.h + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXParser.h" +#include "FBXDocument.h" +#include "FBXUtil.h" +#include "FBXDocumentUtil.h" +#include "FBXProperties.h" + + +namespace Assimp { +namespace FBX { +namespace Util { + +// ------------------------------------------------------------------------------------------------ +// signal DOM construction error, this is always unrecoverable. Throws DeadlyImportError. +void DOMError(const std::string& message, const Token& token) +{ + throw DeadlyImportError(Util::AddTokenText("FBX-DOM",message,&token)); +} + +// ------------------------------------------------------------------------------------------------ +void DOMError(const std::string& message, const Element* element /*= NULL*/) +{ + if(element) { + DOMError(message,element->KeyToken()); + } + throw DeadlyImportError("FBX-DOM " + message); +} + + +// ------------------------------------------------------------------------------------------------ +// print warning, do return +void DOMWarning(const std::string& message, const Token& token) +{ + if(DefaultLogger::get()) { + ASSIMP_LOG_WARN(Util::AddTokenText("FBX-DOM",message,&token)); + } +} + +// ------------------------------------------------------------------------------------------------ +void DOMWarning(const std::string& message, const Element* element /*= NULL*/) +{ + if(element) { + DOMWarning(message,element->KeyToken()); + return; + } + if(DefaultLogger::get()) { + ASSIMP_LOG_WARN("FBX-DOM: " + message); + } +} + + +// ------------------------------------------------------------------------------------------------ +// fetch a property table and the corresponding property template +std::shared_ptr GetPropertyTable(const Document& doc, + const std::string& templateName, + const Element &element, + const Scope& sc, + bool no_warn /*= false*/) +{ + const Element* const Properties70 = sc["Properties70"]; + std::shared_ptr templateProps = std::shared_ptr( + static_cast(NULL)); + + if(templateName.length()) { + PropertyTemplateMap::const_iterator it = doc.Templates().find(templateName); + if(it != doc.Templates().end()) { + templateProps = (*it).second; + } + } + + if(!Properties70 || !Properties70->Compound()) { + if(!no_warn) { + DOMWarning("property table (Properties70) not found",&element); + } + if(templateProps) { + return templateProps; + } + else { + return std::make_shared(); + } + } + return std::make_shared(*Properties70,templateProps); +} +} // !Util +} // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBX/FBXDocumentUtil.h b/thirdparty/assimp/code/FBX/FBXDocumentUtil.h new file mode 100644 index 0000000000..2450109e59 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXDocumentUtil.h @@ -0,0 +1,120 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXDocumentUtil.h + * @brief FBX internal utilities used by the DOM reading code + */ +#ifndef INCLUDED_AI_FBX_DOCUMENT_UTIL_H +#define INCLUDED_AI_FBX_DOCUMENT_UTIL_H + +#include +#include +#include +#include "FBXDocument.h" + +struct Token; +struct Element; + +namespace Assimp { +namespace FBX { +namespace Util { + +/* DOM/Parse error reporting - does not return */ +AI_WONT_RETURN void DOMError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX; +AI_WONT_RETURN void DOMError(const std::string& message, const Element* element = NULL) AI_WONT_RETURN_SUFFIX; + +// does return +void DOMWarning(const std::string& message, const Token& token); +void DOMWarning(const std::string& message, const Element* element = NULL); + + +// fetch a property table and the corresponding property template +std::shared_ptr GetPropertyTable(const Document& doc, + const std::string& templateName, + const Element &element, + const Scope& sc, + bool no_warn = false); + +// ------------------------------------------------------------------------------------------------ +template +inline +const T* ProcessSimpleConnection(const Connection& con, + bool is_object_property_conn, + const char* name, + const Element& element, + const char** propNameOut = nullptr) +{ + if (is_object_property_conn && !con.PropertyName().length()) { + DOMWarning("expected incoming " + std::string(name) + + " link to be an object-object connection, ignoring", + &element + ); + return nullptr; + } + else if (!is_object_property_conn && con.PropertyName().length()) { + DOMWarning("expected incoming " + std::string(name) + + " link to be an object-property connection, ignoring", + &element + ); + return nullptr; + } + + if(is_object_property_conn && propNameOut) { + // note: this is ok, the return value of PropertyValue() is guaranteed to + // remain valid and unchanged as long as the document exists. + *propNameOut = con.PropertyName().c_str(); + } + + const Object* const ob = con.SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for incoming " + std::string(name) + + " link, ignoring", + &element); + return nullptr; + } + + return dynamic_cast(ob); +} + +} //!Util +} //!FBX +} //!Assimp + +#endif diff --git a/thirdparty/assimp/code/FBX/FBXExportNode.cpp b/thirdparty/assimp/code/FBX/FBXExportNode.cpp new file mode 100644 index 0000000000..06c89cee46 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXExportNode.cpp @@ -0,0 +1,571 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportNode.h" +#include "FBXCommon.h" + +#include // StreamWriterLE +#include // DeadlyExportError +#include +#include // ai_snprintf + +#include +#include +#include // ostringstream +#include // shared_ptr + +namespace Assimp { +// AddP70 helpers... there's no usable pattern here, +// so all are defined as separate functions. +// Even "animatable" properties are often completely different +// from the standard (nonanimated) property definition, +// so they are specified with an 'A' suffix. + +void FBX::Node::AddP70int( + const std::string& name, int32_t value +) { + FBX::Node n("P"); + n.AddProperties(name, "int", "Integer", "", value); + AddChild(n); +} + +void FBX::Node::AddP70bool( + const std::string& name, bool value +) { + FBX::Node n("P"); + n.AddProperties(name, "bool", "", "", int32_t(value)); + AddChild(n); +} + +void FBX::Node::AddP70double( + const std::string& name, double value +) { + FBX::Node n("P"); + n.AddProperties(name, "double", "Number", "", value); + AddChild(n); +} + +void FBX::Node::AddP70numberA( + const std::string& name, double value +) { + FBX::Node n("P"); + n.AddProperties(name, "Number", "", "A", value); + AddChild(n); +} + +void FBX::Node::AddP70color( + const std::string& name, double r, double g, double b +) { + FBX::Node n("P"); + n.AddProperties(name, "ColorRGB", "Color", "", r, g, b); + AddChild(n); +} + +void FBX::Node::AddP70colorA( + const std::string& name, double r, double g, double b +) { + FBX::Node n("P"); + n.AddProperties(name, "Color", "", "A", r, g, b); + AddChild(n); +} + +void FBX::Node::AddP70vector( + const std::string& name, double x, double y, double z +) { + FBX::Node n("P"); + n.AddProperties(name, "Vector3D", "Vector", "", x, y, z); + AddChild(n); +} + +void FBX::Node::AddP70vectorA( + const std::string& name, double x, double y, double z +) { + FBX::Node n("P"); + n.AddProperties(name, "Vector", "", "A", x, y, z); + AddChild(n); +} + +void FBX::Node::AddP70string( + const std::string& name, const std::string& value +) { + FBX::Node n("P"); + n.AddProperties(name, "KString", "", "", value); + AddChild(n); +} + +void FBX::Node::AddP70enum( + const std::string& name, int32_t value +) { + FBX::Node n("P"); + n.AddProperties(name, "enum", "", "", value); + AddChild(n); +} + +void FBX::Node::AddP70time( + const std::string& name, int64_t value +) { + FBX::Node n("P"); + n.AddProperties(name, "KTime", "Time", "", value); + AddChild(n); +} + + +// public member functions for writing nodes to stream + +void FBX::Node::Dump( + std::shared_ptr outfile, + bool binary, int indent +) { + if (binary) { + Assimp::StreamWriterLE outstream(outfile); + DumpBinary(outstream); + } else { + std::ostringstream ss; + DumpAscii(ss, indent); + std::string s = ss.str(); + outfile->Write(s.c_str(), s.size(), 1); + } +} + +void FBX::Node::Dump( + Assimp::StreamWriterLE &outstream, + bool binary, int indent +) { + if (binary) { + DumpBinary(outstream); + } else { + std::ostringstream ss; + DumpAscii(ss, indent); + outstream.PutString(ss.str()); + } +} + + +// public member functions for low-level writing + +void FBX::Node::Begin( + Assimp::StreamWriterLE &s, + bool binary, int indent +) { + if (binary) { + BeginBinary(s); + } else { + // assume we're at the correct place to start already + (void)indent; + std::ostringstream ss; + BeginAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::DumpProperties( + Assimp::StreamWriterLE& s, + bool binary, int indent +) { + if (binary) { + DumpPropertiesBinary(s); + } else { + std::ostringstream ss; + DumpPropertiesAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::EndProperties( + Assimp::StreamWriterLE &s, + bool binary, int indent +) { + EndProperties(s, binary, indent, properties.size()); +} + +void FBX::Node::EndProperties( + Assimp::StreamWriterLE &s, + bool binary, int indent, + size_t num_properties +) { + if (binary) { + EndPropertiesBinary(s, num_properties); + } else { + // nothing to do + (void)indent; + } +} + +void FBX::Node::BeginChildren( + Assimp::StreamWriterLE &s, + bool binary, int indent +) { + if (binary) { + // nothing to do + } else { + std::ostringstream ss; + BeginChildrenAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::DumpChildren( + Assimp::StreamWriterLE& s, + bool binary, int indent +) { + if (binary) { + DumpChildrenBinary(s); + } else { + std::ostringstream ss; + DumpChildrenAscii(ss, indent); + if (ss.tellp() > 0) + s.PutString(ss.str()); + } +} + +void FBX::Node::End( + Assimp::StreamWriterLE &s, + bool binary, int indent, + bool has_children +) { + if (binary) { + EndBinary(s, has_children); + } else { + std::ostringstream ss; + EndAscii(ss, indent, has_children); + if (ss.tellp() > 0) + s.PutString(ss.str()); + } +} + + +// public member functions for writing to binary fbx + +void FBX::Node::DumpBinary(Assimp::StreamWriterLE &s) +{ + // write header section (with placeholders for some things) + BeginBinary(s); + + // write properties + DumpPropertiesBinary(s); + + // go back and fill in property related placeholders + EndPropertiesBinary(s, properties.size()); + + // write children + DumpChildrenBinary(s); + + // finish, filling in end offset placeholder + EndBinary(s, force_has_children || !children.empty()); +} + + +// public member functions for writing to ascii fbx + +void FBX::Node::DumpAscii(std::ostream &s, int indent) +{ + // write name + BeginAscii(s, indent); + + // write properties + DumpPropertiesAscii(s, indent); + + if (force_has_children || !children.empty()) { + // begin children (with a '{') + BeginChildrenAscii(s, indent + 1); + // write children + DumpChildrenAscii(s, indent + 1); + } + + // finish (also closing the children bracket '}') + EndAscii(s, indent, force_has_children || !children.empty()); +} + + +// private member functions for low-level writing to fbx + +void FBX::Node::BeginBinary(Assimp::StreamWriterLE &s) +{ + // remember start pos so we can come back and write the end pos + this->start_pos = s.Tell(); + + // placeholders for end pos and property section info + s.PutU4(0); // end pos + s.PutU4(0); // number of properties + s.PutU4(0); // total property section length + + // node name + s.PutU1(uint8_t(name.size())); // length of node name + s.PutString(name); // node name as raw bytes + + // property data comes after here + this->property_start = s.Tell(); +} + +void FBX::Node::DumpPropertiesBinary(Assimp::StreamWriterLE& s) +{ + for (auto &p : properties) { + p.DumpBinary(s); + } +} + +void FBX::Node::EndPropertiesBinary( + Assimp::StreamWriterLE &s, + size_t num_properties +) { + if (num_properties == 0) { return; } + size_t pos = s.Tell(); + ai_assert(pos > property_start); + size_t property_section_size = pos - property_start; + s.Seek(start_pos + 4); + s.PutU4(uint32_t(num_properties)); + s.PutU4(uint32_t(property_section_size)); + s.Seek(pos); +} + +void FBX::Node::DumpChildrenBinary(Assimp::StreamWriterLE& s) +{ + for (FBX::Node& child : children) { + child.DumpBinary(s); + } +} + +void FBX::Node::EndBinary( + Assimp::StreamWriterLE &s, + bool has_children +) { + // if there were children, add a null record + if (has_children) { s.PutString(Assimp::FBX::NULL_RECORD); } + + // now go back and write initial pos + this->end_pos = s.Tell(); + s.Seek(start_pos); + s.PutU4(uint32_t(end_pos)); + s.Seek(end_pos); +} + + +void FBX::Node::BeginAscii(std::ostream& s, int indent) +{ + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << name << ": "; +} + +void FBX::Node::DumpPropertiesAscii(std::ostream &s, int indent) +{ + for (size_t i = 0; i < properties.size(); ++i) { + if (i > 0) { s << ", "; } + properties[i].DumpAscii(s, indent); + } +} + +void FBX::Node::BeginChildrenAscii(std::ostream& s, int indent) +{ + // only call this if there are actually children + s << " {"; + (void)indent; +} + +void FBX::Node::DumpChildrenAscii(std::ostream& s, int indent) +{ + // children will need a lot of padding and corralling + if (children.size() || force_has_children) { + for (size_t i = 0; i < children.size(); ++i) { + // no compression in ascii files, so skip this node if it exists + if (children[i].name == "EncryptionType") { continue; } + // the child can dump itself + children[i].DumpAscii(s, indent); + } + } +} + +void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children) +{ + if (!has_children) { return; } // nothing to do + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "}"; +} + +// private helpers for static member functions + +// ascii property node from vector of doubles +void FBX::Node::WritePropertyNodeAscii( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + int indent +){ + char buffer[32]; + FBX::Node node(name); + node.Begin(s, false, indent); + std::string vsize = to_string(v.size()); + // * { + s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n"); + // indent + 1 + for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); } + // a: value,value,value,... + s.PutString("a: "); + int count = 0; + for (size_t i = 0; i < v.size(); ++i) { + if (i > 0) { s.PutChar(','); } + int len = ai_snprintf(buffer, sizeof(buffer), "%f", v[i]); + count += len; + if (count > 2048) { s.PutChar('\n'); count = 0; } + if (len < 0 || len > 31) { + // this should never happen + throw DeadlyExportError("failed to convert double to string"); + } + for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); } + } + // } + s.PutChar('\n'); + for (int i = 0; i < indent; ++i) { s.PutChar('\t'); } + s.PutChar('}'); s.PutChar(' '); + node.End(s, false, indent, false); +} + +// ascii property node from vector of int32_t +void FBX::Node::WritePropertyNodeAscii( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + int indent +){ + char buffer[32]; + FBX::Node node(name); + node.Begin(s, false, indent); + std::string vsize = to_string(v.size()); + // * { + s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n"); + // indent + 1 + for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); } + // a: value,value,value,... + s.PutString("a: "); + int count = 0; + for (size_t i = 0; i < v.size(); ++i) { + if (i > 0) { s.PutChar(','); } + int len = ai_snprintf(buffer, sizeof(buffer), "%d", v[i]); + count += len; + if (count > 2048) { s.PutChar('\n'); count = 0; } + if (len < 0 || len > 31) { + // this should never happen + throw DeadlyExportError("failed to convert double to string"); + } + for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); } + } + // } + s.PutChar('\n'); + for (int i = 0; i < indent; ++i) { s.PutChar('\t'); } + s.PutChar('}'); s.PutChar(' '); + node.End(s, false, indent, false); +} + +// binary property node from vector of doubles +// TODO: optional zip compression! +void FBX::Node::WritePropertyNodeBinary( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s +){ + FBX::Node node(name); + node.BeginBinary(s); + s.PutU1('d'); + s.PutU4(uint32_t(v.size())); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + s.PutU4(uint32_t(v.size()) * 8); // data size + for (auto it = v.begin(); it != v.end(); ++it) { s.PutF8(*it); } + node.EndPropertiesBinary(s, 1); + node.EndBinary(s, false); +} + +// binary property node from vector of int32_t +// TODO: optional zip compression! +void FBX::Node::WritePropertyNodeBinary( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s +){ + FBX::Node node(name); + node.BeginBinary(s); + s.PutU1('i'); + s.PutU4(uint32_t(v.size())); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + s.PutU4(uint32_t(v.size()) * 4); // data size + for (auto it = v.begin(); it != v.end(); ++it) { s.PutI4(*it); } + node.EndPropertiesBinary(s, 1); + node.EndBinary(s, false); +} + +// public static member functions + +// convenience function to create and write a property node, +// holding a single property which is an array of values. +// does not copy the data, so is efficient for large arrays. +void FBX::Node::WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + bool binary, int indent +){ + if (binary) { + FBX::Node::WritePropertyNodeBinary(name, v, s); + } else { + FBX::Node::WritePropertyNodeAscii(name, v, s, indent); + } +} + +// convenience function to create and write a property node, +// holding a single property which is an array of values. +// does not copy the data, so is efficient for large arrays. +void FBX::Node::WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + bool binary, int indent +){ + if (binary) { + FBX::Node::WritePropertyNodeBinary(name, v, s); + } else { + FBX::Node::WritePropertyNodeAscii(name, v, s, indent); + } +} +} +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/thirdparty/assimp/code/FBX/FBXExportNode.h b/thirdparty/assimp/code/FBX/FBXExportNode.h new file mode 100644 index 0000000000..ef3bc781a4 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXExportNode.h @@ -0,0 +1,271 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXExportNode.h +* Declares the FBX::Node helper class for fbx export. +*/ +#ifndef AI_FBXEXPORTNODE_H_INC +#define AI_FBXEXPORTNODE_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportProperty.h" + +#include // StreamWriterLE + +#include +#include + +namespace Assimp { +namespace FBX { + class Node; +} + +class FBX::Node { +public: + // TODO: accessors + std::string name; // node name + std::vector properties; // node properties + std::vector children; // child nodes + + // some nodes always pretend they have children... + bool force_has_children = false; + +public: // constructors + /// The default class constructor. + Node() = default; + + /// The class constructor with the name. + Node(const std::string& n) + : name(n) + , properties() + , children() + , force_has_children( false ) { + // empty + } + + // convenience template to construct with properties directly + template + Node(const std::string& n, const More... more) + : name(n) + , properties() + , children() + , force_has_children(false) { + AddProperties(more...); + } + +public: // functions to add properties or children + // add a single property to the node + template + void AddProperty(T value) { + properties.emplace_back(value); + } + + // convenience function to add multiple properties at once + template + void AddProperties(T value, More... more) { + properties.emplace_back(value); + AddProperties(more...); + } + void AddProperties() {} + + // add a child node directly + void AddChild(const Node& node) { children.push_back(node); } + + // convenience function to add a child node with a single property + template + void AddChild( + const std::string& name, + More... more + ) { + FBX::Node c(name); + c.AddProperties(more...); + children.push_back(c); + } + +public: // support specifically for dealing with Properties70 nodes + + // it really is simpler to make these all separate functions. + // the versions with 'A' suffixes are for animatable properties. + // those often follow a completely different format internally in FBX. + void AddP70int(const std::string& name, int32_t value); + void AddP70bool(const std::string& name, bool value); + void AddP70double(const std::string& name, double value); + void AddP70numberA(const std::string& name, double value); + void AddP70color(const std::string& name, double r, double g, double b); + void AddP70colorA(const std::string& name, double r, double g, double b); + void AddP70vector(const std::string& name, double x, double y, double z); + void AddP70vectorA(const std::string& name, double x, double y, double z); + void AddP70string(const std::string& name, const std::string& value); + void AddP70enum(const std::string& name, int32_t value); + void AddP70time(const std::string& name, int64_t value); + + // template for custom P70 nodes. + // anything that doesn't fit in the above can be created manually. + template + void AddP70( + const std::string& name, + const std::string& type, + const std::string& type2, + const std::string& flags, + More... more + ) { + Node n("P"); + n.AddProperties(name, type, type2, flags, more...); + AddChild(n); + } + +public: // member functions for writing data to a file or stream + + // write the full node to the given file or stream + void Dump( + std::shared_ptr outfile, + bool binary, int indent + ); + void Dump(Assimp::StreamWriterLE &s, bool binary, int indent); + + // these other functions are for writing data piece by piece. + // they must be used carefully. + // for usage examples see FBXExporter.cpp. + void Begin(Assimp::StreamWriterLE &s, bool binary, int indent); + void DumpProperties(Assimp::StreamWriterLE& s, bool binary, int indent); + void EndProperties(Assimp::StreamWriterLE &s, bool binary, int indent); + void EndProperties( + Assimp::StreamWriterLE &s, bool binary, int indent, + size_t num_properties + ); + void BeginChildren(Assimp::StreamWriterLE &s, bool binary, int indent); + void DumpChildren(Assimp::StreamWriterLE& s, bool binary, int indent); + void End( + Assimp::StreamWriterLE &s, bool binary, int indent, + bool has_children + ); + +private: // internal functions used for writing + + void DumpBinary(Assimp::StreamWriterLE &s); + void DumpAscii(Assimp::StreamWriterLE &s, int indent); + void DumpAscii(std::ostream &s, int indent); + + void BeginBinary(Assimp::StreamWriterLE &s); + void DumpPropertiesBinary(Assimp::StreamWriterLE& s); + void EndPropertiesBinary(Assimp::StreamWriterLE &s); + void EndPropertiesBinary(Assimp::StreamWriterLE &s, size_t num_properties); + void DumpChildrenBinary(Assimp::StreamWriterLE& s); + void EndBinary(Assimp::StreamWriterLE &s, bool has_children); + + void BeginAscii(std::ostream &s, int indent); + void DumpPropertiesAscii(std::ostream &s, int indent); + void BeginChildrenAscii(std::ostream &s, int indent); + void DumpChildrenAscii(std::ostream &s, int indent); + void EndAscii(std::ostream &s, int indent, bool has_children); + +private: // data used for binary dumps + size_t start_pos; // starting position in stream + size_t end_pos; // ending position in stream + size_t property_start; // starting position of property section + +public: // static member functions + + // convenience function to create a node with a single property, + // and write it to the stream. + template + static void WritePropertyNode( + const std::string& name, + const T value, + Assimp::StreamWriterLE& s, + bool binary, int indent + ) { + FBX::FBXExportProperty p(value); + FBX::Node node(name, p); + node.Dump(s, binary, indent); + } + + // convenience function to create and write a property node, + // holding a single property which is an array of values. + // does not copy the data, so is efficient for large arrays. + static void WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + bool binary, int indent + ); + + // convenience function to create and write a property node, + // holding a single property which is an array of values. + // does not copy the data, so is efficient for large arrays. + static void WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + bool binary, int indent + ); + +private: // static helper functions + static void WritePropertyNodeAscii( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + int indent + ); + static void WritePropertyNodeAscii( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + int indent + ); + static void WritePropertyNodeBinary( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s + ); + static void WritePropertyNodeBinary( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s + ); + +}; +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXEXPORTNODE_H_INC diff --git a/thirdparty/assimp/code/FBX/FBXExportProperty.cpp b/thirdparty/assimp/code/FBX/FBXExportProperty.cpp new file mode 100644 index 0000000000..f8593e6295 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXExportProperty.cpp @@ -0,0 +1,389 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportProperty.h" + +#include // StreamWriterLE +#include // DeadlyExportError + +#include +#include +#include +#include +#include // ostringstream + +namespace Assimp { +namespace FBX { + +// constructors for single element properties + +FBXExportProperty::FBXExportProperty(bool v) +: type('C') +, data(1) { + data = { + uint8_t(v) + }; +} + +FBXExportProperty::FBXExportProperty(int16_t v) +: type('Y') +, data(2) { + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + +FBXExportProperty::FBXExportProperty(int32_t v) +: type('I') +, data(4) { + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + +FBXExportProperty::FBXExportProperty(float v) +: type('F') +, data(4) { + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + +FBXExportProperty::FBXExportProperty(double v) +: type('D') +, data(8) { + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + +FBXExportProperty::FBXExportProperty(int64_t v) +: type('L') +, data(8) { + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + +// constructors for array-type properties + +FBXExportProperty::FBXExportProperty(const char* c, bool raw) +: FBXExportProperty(std::string(c), raw) { + // empty +} + +// strings can either be saved as "raw" (R) data, or "string" (S) data +FBXExportProperty::FBXExportProperty(const std::string& s, bool raw) +: type(raw ? 'R' : 'S') +, data(s.size()) { + for (size_t i = 0; i < s.size(); ++i) { + data[i] = uint8_t(s[i]); + } +} + +FBXExportProperty::FBXExportProperty(const std::vector& r) +: type('R') +, data(r) { + // empty +} + +FBXExportProperty::FBXExportProperty(const std::vector& va) +: type('i') +, data(4 * va.size() ) { + int32_t* d = reinterpret_cast(data.data()); + for (size_t i = 0; i < va.size(); ++i) { + d[i] = va[i]; + } +} + +FBXExportProperty::FBXExportProperty(const std::vector& va) +: type('l') +, data(8 * va.size()) { + int64_t* d = reinterpret_cast(data.data()); + for (size_t i = 0; i < va.size(); ++i) { + d[i] = va[i]; + } +} + +FBXExportProperty::FBXExportProperty(const std::vector& va) +: type('f') +, data(4 * va.size()) { + float* d = reinterpret_cast(data.data()); + for (size_t i = 0; i < va.size(); ++i) { + d[i] = va[i]; + } +} + +FBXExportProperty::FBXExportProperty(const std::vector& va) +: type('d') +, data(8 * va.size()) { + double* d = reinterpret_cast(data.data()); + for (size_t i = 0; i < va.size(); ++i) { + d[i] = va[i]; + } +} + +FBXExportProperty::FBXExportProperty(const aiMatrix4x4& vm) +: type('d') +, data(8 * 16) { + double* d = reinterpret_cast(data.data()); + for (unsigned int c = 0; c < 4; ++c) { + for (unsigned int r = 0; r < 4; ++r) { + d[4 * c + r] = vm[r][c]; + } + } +} + +// public member functions + +size_t FBXExportProperty::size() { + switch (type) { + case 'C': + case 'Y': + case 'I': + case 'F': + case 'D': + case 'L': + return data.size() + 1; + case 'S': + case 'R': + return data.size() + 5; + case 'i': + case 'd': + return data.size() + 13; + default: + throw DeadlyExportError("Requested size on property of unknown type"); + } +} + +void FBXExportProperty::DumpBinary(Assimp::StreamWriterLE& s) { + s.PutU1(type); + uint8_t* d = data.data(); + size_t N; + switch (type) { + case 'C': s.PutU1(*(reinterpret_cast(d))); return; + case 'Y': s.PutI2(*(reinterpret_cast(d))); return; + case 'I': s.PutI4(*(reinterpret_cast(d))); return; + case 'F': s.PutF4(*(reinterpret_cast(d))); return; + case 'D': s.PutF8(*(reinterpret_cast(d))); return; + case 'L': s.PutI8(*(reinterpret_cast(d))); return; + case 'S': + case 'R': + s.PutU4(uint32_t(data.size())); + for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); } + return; + case 'i': + N = data.size() / 4; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + for (size_t i = 0; i < N; ++i) { + s.PutI4((reinterpret_cast(d))[i]); + } + return; + case 'l': + N = data.size() / 8; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + for (size_t i = 0; i < N; ++i) { + s.PutI8((reinterpret_cast(d))[i]); + } + return; + case 'f': + N = data.size() / 4; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + for (size_t i = 0; i < N; ++i) { + s.PutF4((reinterpret_cast(d))[i]); + } + return; + case 'd': + N = data.size() / 8; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + for (size_t i = 0; i < N; ++i) { + s.PutF8((reinterpret_cast(d))[i]); + } + return; + default: + std::ostringstream err; + err << "Tried to dump property with invalid type '"; + err << type << "'!"; + throw DeadlyExportError(err.str()); + } +} + +void FBXExportProperty::DumpAscii(Assimp::StreamWriterLE& outstream, int indent) { + std::ostringstream ss; + ss.imbue(std::locale::classic()); + ss.precision(15); // this seems to match official FBX SDK exports + DumpAscii(ss, indent); + outstream.PutString(ss.str()); +} + +void FBXExportProperty::DumpAscii(std::ostream& s, int indent) { + // no writing type... or anything. just shove it into the stream. + uint8_t* d = data.data(); + size_t N; + size_t swap = data.size(); + size_t count = 0; + switch (type) { + case 'C': + if (*(reinterpret_cast(d))) { s << 'T'; } + else { s << 'F'; } + return; + case 'Y': s << *(reinterpret_cast(d)); return; + case 'I': s << *(reinterpret_cast(d)); return; + case 'F': s << *(reinterpret_cast(d)); return; + case 'D': s << *(reinterpret_cast(d)); return; + case 'L': s << *(reinterpret_cast(d)); return; + case 'S': + // first search to see if it has "\x00\x01" in it - + // which separates fields which are reversed in the ascii version. + // yeah. + // FBX, yeah. + for (size_t i = 0; i < data.size(); ++i) { + if (data[i] == '\0') { + swap = i; + break; + } + } + case 'R': + s << '"'; + // we might as well check this now, + // probably it will never happen + for (size_t i = 0; i < data.size(); ++i) { + char c = data[i]; + if (c == '"') { + throw runtime_error("can't handle quotes in property string"); + } + } + // first write the SWAPPED member (if any) + for (size_t i = swap + 2; i < data.size(); ++i) { + char c = data[i]; + s << c; + } + // then a separator + if (swap != data.size()) { + s << "::"; + } + // then the initial member + for (size_t i = 0; i < swap; ++i) { + char c = data[i]; + s << c; + } + s << '"'; + return; + case 'i': + N = data.size() / 4; // number of elements + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + case 'l': + N = data.size() / 8; + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + case 'f': + N = data.size() / 4; + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + case 'd': + N = data.size() / 8; + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + // set precision to something that can handle doubles + s.precision(15); + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + default: + std::ostringstream err; + err << "Tried to dump property with invalid type '"; + err << type << "'!"; + throw runtime_error(err.str()); + } +} + +} // Namespace FBX +} // Namespace Assimp + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/thirdparty/assimp/code/FBX/FBXExportProperty.h b/thirdparty/assimp/code/FBX/FBXExportProperty.h new file mode 100644 index 0000000000..d692fe6ee3 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXExportProperty.h @@ -0,0 +1,129 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXExportProperty.h +* Declares the FBX::Property helper class for fbx export. +*/ +#ifndef AI_FBXEXPORTPROPERTY_H_INC +#define AI_FBXEXPORTPROPERTY_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include // aiMatrix4x4 +#include // StreamWriterLE + +#include +#include +#include +#include // is_void + +namespace Assimp { +namespace FBX { + +/** @brief FBX::Property + * + * Holds a value of any of FBX's recognized types, + * each represented by a particular one-character code. + * C : 1-byte uint8, usually 0x00 or 0x01 to represent boolean false and true + * Y : 2-byte int16 + * I : 4-byte int32 + * F : 4-byte float + * D : 8-byte double + * L : 8-byte int64 + * i : array of int32 + * f : array of float + * d : array of double + * l : array of int64 + * b : array of 1-byte booleans (0x00 or 0x01) + * S : string (array of 1-byte char) + * R : raw data (array of bytes) + */ +class FBXExportProperty { +public: + // constructors for basic types. + // all explicit to avoid accidental typecasting + explicit FBXExportProperty(bool v); + // TODO: determine if there is actually a byte type, + // or if this always means . 'C' seems to imply , + // so possibly the above was intended to represent both. + explicit FBXExportProperty(int16_t v); + explicit FBXExportProperty(int32_t v); + explicit FBXExportProperty(float v); + explicit FBXExportProperty(double v); + explicit FBXExportProperty(int64_t v); + // strings can either be stored as 'R' (raw) or 'S' (string) type + explicit FBXExportProperty(const char* c, bool raw = false); + explicit FBXExportProperty(const std::string& s, bool raw = false); + explicit FBXExportProperty(const std::vector& r); + explicit FBXExportProperty(const std::vector& va); + explicit FBXExportProperty(const std::vector& va); + explicit FBXExportProperty(const std::vector& va); + explicit FBXExportProperty(const std::vector& va); + explicit FBXExportProperty(const aiMatrix4x4& vm); + + // this will catch any type not defined above, + // so that we don't accidentally convert something we don't want. + // for example (const char*) --> (bool)... seriously wtf C++ + template + explicit FBXExportProperty(T v) : type('X') { + static_assert(std::is_void::value, "TRIED TO CREATE FBX PROPERTY WITH UNSUPPORTED TYPE, CHECK YOUR PROPERTY INSTANTIATION"); + } // note: no line wrap so it appears verbatim on the compiler error + + // the size of this property node in a binary file, in bytes + size_t size(); + + // write this property node as binary data to the given stream + void DumpBinary(Assimp::StreamWriterLE& s); + void DumpAscii(Assimp::StreamWriterLE& s, int indent = 0); + void DumpAscii(std::ostream& s, int indent = 0); + // note: make sure the ostream is in classic "C" locale + +private: + char type; + std::vector data; +}; + +} // Namespace FBX +} // Namespace Assimp + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXEXPORTPROPERTY_H_INC diff --git a/thirdparty/assimp/code/FBX/FBXExporter.cpp b/thirdparty/assimp/code/FBX/FBXExporter.cpp new file mode 100644 index 0000000000..153e676506 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXExporter.cpp @@ -0,0 +1,2543 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExporter.h" +#include "FBXExportNode.h" +#include "FBXExportProperty.h" +#include "FBXCommon.h" +#include "FBXUtil.h" + +#include // aiGetVersion +#include +#include +#include +#include // StreamWriterLE +#include // DeadlyExportError +#include // aiTextureType +#include +#include + +// Header files, standard library. +#include // shared_ptr +#include +#include // stringstream +#include // localtime, tm_* +#include +#include +#include +#include +#include + +// RESOURCES: +// https://code.blender.org/2013/08/fbx-binary-file-format-specification/ +// https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure + +const ai_real DEG = ai_real( 57.29577951308232087679815481 ); // degrees per radian + +using namespace Assimp; +using namespace Assimp::FBX; + +// some constants that we'll use for writing metadata +namespace Assimp { +namespace FBX { + const std::string EXPORT_VERSION_STR = "7.4.0"; + const uint32_t EXPORT_VERSION_INT = 7400; // 7.4 == 2014/2015 + // FBX files have some hashed values that depend on the creation time field, + // but for now we don't actually know how to generate these. + // what we can do is set them to a known-working version. + // this is the data that Blender uses in their FBX export process. + const std::string GENERIC_CTIME = "1970-01-01 10:00:00:000"; + const std::string GENERIC_FILEID = + "\x28\xb3\x2a\xeb\xb6\x24\xcc\xc2\xbf\xc8\xb0\x2a\xa9\x2b\xfc\xf1"; + const std::string GENERIC_FOOTID = + "\xfa\xbc\xab\x09\xd0\xc8\xd4\x66\xb1\x76\xfb\x83\x1c\xf7\x26\x7e"; + const std::string FOOT_MAGIC = + "\xf8\x5a\x8c\x6a\xde\xf5\xd9\x7e\xec\xe9\x0c\xe3\x75\x8f\x29\x0b"; + const std::string COMMENT_UNDERLINE = + ";------------------------------------------------------------------"; +} + + // --------------------------------------------------------------------- + // Worker function for exporting a scene to binary FBX. + // Prototyped and registered in Exporter.cpp + void ExportSceneFBX ( + const char* pFile, + IOSystem* pIOSystem, + const aiScene* pScene, + const ExportProperties* pProperties + ){ + // initialize the exporter + FBXExporter exporter(pScene, pProperties); + + // perform binary export + exporter.ExportBinary(pFile, pIOSystem); + } + + // --------------------------------------------------------------------- + // Worker function for exporting a scene to ASCII FBX. + // Prototyped and registered in Exporter.cpp + void ExportSceneFBXA ( + const char* pFile, + IOSystem* pIOSystem, + const aiScene* pScene, + const ExportProperties* pProperties + + ){ + // initialize the exporter + FBXExporter exporter(pScene, pProperties); + + // perform ascii export + exporter.ExportAscii(pFile, pIOSystem); + } + +} // end of namespace Assimp + +FBXExporter::FBXExporter ( const aiScene* pScene, const ExportProperties* pProperties ) +: binary(false) +, mScene(pScene) +, mProperties(pProperties) +, outfile() +, connections() +, mesh_uids() +, material_uids() +, node_uids() { + // will probably need to determine UIDs, connections, etc here. + // basically anything that needs to be known + // before we start writing sections to the stream. +} + +void FBXExporter::ExportBinary ( + const char* pFile, + IOSystem* pIOSystem +){ + // remember that we're exporting in binary mode + binary = true; + + // we're not currently using these preferences, + // but clang will cry about it if we never touch it. + // TODO: some of these might be relevant to export + (void)mProperties; + + // open the indicated file for writing (in binary mode) + outfile.reset(pIOSystem->Open(pFile,"wb")); + if (!outfile) { + throw DeadlyExportError( + "could not open output .fbx file: " + std::string(pFile) + ); + } + + // first a binary-specific file header + WriteBinaryHeader(); + + // the rest of the file is in node entries. + // we have to serialize each entry before we write to the output, + // as the first thing we write is the byte offset of the _next_ entry. + // Either that or we can skip back to write the offset when we finish. + WriteAllNodes(); + + // finally we have a binary footer to the file + WriteBinaryFooter(); + + // explicitly release file pointer, + // so we don't have to rely on class destruction. + outfile.reset(); +} + +void FBXExporter::ExportAscii ( + const char* pFile, + IOSystem* pIOSystem +){ + // remember that we're exporting in ascii mode + binary = false; + + // open the indicated file for writing in text mode + outfile.reset(pIOSystem->Open(pFile,"wt")); + if (!outfile) { + throw DeadlyExportError( + "could not open output .fbx file: " + std::string(pFile) + ); + } + + // write the ascii header + WriteAsciiHeader(); + + // write all the sections + WriteAllNodes(); + + // make sure the file ends with a newline. + // note: if the file is opened in text mode, + // this should do the right cross-platform thing. + outfile->Write("\n", 1, 1); + + // explicitly release file pointer, + // so we don't have to rely on class destruction. + outfile.reset(); +} + +void FBXExporter::WriteAsciiHeader() +{ + // basically just a comment at the top of the file + std::stringstream head; + head << "; FBX " << EXPORT_VERSION_STR << " project file\n"; + head << "; Created by the Open Asset Import Library (Assimp)\n"; + head << "; http://assimp.org\n"; + head << "; -------------------------------------------------\n"; + const std::string ascii_header = head.str(); + outfile->Write(ascii_header.c_str(), ascii_header.size(), 1); +} + +void FBXExporter::WriteAsciiSectionHeader(const std::string& title) +{ + StreamWriterLE outstream(outfile); + std::stringstream s; + s << "\n\n; " << title << '\n'; + s << FBX::COMMENT_UNDERLINE << "\n"; + outstream.PutString(s.str()); +} + +void FBXExporter::WriteBinaryHeader() +{ + // first a specific sequence of 23 bytes, always the same + const char binary_header[24] = "Kaydara FBX Binary\x20\x20\x00\x1a\x00"; + outfile->Write(binary_header, 1, 23); + + // then FBX version number, "multiplied" by 1000, as little-endian uint32. + // so 7.3 becomes 7300 == 0x841C0000, 7.4 becomes 7400 == 0xE81C0000, etc + { + StreamWriterLE outstream(outfile); + outstream.PutU4(EXPORT_VERSION_INT); + } // StreamWriter destructor writes the data to the file + + // after this the node data starts immediately + // (probably with the FBXHEaderExtension node) +} + +void FBXExporter::WriteBinaryFooter() +{ + outfile->Write(NULL_RECORD.c_str(), NULL_RECORD.size(), 1); + + outfile->Write(GENERIC_FOOTID.c_str(), GENERIC_FOOTID.size(), 1); + + // here some padding is added for alignment to 16 bytes. + // if already aligned, the full 16 bytes is added. + size_t pos = outfile->Tell(); + size_t pad = 16 - (pos % 16); + for (size_t i = 0; i < pad; ++i) { + outfile->Write("\x00", 1, 1); + } + + // not sure what this is, but it seems to always be 0 in modern files + for (size_t i = 0; i < 4; ++i) { + outfile->Write("\x00", 1, 1); + } + + // now the file version again + { + StreamWriterLE outstream(outfile); + outstream.PutU4(EXPORT_VERSION_INT); + } // StreamWriter destructor writes the data to the file + + // and finally some binary footer added to all files + for (size_t i = 0; i < 120; ++i) { + outfile->Write("\x00", 1, 1); + } + outfile->Write(FOOT_MAGIC.c_str(), FOOT_MAGIC.size(), 1); +} + +void FBXExporter::WriteAllNodes () +{ + // header + // (and fileid, creation time, creator, if binary) + WriteHeaderExtension(); + + // global settings + WriteGlobalSettings(); + + // documents + WriteDocuments(); + + // references + WriteReferences(); + + // definitions + WriteDefinitions(); + + // objects + WriteObjects(); + + // connections + WriteConnections(); + + // WriteTakes? (deprecated since at least 2015 (fbx 7.4)) +} + +//FBXHeaderExtension top-level node +void FBXExporter::WriteHeaderExtension () +{ + if (!binary) { + // no title, follows directly from the top comment + } + FBX::Node n("FBXHeaderExtension"); + StreamWriterLE outstream(outfile); + int indent = 0; + + // begin node + n.Begin(outstream, binary, indent); + + // write properties + // (none) + + // finish properties + n.EndProperties(outstream, binary, indent, 0); + + // begin children + n.BeginChildren(outstream, binary, indent); + + indent = 1; + + // write child nodes + FBX::Node::WritePropertyNode( + "FBXHeaderVersion", int32_t(1003), outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "FBXVersion", int32_t(EXPORT_VERSION_INT), outstream, binary, indent + ); + if (binary) { + FBX::Node::WritePropertyNode( + "EncryptionType", int32_t(0), outstream, binary, indent + ); + } + + FBX::Node CreationTimeStamp("CreationTimeStamp"); + time_t rawtime; + time(&rawtime); + struct tm * now = localtime(&rawtime); + CreationTimeStamp.AddChild("Version", int32_t(1000)); + CreationTimeStamp.AddChild("Year", int32_t(now->tm_year + 1900)); + CreationTimeStamp.AddChild("Month", int32_t(now->tm_mon + 1)); + CreationTimeStamp.AddChild("Day", int32_t(now->tm_mday)); + CreationTimeStamp.AddChild("Hour", int32_t(now->tm_hour)); + CreationTimeStamp.AddChild("Minute", int32_t(now->tm_min)); + CreationTimeStamp.AddChild("Second", int32_t(now->tm_sec)); + CreationTimeStamp.AddChild("Millisecond", int32_t(0)); + CreationTimeStamp.Dump(outstream, binary, indent); + + std::stringstream creator; + creator << "Open Asset Import Library (Assimp) " << aiGetVersionMajor() + << "." << aiGetVersionMinor() << "." << aiGetVersionRevision(); + FBX::Node::WritePropertyNode( + "Creator", creator.str(), outstream, binary, indent + ); + + //FBX::Node sceneinfo("SceneInfo"); + //sceneinfo.AddProperty("GlobalInfo" + FBX::SEPARATOR + "SceneInfo"); + // not sure if any of this is actually needed, + // so just write an empty node for now. + //sceneinfo.Dump(outstream, binary, indent); + + indent = 0; + + // finish node + n.End(outstream, binary, indent, true); + + // that's it for FBXHeaderExtension... + if (!binary) { return; } + + // but binary files also need top-level FileID, CreationTime, Creator: + std::vector raw(GENERIC_FILEID.size()); + for (size_t i = 0; i < GENERIC_FILEID.size(); ++i) { + raw[i] = uint8_t(GENERIC_FILEID[i]); + } + FBX::Node::WritePropertyNode( + "FileId", raw, outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "CreationTime", GENERIC_CTIME, outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "Creator", creator.str(), outstream, binary, indent + ); +} + +void FBXExporter::WriteGlobalSettings () +{ + if (!binary) { + // no title, follows directly from the header extension + } + FBX::Node gs("GlobalSettings"); + gs.AddChild("Version", int32_t(1000)); + + FBX::Node p("Properties70"); + p.AddP70int("UpAxis", 1); + p.AddP70int("UpAxisSign", 1); + p.AddP70int("FrontAxis", 2); + p.AddP70int("FrontAxisSign", 1); + p.AddP70int("CoordAxis", 0); + p.AddP70int("CoordAxisSign", 1); + p.AddP70int("OriginalUpAxis", 1); + p.AddP70int("OriginalUpAxisSign", 1); + p.AddP70double("UnitScaleFactor", 1.0); + p.AddP70double("OriginalUnitScaleFactor", 1.0); + p.AddP70color("AmbientColor", 0.0, 0.0, 0.0); + p.AddP70string("DefaultCamera", "Producer Perspective"); + p.AddP70enum("TimeMode", 11); + p.AddP70enum("TimeProtocol", 2); + p.AddP70enum("SnapOnFrameMode", 0); + p.AddP70time("TimeSpanStart", 0); // TODO: animation support + p.AddP70time("TimeSpanStop", FBX::SECOND); // TODO: animation support + p.AddP70double("CustomFrameRate", -1.0); + p.AddP70("TimeMarker", "Compound", "", ""); // not sure what this is + p.AddP70int("CurrentTimeMarker", -1); + gs.AddChild(p); + + gs.Dump(outfile, binary, 0); +} + +void FBXExporter::WriteDocuments () +{ + if (!binary) { + WriteAsciiSectionHeader("Documents Description"); + } + + // not sure what the use of multiple documents would be, + // or whether any end-application supports it + FBX::Node docs("Documents"); + docs.AddChild("Count", int32_t(1)); + FBX::Node doc("Document"); + + // generate uid + int64_t uid = generate_uid(); + doc.AddProperties(uid, "", "Scene"); + FBX::Node p("Properties70"); + p.AddP70("SourceObject", "object", "", ""); // what is this even for? + p.AddP70string("ActiveAnimStackName", ""); // should do this properly? + doc.AddChild(p); + + // UID for root node in scene hierarchy. + // always set to 0 in the case of a single document. + // not sure what happens if more than one document exists, + // but that won't matter to us as we're exporting a single scene. + doc.AddChild("RootNode", int64_t(0)); + + docs.AddChild(doc); + docs.Dump(outfile, binary, 0); +} + +void FBXExporter::WriteReferences () +{ + if (!binary) { + WriteAsciiSectionHeader("Document References"); + } + // always empty for now. + // not really sure what this is for. + FBX::Node n("References"); + n.force_has_children = true; + n.Dump(outfile, binary, 0); +} + + +// --------------------------------------------------------------- +// some internal helper functions used for writing the definitions +// (before any actual data is written) +// --------------------------------------------------------------- + +size_t count_nodes(const aiNode* n) { + size_t count = 1; + for (size_t i = 0; i < n->mNumChildren; ++i) { + count += count_nodes(n->mChildren[i]); + } + return count; +} + +bool has_phong_mat(const aiScene* scene) +{ + // just search for any material with a shininess exponent + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + aiMaterial* mat = scene->mMaterials[i]; + float shininess = 0; + mat->Get(AI_MATKEY_SHININESS, shininess); + if (shininess > 0) { + return true; + } + } + return false; +} + +size_t count_images(const aiScene* scene) { + std::unordered_set images; + aiString texpath; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + aiMaterial* mat = scene->mMaterials[i]; + for ( + size_t tt = aiTextureType_DIFFUSE; + tt < aiTextureType_UNKNOWN; + ++tt + ){ + const aiTextureType textype = static_cast(tt); + const size_t texcount = mat->GetTextureCount(textype); + for (unsigned int j = 0; j < texcount; ++j) { + mat->GetTexture(textype, j, &texpath); + images.insert(std::string(texpath.C_Str())); + } + } + } + return images.size(); +} + +size_t count_textures(const aiScene* scene) { + size_t count = 0; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + aiMaterial* mat = scene->mMaterials[i]; + for ( + size_t tt = aiTextureType_DIFFUSE; + tt < aiTextureType_UNKNOWN; + ++tt + ){ + // TODO: handle layered textures + if (mat->GetTextureCount(static_cast(tt)) > 0) { + count += 1; + } + } + } + return count; +} + +size_t count_deformers(const aiScene* scene) { + size_t count = 0; + for (size_t i = 0; i < scene->mNumMeshes; ++i) { + const size_t n = scene->mMeshes[i]->mNumBones; + if (n) { + // 1 main deformer, 1 subdeformer per bone + count += n + 1; + } + } + return count; +} + +void FBXExporter::WriteDefinitions () +{ + // basically this is just bookkeeping: + // determining how many of each type of object there are + // and specifying the base properties to use when otherwise unspecified. + + // ascii section header + if (!binary) { + WriteAsciiSectionHeader("Object definitions"); + } + + // we need to count the objects + int32_t count; + int32_t total_count = 0; + + // and store them + std::vector object_nodes; + FBX::Node n, pt, p; + + // GlobalSettings + // this seems to always be here in Maya exports + n = FBX::Node("ObjectType", "GlobalSettings"); + count = 1; + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + + // AnimationStack / FbxAnimStack + // this seems to always be here in Maya exports, + // but no harm seems to come of leaving it out. + count = mScene->mNumAnimations; + if (count) { + n = FBX::Node("ObjectType", "AnimationStack"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxAnimStack"); + p = FBX::Node("Properties70"); + p.AddP70string("Description", ""); + p.AddP70time("LocalStart", 0); + p.AddP70time("LocalStop", 0); + p.AddP70time("ReferenceStart", 0); + p.AddP70time("ReferenceStop", 0); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // AnimationLayer / FbxAnimLayer + // this seems to always be here in Maya exports, + // but no harm seems to come of leaving it out. + // Assimp doesn't support animation layers, + // so there will be one per aiAnimation + count = mScene->mNumAnimations; + if (count) { + n = FBX::Node("ObjectType", "AnimationLayer"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FBXAnimLayer"); + p = FBX::Node("Properties70"); + p.AddP70("Weight", "Number", "", "A", double(100)); + p.AddP70bool("Mute", 0); + p.AddP70bool("Solo", 0); + p.AddP70bool("Lock", 0); + p.AddP70color("Color", 0.8, 0.8, 0.8); + p.AddP70("BlendMode", "enum", "", "", int32_t(0)); + p.AddP70("RotationAccumulationMode", "enum", "", "", int32_t(0)); + p.AddP70("ScaleAccumulationMode", "enum", "", "", int32_t(0)); + p.AddP70("BlendModeBypass", "ULongLong", "", "", int64_t(0)); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // NodeAttribute + // this is completely absurd. + // there can only be one "NodeAttribute" template, + // but FbxSkeleton, FbxCamera, FbxLight all are "NodeAttributes". + // so if only one exists we should set the template for that, + // otherwise... we just pick one :/. + // the others have to set all their properties every instance, + // because there's no template. + count = 1; // TODO: select properly + if (count) { + // FbxSkeleton + n = FBX::Node("ObjectType", "NodeAttribute"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxSkeleton"); + p = FBX::Node("Properties70"); + p.AddP70color("Color", 0.8, 0.8, 0.8); + p.AddP70double("Size", 33.333333333333); + p.AddP70("LimbLength", "double", "Number", "H", double(1)); + // note: not sure what the "H" flag is for - hidden? + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Model / FbxNode + // <~~ node hierarchy + count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node) + if (count) { + n = FBX::Node("ObjectType", "Model"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxNode"); + p = FBX::Node("Properties70"); + p.AddP70enum("QuaternionInterpolate", 0); + p.AddP70vector("RotationOffset", 0.0, 0.0, 0.0); + p.AddP70vector("RotationPivot", 0.0, 0.0, 0.0); + p.AddP70vector("ScalingOffset", 0.0, 0.0, 0.0); + p.AddP70vector("ScalingPivot", 0.0, 0.0, 0.0); + p.AddP70bool("TranslationActive", 0); + p.AddP70vector("TranslationMin", 0.0, 0.0, 0.0); + p.AddP70vector("TranslationMax", 0.0, 0.0, 0.0); + p.AddP70bool("TranslationMinX", 0); + p.AddP70bool("TranslationMinY", 0); + p.AddP70bool("TranslationMinZ", 0); + p.AddP70bool("TranslationMaxX", 0); + p.AddP70bool("TranslationMaxY", 0); + p.AddP70bool("TranslationMaxZ", 0); + p.AddP70enum("RotationOrder", 0); + p.AddP70bool("RotationSpaceForLimitOnly", 0); + p.AddP70double("RotationStiffnessX", 0.0); + p.AddP70double("RotationStiffnessY", 0.0); + p.AddP70double("RotationStiffnessZ", 0.0); + p.AddP70double("AxisLen", 10.0); + p.AddP70vector("PreRotation", 0.0, 0.0, 0.0); + p.AddP70vector("PostRotation", 0.0, 0.0, 0.0); + p.AddP70bool("RotationActive", 0); + p.AddP70vector("RotationMin", 0.0, 0.0, 0.0); + p.AddP70vector("RotationMax", 0.0, 0.0, 0.0); + p.AddP70bool("RotationMinX", 0); + p.AddP70bool("RotationMinY", 0); + p.AddP70bool("RotationMinZ", 0); + p.AddP70bool("RotationMaxX", 0); + p.AddP70bool("RotationMaxY", 0); + p.AddP70bool("RotationMaxZ", 0); + p.AddP70enum("InheritType", 0); + p.AddP70bool("ScalingActive", 0); + p.AddP70vector("ScalingMin", 0.0, 0.0, 0.0); + p.AddP70vector("ScalingMax", 1.0, 1.0, 1.0); + p.AddP70bool("ScalingMinX", 0); + p.AddP70bool("ScalingMinY", 0); + p.AddP70bool("ScalingMinZ", 0); + p.AddP70bool("ScalingMaxX", 0); + p.AddP70bool("ScalingMaxY", 0); + p.AddP70bool("ScalingMaxZ", 0); + p.AddP70vector("GeometricTranslation", 0.0, 0.0, 0.0); + p.AddP70vector("GeometricRotation", 0.0, 0.0, 0.0); + p.AddP70vector("GeometricScaling", 1.0, 1.0, 1.0); + p.AddP70double("MinDampRangeX", 0.0); + p.AddP70double("MinDampRangeY", 0.0); + p.AddP70double("MinDampRangeZ", 0.0); + p.AddP70double("MaxDampRangeX", 0.0); + p.AddP70double("MaxDampRangeY", 0.0); + p.AddP70double("MaxDampRangeZ", 0.0); + p.AddP70double("MinDampStrengthX", 0.0); + p.AddP70double("MinDampStrengthY", 0.0); + p.AddP70double("MinDampStrengthZ", 0.0); + p.AddP70double("MaxDampStrengthX", 0.0); + p.AddP70double("MaxDampStrengthY", 0.0); + p.AddP70double("MaxDampStrengthZ", 0.0); + p.AddP70double("PreferedAngleX", 0.0); + p.AddP70double("PreferedAngleY", 0.0); + p.AddP70double("PreferedAngleZ", 0.0); + p.AddP70("LookAtProperty", "object", "", ""); + p.AddP70("UpVectorProperty", "object", "", ""); + p.AddP70bool("Show", 1); + p.AddP70bool("NegativePercentShapeSupport", 1); + p.AddP70int("DefaultAttributeIndex", -1); + p.AddP70bool("Freeze", 0); + p.AddP70bool("LODBox", 0); + p.AddP70( + "Lcl Translation", "Lcl Translation", "", "A", + double(0), double(0), double(0) + ); + p.AddP70( + "Lcl Rotation", "Lcl Rotation", "", "A", + double(0), double(0), double(0) + ); + p.AddP70( + "Lcl Scaling", "Lcl Scaling", "", "A", + double(1), double(1), double(1) + ); + p.AddP70("Visibility", "Visibility", "", "A", double(1)); + p.AddP70( + "Visibility Inheritance", "Visibility Inheritance", "", "", + int32_t(1) + ); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Geometry / FbxMesh + // <~~ aiMesh + count = mScene->mNumMeshes; + if (count) { + n = FBX::Node("ObjectType", "Geometry"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxMesh"); + p = FBX::Node("Properties70"); + p.AddP70color("Color", 0, 0, 0); + p.AddP70vector("BBoxMin", 0, 0, 0); + p.AddP70vector("BBoxMax", 0, 0, 0); + p.AddP70bool("Primary Visibility", 1); + p.AddP70bool("Casts Shadows", 1); + p.AddP70bool("Receive Shadows", 1); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Material / FbxSurfacePhong, FbxSurfaceLambert, FbxSurfaceMaterial + // <~~ aiMaterial + // basically if there's any phong material this is defined as phong, + // and otherwise lambert. + // More complex materials cause a bare-bones FbxSurfaceMaterial definition + // and are treated specially, as they're not really supported by FBX. + // TODO: support Maya's Stingray PBS material + count = mScene->mNumMaterials; + if (count) { + bool has_phong = has_phong_mat(mScene); + n = FBX::Node("ObjectType", "Material"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate"); + if (has_phong) { + pt.AddProperty("FbxSurfacePhong"); + } else { + pt.AddProperty("FbxSurfaceLambert"); + } + p = FBX::Node("Properties70"); + if (has_phong) { + p.AddP70string("ShadingModel", "Phong"); + } else { + p.AddP70string("ShadingModel", "Lambert"); + } + p.AddP70bool("MultiLayer", 0); + p.AddP70colorA("EmissiveColor", 0.0, 0.0, 0.0); + p.AddP70numberA("EmissiveFactor", 1.0); + p.AddP70colorA("AmbientColor", 0.2, 0.2, 0.2); + p.AddP70numberA("AmbientFactor", 1.0); + p.AddP70colorA("DiffuseColor", 0.8, 0.8, 0.8); + p.AddP70numberA("DiffuseFactor", 1.0); + p.AddP70vector("Bump", 0.0, 0.0, 0.0); + p.AddP70vector("NormalMap", 0.0, 0.0, 0.0); + p.AddP70double("BumpFactor", 1.0); + p.AddP70colorA("TransparentColor", 0.0, 0.0, 0.0); + p.AddP70numberA("TransparencyFactor", 0.0); + p.AddP70color("DisplacementColor", 0.0, 0.0, 0.0); + p.AddP70double("DisplacementFactor", 1.0); + p.AddP70color("VectorDisplacementColor", 0.0, 0.0, 0.0); + p.AddP70double("VectorDisplacementFactor", 1.0); + if (has_phong) { + p.AddP70colorA("SpecularColor", 0.2, 0.2, 0.2); + p.AddP70numberA("SpecularFactor", 1.0); + p.AddP70numberA("ShininessExponent", 20.0); + p.AddP70colorA("ReflectionColor", 0.0, 0.0, 0.0); + p.AddP70numberA("ReflectionFactor", 1.0); + } + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Video / FbxVideo + // one for each image file. + count = int32_t(count_images(mScene)); + if (count) { + n = FBX::Node("ObjectType", "Video"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxVideo"); + p = FBX::Node("Properties70"); + p.AddP70bool("ImageSequence", 0); + p.AddP70int("ImageSequenceOffset", 0); + p.AddP70double("FrameRate", 0.0); + p.AddP70int("LastFrame", 0); + p.AddP70int("Width", 0); + p.AddP70int("Height", 0); + p.AddP70("Path", "KString", "XRefUrl", "", ""); + p.AddP70int("StartFrame", 0); + p.AddP70int("StopFrame", 0); + p.AddP70double("PlaySpeed", 0.0); + p.AddP70time("Offset", 0); + p.AddP70enum("InterlaceMode", 0); + p.AddP70bool("FreeRunning", 0); + p.AddP70bool("Loop", 0); + p.AddP70enum("AccessMode", 0); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Texture / FbxFileTexture + // <~~ aiTexture + count = int32_t(count_textures(mScene)); + if (count) { + n = FBX::Node("ObjectType", "Texture"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxFileTexture"); + p = FBX::Node("Properties70"); + p.AddP70enum("TextureTypeUse", 0); + p.AddP70numberA("Texture alpha", 1.0); + p.AddP70enum("CurrentMappingType", 0); + p.AddP70enum("WrapModeU", 0); + p.AddP70enum("WrapModeV", 0); + p.AddP70bool("UVSwap", 0); + p.AddP70bool("PremultiplyAlpha", 1); + p.AddP70vectorA("Translation", 0.0, 0.0, 0.0); + p.AddP70vectorA("Rotation", 0.0, 0.0, 0.0); + p.AddP70vectorA("Scaling", 1.0, 1.0, 1.0); + p.AddP70vector("TextureRotationPivot", 0.0, 0.0, 0.0); + p.AddP70vector("TextureScalingPivot", 0.0, 0.0, 0.0); + p.AddP70enum("CurrentTextureBlendMode", 1); + p.AddP70string("UVSet", "default"); + p.AddP70bool("UseMaterial", 0); + p.AddP70bool("UseMipMap", 0); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // AnimationCurveNode / FbxAnimCurveNode + count = mScene->mNumAnimations * 3; + if (count) { + n = FBX::Node("ObjectType", "AnimationCurveNode"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxAnimCurveNode"); + p = FBX::Node("Properties70"); + p.AddP70("d", "Compound", "", ""); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // AnimationCurve / FbxAnimCurve + count = mScene->mNumAnimations * 9; + if (count) { + n = FBX::Node("ObjectType", "AnimationCurve"); + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + } + + // Pose + count = 0; + for (size_t i = 0; i < mScene->mNumMeshes; ++i) { + aiMesh* mesh = mScene->mMeshes[i]; + if (mesh->HasBones()) { ++count; } + } + if (count) { + n = FBX::Node("ObjectType", "Pose"); + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + } + + // Deformer + count = int32_t(count_deformers(mScene)); + if (count) { + n = FBX::Node("ObjectType", "Deformer"); + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + } + + // (template) + count = 0; + if (count) { + n = FBX::Node("ObjectType", ""); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", ""); + p = FBX::Node("Properties70"); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // now write it all + FBX::Node defs("Definitions"); + defs.AddChild("Version", int32_t(100)); + defs.AddChild("Count", int32_t(total_count)); + for (auto &n : object_nodes) { defs.AddChild(n); } + defs.Dump(outfile, binary, 0); +} + + +// ------------------------------------------------------------------- +// some internal helper functions used for writing the objects section +// (which holds the actual data) +// ------------------------------------------------------------------- + +aiNode* get_node_for_mesh(unsigned int meshIndex, aiNode* node) +{ + for (size_t i = 0; i < node->mNumMeshes; ++i) { + if (node->mMeshes[i] == meshIndex) { + return node; + } + } + for (size_t i = 0; i < node->mNumChildren; ++i) { + aiNode* ret = get_node_for_mesh(meshIndex, node->mChildren[i]); + if (ret) { return ret; } + } + return nullptr; +} + +aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene) +{ + std::vector node_chain; + while (node != scene->mRootNode) { + node_chain.push_back(node); + node = node->mParent; + } + aiMatrix4x4 transform; + for (auto n = node_chain.rbegin(); n != node_chain.rend(); ++n) { + transform *= (*n)->mTransformation; + } + return transform; +} + +int64_t to_ktime(double ticks, const aiAnimation* anim) { + if (anim->mTicksPerSecond <= 0) { + return static_cast(ticks) * FBX::SECOND; + } + return (static_cast(ticks) / static_cast(anim->mTicksPerSecond)) * FBX::SECOND; +} + +int64_t to_ktime(double time) { + return (static_cast(time * FBX::SECOND)); +} + +void FBXExporter::WriteObjects () +{ + if (!binary) { + WriteAsciiSectionHeader("Object properties"); + } + // numbers should match those given in definitions! make sure to check + StreamWriterLE outstream(outfile); + FBX::Node object_node("Objects"); + int indent = 0; + object_node.Begin(outstream, binary, indent); + object_node.EndProperties(outstream, binary, indent); + object_node.BeginChildren(outstream, binary, indent); + + // geometry (aiMesh) + mesh_uids.clear(); + indent = 1; + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + // it's all about this mesh + aiMesh* m = mScene->mMeshes[mi]; + + // start the node record + FBX::Node n("Geometry"); + int64_t uid = generate_uid(); + mesh_uids.push_back(uid); + n.AddProperty(uid); + n.AddProperty(FBX::SEPARATOR + "Geometry"); + n.AddProperty("Mesh"); + n.Begin(outstream, binary, indent); + n.DumpProperties(outstream, binary, indent); + n.EndProperties(outstream, binary, indent); + n.BeginChildren(outstream, binary, indent); + indent = 2; + + // output vertex data - each vertex should be unique (probably) + std::vector flattened_vertices; + // index of original vertex in vertex data vector + std::vector vertex_indices; + // map of vertex value to its index in the data vector + std::map index_by_vertex_value; + int32_t index = 0; + for (size_t vi = 0; vi < m->mNumVertices; ++vi) { + aiVector3D vtx = m->mVertices[vi]; + auto elem = index_by_vertex_value.find(vtx); + if (elem == index_by_vertex_value.end()) { + vertex_indices.push_back(index); + index_by_vertex_value[vtx] = index; + flattened_vertices.push_back(vtx[0]); + flattened_vertices.push_back(vtx[1]); + flattened_vertices.push_back(vtx[2]); + ++index; + } else { + vertex_indices.push_back(int32_t(elem->second)); + } + } + FBX::Node::WritePropertyNode( + "Vertices", flattened_vertices, outstream, binary, indent + ); + + // output polygon data as a flattened array of vertex indices. + // the last vertex index of each polygon is negated and - 1 + std::vector polygon_data; + for (size_t fi = 0; fi < m->mNumFaces; ++fi) { + const aiFace &f = m->mFaces[fi]; + for (size_t pvi = 0; pvi < f.mNumIndices - 1; ++pvi) { + polygon_data.push_back(vertex_indices[f.mIndices[pvi]]); + } + polygon_data.push_back( + -1 - vertex_indices[f.mIndices[f.mNumIndices-1]] + ); + } + FBX::Node::WritePropertyNode( + "PolygonVertexIndex", polygon_data, outstream, binary, indent + ); + + // here could be edges but they're insane. + // it's optional anyway, so let's ignore it. + + FBX::Node::WritePropertyNode( + "GeometryVersion", int32_t(124), outstream, binary, indent + ); + + // normals, if any + if (m->HasNormals()) { + FBX::Node normals("LayerElementNormal", int32_t(0)); + normals.Begin(outstream, binary, indent); + normals.DumpProperties(outstream, binary, indent); + normals.EndProperties(outstream, binary, indent); + normals.BeginChildren(outstream, binary, indent); + indent = 3; + FBX::Node::WritePropertyNode( + "Version", int32_t(101), outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "Name", "", outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "MappingInformationType", "ByPolygonVertex", + outstream, binary, indent + ); + // TODO: vertex-normals or indexed normals when appropriate + FBX::Node::WritePropertyNode( + "ReferenceInformationType", "Direct", + outstream, binary, indent + ); + std::vector normal_data; + normal_data.reserve(3 * polygon_data.size()); + for (size_t fi = 0; fi < m->mNumFaces; ++fi) { + const aiFace &f = m->mFaces[fi]; + for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) { + const aiVector3D &n = m->mNormals[f.mIndices[pvi]]; + normal_data.push_back(n.x); + normal_data.push_back(n.y); + normal_data.push_back(n.z); + } + } + FBX::Node::WritePropertyNode( + "Normals", normal_data, outstream, binary, indent + ); + // note: version 102 has a NormalsW also... not sure what it is, + // so we can stick with version 101 for now. + indent = 2; + normals.End(outstream, binary, indent, true); + } + + // uvs, if any + for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) { + if (m->mNumUVComponents[uvi] > 2) { + // FBX only supports 2-channel UV maps... + // or at least i'm not sure how to indicate a different number + std::stringstream err; + err << "Only 2-channel UV maps supported by FBX,"; + err << " but mesh " << mi; + if (m->mName.length) { + err << " (" << m->mName.C_Str() << ")"; + } + err << " UV map " << uvi; + err << " has " << m->mNumUVComponents[uvi]; + err << " components! Data will be preserved,"; + err << " but may be incorrectly interpreted on load."; + ASSIMP_LOG_WARN(err.str()); + } + FBX::Node uv("LayerElementUV", int32_t(uvi)); + uv.Begin(outstream, binary, indent); + uv.DumpProperties(outstream, binary, indent); + uv.EndProperties(outstream, binary, indent); + uv.BeginChildren(outstream, binary, indent); + indent = 3; + FBX::Node::WritePropertyNode( + "Version", int32_t(101), outstream, binary, indent + ); + // it doesn't seem like assimp keeps the uv map name, + // so just leave it blank. + FBX::Node::WritePropertyNode( + "Name", "", outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "MappingInformationType", "ByPolygonVertex", + outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "ReferenceInformationType", "IndexToDirect", + outstream, binary, indent + ); + + std::vector uv_data; + std::vector uv_indices; + std::map index_by_uv; + int32_t index = 0; + for (size_t fi = 0; fi < m->mNumFaces; ++fi) { + const aiFace &f = m->mFaces[fi]; + for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) { + const aiVector3D &uv = + m->mTextureCoords[uvi][f.mIndices[pvi]]; + auto elem = index_by_uv.find(uv); + if (elem == index_by_uv.end()) { + index_by_uv[uv] = index; + uv_indices.push_back(index); + for (unsigned int x = 0; x < m->mNumUVComponents[uvi]; ++x) { + uv_data.push_back(uv[x]); + } + ++index; + } else { + uv_indices.push_back(elem->second); + } + } + } + FBX::Node::WritePropertyNode( + "UV", uv_data, outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "UVIndex", uv_indices, outstream, binary, indent + ); + indent = 2; + uv.End(outstream, binary, indent, true); + } + + // i'm not really sure why this material section exists, + // as the material is linked via "Connections". + // it seems to always have the same "0" value. + FBX::Node mat("LayerElementMaterial", int32_t(0)); + mat.AddChild("Version", int32_t(101)); + mat.AddChild("Name", ""); + mat.AddChild("MappingInformationType", "AllSame"); + mat.AddChild("ReferenceInformationType", "IndexToDirect"); + std::vector mat_indices = {0}; + mat.AddChild("Materials", mat_indices); + mat.Dump(outstream, binary, indent); + + // finally we have the layer specifications, + // which select the normals / UV set / etc to use. + // TODO: handle multiple uv sets correctly? + FBX::Node layer("Layer", int32_t(0)); + layer.AddChild("Version", int32_t(100)); + FBX::Node le("LayerElement"); + le.AddChild("Type", "LayerElementNormal"); + le.AddChild("TypedIndex", int32_t(0)); + layer.AddChild(le); + le = FBX::Node("LayerElement"); + le.AddChild("Type", "LayerElementMaterial"); + le.AddChild("TypedIndex", int32_t(0)); + layer.AddChild(le); + le = FBX::Node("LayerElement"); + le.AddChild("Type", "LayerElementUV"); + le.AddChild("TypedIndex", int32_t(0)); + layer.AddChild(le); + layer.Dump(outstream, binary, indent); + + for(unsigned int lr = 1; lr < m->GetNumUVChannels(); ++ lr) + { + FBX::Node layerExtra("Layer", int32_t(1)); + layerExtra.AddChild("Version", int32_t(100)); + FBX::Node leExtra("LayerElement"); + leExtra.AddChild("Type", "LayerElementUV"); + leExtra.AddChild("TypedIndex", int32_t(lr)); + layerExtra.AddChild(leExtra); + layerExtra.Dump(outstream, binary, indent); + } + // finish the node record + indent = 1; + n.End(outstream, binary, indent, true); + } + + // aiMaterial + material_uids.clear(); + for (size_t i = 0; i < mScene->mNumMaterials; ++i) { + // it's all about this material + aiMaterial* m = mScene->mMaterials[i]; + + // these are used to receive material data + float f; aiColor3D c; + + // start the node record + FBX::Node n("Material"); + + int64_t uid = generate_uid(); + material_uids.push_back(uid); + n.AddProperty(uid); + + aiString name; + m->Get(AI_MATKEY_NAME, name); + n.AddProperty(name.C_Str() + FBX::SEPARATOR + "Material"); + + n.AddProperty(""); + + n.AddChild("Version", int32_t(102)); + f = 0; + m->Get(AI_MATKEY_SHININESS, f); + bool phong = (f > 0); + if (phong) { + n.AddChild("ShadingModel", "phong"); + } else { + n.AddChild("ShadingModel", "lambert"); + } + n.AddChild("MultiLayer", int32_t(0)); + + FBX::Node p("Properties70"); + + // materials exported using the FBX SDK have two sets of fields. + // there are the properties specified in the PropertyTemplate, + // which are those supported by the modernFBX SDK, + // and an extra set of properties with simpler names. + // The extra properties are a legacy material system from pre-2009. + // + // In the modern system, each property has "color" and "factor". + // Generally the interpretation of these seems to be + // that the colour is multiplied by the factor before use, + // but this is not always clear-cut. + // + // Usually assimp only stores the colour, + // so we can just leave the factors at the default "1.0". + + // first we can export the "standard" properties + if (m->Get(AI_MATKEY_COLOR_AMBIENT, c) == aiReturn_SUCCESS) { + p.AddP70colorA("AmbientColor", c.r, c.g, c.b); + //p.AddP70numberA("AmbientFactor", 1.0); + } + if (m->Get(AI_MATKEY_COLOR_DIFFUSE, c) == aiReturn_SUCCESS) { + p.AddP70colorA("DiffuseColor", c.r, c.g, c.b); + //p.AddP70numberA("DiffuseFactor", 1.0); + } + if (m->Get(AI_MATKEY_COLOR_TRANSPARENT, c) == aiReturn_SUCCESS) { + // "TransparentColor" / "TransparencyFactor"... + // thanks FBX, for your insightful interpretation of consistency + p.AddP70colorA("TransparentColor", c.r, c.g, c.b); + // TransparencyFactor defaults to 0.0, so set it to 1.0. + // note: Maya always sets this to 1.0, + // so we can't use it sensibly as "Opacity". + // In stead we rely on the legacy "Opacity" value, below. + // Blender also relies on "Opacity" not "TransparencyFactor", + // probably for a similar reason. + p.AddP70numberA("TransparencyFactor", 1.0); + } + if (m->Get(AI_MATKEY_COLOR_REFLECTIVE, c) == aiReturn_SUCCESS) { + p.AddP70colorA("ReflectionColor", c.r, c.g, c.b); + } + if (m->Get(AI_MATKEY_REFLECTIVITY, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ReflectionFactor", f); + } + if (phong) { + if (m->Get(AI_MATKEY_COLOR_SPECULAR, c) == aiReturn_SUCCESS) { + p.AddP70colorA("SpecularColor", c.r, c.g, c.b); + } + if (m->Get(AI_MATKEY_SHININESS_STRENGTH, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ShininessFactor", f); + } + if (m->Get(AI_MATKEY_SHININESS, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ShininessExponent", f); + } + if (m->Get(AI_MATKEY_REFLECTIVITY, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ReflectionFactor", f); + } + } + + // Now the legacy system. + // For safety let's include it. + // thrse values don't exist in the property template, + // and usually are completely ignored when loading. + // One notable exception is the "Opacity" property, + // which Blender uses as (1.0 - alpha). + c.r = 0.0f; c.g = 0.0f; c.b = 0.0f; + m->Get(AI_MATKEY_COLOR_EMISSIVE, c); + p.AddP70vector("Emissive", c.r, c.g, c.b); + c.r = 0.2f; c.g = 0.2f; c.b = 0.2f; + m->Get(AI_MATKEY_COLOR_AMBIENT, c); + p.AddP70vector("Ambient", c.r, c.g, c.b); + c.r = 0.8f; c.g = 0.8f; c.b = 0.8f; + m->Get(AI_MATKEY_COLOR_DIFFUSE, c); + p.AddP70vector("Diffuse", c.r, c.g, c.b); + // The FBX SDK determines "Opacity" from transparency colour (RGB) + // and factor (F) as: O = (1.0 - F * ((R + G + B) / 3)). + // However we actually have an opacity value, + // so we should take it from AI_MATKEY_OPACITY if possible. + // It might make more sense to use TransparencyFactor, + // but Blender actually loads "Opacity" correctly, so let's use it. + f = 1.0f; + if (m->Get(AI_MATKEY_COLOR_TRANSPARENT, c) == aiReturn_SUCCESS) { + f = 1.0f - ((c.r + c.g + c.b) / 3.0f); + } + m->Get(AI_MATKEY_OPACITY, f); + p.AddP70double("Opacity", f); + if (phong) { + // specular color is multiplied by shininess_strength + c.r = 0.2f; c.g = 0.2f; c.b = 0.2f; + m->Get(AI_MATKEY_COLOR_SPECULAR, c); + f = 1.0f; + m->Get(AI_MATKEY_SHININESS_STRENGTH, f); + p.AddP70vector("Specular", f*c.r, f*c.g, f*c.b); + f = 20.0f; + m->Get(AI_MATKEY_SHININESS, f); + p.AddP70double("Shininess", f); + // Legacy "Reflectivity" is F*F*((R+G+B)/3), + // where F is the proportion of light reflected (AKA reflectivity), + // and RGB is the reflective colour of the material. + // No idea why, but we might as well set it the same way. + f = 0.0f; + m->Get(AI_MATKEY_REFLECTIVITY, f); + c.r = 1.0f, c.g = 1.0f, c.b = 1.0f; + m->Get(AI_MATKEY_COLOR_REFLECTIVE, c); + p.AddP70double("Reflectivity", f*f*((c.r+c.g+c.b)/3.0)); + } + + n.AddChild(p); + + n.Dump(outstream, binary, indent); + } + + // we need to look up all the images we're using, + // so we can generate uids, and eliminate duplicates. + std::map uid_by_image; + for (size_t i = 0; i < mScene->mNumMaterials; ++i) { + aiString texpath; + aiMaterial* mat = mScene->mMaterials[i]; + for ( + size_t tt = aiTextureType_DIFFUSE; + tt < aiTextureType_UNKNOWN; + ++tt + ){ + const aiTextureType textype = static_cast(tt); + const size_t texcount = mat->GetTextureCount(textype); + for (size_t j = 0; j < texcount; ++j) { + mat->GetTexture(textype, (unsigned int)j, &texpath); + const std::string texstring = texpath.C_Str(); + auto elem = uid_by_image.find(texstring); + if (elem == uid_by_image.end()) { + uid_by_image[texstring] = generate_uid(); + } + } + } + } + + // FbxVideo - stores images used by textures. + for (const auto &it : uid_by_image) { + FBX::Node n("Video"); + const int64_t& uid = it.second; + const std::string name = ""; // TODO: ... name??? + n.AddProperties(uid, name + FBX::SEPARATOR + "Video", "Clip"); + n.AddChild("Type", "Clip"); + FBX::Node p("Properties70"); + // TODO: get full path... relative path... etc... ugh... + // for now just use the same path for everything, + // and hopefully one of them will work out. + std::string path = it.first; + // try get embedded texture + const aiTexture* embedded_texture = mScene->GetEmbeddedTexture(it.first.c_str()); + if (embedded_texture != nullptr) { + // change the path (use original filename, if available. If name is empty, concatenate texture index with file extension) + std::stringstream newPath; + if (embedded_texture->mFilename.length > 0) { + newPath << embedded_texture->mFilename.C_Str(); + } else if (embedded_texture->achFormatHint[0]) { + int texture_index = std::stoi(path.substr(1, path.size() - 1)); + newPath << texture_index << "." << embedded_texture->achFormatHint; + } + path = newPath.str(); + // embed the texture + size_t texture_size = static_cast(embedded_texture->mWidth * std::max(embedded_texture->mHeight, 1u)); + if (binary) { + // embed texture as binary data + std::vector tex_data; + tex_data.resize(texture_size); + memcpy(&tex_data[0], (char*)embedded_texture->pcData, texture_size); + n.AddChild("Content", tex_data); + } else { + // embed texture in base64 encoding + std::string encoded_texture = FBX::Util::EncodeBase64((char*)embedded_texture->pcData, texture_size); + n.AddChild("Content", encoded_texture); + } + } + p.AddP70("Path", "KString", "XRefUrl", "", path); + n.AddChild(p); + n.AddChild("UseMipMap", int32_t(0)); + n.AddChild("Filename", path); + n.AddChild("RelativeFilename", path); + n.Dump(outstream, binary, indent); + } + + // Textures + // referenced by material_index/texture_type pairs. + std::map,int64_t> texture_uids; + const std::map prop_name_by_tt = { + {aiTextureType_DIFFUSE, "DiffuseColor"}, + {aiTextureType_SPECULAR, "SpecularColor"}, + {aiTextureType_AMBIENT, "AmbientColor"}, + {aiTextureType_EMISSIVE, "EmissiveColor"}, + {aiTextureType_HEIGHT, "Bump"}, + {aiTextureType_NORMALS, "NormalMap"}, + {aiTextureType_SHININESS, "ShininessExponent"}, + {aiTextureType_OPACITY, "TransparentColor"}, + {aiTextureType_DISPLACEMENT, "DisplacementColor"}, + //{aiTextureType_LIGHTMAP, "???"}, + {aiTextureType_REFLECTION, "ReflectionColor"} + //{aiTextureType_UNKNOWN, ""} + }; + for (size_t i = 0; i < mScene->mNumMaterials; ++i) { + // textures are attached to materials + aiMaterial* mat = mScene->mMaterials[i]; + int64_t material_uid = material_uids[i]; + + for ( + size_t j = aiTextureType_DIFFUSE; + j < aiTextureType_UNKNOWN; + ++j + ) { + const aiTextureType tt = static_cast(j); + size_t n = mat->GetTextureCount(tt); + + if (n < 1) { // no texture of this type + continue; + } + + if (n > 1) { + // TODO: multilayer textures + std::stringstream err; + err << "Multilayer textures not supported (for now),"; + err << " skipping texture type " << j; + err << " of material " << i; + ASSIMP_LOG_WARN(err.str()); + } + + // get image path for this (single-image) texture + aiString tpath; + if (mat->GetTexture(tt, 0, &tpath) != aiReturn_SUCCESS) { + std::stringstream err; + err << "Failed to get texture 0 for texture of type " << tt; + err << " on material " << i; + err << ", however GetTextureCount returned 1."; + throw DeadlyExportError(err.str()); + } + const std::string texture_path(tpath.C_Str()); + + // get connected image uid + auto elem = uid_by_image.find(texture_path); + if (elem == uid_by_image.end()) { + // this should never happen + std::stringstream err; + err << "Failed to find video element for texture with path"; + err << " \"" << texture_path << "\""; + err << ", type " << j << ", material " << i; + throw DeadlyExportError(err.str()); + } + const int64_t image_uid = elem->second; + + // get the name of the material property to connect to + auto elem2 = prop_name_by_tt.find(tt); + if (elem2 == prop_name_by_tt.end()) { + // don't know how to handle this type of texture, + // so skip it. + std::stringstream err; + err << "Not sure how to handle texture of type " << j; + err << " on material " << i; + err << ", skipping..."; + ASSIMP_LOG_WARN(err.str()); + continue; + } + const std::string& prop_name = elem2->second; + + // generate a uid for this texture + const int64_t texture_uid = generate_uid(); + + // link the texture to the material + connections.emplace_back( + "C", "OP", texture_uid, material_uid, prop_name + ); + + // link the image data to the texture + connections.emplace_back("C", "OO", image_uid, texture_uid); + + // now write the actual texture node + FBX::Node tnode("Texture"); + // TODO: some way to determine texture name? + const std::string texture_name = "" + FBX::SEPARATOR + "Texture"; + tnode.AddProperties(texture_uid, texture_name, ""); + // there really doesn't seem to be a better type than this: + tnode.AddChild("Type", "TextureVideoClip"); + tnode.AddChild("Version", int32_t(202)); + tnode.AddChild("TextureName", texture_name); + FBX::Node p("Properties70"); + p.AddP70enum("CurrentTextureBlendMode", 0); // TODO: verify + //p.AddP70string("UVSet", ""); // TODO: how should this work? + p.AddP70bool("UseMaterial", 1); + tnode.AddChild(p); + // can't easily detrmine which texture path will be correct, + // so just store what we have in every field. + // these being incorrect is a common problem with FBX anyway. + tnode.AddChild("FileName", texture_path); + tnode.AddChild("RelativeFilename", texture_path); + tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0)); + tnode.AddChild("ModelUVScaling", double(1.0), double(1.0)); + tnode.AddChild("Texture_Alpha_Source", "None"); + tnode.AddChild( + "Cropping", int32_t(0), int32_t(0), int32_t(0), int32_t(0) + ); + tnode.Dump(outstream, binary, indent); + } + } + + // bones. + // + // output structure: + // subset of node hierarchy that are "skeleton", + // i.e. do not have meshes but only bones. + // but.. i'm not sure how anyone could guarantee that... + // + // input... + // well, for each mesh it has "bones", + // and the bone names correspond to nodes. + // of course we also need the parent nodes, + // as they give some of the transform........ + // + // well. we can assume a sane input, i suppose. + // + // so input is the bone node hierarchy, + // with an extra thing for the transformation of the MESH in BONE space. + // + // output is a set of bone nodes, + // a "bindpose" which indicates the default local transform of all bones, + // and a set of "deformers". + // each deformer is parented to a mesh geometry, + // and has one or more "subdeformer"s as children. + // each subdeformer has one bone node as a child, + // and represents the influence of that bone on the grandparent mesh. + // the subdeformer has a list of indices, and weights, + // with indices specifying vertex indices, + // and weights specifying the corresponding influence of this bone. + // it also has Transform and TransformLink elements, + // specifying the transform of the MESH in BONE space, + // and the transformation of the BONE in WORLD space, + // likely in the bindpose. + // + // the input bone structure is different but similar, + // storing the number of weights for this bone, + // and an array of (vertex index, weight) pairs. + // + // one sticky point is that the number of vertices may not match, + // because assimp splits vertices by normal, uv, etc. + + // functor for aiNode sorting + struct SortNodeByName + { + bool operator()(const aiNode *lhs, const aiNode *rhs) const + { + return strcmp(lhs->mName.C_Str(), rhs->mName.C_Str()) < 0; + } + }; + + // first we should mark the skeleton for each mesh. + // the skeleton must include not only the aiBones, + // but also all their parent nodes. + // anything that affects the position of any bone node must be included. + // Use SorNodeByName to make sure the exported result will be the same across all systems + // Otherwise the aiNodes of the skeleton would be sorted based on the pointer address, which isn't consistent + std::vector> skeleton_by_mesh(mScene->mNumMeshes); + // at the same time we can build a list of all the skeleton nodes, + // which will be used later to mark them as type "limbNode". + std::unordered_set limbnodes; + + //actual bone nodes in fbx, without parenting-up + std::unordered_set setAllBoneNamesInScene; + for(unsigned int m = 0; m < mScene->mNumMeshes; ++ m) + { + aiMesh* pMesh = mScene->mMeshes[m]; + for(unsigned int b = 0; b < pMesh->mNumBones; ++ b) + setAllBoneNamesInScene.insert(pMesh->mBones[b]->mName.data); + } + aiMatrix4x4 mxTransIdentity; + + // and a map of nodes by bone name, as finding them is annoying. + std::map node_by_bone; + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + const aiMesh* m = mScene->mMeshes[mi]; + std::set skeleton; + for (size_t bi =0; bi < m->mNumBones; ++bi) { + const aiBone* b = m->mBones[bi]; + const std::string name(b->mName.C_Str()); + auto elem = node_by_bone.find(name); + aiNode* n; + if (elem != node_by_bone.end()) { + n = elem->second; + } else { + n = mScene->mRootNode->FindNode(b->mName); + if (!n) { + // this should never happen + std::stringstream err; + err << "Failed to find node for bone: \"" << name << "\""; + throw DeadlyExportError(err.str()); + } + node_by_bone[name] = n; + limbnodes.insert(n); + } + skeleton.insert(n); + // mark all parent nodes as skeleton as well, + // up until we find the root node, + // or else the node containing the mesh, + // or else the parent of a node containig the mesh. + for ( + const aiNode* parent = n->mParent; + parent && parent != mScene->mRootNode; + parent = parent->mParent + ) { + // if we've already done this node we can skip it all + if (skeleton.count(parent)) { + break; + } + // ignore fbx transform nodes as these will be collapsed later + // TODO: cache this by aiNode* + const std::string node_name(parent->mName.C_Str()); + if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) { + continue; + } + //not a bone in scene && no effect in transform + if(setAllBoneNamesInScene.find(node_name)==setAllBoneNamesInScene.end() + && parent->mTransformation == mxTransIdentity) { + continue; + } + // otherwise check if this is the root of the skeleton + bool end = false; + // is the mesh part of this node? + for (size_t i = 0; i < parent->mNumMeshes; ++i) { + if (parent->mMeshes[i] == mi) { + end = true; + break; + } + } + // is the mesh in one of the children of this node? + for (size_t j = 0; j < parent->mNumChildren; ++j) { + aiNode* child = parent->mChildren[j]; + for (size_t i = 0; i < child->mNumMeshes; ++i) { + if (child->mMeshes[i] == mi) { + end = true; + break; + } + } + if (end) { break; } + } + limbnodes.insert(parent); + skeleton.insert(parent); + // if it was the skeleton root we can finish here + if (end) { break; } + } + } + skeleton_by_mesh[mi] = skeleton; + } + + // we'll need the uids for the bone nodes, so generate them now + for (size_t i = 0; i < mScene->mNumMeshes; ++i) { + auto &s = skeleton_by_mesh[i]; + for (const aiNode* n : s) { + auto elem = node_uids.find(n); + if (elem == node_uids.end()) { + node_uids[n] = generate_uid(); + } + } + } + + // now, for each aiMesh, we need to export a deformer, + // and for each aiBone a subdeformer, + // which should have all the skinning info. + // these will need to be connected properly to the mesh, + // and we can do that all now. + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + const aiMesh* m = mScene->mMeshes[mi]; + if (!m->HasBones()) { + continue; + } + // make a deformer for this mesh + int64_t deformer_uid = generate_uid(); + FBX::Node dnode("Deformer"); + dnode.AddProperties(deformer_uid, FBX::SEPARATOR + "Deformer", "Skin"); + dnode.AddChild("Version", int32_t(101)); + // "acuracy"... this is not a typo.... + dnode.AddChild("Link_DeformAcuracy", double(50)); + dnode.AddChild("SkinningType", "Linear"); // TODO: other modes? + dnode.Dump(outstream, binary, indent); + + // connect it + connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]); + + // we will be indexing by vertex... + // but there might be a different number of "vertices" + // between assimp and our output FBX. + // this code is cut-and-pasted from the geometry section above... + // ideally this should not be so. + // --- + // index of original vertex in vertex data vector + std::vector vertex_indices; + // map of vertex value to its index in the data vector + std::map index_by_vertex_value; + int32_t index = 0; + for (size_t vi = 0; vi < m->mNumVertices; ++vi) { + aiVector3D vtx = m->mVertices[vi]; + auto elem = index_by_vertex_value.find(vtx); + if (elem == index_by_vertex_value.end()) { + vertex_indices.push_back(index); + index_by_vertex_value[vtx] = index; + ++index; + } else { + vertex_indices.push_back(int32_t(elem->second)); + } + } + + // TODO, FIXME: this won't work if anything is not in the bind pose. + // for now if such a situation is detected, we throw an exception. + std::set not_in_bind_pose; + std::set no_offset_matrix; + + // first get this mesh's position in world space, + // as we'll need it for each subdeformer. + // + // ...of course taking the position of the MESH doesn't make sense, + // as it can be instanced to many nodes. + // All we can do is assume no instancing, + // and take the first node we find that contains the mesh. + aiNode* mesh_node = get_node_for_mesh((unsigned int)mi, mScene->mRootNode); + aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene); + + // now make a subdeformer for each bone in the skeleton + const std::set skeleton= skeleton_by_mesh[mi]; + for (const aiNode* bone_node : skeleton) { + // if there's a bone for this node, find it + const aiBone* b = nullptr; + for (size_t bi = 0; bi < m->mNumBones; ++bi) { + // TODO: this probably should index by something else + const std::string name(m->mBones[bi]->mName.C_Str()); + if (node_by_bone[name] == bone_node) { + b = m->mBones[bi]; + break; + } + } + if (!b) { + no_offset_matrix.insert(bone_node); + } + + // start the subdeformer node + const int64_t subdeformer_uid = generate_uid(); + FBX::Node sdnode("Deformer"); + sdnode.AddProperties( + subdeformer_uid, FBX::SEPARATOR + "SubDeformer", "Cluster" + ); + sdnode.AddChild("Version", int32_t(100)); + sdnode.AddChild("UserData", "", ""); + + // add indices and weights, if any + if (b) { + std::vector subdef_indices; + std::vector subdef_weights; + int32_t last_index = -1; + for (size_t wi = 0; wi < b->mNumWeights; ++wi) { + int32_t vi = vertex_indices[b->mWeights[wi].mVertexId]; + if (vi == last_index) { + // only for vertices we exported to fbx + // TODO, FIXME: this assumes identically-located vertices + // will always deform in the same way. + // as assimp doesn't store a separate list of "positions", + // there's not much that can be done about this + // other than assuming that identical position means + // identical vertex. + continue; + } + subdef_indices.push_back(vi); + subdef_weights.push_back(b->mWeights[wi].mWeight); + last_index = vi; + } + // yes, "indexes" + sdnode.AddChild("Indexes", subdef_indices); + sdnode.AddChild("Weights", subdef_weights); + } + + // transform is the transform of the mesh, but in bone space. + // if the skeleton is in the bind pose, + // we can take the inverse of the world-space bone transform + // and multiply by the world-space transform of the mesh. + aiMatrix4x4 bone_xform = get_world_transform(bone_node, mScene); + aiMatrix4x4 inverse_bone_xform = bone_xform; + inverse_bone_xform.Inverse(); + aiMatrix4x4 tr = inverse_bone_xform * mesh_xform; + + // this should be the same as the bone's mOffsetMatrix. + // if it's not the same, the skeleton isn't in the bind pose. + float epsilon = 1e-4f; // some error is to be expected + float epsilon_custom = mProperties->GetPropertyFloat("BINDPOSE_EPSILON", -1); + if(epsilon_custom > 0) + epsilon = epsilon_custom; + bool bone_xform_okay = true; + if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) { + not_in_bind_pose.insert(b); + bone_xform_okay = false; + } + + // if we have a bone we should use the mOffsetMatrix, + // otherwise try to just use the calculated transform. + if (b) { + sdnode.AddChild("Transform", b->mOffsetMatrix); + } else { + sdnode.AddChild("Transform", tr); + } + // note: it doesn't matter if we mix these, + // because if they disagree we'll throw an exception later. + // it could be that the skeleton is not in the bone pose + // but all bones are still defined, + // in which case this would use the mOffsetMatrix for everything + // and a correct skeleton would still be output. + + // transformlink should be the position of the bone in world space. + // if the bone is in the bind pose (or nonexistent), + // we can just use the matrix we already calculated + if (bone_xform_okay) { + sdnode.AddChild("TransformLink", bone_xform); + // otherwise we can only work it out using the mesh position. + } else { + aiMatrix4x4 trl = b->mOffsetMatrix; + trl.Inverse(); + trl *= mesh_xform; + sdnode.AddChild("TransformLink", trl); + } + // note: this means we ALWAYS rely on the mesh node transform + // being unchanged from the time the skeleton was bound. + // there's not really any way around this at the moment. + + // done + sdnode.Dump(outstream, binary, indent); + + // lastly, connect to the parent deformer + connections.emplace_back( + "C", "OO", subdeformer_uid, deformer_uid + ); + + // we also need to connect the limb node to the subdeformer. + connections.emplace_back( + "C", "OO", node_uids[bone_node], subdeformer_uid + ); + } + + // if we cannot create a valid FBX file, simply die. + // this will both prevent unnecessary bug reports, + // and tell the user what they can do to fix the situation + // (i.e. export their model in the bind pose). + if (no_offset_matrix.size() && not_in_bind_pose.size()) { + std::stringstream err; + err << "Not enough information to construct bind pose"; + err << " for mesh " << mi << "!"; + err << " Transform matrix for bone \""; + err << (*not_in_bind_pose.begin())->mName.C_Str() << "\""; + if (not_in_bind_pose.size() > 1) { + err << " (and " << not_in_bind_pose.size() - 1 << " more)"; + } + err << " does not match mOffsetMatrix,"; + err << " and node \""; + err << (*no_offset_matrix.begin())->mName.C_Str() << "\""; + if (no_offset_matrix.size() > 1) { + err << " (and " << no_offset_matrix.size() - 1 << " more)"; + } + err << " has no offset matrix to rely on."; + err << " Please ensure bones are in the bind pose to export."; + throw DeadlyExportError(err.str()); + } + + } + + // BindPose + // + // This is a legacy system, which should be unnecessary. + // + // Somehow including it slows file loading by the official FBX SDK, + // and as it can reconstruct it from the deformers anyway, + // this is not currently included. + // + // The code is kept here in case it's useful in the future, + // but it's pretty much a hack anyway, + // as assimp doesn't store bindpose information for full skeletons. + // + /*for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + aiMesh* mesh = mScene->mMeshes[mi]; + if (! mesh->HasBones()) { continue; } + int64_t bindpose_uid = generate_uid(); + FBX::Node bpnode("Pose"); + bpnode.AddProperty(bindpose_uid); + // note: this uid is never linked or connected to anything. + bpnode.AddProperty(FBX::SEPARATOR + "Pose"); // blank name + bpnode.AddProperty("BindPose"); + + bpnode.AddChild("Type", "BindPose"); + bpnode.AddChild("Version", int32_t(100)); + + aiNode* mesh_node = get_node_for_mesh(mi, mScene->mRootNode); + + // next get the whole skeleton for this mesh. + // we need it all to define the bindpose section. + // the FBX SDK will complain if it's missing, + // and also if parents of used bones don't have a subdeformer. + // order shouldn't matter. + std::set skeleton; + for (size_t bi = 0; bi < mesh->mNumBones; ++bi) { + // bone node should have already been indexed + const aiBone* b = mesh->mBones[bi]; + const std::string bone_name(b->mName.C_Str()); + aiNode* parent = node_by_bone[bone_name]; + // insert all nodes down to the root or mesh node + while ( + parent + && parent != mScene->mRootNode + && parent != mesh_node + ) { + skeleton.insert(parent); + parent = parent->mParent; + } + } + + // number of pose nodes. includes one for the mesh itself. + bpnode.AddChild("NbPoseNodes", int32_t(1 + skeleton.size())); + + // the first pose node is always the mesh itself + FBX::Node pose("PoseNode"); + pose.AddChild("Node", mesh_uids[mi]); + aiMatrix4x4 mesh_node_xform = get_world_transform(mesh_node, mScene); + pose.AddChild("Matrix", mesh_node_xform); + bpnode.AddChild(pose); + + for (aiNode* bonenode : skeleton) { + // does this node have a uid yet? + int64_t node_uid; + auto node_uid_iter = node_uids.find(bonenode); + if (node_uid_iter != node_uids.end()) { + node_uid = node_uid_iter->second; + } else { + node_uid = generate_uid(); + node_uids[bonenode] = node_uid; + } + + // make a pose thingy + pose = FBX::Node("PoseNode"); + pose.AddChild("Node", node_uid); + aiMatrix4x4 node_xform = get_world_transform(bonenode, mScene); + pose.AddChild("Matrix", node_xform); + bpnode.AddChild(pose); + } + + // now write it + bpnode.Dump(outstream, binary, indent); + }*/ + + // TODO: cameras, lights + + // write nodes (i.e. model hierarchy) + // start at root node + WriteModelNodes( + outstream, mScene->mRootNode, 0, limbnodes + ); + + // animations + // + // in FBX there are: + // * AnimationStack - corresponds to an aiAnimation + // * AnimationLayer - a combinable animation component + // * AnimationCurveNode - links the property to be animated + // * AnimationCurve - defines animation data for a single property value + // + // the CurveNode also provides the default value for a property, + // such as the X, Y, Z coordinates for animatable translation. + // + // the Curve only specifies values for one component of the property, + // so there will be a separate AnimationCurve for X, Y, and Z. + // + // Assimp has: + // * aiAnimation - basically corresponds to an AnimationStack + // * aiNodeAnim - defines all animation for one aiNode + // * aiVectorKey/aiQuatKey - define the keyframe data for T/R/S + // + // assimp has no equivalent for AnimationLayer, + // and these are flattened on FBX import. + // we can assume there will be one per AnimationStack. + // + // the aiNodeAnim contains all animation data for a single aiNode, + // which will correspond to three AnimationCurveNode's: + // one each for translation, rotation and scale. + // The data for each of these will be put in 9 AnimationCurve's, + // T.X, T.Y, T.Z, R.X, R.Y, R.Z, etc. + + // AnimationStack / aiAnimation + std::vector animation_stack_uids(mScene->mNumAnimations); + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + int64_t animstack_uid = generate_uid(); + animation_stack_uids[ai] = animstack_uid; + const aiAnimation* anim = mScene->mAnimations[ai]; + + FBX::Node asnode("AnimationStack"); + std::string name = anim->mName.C_Str() + FBX::SEPARATOR + "AnimStack"; + asnode.AddProperties(animstack_uid, name, ""); + FBX::Node p("Properties70"); + p.AddP70time("LocalStart", 0); // assimp doesn't store this + p.AddP70time("LocalStop", to_ktime(anim->mDuration, anim)); + p.AddP70time("ReferenceStart", 0); + p.AddP70time("ReferenceStop", to_ktime(anim->mDuration, anim)); + asnode.AddChild(p); + + // this node absurdly always pretends it has children + // (in this case it does, but just in case...) + asnode.force_has_children = true; + asnode.Dump(outstream, binary, indent); + + // note: animation stacks are not connected to anything + } + + // AnimationLayer - one per aiAnimation + std::vector animation_layer_uids(mScene->mNumAnimations); + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + int64_t animlayer_uid = generate_uid(); + animation_layer_uids[ai] = animlayer_uid; + FBX::Node alnode("AnimationLayer"); + alnode.AddProperties(animlayer_uid, FBX::SEPARATOR + "AnimLayer", ""); + + // this node absurdly always pretends it has children + alnode.force_has_children = true; + alnode.Dump(outstream, binary, indent); + + // connect to the relevant animstack + connections.emplace_back( + "C", "OO", animlayer_uid, animation_stack_uids[ai] + ); + } + + // AnimCurveNode - three per aiNodeAnim + std::vector>> curve_node_uids; + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + const aiAnimation* anim = mScene->mAnimations[ai]; + const int64_t layer_uid = animation_layer_uids[ai]; + std::vector> nodeanim_uids; + for (size_t nai = 0; nai < anim->mNumChannels; ++nai) { + const aiNodeAnim* na = anim->mChannels[nai]; + // get the corresponding aiNode + const aiNode* node = mScene->mRootNode->FindNode(na->mNodeName); + // and its transform + const aiMatrix4x4 node_xfm = get_world_transform(node, mScene); + aiVector3D T, R, S; + node_xfm.Decompose(S, R, T); + + // AnimationCurveNode uids + std::array ids; + ids[0] = generate_uid(); // T + ids[1] = generate_uid(); // R + ids[2] = generate_uid(); // S + + // translation + WriteAnimationCurveNode(outstream, + ids[0], "T", T, "Lcl Translation", + layer_uid, node_uids[node] + ); + + // rotation + WriteAnimationCurveNode(outstream, + ids[1], "R", R, "Lcl Rotation", + layer_uid, node_uids[node] + ); + + // scale + WriteAnimationCurveNode(outstream, + ids[2], "S", S, "Lcl Scale", + layer_uid, node_uids[node] + ); + + // store the uids for later use + nodeanim_uids.push_back(ids); + } + curve_node_uids.push_back(nodeanim_uids); + } + + // AnimCurve - defines actual keyframe data. + // there's a separate curve for every component of every vector, + // for example a transform curvenode will have separate X/Y/Z AnimCurve's + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + const aiAnimation* anim = mScene->mAnimations[ai]; + for (size_t nai = 0; nai < anim->mNumChannels; ++nai) { + const aiNodeAnim* na = anim->mChannels[nai]; + // get the corresponding aiNode + const aiNode* node = mScene->mRootNode->FindNode(na->mNodeName); + // and its transform + const aiMatrix4x4 node_xfm = get_world_transform(node, mScene); + aiVector3D T, R, S; + node_xfm.Decompose(S, R, T); + const std::array& ids = curve_node_uids[ai][nai]; + + std::vector times; + std::vector xval, yval, zval; + + // position/translation + for (size_t ki = 0; ki < na->mNumPositionKeys; ++ki) { + const aiVectorKey& k = na->mPositionKeys[ki]; + times.push_back(to_ktime(k.mTime)); + xval.push_back(k.mValue.x); + yval.push_back(k.mValue.y); + zval.push_back(k.mValue.z); + } + // one curve each for X, Y, Z + WriteAnimationCurve(outstream, T.x, times, xval, ids[0], "d|X"); + WriteAnimationCurve(outstream, T.y, times, yval, ids[0], "d|Y"); + WriteAnimationCurve(outstream, T.z, times, zval, ids[0], "d|Z"); + + // rotation + times.clear(); xval.clear(); yval.clear(); zval.clear(); + for (size_t ki = 0; ki < na->mNumRotationKeys; ++ki) { + const aiQuatKey& k = na->mRotationKeys[ki]; + times.push_back(to_ktime(k.mTime)); + // TODO: aiQuaternion method to convert to Euler... + aiMatrix4x4 m(k.mValue.GetMatrix()); + aiVector3D qs, qr, qt; + m.Decompose(qs, qr, qt); + qr *= DEG; + xval.push_back(qr.x); + yval.push_back(qr.y); + zval.push_back(qr.z); + } + WriteAnimationCurve(outstream, R.x, times, xval, ids[1], "d|X"); + WriteAnimationCurve(outstream, R.y, times, yval, ids[1], "d|Y"); + WriteAnimationCurve(outstream, R.z, times, zval, ids[1], "d|Z"); + + // scaling/scale + times.clear(); xval.clear(); yval.clear(); zval.clear(); + for (size_t ki = 0; ki < na->mNumScalingKeys; ++ki) { + const aiVectorKey& k = na->mScalingKeys[ki]; + times.push_back(to_ktime(k.mTime)); + xval.push_back(k.mValue.x); + yval.push_back(k.mValue.y); + zval.push_back(k.mValue.z); + } + WriteAnimationCurve(outstream, S.x, times, xval, ids[2], "d|X"); + WriteAnimationCurve(outstream, S.y, times, yval, ids[2], "d|Y"); + WriteAnimationCurve(outstream, S.z, times, zval, ids[2], "d|Z"); + } + } + + indent = 0; + object_node.End(outstream, binary, indent, true); +} + +// convenience map of magic node name strings to FBX properties, +// including the expected type of transform. +const std::map> transform_types = { + {"Translation", {"Lcl Translation", 't'}}, + {"RotationOffset", {"RotationOffset", 't'}}, + {"RotationPivot", {"RotationPivot", 't'}}, + {"PreRotation", {"PreRotation", 'r'}}, + {"Rotation", {"Lcl Rotation", 'r'}}, + {"PostRotation", {"PostRotation", 'r'}}, + {"RotationPivotInverse", {"RotationPivotInverse", 'i'}}, + {"ScalingOffset", {"ScalingOffset", 't'}}, + {"ScalingPivot", {"ScalingPivot", 't'}}, + {"Scaling", {"Lcl Scaling", 's'}}, + {"ScalingPivotInverse", {"ScalingPivotInverse", 'i'}}, + {"GeometricScaling", {"GeometricScaling", 's'}}, + {"GeometricRotation", {"GeometricRotation", 'r'}}, + {"GeometricTranslation", {"GeometricTranslation", 't'}}, + {"GeometricTranslationInverse", {"GeometricTranslationInverse", 'i'}}, + {"GeometricRotationInverse", {"GeometricRotationInverse", 'i'}}, + {"GeometricScalingInverse", {"GeometricScalingInverse", 'i'}} +}; + +// write a single model node to the stream +void FBXExporter::WriteModelNode( + StreamWriterLE& outstream, + bool binary, + const aiNode* node, + int64_t node_uid, + const std::string& type, + const std::vector>& transform_chain, + TransformInheritance inherit_type +){ + const aiVector3D zero = {0, 0, 0}; + const aiVector3D one = {1, 1, 1}; + FBX::Node m("Model"); + std::string name = node->mName.C_Str() + FBX::SEPARATOR + "Model"; + m.AddProperties(node_uid, name, type); + m.AddChild("Version", int32_t(232)); + FBX::Node p("Properties70"); + p.AddP70bool("RotationActive", 1); + p.AddP70int("DefaultAttributeIndex", 0); + p.AddP70enum("InheritType", inherit_type); + if (transform_chain.empty()) { + // decompose 4x4 transform matrix into TRS + aiVector3D t, r, s; + node->mTransformation.Decompose(s, r, t); + if (t != zero) { + p.AddP70( + "Lcl Translation", "Lcl Translation", "", "A", + double(t.x), double(t.y), double(t.z) + ); + } + if (r != zero) { + p.AddP70( + "Lcl Rotation", "Lcl Rotation", "", "A", + double(DEG*r.x), double(DEG*r.y), double(DEG*r.z) + ); + } + if (s != one) { + p.AddP70( + "Lcl Scaling", "Lcl Scaling", "", "A", + double(s.x), double(s.y), double(s.z) + ); + } + } else { + // apply the transformation chain. + // these transformation elements are created when importing FBX, + // which has a complex transformation hierarchy for each node. + // as such we can bake the hierarchy back into the node on export. + for (auto &item : transform_chain) { + auto elem = transform_types.find(item.first); + if (elem == transform_types.end()) { + // then this is a bug + std::stringstream err; + err << "unrecognized FBX transformation type: "; + err << item.first; + throw DeadlyExportError(err.str()); + } + const std::string &name = elem->second.first; + const aiVector3D &v = item.second; + if (name.compare(0, 4, "Lcl ") == 0) { + // special handling for animatable properties + p.AddP70( + name, name, "", "A", + double(v.x), double(v.y), double(v.z) + ); + } else { + p.AddP70vector(name, v.x, v.y, v.z); + } + } + } + m.AddChild(p); + + // not sure what these are for, + // but they seem to be omnipresent + m.AddChild("Shading", FBXExportProperty(true)); + m.AddChild("Culling", FBXExportProperty("CullingOff")); + + m.Dump(outstream, binary, 1); +} + +// wrapper for WriteModelNodes to create and pass a blank transform chain +void FBXExporter::WriteModelNodes( + StreamWriterLE& s, + const aiNode* node, + int64_t parent_uid, + const std::unordered_set& limbnodes +) { + std::vector> chain; + WriteModelNodes(s, node, parent_uid, limbnodes, chain); +} + +void FBXExporter::WriteModelNodes( + StreamWriterLE& outstream, + const aiNode* node, + int64_t parent_uid, + const std::unordered_set& limbnodes, + std::vector>& transform_chain +) { + // first collapse any expanded transformation chains created by FBX import. + std::string node_name(node->mName.C_Str()); + if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) { + auto pos = node_name.find(MAGIC_NODE_TAG) + MAGIC_NODE_TAG.size() + 1; + std::string type_name = node_name.substr(pos); + auto elem = transform_types.find(type_name); + if (elem == transform_types.end()) { + // then this is a bug and should be fixed + std::stringstream err; + err << "unrecognized FBX transformation node"; + err << " of type " << type_name << " in node " << node_name; + throw DeadlyExportError(err.str()); + } + aiVector3D t, r, s; + node->mTransformation.Decompose(s, r, t); + switch (elem->second.second) { + case 'i': // inverse + // we don't need to worry about the inverse matrices + break; + case 't': // translation + transform_chain.emplace_back(elem->first, t); + break; + case 'r': // rotation + r *= float(DEG); + transform_chain.emplace_back(elem->first, r); + break; + case 's': // scale + transform_chain.emplace_back(elem->first, s); + break; + default: + // this should never happen + std::stringstream err; + err << "unrecognized FBX transformation type code: "; + err << elem->second.second; + throw DeadlyExportError(err.str()); + } + // now continue on to any child nodes + for (unsigned i = 0; i < node->mNumChildren; ++i) { + WriteModelNodes( + outstream, + node->mChildren[i], + parent_uid, + limbnodes, + transform_chain + ); + } + return; + } + + int64_t node_uid = 0; + // generate uid and connect to parent, if not the root node, + if (node != mScene->mRootNode) { + auto elem = node_uids.find(node); + if (elem != node_uids.end()) { + node_uid = elem->second; + } else { + node_uid = generate_uid(); + node_uids[node] = node_uid; + } + connections.emplace_back("C", "OO", node_uid, parent_uid); + } + + // what type of node is this? + if (node == mScene->mRootNode) { + // handled later + } else if (node->mNumMeshes == 1) { + // connect to child mesh, which should have been written previously + connections.emplace_back( + "C", "OO", mesh_uids[node->mMeshes[0]], node_uid + ); + // also connect to the material for the child mesh + connections.emplace_back( + "C", "OO", + material_uids[mScene->mMeshes[node->mMeshes[0]]->mMaterialIndex], + node_uid + ); + // write model node + WriteModelNode( + outstream, binary, node, node_uid, "Mesh", transform_chain + ); + } else if (limbnodes.count(node)) { + WriteModelNode( + outstream, binary, node, node_uid, "LimbNode", transform_chain + ); + // we also need to write a nodeattribute to mark it as a skeleton + int64_t node_attribute_uid = generate_uid(); + FBX::Node na("NodeAttribute"); + na.AddProperties( + node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode" + ); + na.AddChild("TypeFlags", FBXExportProperty("Skeleton")); + na.Dump(outstream, binary, 1); + // and connect them + connections.emplace_back("C", "OO", node_attribute_uid, node_uid); + } else { + // generate a null node so we can add children to it + WriteModelNode( + outstream, binary, node, node_uid, "Null", transform_chain + ); + } + + // if more than one child mesh, make nodes for each mesh + if (node->mNumMeshes > 1 || node == mScene->mRootNode) { + for (size_t i = 0; i < node->mNumMeshes; ++i) { + // make a new model node + int64_t new_node_uid = generate_uid(); + // connect to parent node + connections.emplace_back("C", "OO", new_node_uid, node_uid); + // connect to child mesh, which should have been written previously + connections.emplace_back( + "C", "OO", mesh_uids[node->mMeshes[i]], new_node_uid + ); + // also connect to the material for the child mesh + connections.emplace_back( + "C", "OO", + material_uids[ + mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex + ], + new_node_uid + ); + // write model node + FBX::Node m("Model"); + // take name from mesh name, if it exists + std::string name = mScene->mMeshes[node->mMeshes[i]]->mName.C_Str(); + name += FBX::SEPARATOR + "Model"; + m.AddProperties(new_node_uid, name, "Mesh"); + m.AddChild("Version", int32_t(232)); + FBX::Node p("Properties70"); + p.AddP70enum("InheritType", 1); + m.AddChild(p); + m.Dump(outstream, binary, 1); + } + } + + // now recurse into children + for (size_t i = 0; i < node->mNumChildren; ++i) { + WriteModelNodes( + outstream, node->mChildren[i], node_uid, limbnodes + ); + } +} + + +void FBXExporter::WriteAnimationCurveNode( + StreamWriterLE& outstream, + int64_t uid, + std::string name, // "T", "R", or "S" + aiVector3D default_value, + std::string property_name, // "Lcl Translation" etc + int64_t layer_uid, + int64_t node_uid +) { + FBX::Node n("AnimationCurveNode"); + n.AddProperties(uid, name + FBX::SEPARATOR + "AnimCurveNode", ""); + FBX::Node p("Properties70"); + p.AddP70numberA("d|X", default_value.x); + p.AddP70numberA("d|Y", default_value.y); + p.AddP70numberA("d|Z", default_value.z); + n.AddChild(p); + n.Dump(outstream, binary, 1); + // connect to layer + this->connections.emplace_back("C", "OO", uid, layer_uid); + // connect to bone + this->connections.emplace_back("C", "OP", uid, node_uid, property_name); +} + + +void FBXExporter::WriteAnimationCurve( + StreamWriterLE& outstream, + double default_value, + const std::vector& times, + const std::vector& values, + int64_t curvenode_uid, + const std::string& property_link // "d|X", "d|Y", etc +) { + FBX::Node n("AnimationCurve"); + int64_t curve_uid = generate_uid(); + n.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); + n.AddChild("Default", default_value); + n.AddChild("KeyVer", int32_t(4009)); + n.AddChild("KeyTime", times); + n.AddChild("KeyValueFloat", values); + // TODO: keyattr flags and data (STUB for now) + n.AddChild("KeyAttrFlags", std::vector{0}); + n.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); + n.AddChild( + "KeyAttrRefCount", + std::vector{static_cast(times.size())} + ); + n.Dump(outstream, binary, 1); + this->connections.emplace_back( + "C", "OP", curve_uid, curvenode_uid, property_link + ); +} + + +void FBXExporter::WriteConnections () +{ + // we should have completed the connection graph already, + // so basically just dump it here + if (!binary) { + WriteAsciiSectionHeader("Object connections"); + } + // TODO: comments with names in the ascii version + FBX::Node conn("Connections"); + StreamWriterLE outstream(outfile); + conn.Begin(outstream, binary, 0); + conn.BeginChildren(outstream, binary, 0); + for (auto &n : connections) { + n.Dump(outstream, binary, 1); + } + conn.End(outstream, binary, 0, !connections.empty()); + connections.clear(); +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/thirdparty/assimp/code/FBX/FBXExporter.h b/thirdparty/assimp/code/FBX/FBXExporter.h new file mode 100644 index 0000000000..71fb55c57f --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXExporter.h @@ -0,0 +1,178 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXExporter.h +* Declares the exporter class to write a scene to an fbx file +*/ +#ifndef AI_FBXEXPORTER_H_INC +#define AI_FBXEXPORTER_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportNode.h" // FBX::Node +#include "FBXCommon.h" // FBX::TransformInheritance + +#include +//#include +#include // StreamWriterLE +#include // DeadlyExportError + +#include +#include +#include +#include // shared_ptr +#include // stringstream + +struct aiScene; +struct aiNode; +//struct aiMaterial; + +namespace Assimp +{ + class IOSystem; + class IOStream; + class ExportProperties; + + // --------------------------------------------------------------------- + /** Helper class to export a given scene to an FBX file. */ + // --------------------------------------------------------------------- + class FBXExporter + { + public: + /// Constructor for a specific scene to export + FBXExporter(const aiScene* pScene, const ExportProperties* pProperties); + + // call one of these methods to export + void ExportBinary(const char* pFile, IOSystem* pIOSystem); + void ExportAscii(const char* pFile, IOSystem* pIOSystem); + + private: + bool binary; // whether current export is in binary or ascii format + const aiScene* mScene; // the scene to export + const ExportProperties* mProperties; // currently unused + std::shared_ptr outfile; // file to write to + + std::vector connections; // connection storage + + std::vector mesh_uids; + std::vector material_uids; + std::map node_uids; + + // this crude unique-ID system is actually fine + int64_t last_uid = 999999; + int64_t generate_uid() { return ++last_uid; } + + // binary files have a specific header and footer, + // in addition to the actual data + void WriteBinaryHeader(); + void WriteBinaryFooter(); + + // ascii files have a comment at the top + void WriteAsciiHeader(); + + // WriteAllNodes does the actual export. + // It just calls all the Write
methods below in order. + void WriteAllNodes(); + + // Methods to write individual sections. + // The order here matches the order inside an FBX file. + // Each method corresponds to a top-level FBX section, + // except WriteHeader which also includes some binary-only sections + // and WriteFooter which is binary data only. + void WriteHeaderExtension(); + // WriteFileId(); // binary-only, included in WriteHeader + // WriteCreationTime(); // binary-only, included in WriteHeader + // WriteCreator(); // binary-only, included in WriteHeader + void WriteGlobalSettings(); + void WriteDocuments(); + void WriteReferences(); + void WriteDefinitions(); + void WriteObjects(); + void WriteConnections(); + // WriteTakes(); // deprecated since at least 2015 (fbx 7.4) + + // helpers + void WriteAsciiSectionHeader(const std::string& title); + void WriteModelNodes( + Assimp::StreamWriterLE& s, + const aiNode* node, + int64_t parent_uid, + const std::unordered_set& limbnodes + ); + void WriteModelNodes( // usually don't call this directly + StreamWriterLE& s, + const aiNode* node, + int64_t parent_uid, + const std::unordered_set& limbnodes, + std::vector>& transform_chain + ); + void WriteModelNode( // nor this + StreamWriterLE& s, + bool binary, + const aiNode* node, + int64_t node_uid, + const std::string& type, + const std::vector>& xfm_chain, + FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs + ); + void WriteAnimationCurveNode( + StreamWriterLE& outstream, + int64_t uid, + std::string name, // "T", "R", or "S" + aiVector3D default_value, + std::string property_name, // "Lcl Translation" etc + int64_t animation_layer_uid, + int64_t node_uid + ); + void WriteAnimationCurve( + StreamWriterLE& outstream, + double default_value, + const std::vector& times, + const std::vector& values, + int64_t curvenode_id, + const std::string& property_link // "d|X", "d|Y", etc + ); + }; +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXEXPORTER_H_INC diff --git a/thirdparty/assimp/code/FBX/FBXImportSettings.h b/thirdparty/assimp/code/FBX/FBXImportSettings.h new file mode 100644 index 0000000000..1a4c80f8b2 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXImportSettings.h @@ -0,0 +1,164 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXImportSettings.h + * @brief FBX importer runtime configuration + */ +#ifndef INCLUDED_AI_FBX_IMPORTSETTINGS_H +#define INCLUDED_AI_FBX_IMPORTSETTINGS_H + +namespace Assimp { +namespace FBX { + +/** FBX import settings, parts of which are publicly accessible via their corresponding AI_CONFIG constants */ +struct ImportSettings +{ + ImportSettings() + : strictMode(true) + , readAllLayers(true) + , readAllMaterials(false) + , readMaterials(true) + , readTextures(true) + , readCameras(true) + , readLights(true) + , readAnimations(true) + , readWeights(true) + , preservePivots(true) + , optimizeEmptyAnimationCurves(true) + , useLegacyEmbeddedTextureNaming(false) + , removeEmptyBones( true ) + , convertToMeters( false ) { + // empty + } + + + /** enable strict mode: + * - only accept fbx 2012, 2013 files + * - on the slightest error, give up. + * + * Basically, strict mode means that the fbx file will actually + * be validated. Strict mode is off by default. */ + bool strictMode; + + /** specifies whether all geometry layers are read and scanned for + * usable data channels. The FBX spec indicates that many readers + * will only read the first channel and that this is in some way + * the recommended way- in reality, however, it happens a lot that + * vertex data is spread among multiple layers. The default + * value for this option is true.*/ + bool readAllLayers; + + /** specifies whether all materials are read, or only those that + * are referenced by at least one mesh. Reading all materials + * may make FBX reading a lot slower since all objects + * need to be processed . + * This bit is ignored unless readMaterials=true*/ + bool readAllMaterials; + + + /** import materials (true) or skip them and assign a default + * material. The default value is true.*/ + bool readMaterials; + + /** import embedded textures? Default value is true.*/ + bool readTextures; + + /** import cameras? Default value is true.*/ + bool readCameras; + + /** import light sources? Default value is true.*/ + bool readLights; + + /** import animations (i.e. animation curves, the node + * skeleton is always imported). Default value is true. */ + bool readAnimations; + + /** read bones (vertex weights and deform info). + * Default value is true. */ + bool readWeights; + + /** preserve transformation pivots and offsets. Since these can + * not directly be represented in assimp, additional dummy + * nodes will be generated. Note that settings this to false + * can make animation import a lot slower. The default value + * is true. + * + * The naming scheme for the generated nodes is: + * _$AssimpFbx$_ + * + * where is one of + * RotationPivot + * RotationOffset + * PreRotation + * PostRotation + * ScalingPivot + * ScalingOffset + * Translation + * Scaling + * Rotation + **/ + bool preservePivots; + + /** do not import animation curves that specify a constant + * values matching the corresponding node transformation. + * The default value is true. */ + bool optimizeEmptyAnimationCurves; + + /** use legacy naming for embedded textures eg: (*0, *1, *2) + */ + bool useLegacyEmbeddedTextureNaming; + + /** Empty bones shall be removed + */ + bool removeEmptyBones; + + /** Set to true to perform a conversion from cm to meter after the import + */ + bool convertToMeters; +}; + + +} // !FBX +} // !Assimp + +#endif + diff --git a/thirdparty/assimp/code/FBX/FBXImporter.cpp b/thirdparty/assimp/code/FBX/FBXImporter.cpp new file mode 100644 index 0000000000..ec8bbd2b47 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXImporter.cpp @@ -0,0 +1,203 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. +r +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXImporter.cpp + * @brief Implementation of the FBX importer. + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXImporter.h" + +#include "FBXTokenizer.h" +#include "FBXParser.h" +#include "FBXUtil.h" +#include "FBXDocument.h" +#include "FBXConverter.h" + +#include +#include +#include +#include + +namespace Assimp { + +template<> +const char* LogFunctions::Prefix() { + static auto prefix = "FBX: "; + return prefix; +} + +} + +using namespace Assimp; +using namespace Assimp::Formatter; +using namespace Assimp::FBX; + +namespace { + +static const aiImporterDesc desc = { + "Autodesk FBX Importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "fbx" +}; +} + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by #Importer +FBXImporter::FBXImporter() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FBXImporter::~FBXImporter() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const +{ + const std::string& extension = GetExtension(pFile); + if (extension == std::string( desc.mFileExtensions ) ) { + return true; + } + + else if ((!extension.length() || checkSig) && pIOHandler) { + // at least ASCII-FBX files usually have a 'FBX' somewhere in their head + const char* tokens[] = {"fbx"}; + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// List all extensions handled by this loader +const aiImporterDesc* FBXImporter::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Setup configuration properties for the loader +void FBXImporter::SetupProperties(const Importer* pImp) +{ + settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true); + settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false); + settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true); + settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true); + settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true); + settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true); + settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true); + settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false); + settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true); + settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true); + settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false); + settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true); + settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false); +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) +{ + std::unique_ptr stream(pIOHandler->Open(pFile,"rb")); + if (!stream) { + ThrowException("Could not open file for reading"); + } + + // read entire file into memory - no streaming for this, fbx + // files can grow large, but the assimp output data structure + // then becomes very large, too. Assimp doesn't support + // streaming for its output data structures so the net win with + // streaming input data would be very low. + std::vector contents; + contents.resize(stream->FileSize()+1); + stream->Read( &*contents.begin(), 1, contents.size()-1 ); + contents[ contents.size() - 1 ] = 0; + const char* const begin = &*contents.begin(); + + // broadphase tokenizing pass in which we identify the core + // syntax elements of FBX (brackets, commas, key:value mappings) + TokenList tokens; + try { + + bool is_binary = false; + if (!strncmp(begin,"Kaydara FBX Binary",18)) { + is_binary = true; + TokenizeBinary(tokens,begin,contents.size()); + } + else { + Tokenize(tokens,begin); + } + + // use this information to construct a very rudimentary + // parse-tree representing the FBX scope structure + Parser parser(tokens, is_binary); + + // take the raw parse-tree and convert it to a FBX DOM + Document doc(parser,settings); + + FbxUnit unit(FbxUnit::cm); + if (settings.convertToMeters) { + unit = FbxUnit::m; + } + // convert the FBX DOM to aiScene + ConvertToAssimpScene(pScene,doc, settings.removeEmptyBones, unit); + + std::for_each(tokens.begin(),tokens.end(),Util::delete_fun()); + } + catch(std::exception&) { + std::for_each(tokens.begin(),tokens.end(),Util::delete_fun()); + throw; + } +} + +#endif // !ASSIMP_BUILD_NO_FBX_IMPORTER diff --git a/thirdparty/assimp/code/FBX/FBXImporter.h b/thirdparty/assimp/code/FBX/FBXImporter.h new file mode 100644 index 0000000000..c365b2cddf --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXImporter.h @@ -0,0 +1,100 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXImporter.h + * @brief Declaration of the FBX main importer class + */ +#ifndef INCLUDED_AI_FBX_IMPORTER_H +#define INCLUDED_AI_FBX_IMPORTER_H + +#include +#include + +#include "FBXImportSettings.h" + +namespace Assimp { + +// TinyFormatter.h +namespace Formatter { + template class basic_formatter; + typedef class basic_formatter< char, std::char_traits, std::allocator > format; +} + +// ------------------------------------------------------------------------------------------- +/** Load the Autodesk FBX file format. + + See http://en.wikipedia.org/wiki/FBX +*/ +// ------------------------------------------------------------------------------------------- +class FBXImporter : public BaseImporter, public LogFunctions +{ +public: + FBXImporter(); + virtual ~FBXImporter(); + + // -------------------- + bool CanRead( const std::string& pFile, + IOSystem* pIOHandler, + bool checkSig + ) const; + +protected: + + // -------------------- + const aiImporterDesc* GetInfo () const; + + // -------------------- + void SetupProperties(const Importer* pImp); + + // -------------------- + void InternReadFile( const std::string& pFile, + aiScene* pScene, + IOSystem* pIOHandler + ); + +private: + FBX::ImportSettings settings; +}; // !class FBXImporter + +} // end of namespace Assimp +#endif // !INCLUDED_AI_FBX_IMPORTER_H + diff --git a/thirdparty/assimp/code/FBX/FBXMaterial.cpp b/thirdparty/assimp/code/FBX/FBXMaterial.cpp new file mode 100644 index 0000000000..f43a8b84b0 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXMaterial.cpp @@ -0,0 +1,405 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXMaterial.cpp + * @brief Assimp::FBX::Material and Assimp::FBX::Texture implementation + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXParser.h" +#include "FBXDocument.h" +#include "FBXImporter.h" +#include "FBXImportSettings.h" +#include "FBXDocumentUtil.h" +#include "FBXProperties.h" +#include + +#include // std::transform +#include "FBXUtil.h" + +namespace Assimp { +namespace FBX { + + using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Material::Material(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: Object(id,element,name) +{ + const Scope& sc = GetRequiredScope(element); + + const Element* const ShadingModel = sc["ShadingModel"]; + const Element* const MultiLayer = sc["MultiLayer"]; + + if(MultiLayer) { + multilayer = !!ParseTokenAsInt(GetRequiredToken(*MultiLayer,0)); + } + + if(ShadingModel) { + shading = ParseTokenAsString(GetRequiredToken(*ShadingModel,0)); + } + else { + DOMWarning("shading mode not specified, assuming phong",&element); + shading = "phong"; + } + + std::string templateName; + + // lower-case shading because Blender (for example) writes "Phong" + std::transform(shading.begin(), shading.end(), shading.begin(), ::tolower); + if(shading == "phong") { + templateName = "Material.FbxSurfacePhong"; + } + else if(shading == "lambert") { + templateName = "Material.FbxSurfaceLambert"; + } + else { + DOMWarning("shading mode not recognized: " + shading,&element); + } + + props = GetPropertyTable(doc,templateName,element,sc); + + // resolve texture links + const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID()); + for(const Connection* con : conns) { + + // texture link to properties, not objects + if (!con->PropertyName().length()) { + continue; + } + + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for texture link, ignoring",&element); + continue; + } + + const Texture* const tex = dynamic_cast(ob); + if(!tex) { + const LayeredTexture* const layeredTexture = dynamic_cast(ob); + if(!layeredTexture) { + DOMWarning("source object for texture link is not a texture or layered texture, ignoring",&element); + continue; + } + const std::string& prop = con->PropertyName(); + if (layeredTextures.find(prop) != layeredTextures.end()) { + DOMWarning("duplicate layered texture link: " + prop,&element); + } + + layeredTextures[prop] = layeredTexture; + ((LayeredTexture*)layeredTexture)->fillTexture(doc); + } + else + { + const std::string& prop = con->PropertyName(); + if (textures.find(prop) != textures.end()) { + DOMWarning("duplicate texture link: " + prop,&element); + } + + textures[prop] = tex; + } + + } +} + + +// ------------------------------------------------------------------------------------------------ +Material::~Material() +{ +} + + +// ------------------------------------------------------------------------------------------------ +Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: Object(id,element,name) +, uvScaling(1.0f,1.0f) +, media(0) +{ + const Scope& sc = GetRequiredScope(element); + + const Element* const Type = sc["Type"]; + const Element* const FileName = sc["FileName"]; + const Element* const RelativeFilename = sc["RelativeFilename"]; + const Element* const ModelUVTranslation = sc["ModelUVTranslation"]; + const Element* const ModelUVScaling = sc["ModelUVScaling"]; + const Element* const Texture_Alpha_Source = sc["Texture_Alpha_Source"]; + const Element* const Cropping = sc["Cropping"]; + + if(Type) { + type = ParseTokenAsString(GetRequiredToken(*Type,0)); + } + + if(FileName) { + fileName = ParseTokenAsString(GetRequiredToken(*FileName,0)); + } + + if(RelativeFilename) { + relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0)); + } + + if(ModelUVTranslation) { + uvTrans = aiVector2D(ParseTokenAsFloat(GetRequiredToken(*ModelUVTranslation,0)), + ParseTokenAsFloat(GetRequiredToken(*ModelUVTranslation,1)) + ); + } + + if(ModelUVScaling) { + uvScaling = aiVector2D(ParseTokenAsFloat(GetRequiredToken(*ModelUVScaling,0)), + ParseTokenAsFloat(GetRequiredToken(*ModelUVScaling,1)) + ); + } + + if(Cropping) { + crop[0] = ParseTokenAsInt(GetRequiredToken(*Cropping,0)); + crop[1] = ParseTokenAsInt(GetRequiredToken(*Cropping,1)); + crop[2] = ParseTokenAsInt(GetRequiredToken(*Cropping,2)); + crop[3] = ParseTokenAsInt(GetRequiredToken(*Cropping,3)); + } + else { + // vc8 doesn't support the crop() syntax in initialization lists + // (and vc9 WARNS about the new (i.e. compliant) behaviour). + crop[0] = crop[1] = crop[2] = crop[3] = 0; + } + + if(Texture_Alpha_Source) { + alphaSource = ParseTokenAsString(GetRequiredToken(*Texture_Alpha_Source,0)); + } + + props = GetPropertyTable(doc,"Texture.FbxFileTexture",element,sc); + + // 3DS Max and FBX SDK use "Scaling" and "Translation" instead of "ModelUVScaling" and "ModelUVTranslation". Use these properties if available. + bool ok; + const aiVector3D& scaling = PropertyGet(*props, "Scaling", ok); + if (ok) { + uvScaling.x = scaling.x; + uvScaling.y = scaling.y; + } + + const aiVector3D& trans = PropertyGet(*props, "Translation", ok); + if (ok) { + uvTrans.x = trans.x; + uvTrans.y = trans.y; + } + + // resolve video links + if(doc.Settings().readTextures) { + const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID()); + for(const Connection* con : conns) { + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for texture link, ignoring",&element); + continue; + } + + const Video* const video = dynamic_cast(ob); + if(video) { + media = video; + } + } + } +} + + +Texture::~Texture() +{ + +} + +LayeredTexture::LayeredTexture(uint64_t id, const Element& element, const Document& /*doc*/, const std::string& name) +: Object(id,element,name) +,blendMode(BlendMode_Modulate) +,alpha(1) +{ + const Scope& sc = GetRequiredScope(element); + + const Element* const BlendModes = sc["BlendModes"]; + const Element* const Alphas = sc["Alphas"]; + + + if(BlendModes!=0) + { + blendMode = (BlendMode)ParseTokenAsInt(GetRequiredToken(*BlendModes,0)); + } + if(Alphas!=0) + { + alpha = ParseTokenAsFloat(GetRequiredToken(*Alphas,0)); + } +} + +LayeredTexture::~LayeredTexture() +{ + +} + +void LayeredTexture::fillTexture(const Document& doc) +{ + const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID()); + for(size_t i = 0; i < conns.size();++i) + { + const Connection* con = conns.at(i); + + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for texture link, ignoring",&element); + continue; + } + + const Texture* const tex = dynamic_cast(ob); + + textures.push_back(tex); + } +} + + +// ------------------------------------------------------------------------------------------------ +Video::Video(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: Object(id,element,name) +, contentLength(0) +, content(0) +{ + const Scope& sc = GetRequiredScope(element); + + const Element* const Type = sc["Type"]; + const Element* const FileName = sc.FindElementCaseInsensitive("FileName"); //some files retain the information as "Filename", others "FileName", who knows + const Element* const RelativeFilename = sc["RelativeFilename"]; + const Element* const Content = sc["Content"]; + + if(Type) { + type = ParseTokenAsString(GetRequiredToken(*Type,0)); + } + + if(FileName) { + fileName = ParseTokenAsString(GetRequiredToken(*FileName,0)); + } + + if(RelativeFilename) { + relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0)); + } + + if(Content && !Content->Tokens().empty()) { + //this field is omitted when the embedded texture is already loaded, let's ignore if it's not found + try { + const Token& token = GetRequiredToken(*Content, 0); + const char* data = token.begin(); + if (!token.IsBinary()) { + if (*data != '"') { + DOMError("embedded content is not surrounded by quotation marks", &element); + } + else { + size_t targetLength = 0; + auto numTokens = Content->Tokens().size(); + // First time compute size (it could be large like 64Gb and it is good to allocate it once) + for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) + { + const Token& dataToken = GetRequiredToken(*Content, tokenIdx); + size_t tokenLength = dataToken.end() - dataToken.begin() - 2; // ignore double quotes + const char* base64data = dataToken.begin() + 1; + const size_t outLength = Util::ComputeDecodedSizeBase64(base64data, tokenLength); + if (outLength == 0) + { + DOMError("Corrupted embedded content found", &element); + } + targetLength += outLength; + } + if (targetLength == 0) + { + DOMError("Corrupted embedded content found", &element); + } + content = new uint8_t[targetLength]; + contentLength = static_cast(targetLength); + size_t dst_offset = 0; + for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) + { + const Token& dataToken = GetRequiredToken(*Content, tokenIdx); + size_t tokenLength = dataToken.end() - dataToken.begin() - 2; // ignore double quotes + const char* base64data = dataToken.begin() + 1; + dst_offset += Util::DecodeBase64(base64data, tokenLength, content + dst_offset, targetLength - dst_offset); + } + if (targetLength != dst_offset) + { + delete[] content; + contentLength = 0; + DOMError("Corrupted embedded content found", &element); + } + } + } + else if (static_cast(token.end() - data) < 5) { + DOMError("binary data array is too short, need five (5) bytes for type signature and element count", &element); + } + else if (*data != 'R') { + DOMWarning("video content is not raw binary data, ignoring", &element); + } + else { + // read number of elements + uint32_t len = 0; + ::memcpy(&len, data + 1, sizeof(len)); + AI_SWAP4(len); + + contentLength = len; + + content = new uint8_t[len]; + ::memcpy(content, data + 5, len); + } + } catch (const runtime_error& runtimeError) + { + //we don't need the content data for contents that has already been loaded + ASSIMP_LOG_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ", + runtimeError.what()); + } + } + + props = GetPropertyTable(doc,"Video.FbxVideo",element,sc); +} + + +Video::~Video() +{ + if(content) { + delete[] content; + } +} + +} //!FBX +} //!Assimp + +#endif diff --git a/thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp b/thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp new file mode 100644 index 0000000000..44a0264ca0 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp @@ -0,0 +1,712 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXMeshGeometry.cpp + * @brief Assimp::FBX::MeshGeometry implementation + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include + +#include "FBXMeshGeometry.h" +#include "FBXDocument.h" +#include "FBXImporter.h" +#include "FBXImportSettings.h" +#include "FBXDocumentUtil.h" + + +namespace Assimp { +namespace FBX { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) + : Object(id, element, name) + , skin() +{ + const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer"); + for(const Connection* con : conns) { + const Skin* const sk = ProcessSimpleConnection(*con, false, "Skin -> Geometry", element); + if(sk) { + skin = sk; + } + const BlendShape* const bsp = ProcessSimpleConnection(*con, false, "BlendShape -> Geometry", element); + if (bsp) { + blendShapes.push_back(bsp); + } + } +} + +// ------------------------------------------------------------------------------------------------ +Geometry::~Geometry() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +const std::vector& Geometry::GetBlendShapes() const { + return blendShapes; +} + +// ------------------------------------------------------------------------------------------------ +const Skin* Geometry::DeformerSkin() const { + return skin; +} + +// ------------------------------------------------------------------------------------------------ +MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) +: Geometry(id, element,name, doc) +{ + const Scope* sc = element.Compound(); + if (!sc) { + DOMError("failed to read Geometry object (class: Mesh), no data scope found"); + } + + // must have Mesh elements: + const Element& Vertices = GetRequiredElement(*sc,"Vertices",&element); + const Element& PolygonVertexIndex = GetRequiredElement(*sc,"PolygonVertexIndex",&element); + + // optional Mesh elements: + const ElementCollection& Layer = sc->GetCollection("Layer"); + + std::vector tempVerts; + ParseVectorDataArray(tempVerts,Vertices); + + if(tempVerts.empty()) { + FBXImporter::LogWarn("encountered mesh with no vertices"); + return; + } + + std::vector tempFaces; + ParseVectorDataArray(tempFaces,PolygonVertexIndex); + + if(tempFaces.empty()) { + FBXImporter::LogWarn("encountered mesh with no faces"); + return; + } + + m_vertices.reserve(tempFaces.size()); + m_faces.reserve(tempFaces.size() / 3); + + m_mapping_offsets.resize(tempVerts.size()); + m_mapping_counts.resize(tempVerts.size(),0); + m_mappings.resize(tempFaces.size()); + + const size_t vertex_count = tempVerts.size(); + + // generate output vertices, computing an adjacency table to + // preserve the mapping from fbx indices to *this* indexing. + unsigned int count = 0; + for(int index : tempFaces) { + const int absi = index < 0 ? (-index - 1) : index; + if(static_cast(absi) >= vertex_count) { + DOMError("polygon vertex index out of range",&PolygonVertexIndex); + } + + m_vertices.push_back(tempVerts[absi]); + ++count; + + ++m_mapping_counts[absi]; + + if (index < 0) { + m_faces.push_back(count); + count = 0; + } + } + + unsigned int cursor = 0; + for (size_t i = 0, e = tempVerts.size(); i < e; ++i) { + m_mapping_offsets[i] = cursor; + cursor += m_mapping_counts[i]; + + m_mapping_counts[i] = 0; + } + + cursor = 0; + for(int index : tempFaces) { + const int absi = index < 0 ? (-index - 1) : index; + m_mappings[m_mapping_offsets[absi] + m_mapping_counts[absi]++] = cursor++; + } + + // if settings.readAllLayers is true: + // * read all layers, try to load as many vertex channels as possible + // if settings.readAllLayers is false: + // * read only the layer with index 0, but warn about any further layers + for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) { + const TokenList& tokens = (*it).second->Tokens(); + + const char* err; + const int index = ParseTokenAsInt(*tokens[0], err); + if(err) { + DOMError(err,&element); + } + + if(doc.Settings().readAllLayers || index == 0) { + const Scope& layer = GetRequiredScope(*(*it).second); + ReadLayer(layer); + } + else { + FBXImporter::LogWarn("ignoring additional geometry layers"); + } + } +} + +// ------------------------------------------------------------------------------------------------ +MeshGeometry::~MeshGeometry() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +const std::vector& MeshGeometry::GetVertices() const { + return m_vertices; +} + +// ------------------------------------------------------------------------------------------------ +const std::vector& MeshGeometry::GetNormals() const { + return m_normals; +} + +// ------------------------------------------------------------------------------------------------ +const std::vector& MeshGeometry::GetTangents() const { + return m_tangents; +} + +// ------------------------------------------------------------------------------------------------ +const std::vector& MeshGeometry::GetBinormals() const { + return m_binormals; +} + +// ------------------------------------------------------------------------------------------------ +const std::vector& MeshGeometry::GetFaceIndexCounts() const { + return m_faces; +} + +// ------------------------------------------------------------------------------------------------ +const std::vector& MeshGeometry::GetTextureCoords( unsigned int index ) const { + static const std::vector empty; + return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : m_uvs[ index ]; +} + +std::string MeshGeometry::GetTextureCoordChannelName( unsigned int index ) const { + return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? "" : m_uvNames[ index ]; +} + +const std::vector& MeshGeometry::GetVertexColors( unsigned int index ) const { + static const std::vector empty; + return index >= AI_MAX_NUMBER_OF_COLOR_SETS ? empty : m_colors[ index ]; +} + +const MatIndexArray& MeshGeometry::GetMaterialIndices() const { + return m_materials; +} +// ------------------------------------------------------------------------------------------------ +const unsigned int* MeshGeometry::ToOutputVertexIndex( unsigned int in_index, unsigned int& count ) const { + if ( in_index >= m_mapping_counts.size() ) { + return NULL; + } + + ai_assert( m_mapping_counts.size() == m_mapping_offsets.size() ); + count = m_mapping_counts[ in_index ]; + + ai_assert( m_mapping_offsets[ in_index ] + count <= m_mappings.size() ); + + return &m_mappings[ m_mapping_offsets[ in_index ] ]; +} + +// ------------------------------------------------------------------------------------------------ +unsigned int MeshGeometry::FaceForVertexIndex( unsigned int in_index ) const { + ai_assert( in_index < m_vertices.size() ); + + // in the current conversion pattern this will only be needed if + // weights are present, so no need to always pre-compute this table + if ( m_facesVertexStartIndices.empty() ) { + m_facesVertexStartIndices.resize( m_faces.size() + 1, 0 ); + + std::partial_sum( m_faces.begin(), m_faces.end(), m_facesVertexStartIndices.begin() + 1 ); + m_facesVertexStartIndices.pop_back(); + } + + ai_assert( m_facesVertexStartIndices.size() == m_faces.size() ); + const std::vector::iterator it = std::upper_bound( + m_facesVertexStartIndices.begin(), + m_facesVertexStartIndices.end(), + in_index + ); + + return static_cast< unsigned int >( std::distance( m_facesVertexStartIndices.begin(), it - 1 ) ); +} + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadLayer(const Scope& layer) +{ + const ElementCollection& LayerElement = layer.GetCollection("LayerElement"); + for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) { + const Scope& elayer = GetRequiredScope(*(*eit).second); + + ReadLayerElement(elayer); + } +} + + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadLayerElement(const Scope& layerElement) +{ + const Element& Type = GetRequiredElement(layerElement,"Type"); + const Element& TypedIndex = GetRequiredElement(layerElement,"TypedIndex"); + + const std::string& type = ParseTokenAsString(GetRequiredToken(Type,0)); + const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex,0)); + + const Scope& top = GetRequiredScope(element); + const ElementCollection candidates = top.GetCollection(type); + + for (ElementMap::const_iterator it = candidates.first; it != candidates.second; ++it) { + const int index = ParseTokenAsInt(GetRequiredToken(*(*it).second,0)); + if(index == typedIndex) { + ReadVertexData(type,typedIndex,GetRequiredScope(*(*it).second)); + return; + } + } + + FBXImporter::LogError(Formatter::format("failed to resolve vertex layer element: ") + << type << ", index: " << typedIndex); +} + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scope& source) +{ + const std::string& MappingInformationType = ParseTokenAsString(GetRequiredToken( + GetRequiredElement(source,"MappingInformationType"),0) + ); + + const std::string& ReferenceInformationType = ParseTokenAsString(GetRequiredToken( + GetRequiredElement(source,"ReferenceInformationType"),0) + ); + + if (type == "LayerElementUV") { + if(index >= AI_MAX_NUMBER_OF_TEXTURECOORDS) { + FBXImporter::LogError(Formatter::format("ignoring UV layer, maximum number of UV channels exceeded: ") + << index << " (limit is " << AI_MAX_NUMBER_OF_TEXTURECOORDS << ")" ); + return; + } + + const Element* Name = source["Name"]; + m_uvNames[index] = ""; + if(Name) { + m_uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name,0)); + } + + ReadVertexDataUV(m_uvs[index],source, + MappingInformationType, + ReferenceInformationType + ); + } + else if (type == "LayerElementMaterial") { + if (m_materials.size() > 0) { + FBXImporter::LogError("ignoring additional material layer"); + return; + } + + std::vector temp_materials; + + ReadVertexDataMaterials(temp_materials,source, + MappingInformationType, + ReferenceInformationType + ); + + // sometimes, there will be only negative entries. Drop the material + // layer in such a case (I guess it means a default material should + // be used). This is what the converter would do anyway, and it + // avoids losing the material if there are more material layers + // coming of which at least one contains actual data (did observe + // that with one test file). + const size_t count_neg = std::count_if(temp_materials.begin(),temp_materials.end(),[](int n) { return n < 0; }); + if(count_neg == temp_materials.size()) { + FBXImporter::LogWarn("ignoring dummy material layer (all entries -1)"); + return; + } + + std::swap(temp_materials, m_materials); + } + else if (type == "LayerElementNormal") { + if (m_normals.size() > 0) { + FBXImporter::LogError("ignoring additional normal layer"); + return; + } + + ReadVertexDataNormals(m_normals,source, + MappingInformationType, + ReferenceInformationType + ); + } + else if (type == "LayerElementTangent") { + if (m_tangents.size() > 0) { + FBXImporter::LogError("ignoring additional tangent layer"); + return; + } + + ReadVertexDataTangents(m_tangents,source, + MappingInformationType, + ReferenceInformationType + ); + } + else if (type == "LayerElementBinormal") { + if (m_binormals.size() > 0) { + FBXImporter::LogError("ignoring additional binormal layer"); + return; + } + + ReadVertexDataBinormals(m_binormals,source, + MappingInformationType, + ReferenceInformationType + ); + } + else if (type == "LayerElementColor") { + if(index >= AI_MAX_NUMBER_OF_COLOR_SETS) { + FBXImporter::LogError(Formatter::format("ignoring vertex color layer, maximum number of color sets exceeded: ") + << index << " (limit is " << AI_MAX_NUMBER_OF_COLOR_SETS << ")" ); + return; + } + + ReadVertexDataColors(m_colors[index],source, + MappingInformationType, + ReferenceInformationType + ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Lengthy utility function to read and resolve a FBX vertex data array - that is, the +// output is in polygon vertex order. This logic is used for reading normals, UVs, colors, +// tangents .. +template +void ResolveVertexDataArray(std::vector& data_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType, + const char* dataElementName, + const char* indexDataElementName, + size_t vertex_count, + const std::vector& mapping_counts, + const std::vector& mapping_offsets, + const std::vector& mappings) +{ + bool isDirect = ReferenceInformationType == "Direct"; + bool isIndexToDirect = ReferenceInformationType == "IndexToDirect"; + + // fall-back to direct data if there is no index data element + if ( isIndexToDirect && !HasElement( source, indexDataElementName ) ) { + isDirect = true; + isIndexToDirect = false; + } + + // handle permutations of Mapping and Reference type - it would be nice to + // deal with this more elegantly and with less redundancy, but right + // now it seems unavoidable. + if (MappingInformationType == "ByVertice" && isDirect) { + if (!HasElement(source, dataElementName)) { + return; + } + std::vector tempData; + ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); + + data_out.resize(vertex_count); + for (size_t i = 0, e = tempData.size(); i < e; ++i) { + + const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i]; + for (unsigned int j = istart; j < iend; ++j) { + data_out[mappings[j]] = tempData[i]; + } + } + } + else if (MappingInformationType == "ByVertice" && isIndexToDirect) { + std::vector tempData; + ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); + + data_out.resize(vertex_count); + + std::vector uvIndices; + ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName)); + for (size_t i = 0, e = uvIndices.size(); i < e; ++i) { + + const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i]; + for (unsigned int j = istart; j < iend; ++j) { + if (static_cast(uvIndices[i]) >= tempData.size()) { + DOMError("index out of range",&GetRequiredElement(source,indexDataElementName)); + } + data_out[mappings[j]] = tempData[uvIndices[i]]; + } + } + } + else if (MappingInformationType == "ByPolygonVertex" && isDirect) { + std::vector tempData; + ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); + + if (tempData.size() != vertex_count) { + FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ") + << tempData.size() << ", expected " << vertex_count + ); + return; + } + + data_out.swap(tempData); + } + else if (MappingInformationType == "ByPolygonVertex" && isIndexToDirect) { + std::vector tempData; + ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); + + data_out.resize(vertex_count); + + std::vector uvIndices; + ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName)); + + if (uvIndices.size() != vertex_count) { + FBXImporter::LogError("length of input data unexpected for ByPolygonVertex mapping"); + return; + } + + const T empty; + unsigned int next = 0; + for(int i : uvIndices) { + if ( -1 == i ) { + data_out[ next++ ] = empty; + continue; + } + if (static_cast(i) >= tempData.size()) { + DOMError("index out of range",&GetRequiredElement(source,indexDataElementName)); + } + + data_out[next++] = tempData[i]; + } + } + else { + FBXImporter::LogError(Formatter::format("ignoring vertex data channel, access type not implemented: ") + << MappingInformationType << "," << ReferenceInformationType); + } +} + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadVertexDataNormals(std::vector& normals_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType) +{ + ResolveVertexDataArray(normals_out,source,MappingInformationType,ReferenceInformationType, + "Normals", + "NormalsIndex", + m_vertices.size(), + m_mapping_counts, + m_mapping_offsets, + m_mappings); +} + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadVertexDataUV(std::vector& uv_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType) +{ + ResolveVertexDataArray(uv_out,source,MappingInformationType,ReferenceInformationType, + "UV", + "UVIndex", + m_vertices.size(), + m_mapping_counts, + m_mapping_offsets, + m_mappings); +} + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadVertexDataColors(std::vector& colors_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType) +{ + ResolveVertexDataArray(colors_out,source,MappingInformationType,ReferenceInformationType, + "Colors", + "ColorIndex", + m_vertices.size(), + m_mapping_counts, + m_mapping_offsets, + m_mappings); +} + +// ------------------------------------------------------------------------------------------------ +static const char *TangentIndexToken = "TangentIndex"; +static const char *TangentsIndexToken = "TangentsIndex"; + +void MeshGeometry::ReadVertexDataTangents(std::vector& tangents_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType) +{ + const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent"; + const char * strIdx = source.Elements().count( "Tangents" ) > 0 ? TangentsIndexToken : TangentIndexToken; + ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType, + str, + strIdx, + m_vertices.size(), + m_mapping_counts, + m_mapping_offsets, + m_mappings); +} + +// ------------------------------------------------------------------------------------------------ +static const std::string BinormalIndexToken = "BinormalIndex"; +static const std::string BinormalsIndexToken = "BinormalsIndex"; + +void MeshGeometry::ReadVertexDataBinormals(std::vector& binormals_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType) +{ + const char * str = source.Elements().count( "Binormals" ) > 0 ? "Binormals" : "Binormal"; + const char * strIdx = source.Elements().count( "Binormals" ) > 0 ? BinormalsIndexToken.c_str() : BinormalIndexToken.c_str(); + ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType, + str, + strIdx, + m_vertices.size(), + m_mapping_counts, + m_mapping_offsets, + m_mappings); +} + + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadVertexDataMaterials(std::vector& materials_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType) +{ + const size_t face_count = m_faces.size(); + ai_assert(face_count); + + // materials are handled separately. First of all, they are assigned per-face + // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect + // has a slightly different meaning for materials. + ParseVectorDataArray(materials_out,GetRequiredElement(source,"Materials")); + + if (MappingInformationType == "AllSame") { + // easy - same material for all faces + if (materials_out.empty()) { + FBXImporter::LogError(Formatter::format("expected material index, ignoring")); + return; + } + else if (materials_out.size() > 1) { + FBXImporter::LogWarn(Formatter::format("expected only a single material index, ignoring all except the first one")); + materials_out.clear(); + } + + materials_out.resize(m_vertices.size()); + std::fill(materials_out.begin(), materials_out.end(), materials_out.at(0)); + } + else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") { + materials_out.resize(face_count); + + if(materials_out.size() != face_count) { + FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ") + << materials_out.size() << ", expected " << face_count + ); + return; + } + } + else { + FBXImporter::LogError(Formatter::format("ignoring material assignments, access type not implemented: ") + << MappingInformationType << "," << ReferenceInformationType); + } +} +// ------------------------------------------------------------------------------------------------ +ShapeGeometry::ShapeGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) + : Geometry(id, element, name, doc) +{ + const Scope* sc = element.Compound(); + if (!sc) { + DOMError("failed to read Geometry object (class: Shape), no data scope found"); + } + const Element& Indexes = GetRequiredElement(*sc, "Indexes", &element); + const Element& Normals = GetRequiredElement(*sc, "Normals", &element); + const Element& Vertices = GetRequiredElement(*sc, "Vertices", &element); + ParseVectorDataArray(m_indices, Indexes); + ParseVectorDataArray(m_vertices, Vertices); + ParseVectorDataArray(m_normals, Normals); +} + +// ------------------------------------------------------------------------------------------------ +ShapeGeometry::~ShapeGeometry() { + // empty +} +// ------------------------------------------------------------------------------------------------ +const std::vector& ShapeGeometry::GetVertices() const { + return m_vertices; +} +// ------------------------------------------------------------------------------------------------ +const std::vector& ShapeGeometry::GetNormals() const { + return m_normals; +} +// ------------------------------------------------------------------------------------------------ +const std::vector& ShapeGeometry::GetIndices() const { + return m_indices; +} +// ------------------------------------------------------------------------------------------------ +LineGeometry::LineGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) + : Geometry(id, element, name, doc) +{ + const Scope* sc = element.Compound(); + if (!sc) { + DOMError("failed to read Geometry object (class: Line), no data scope found"); + } + const Element& Points = GetRequiredElement(*sc, "Points", &element); + const Element& PointsIndex = GetRequiredElement(*sc, "PointsIndex", &element); + ParseVectorDataArray(m_vertices, Points); + ParseVectorDataArray(m_indices, PointsIndex); +} + +// ------------------------------------------------------------------------------------------------ +LineGeometry::~LineGeometry() { + // empty +} +// ------------------------------------------------------------------------------------------------ +const std::vector& LineGeometry::GetVertices() const { + return m_vertices; +} +// ------------------------------------------------------------------------------------------------ +const std::vector& LineGeometry::GetIndices() const { + return m_indices; +} +} // !FBX +} // !Assimp +#endif + diff --git a/thirdparty/assimp/code/FBX/FBXMeshGeometry.h b/thirdparty/assimp/code/FBX/FBXMeshGeometry.h new file mode 100644 index 0000000000..d6d4512177 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXMeshGeometry.h @@ -0,0 +1,235 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXImporter.h +* @brief Declaration of the FBX main importer class +*/ +#ifndef INCLUDED_AI_FBX_MESHGEOMETRY_H +#define INCLUDED_AI_FBX_MESHGEOMETRY_H + +#include "FBXParser.h" +#include "FBXDocument.h" + +namespace Assimp { +namespace FBX { + +/** + * DOM base class for all kinds of FBX geometry + */ +class Geometry : public Object +{ +public: + Geometry( uint64_t id, const Element& element, const std::string& name, const Document& doc ); + virtual ~Geometry(); + + /** Get the Skin attached to this geometry or NULL */ + const Skin* DeformerSkin() const; + + /** Get the BlendShape attached to this geometry or NULL */ + const std::vector& GetBlendShapes() const; + +private: + const Skin* skin; + std::vector blendShapes; + +}; + +typedef std::vector MatIndexArray; + + +/** + * DOM class for FBX geometry of type "Mesh" + */ +class MeshGeometry : public Geometry +{ +public: + /** The class constructor */ + MeshGeometry( uint64_t id, const Element& element, const std::string& name, const Document& doc ); + + /** The class destructor */ + virtual ~MeshGeometry(); + + /** Get a list of all vertex points, non-unique*/ + const std::vector& GetVertices() const; + + /** Get a list of all vertex normals or an empty array if + * no normals are specified. */ + const std::vector& GetNormals() const; + + /** Get a list of all vertex tangents or an empty array + * if no tangents are specified */ + const std::vector& GetTangents() const; + + /** Get a list of all vertex bi-normals or an empty array + * if no bi-normals are specified */ + const std::vector& GetBinormals() const; + + /** Return list of faces - each entry denotes a face and specifies + * how many vertices it has. Vertices are taken from the + * vertex data arrays in sequential order. */ + const std::vector& GetFaceIndexCounts() const; + + /** Get a UV coordinate slot, returns an empty array if + * the requested slot does not exist. */ + const std::vector& GetTextureCoords( unsigned int index ) const; + + /** Get a UV coordinate slot, returns an empty array if + * the requested slot does not exist. */ + std::string GetTextureCoordChannelName( unsigned int index ) const; + + /** Get a vertex color coordinate slot, returns an empty array if + * the requested slot does not exist. */ + const std::vector& GetVertexColors( unsigned int index ) const; + + /** Get per-face-vertex material assignments */ + const MatIndexArray& GetMaterialIndices() const; + + /** Convert from a fbx file vertex index (for example from a #Cluster weight) or NULL + * if the vertex index is not valid. */ + const unsigned int* ToOutputVertexIndex( unsigned int in_index, unsigned int& count ) const; + + /** Determine the face to which a particular output vertex index belongs. + * This mapping is always unique. */ + unsigned int FaceForVertexIndex( unsigned int in_index ) const; +private: + void ReadLayer( const Scope& layer ); + void ReadLayerElement( const Scope& layerElement ); + void ReadVertexData( const std::string& type, int index, const Scope& source ); + + void ReadVertexDataUV( std::vector& uv_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType ); + + void ReadVertexDataNormals( std::vector& normals_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType ); + + void ReadVertexDataColors( std::vector& colors_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType ); + + void ReadVertexDataTangents( std::vector& tangents_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType ); + + void ReadVertexDataBinormals( std::vector& binormals_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType ); + + void ReadVertexDataMaterials( MatIndexArray& materials_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType ); + +private: + // cached data arrays + MatIndexArray m_materials; + std::vector m_vertices; + std::vector m_faces; + mutable std::vector m_facesVertexStartIndices; + std::vector m_tangents; + std::vector m_binormals; + std::vector m_normals; + + std::string m_uvNames[ AI_MAX_NUMBER_OF_TEXTURECOORDS ]; + std::vector m_uvs[ AI_MAX_NUMBER_OF_TEXTURECOORDS ]; + std::vector m_colors[ AI_MAX_NUMBER_OF_COLOR_SETS ]; + + std::vector m_mapping_counts; + std::vector m_mapping_offsets; + std::vector m_mappings; +}; + +/** +* DOM class for FBX geometry of type "Shape" +*/ +class ShapeGeometry : public Geometry +{ +public: + /** The class constructor */ + ShapeGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc); + + /** The class destructor */ + virtual ~ShapeGeometry(); + + /** Get a list of all vertex points, non-unique*/ + const std::vector& GetVertices() const; + + /** Get a list of all vertex normals or an empty array if + * no normals are specified. */ + const std::vector& GetNormals() const; + + /** Return list of vertex indices. */ + const std::vector& GetIndices() const; + +private: + std::vector m_vertices; + std::vector m_normals; + std::vector m_indices; +}; +/** +* DOM class for FBX geometry of type "Line" +*/ +class LineGeometry : public Geometry +{ +public: + /** The class constructor */ + LineGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc); + + /** The class destructor */ + virtual ~LineGeometry(); + + /** Get a list of all vertex points, non-unique*/ + const std::vector& GetVertices() const; + + /** Return list of vertex indices. */ + const std::vector& GetIndices() const; + +private: + std::vector m_vertices; + std::vector m_indices; +}; + +} +} + +#endif // INCLUDED_AI_FBX_MESHGEOMETRY_H + diff --git a/thirdparty/assimp/code/FBX/FBXModel.cpp b/thirdparty/assimp/code/FBX/FBXModel.cpp new file mode 100644 index 0000000000..589af36ac7 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXModel.cpp @@ -0,0 +1,153 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXModel.cpp + * @brief Assimp::FBX::Model implementation + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXParser.h" +#include "FBXMeshGeometry.h" +#include "FBXDocument.h" +#include "FBXImporter.h" +#include "FBXDocumentUtil.h" + +namespace Assimp { +namespace FBX { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Model::Model(uint64_t id, const Element& element, const Document& doc, const std::string& name) + : Object(id,element,name) + , shading("Y") +{ + const Scope& sc = GetRequiredScope(element); + const Element* const Shading = sc["Shading"]; + const Element* const Culling = sc["Culling"]; + + if(Shading) { + shading = GetRequiredToken(*Shading,0).StringContents(); + } + + if (Culling) { + culling = ParseTokenAsString(GetRequiredToken(*Culling,0)); + } + + props = GetPropertyTable(doc,"Model.FbxNode",element,sc); + ResolveLinks(element,doc); +} + +// ------------------------------------------------------------------------------------------------ +Model::~Model() +{ + +} + +// ------------------------------------------------------------------------------------------------ +void Model::ResolveLinks(const Element& element, const Document& doc) +{ + const char* const arr[] = {"Geometry","Material","NodeAttribute"}; + + // resolve material + const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),arr, 3); + + materials.reserve(conns.size()); + geometry.reserve(conns.size()); + attributes.reserve(conns.size()); + for(const Connection* con : conns) { + + // material and geometry links should be Object-Object connections + if (con->PropertyName().length()) { + continue; + } + + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for incoming Model link, ignoring",&element); + continue; + } + + const Material* const mat = dynamic_cast(ob); + if(mat) { + materials.push_back(mat); + continue; + } + + const Geometry* const geo = dynamic_cast(ob); + if(geo) { + geometry.push_back(geo); + continue; + } + + const NodeAttribute* const att = dynamic_cast(ob); + if(att) { + attributes.push_back(att); + continue; + } + + DOMWarning("source object for model link is neither Material, NodeAttribute nor Geometry, ignoring",&element); + continue; + } +} + +// ------------------------------------------------------------------------------------------------ +bool Model::IsNull() const +{ + const std::vector& attrs = GetAttributes(); + for(const NodeAttribute* att : attrs) { + + const Null* null_tag = dynamic_cast(att); + if(null_tag) { + return true; + } + } + + return false; +} + + +} //!FBX +} //!Assimp + +#endif diff --git a/thirdparty/assimp/code/FBX/FBXNodeAttribute.cpp b/thirdparty/assimp/code/FBX/FBXNodeAttribute.cpp new file mode 100644 index 0000000000..b72e5637ee --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXNodeAttribute.cpp @@ -0,0 +1,170 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXNoteAttribute.cpp + * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXParser.h" +#include "FBXDocument.h" +#include "FBXImporter.h" +#include "FBXDocumentUtil.h" + +namespace Assimp { +namespace FBX { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +NodeAttribute::NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: Object(id,element,name) +, props() +{ + const Scope& sc = GetRequiredScope(element); + + const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2)); + + // hack on the deriving type but Null/LimbNode attributes are the only case in which + // the property table is by design absent and no warning should be generated + // for it. + const bool is_null_or_limb = !strcmp(classname.c_str(), "Null") || !strcmp(classname.c_str(), "LimbNode"); + props = GetPropertyTable(doc,"NodeAttribute.Fbx" + classname,element,sc, is_null_or_limb); +} + + +// ------------------------------------------------------------------------------------------------ +NodeAttribute::~NodeAttribute() +{ + // empty +} + + +// ------------------------------------------------------------------------------------------------ +CameraSwitcher::CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name) + : NodeAttribute(id,element,doc,name) +{ + const Scope& sc = GetRequiredScope(element); + const Element* const CameraId = sc["CameraId"]; + const Element* const CameraName = sc["CameraName"]; + const Element* const CameraIndexName = sc["CameraIndexName"]; + + if(CameraId) { + cameraId = ParseTokenAsInt(GetRequiredToken(*CameraId,0)); + } + + if(CameraName) { + cameraName = GetRequiredToken(*CameraName,0).StringContents(); + } + + if(CameraIndexName && CameraIndexName->Tokens().size()) { + cameraIndexName = GetRequiredToken(*CameraIndexName,0).StringContents(); + } +} + +// ------------------------------------------------------------------------------------------------ +CameraSwitcher::~CameraSwitcher() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +Camera::Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: NodeAttribute(id,element,doc,name) +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +Camera::~Camera() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +Light::Light(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: NodeAttribute(id,element,doc,name) +{ + // empty +} + + +// ------------------------------------------------------------------------------------------------ +Light::~Light() +{ +} + + +// ------------------------------------------------------------------------------------------------ +Null::Null(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: NodeAttribute(id,element,doc,name) +{ + +} + + +// ------------------------------------------------------------------------------------------------ +Null::~Null() +{ + +} + + +// ------------------------------------------------------------------------------------------------ +LimbNode::LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: NodeAttribute(id,element,doc,name) +{ + +} + + +// ------------------------------------------------------------------------------------------------ +LimbNode::~LimbNode() +{ + +} + +} +} + +#endif diff --git a/thirdparty/assimp/code/FBX/FBXParser.cpp b/thirdparty/assimp/code/FBX/FBXParser.cpp new file mode 100644 index 0000000000..4a9346040d --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXParser.cpp @@ -0,0 +1,1309 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXParser.cpp + * @brief Implementation of the FBX parser and the rudimentary DOM that we use + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#ifdef ASSIMP_BUILD_NO_OWN_ZLIB +# include +#else +# include "../contrib/zlib/zlib.h" +#endif + +#include "FBXTokenizer.h" +#include "FBXParser.h" +#include "FBXUtil.h" + +#include +#include +#include + +#include + +using namespace Assimp; +using namespace Assimp::FBX; + +namespace { + + // ------------------------------------------------------------------------------------------------ + // signal parse error, this is always unrecoverable. Throws DeadlyImportError. + AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX; + AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) + { + throw DeadlyImportError(Util::AddTokenText("FBX-Parser",message,&token)); + } + + // ------------------------------------------------------------------------------------------------ + AI_WONT_RETURN void ParseError(const std::string& message, const Element* element = NULL) AI_WONT_RETURN_SUFFIX; + AI_WONT_RETURN void ParseError(const std::string& message, const Element* element) + { + if(element) { + ParseError(message,element->KeyToken()); + } + throw DeadlyImportError("FBX-Parser " + message); + } + + + // ------------------------------------------------------------------------------------------------ + void ParseError(const std::string& message, TokenPtr token) + { + if(token) { + ParseError(message, *token); + } + ParseError(message); + } + + // Initially, we did reinterpret_cast, breaking strict aliasing rules. + // This actually caused trouble on Android, so let's be safe this time. + // https://github.com/assimp/assimp/issues/24 + template + T SafeParse(const char* data, const char* end) { + // Actual size validation happens during Tokenization so + // this is valid as an assertion. + (void)(end); + ai_assert(static_cast(end - data) >= sizeof(T)); + T result = static_cast(0); + ::memcpy(&result, data, sizeof(T)); + return result; + } +} + +namespace Assimp { +namespace FBX { + +// ------------------------------------------------------------------------------------------------ +Element::Element(const Token& key_token, Parser& parser) +: key_token(key_token) +{ + TokenPtr n = nullptr; + do { + n = parser.AdvanceToNextToken(); + if(!n) { + ParseError("unexpected end of file, expected closing bracket",parser.LastToken()); + } + + if (n->Type() == TokenType_DATA) { + tokens.push_back(n); + TokenPtr prev = n; + n = parser.AdvanceToNextToken(); + if(!n) { + ParseError("unexpected end of file, expected bracket, comma or key",parser.LastToken()); + } + + const TokenType ty = n->Type(); + + // some exporters are missing a comma on the next line + if (ty == TokenType_DATA && prev->Type() == TokenType_DATA && (n->Line() == prev->Line() + 1)) { + tokens.push_back(n); + continue; + } + + if (ty != TokenType_OPEN_BRACKET && ty != TokenType_CLOSE_BRACKET && ty != TokenType_COMMA && ty != TokenType_KEY) { + ParseError("unexpected token; expected bracket, comma or key",n); + } + } + + if (n->Type() == TokenType_OPEN_BRACKET) { + compound.reset(new Scope(parser)); + + // current token should be a TOK_CLOSE_BRACKET + n = parser.CurrentToken(); + ai_assert(n); + + if (n->Type() != TokenType_CLOSE_BRACKET) { + ParseError("expected closing bracket",n); + } + + parser.AdvanceToNextToken(); + return; + } + } + while(n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET); +} + +// ------------------------------------------------------------------------------------------------ +Element::~Element() +{ + // no need to delete tokens, they are owned by the parser +} + +// ------------------------------------------------------------------------------------------------ +Scope::Scope(Parser& parser,bool topLevel) +{ + if(!topLevel) { + TokenPtr t = parser.CurrentToken(); + if (t->Type() != TokenType_OPEN_BRACKET) { + ParseError("expected open bracket",t); + } + } + + TokenPtr n = parser.AdvanceToNextToken(); + if(n == NULL) { + ParseError("unexpected end of file"); + } + + // note: empty scopes are allowed + while(n->Type() != TokenType_CLOSE_BRACKET) { + if (n->Type() != TokenType_KEY) { + ParseError("unexpected token, expected TOK_KEY",n); + } + + const std::string& str = n->StringContents(); + elements.insert(ElementMap::value_type(str,new_Element(*n,parser))); + + // Element() should stop at the next Key token (or right after a Close token) + n = parser.CurrentToken(); + if(n == NULL) { + if (topLevel) { + return; + } + ParseError("unexpected end of file",parser.LastToken()); + } + } +} + +// ------------------------------------------------------------------------------------------------ +Scope::~Scope() +{ + for(ElementMap::value_type& v : elements) { + delete v.second; + } +} + +// ------------------------------------------------------------------------------------------------ +Parser::Parser (const TokenList& tokens, bool is_binary) +: tokens(tokens) +, last() +, current() +, cursor(tokens.begin()) +, is_binary(is_binary) +{ + root.reset(new Scope(*this,true)); +} + +// ------------------------------------------------------------------------------------------------ +Parser::~Parser() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +TokenPtr Parser::AdvanceToNextToken() +{ + last = current; + if (cursor == tokens.end()) { + current = NULL; + } else { + current = *cursor++; + } + return current; +} + +// ------------------------------------------------------------------------------------------------ +TokenPtr Parser::CurrentToken() const +{ + return current; +} + +// ------------------------------------------------------------------------------------------------ +TokenPtr Parser::LastToken() const +{ + return last; +} + +// ------------------------------------------------------------------------------------------------ +uint64_t ParseTokenAsID(const Token& t, const char*& err_out) +{ + err_out = NULL; + + if (t.Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0L; + } + + if(t.IsBinary()) + { + const char* data = t.begin(); + if (data[0] != 'L') { + err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)"; + return 0L; + } + + BE_NCONST uint64_t id = SafeParse(data+1, t.end()); + AI_SWAP8(id); + return id; + } + + // XXX: should use size_t here + unsigned int length = static_cast(t.end() - t.begin()); + ai_assert(length > 0); + + const char* out = nullptr; + const uint64_t id = strtoul10_64(t.begin(),&out,&length); + if (out > t.end()) { + err_out = "failed to parse ID (text)"; + return 0L; + } + + return id; +} + +// ------------------------------------------------------------------------------------------------ +size_t ParseTokenAsDim(const Token& t, const char*& err_out) +{ + // same as ID parsing, except there is a trailing asterisk + err_out = NULL; + + if (t.Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0; + } + + if(t.IsBinary()) + { + const char* data = t.begin(); + if (data[0] != 'L') { + err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)"; + return 0; + } + + BE_NCONST uint64_t id = SafeParse(data+1, t.end()); + AI_SWAP8(id); + return static_cast(id); + } + + if(*t.begin() != '*') { + err_out = "expected asterisk before array dimension"; + return 0; + } + + // XXX: should use size_t here + unsigned int length = static_cast(t.end() - t.begin()); + if(length == 0) { + err_out = "expected valid integer number after asterisk"; + return 0; + } + + const char* out = nullptr; + const size_t id = static_cast(strtoul10_64(t.begin() + 1,&out,&length)); + if (out > t.end()) { + err_out = "failed to parse ID"; + return 0; + } + + return id; +} + + +// ------------------------------------------------------------------------------------------------ +float ParseTokenAsFloat(const Token& t, const char*& err_out) +{ + err_out = NULL; + + if (t.Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0.0f; + } + + if(t.IsBinary()) + { + const char* data = t.begin(); + if (data[0] != 'F' && data[0] != 'D') { + err_out = "failed to parse F(loat) or D(ouble), unexpected data type (binary)"; + return 0.0f; + } + + if (data[0] == 'F') { + return SafeParse(data+1, t.end()); + } + else { + return static_cast( SafeParse(data+1, t.end()) ); + } + } + + // need to copy the input string to a temporary buffer + // first - next in the fbx token stream comes ',', + // which fast_atof could interpret as decimal point. +#define MAX_FLOAT_LENGTH 31 + char temp[MAX_FLOAT_LENGTH + 1]; + const size_t length = static_cast(t.end()-t.begin()); + std::copy(t.begin(),t.end(),temp); + temp[std::min(static_cast(MAX_FLOAT_LENGTH),length)] = '\0'; + + return fast_atof(temp); +} + + +// ------------------------------------------------------------------------------------------------ +int ParseTokenAsInt(const Token& t, const char*& err_out) +{ + err_out = NULL; + + if (t.Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0; + } + + if(t.IsBinary()) + { + const char* data = t.begin(); + if (data[0] != 'I') { + err_out = "failed to parse I(nt), unexpected data type (binary)"; + return 0; + } + + BE_NCONST int32_t ival = SafeParse(data+1, t.end()); + AI_SWAP4(ival); + return static_cast(ival); + } + + ai_assert(static_cast(t.end() - t.begin()) > 0); + + const char* out; + const int intval = strtol10(t.begin(),&out); + if (out != t.end()) { + err_out = "failed to parse ID"; + return 0; + } + + return intval; +} + + +// ------------------------------------------------------------------------------------------------ +int64_t ParseTokenAsInt64(const Token& t, const char*& err_out) +{ + err_out = NULL; + + if (t.Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0L; + } + + if (t.IsBinary()) + { + const char* data = t.begin(); + if (data[0] != 'L') { + err_out = "failed to parse Int64, unexpected data type"; + return 0L; + } + + BE_NCONST int64_t id = SafeParse(data + 1, t.end()); + AI_SWAP8(id); + return id; + } + + // XXX: should use size_t here + unsigned int length = static_cast(t.end() - t.begin()); + ai_assert(length > 0); + + const char* out = nullptr; + const int64_t id = strtol10_64(t.begin(), &out, &length); + if (out > t.end()) { + err_out = "failed to parse Int64 (text)"; + return 0L; + } + + return id; +} + +// ------------------------------------------------------------------------------------------------ +std::string ParseTokenAsString(const Token& t, const char*& err_out) +{ + err_out = NULL; + + if (t.Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return ""; + } + + if(t.IsBinary()) + { + const char* data = t.begin(); + if (data[0] != 'S') { + err_out = "failed to parse S(tring), unexpected data type (binary)"; + return ""; + } + + // read string length + BE_NCONST int32_t len = SafeParse(data+1, t.end()); + AI_SWAP4(len); + + ai_assert(t.end() - data == 5 + len); + return std::string(data + 5, len); + } + + const size_t length = static_cast(t.end() - t.begin()); + if(length < 2) { + err_out = "token is too short to hold a string"; + return ""; + } + + const char* s = t.begin(), *e = t.end() - 1; + if (*s != '\"' || *e != '\"') { + err_out = "expected double quoted string"; + return ""; + } + + return std::string(s+1,length-2); +} + + +namespace { + +// ------------------------------------------------------------------------------------------------ +// read the type code and element count of a binary data array and stop there +void ReadBinaryDataArrayHead(const char*& data, const char* end, char& type, uint32_t& count, + const Element& el) +{ + if (static_cast(end-data) < 5) { + ParseError("binary data array is too short, need five (5) bytes for type signature and element count",&el); + } + + // data type + type = *data; + + // read number of elements + BE_NCONST uint32_t len = SafeParse(data+1, end); + AI_SWAP4(len); + + count = len; + data += 5; +} + + +// ------------------------------------------------------------------------------------------------ +// read binary data array, assume cursor points to the 'compression mode' field (i.e. behind the header) +void ReadBinaryDataArray(char type, uint32_t count, const char*& data, const char* end, + std::vector& buff, + const Element& /*el*/) +{ + BE_NCONST uint32_t encmode = SafeParse(data, end); + AI_SWAP4(encmode); + data += 4; + + // next comes the compressed length + BE_NCONST uint32_t comp_len = SafeParse(data, end); + AI_SWAP4(comp_len); + data += 4; + + ai_assert(data + comp_len == end); + + // determine the length of the uncompressed data by looking at the type signature + uint32_t stride = 0; + switch(type) + { + case 'f': + case 'i': + stride = 4; + break; + + case 'd': + case 'l': + stride = 8; + break; + + default: + ai_assert(false); + }; + + const uint32_t full_length = stride * count; + buff.resize(full_length); + + if(encmode == 0) { + ai_assert(full_length == comp_len); + + // plain data, no compression + std::copy(data, end, buff.begin()); + } + else if(encmode == 1) { + // zlib/deflate, next comes ZIP head (0x78 0x01) + // see http://www.ietf.org/rfc/rfc1950.txt + + z_stream zstream; + zstream.opaque = Z_NULL; + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.data_type = Z_BINARY; + + // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib + if(Z_OK != inflateInit(&zstream)) { + ParseError("failure initializing zlib"); + } + + zstream.next_in = reinterpret_cast( const_cast(data) ); + zstream.avail_in = comp_len; + + zstream.avail_out = static_cast(buff.size()); + zstream.next_out = reinterpret_cast(&*buff.begin()); + const int ret = inflate(&zstream, Z_FINISH); + + if (ret != Z_STREAM_END && ret != Z_OK) { + ParseError("failure decompressing compressed data section"); + } + + // terminate zlib + inflateEnd(&zstream); + } +#ifdef ASSIMP_BUILD_DEBUG + else { + // runtime check for this happens at tokenization stage + ai_assert(false); + } +#endif + + data += comp_len; + ai_assert(data == end); +} + +} // !anon + + +// ------------------------------------------------------------------------------------------------ +// read an array of float3 tuples +void ParseVectorDataArray(std::vector& out, const Element& el) +{ + out.resize( 0 ); + + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(count % 3 != 0) { + ParseError("number of floats is not a multiple of three (3) (binary)",&el); + } + + if(!count) { + return; + } + + if (type != 'd' && type != 'f') { + ParseError("expected float or double array (binary)",&el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + const uint32_t count3 = count / 3; + out.reserve(count3); + + if (type == 'd') { + const double* d = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count3; ++i, d += 3) { + out.push_back(aiVector3D(static_cast(d[0]), + static_cast(d[1]), + static_cast(d[2]))); + } + // for debugging + /*for ( size_t i = 0; i < out.size(); i++ ) { + aiVector3D vec3( out[ i ] ); + std::stringstream stream; + stream << " vec3.x = " << vec3.x << " vec3.y = " << vec3.y << " vec3.z = " << vec3.z << std::endl; + DefaultLogger::get()->info( stream.str() ); + }*/ + } + else if (type == 'f') { + const float* f = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count3; ++i, f += 3) { + out.push_back(aiVector3D(f[0],f[1],f[2])); + } + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // may throw bad_alloc if the input is rubbish, but this need + // not to be prevented - importing would fail but we wouldn't + // crash since assimp handles this case properly. + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + if (a.Tokens().size() % 3 != 0) { + ParseError("number of floats is not a multiple of three (3)",&el); + } + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + aiVector3D v; + v.x = ParseTokenAsFloat(**it++); + v.y = ParseTokenAsFloat(**it++); + v.z = ParseTokenAsFloat(**it++); + + out.push_back(v); + } +} + + +// ------------------------------------------------------------------------------------------------ +// read an array of color4 tuples +void ParseVectorDataArray(std::vector& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(count % 4 != 0) { + ParseError("number of floats is not a multiple of four (4) (binary)",&el); + } + + if(!count) { + return; + } + + if (type != 'd' && type != 'f') { + ParseError("expected float or double array (binary)",&el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + const uint32_t count4 = count / 4; + out.reserve(count4); + + if (type == 'd') { + const double* d = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count4; ++i, d += 4) { + out.push_back(aiColor4D(static_cast(d[0]), + static_cast(d[1]), + static_cast(d[2]), + static_cast(d[3]))); + } + } + else if (type == 'f') { + const float* f = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count4; ++i, f += 4) { + out.push_back(aiColor4D(f[0],f[1],f[2],f[3])); + } + } + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() above + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + if (a.Tokens().size() % 4 != 0) { + ParseError("number of floats is not a multiple of four (4)",&el); + } + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + aiColor4D v; + v.r = ParseTokenAsFloat(**it++); + v.g = ParseTokenAsFloat(**it++); + v.b = ParseTokenAsFloat(**it++); + v.a = ParseTokenAsFloat(**it++); + + out.push_back(v); + } +} + + +// ------------------------------------------------------------------------------------------------ +// read an array of float2 tuples +void ParseVectorDataArray(std::vector& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(count % 2 != 0) { + ParseError("number of floats is not a multiple of two (2) (binary)",&el); + } + + if(!count) { + return; + } + + if (type != 'd' && type != 'f') { + ParseError("expected float or double array (binary)",&el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + const uint32_t count2 = count / 2; + out.reserve(count2); + + if (type == 'd') { + const double* d = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count2; ++i, d += 2) { + out.push_back(aiVector2D(static_cast(d[0]), + static_cast(d[1]))); + } + } + else if (type == 'f') { + const float* f = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count2; ++i, f += 2) { + out.push_back(aiVector2D(f[0],f[1])); + } + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() above + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + if (a.Tokens().size() % 2 != 0) { + ParseError("number of floats is not a multiple of two (2)",&el); + } + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + aiVector2D v; + v.x = ParseTokenAsFloat(**it++); + v.y = ParseTokenAsFloat(**it++); + + out.push_back(v); + } +} + + +// ------------------------------------------------------------------------------------------------ +// read an array of ints +void ParseVectorDataArray(std::vector& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(!count) { + return; + } + + if (type != 'i') { + ParseError("expected int array (binary)",&el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * 4); + + out.reserve(count); + + const int32_t* ip = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + BE_NCONST int32_t val = *ip; + AI_SWAP4(val); + out.push_back(val); + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + const int ival = ParseTokenAsInt(**it++); + out.push_back(ival); + } +} + + +// ------------------------------------------------------------------------------------------------ +// read an array of floats +void ParseVectorDataArray(std::vector& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(!count) { + return; + } + + if (type != 'd' && type != 'f') { + ParseError("expected float or double array (binary)",&el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + if (type == 'd') { + const double* d = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++d) { + out.push_back(static_cast(*d)); + } + } + else if (type == 'f') { + const float* f = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++f) { + out.push_back(*f); + } + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + const float ival = ParseTokenAsFloat(**it++); + out.push_back(ival); + } +} + +// ------------------------------------------------------------------------------------------------ +// read an array of uints +void ParseVectorDataArray(std::vector& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(!count) { + return; + } + + if (type != 'i') { + ParseError("expected (u)int array (binary)",&el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * 4); + + out.reserve(count); + + const int32_t* ip = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + BE_NCONST int32_t val = *ip; + if(val < 0) { + ParseError("encountered negative integer index (binary)"); + } + + AI_SWAP4(val); + out.push_back(val); + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + const int ival = ParseTokenAsInt(**it++); + if(ival < 0) { + ParseError("encountered negative integer index"); + } + out.push_back(static_cast(ival)); + } +} + + +// ------------------------------------------------------------------------------------------------ +// read an array of uint64_ts +void ParseVectorDataArray(std::vector& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(!count) { + return; + } + + if (type != 'l') { + ParseError("expected long array (binary)",&el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * 8); + + out.reserve(count); + + const uint64_t* ip = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + BE_NCONST uint64_t val = *ip; + AI_SWAP8(val); + out.push_back(val); + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + const uint64_t ival = ParseTokenAsID(**it++); + + out.push_back(ival); + } +} + +// ------------------------------------------------------------------------------------------------ +// read an array of int64_ts +void ParseVectorDataArray(std::vector& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if (tok.empty()) { + ParseError("unexpected empty element", &el); + } + + if (tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if (!count) { + return; + } + + if (type != 'l') { + ParseError("expected long array (binary)", &el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * 8); + + out.reserve(count); + + const int64_t* ip = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + BE_NCONST int64_t val = *ip; + AI_SWAP8(val); + out.push_back(val); + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope, "a", &el); + + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end;) { + const int64_t ival = ParseTokenAsInt64(**it++); + + out.push_back(ival); + } +} + +// ------------------------------------------------------------------------------------------------ +aiMatrix4x4 ReadMatrix(const Element& element) +{ + std::vector values; + ParseVectorDataArray(values,element); + + if(values.size() != 16) { + ParseError("expected 16 matrix elements"); + } + + aiMatrix4x4 result; + + + result.a1 = values[0]; + result.a2 = values[1]; + result.a3 = values[2]; + result.a4 = values[3]; + + result.b1 = values[4]; + result.b2 = values[5]; + result.b3 = values[6]; + result.b4 = values[7]; + + result.c1 = values[8]; + result.c2 = values[9]; + result.c3 = values[10]; + result.c4 = values[11]; + + result.d1 = values[12]; + result.d2 = values[13]; + result.d3 = values[14]; + result.d4 = values[15]; + + result.Transpose(); + return result; +} + + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsString() with ParseError handling +std::string ParseTokenAsString(const Token& t) +{ + const char* err; + const std::string& i = ParseTokenAsString(t,err); + if(err) { + ParseError(err,t); + } + return i; +} + +bool HasElement( const Scope& sc, const std::string& index ) { + const Element* el = sc[ index ]; + if ( nullptr == el ) { + return false; + } + + return true; +} + +// ------------------------------------------------------------------------------------------------ +// extract a required element from a scope, abort if the element cannot be found +const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element /*= NULL*/) +{ + const Element* el = sc[index]; + if(!el) { + ParseError("did not find required element \"" + index + "\"",element); + } + return *el; +} + + +// ------------------------------------------------------------------------------------------------ +// extract required compound scope +const Scope& GetRequiredScope(const Element& el) +{ + const Scope* const s = el.Compound(); + if(!s) { + ParseError("expected compound scope",&el); + } + + return *s; +} + + +// ------------------------------------------------------------------------------------------------ +// get token at a particular index +const Token& GetRequiredToken(const Element& el, unsigned int index) +{ + const TokenList& t = el.Tokens(); + if(index >= t.size()) { + ParseError(Formatter::format( "missing token at index " ) << index,&el); + } + + return *t[index]; +} + + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsID() with ParseError handling +uint64_t ParseTokenAsID(const Token& t) +{ + const char* err; + const uint64_t i = ParseTokenAsID(t,err); + if(err) { + ParseError(err,t); + } + return i; +} + + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsDim() with ParseError handling +size_t ParseTokenAsDim(const Token& t) +{ + const char* err; + const size_t i = ParseTokenAsDim(t,err); + if(err) { + ParseError(err,t); + } + return i; +} + + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsFloat() with ParseError handling +float ParseTokenAsFloat(const Token& t) +{ + const char* err; + const float i = ParseTokenAsFloat(t,err); + if(err) { + ParseError(err,t); + } + return i; +} + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsInt() with ParseError handling +int ParseTokenAsInt(const Token& t) +{ + const char* err; + const int i = ParseTokenAsInt(t,err); + if(err) { + ParseError(err,t); + } + return i; +} + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsInt64() with ParseError handling +int64_t ParseTokenAsInt64(const Token& t) +{ + const char* err; + const int64_t i = ParseTokenAsInt64(t, err); + if (err) { + ParseError(err, t); + } + return i; +} + +} // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBX/FBXParser.h b/thirdparty/assimp/code/FBX/FBXParser.h new file mode 100644 index 0000000000..7b0cf72039 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXParser.h @@ -0,0 +1,235 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXParser.h + * @brief FBX parsing code + */ +#ifndef INCLUDED_AI_FBX_PARSER_H +#define INCLUDED_AI_FBX_PARSER_H + +#include +#include +#include +#include +#include + +#include "FBXCompileConfig.h" +#include "FBXTokenizer.h" + +namespace Assimp { +namespace FBX { + +class Scope; +class Parser; +class Element; + +// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03 +typedef std::vector< Scope* > ScopeList; +typedef std::fbx_unordered_multimap< std::string, Element* > ElementMap; + +typedef std::pair ElementCollection; + +# define new_Scope new Scope +# define new_Element new Element + + +/** FBX data entity that consists of a key:value tuple. + * + * Example: + * @verbatim + * AnimationCurve: 23, "AnimCurve::", "" { + * [..] + * } + * @endverbatim + * + * As can be seen in this sample, elements can contain nested #Scope + * as their trailing member. **/ +class Element +{ +public: + Element(const Token& key_token, Parser& parser); + ~Element(); + + const Scope* Compound() const { + return compound.get(); + } + + const Token& KeyToken() const { + return key_token; + } + + const TokenList& Tokens() const { + return tokens; + } + +private: + const Token& key_token; + TokenList tokens; + std::unique_ptr compound; +}; + +/** FBX data entity that consists of a 'scope', a collection + * of not necessarily unique #Element instances. + * + * Example: + * @verbatim + * GlobalSettings: { + * Version: 1000 + * Properties70: + * [...] + * } + * @endverbatim */ +class Scope +{ +public: + Scope(Parser& parser, bool topLevel = false); + ~Scope(); + + const Element* operator[] (const std::string& index) const { + ElementMap::const_iterator it = elements.find(index); + return it == elements.end() ? NULL : (*it).second; + } + + const Element* FindElementCaseInsensitive(const std::string& elementName) const { + const char* elementNameCStr = elementName.c_str(); + for (auto element = elements.begin(); element != elements.end(); ++element) + { + if (!ASSIMP_strincmp(element->first.c_str(), elementNameCStr, MAXLEN)) { + return element->second; + } + } + return NULL; + } + + ElementCollection GetCollection(const std::string& index) const { + return elements.equal_range(index); + } + + const ElementMap& Elements() const { + return elements; + } + +private: + ElementMap elements; +}; + +/** FBX parsing class, takes a list of input tokens and generates a hierarchy + * of nested #Scope instances, representing the fbx DOM.*/ +class Parser +{ +public: + /** Parse given a token list. Does not take ownership of the tokens - + * the objects must persist during the entire parser lifetime */ + Parser (const TokenList& tokens,bool is_binary); + ~Parser(); + + const Scope& GetRootScope() const { + return *root.get(); + } + + bool IsBinary() const { + return is_binary; + } + +private: + friend class Scope; + friend class Element; + + TokenPtr AdvanceToNextToken(); + TokenPtr LastToken() const; + TokenPtr CurrentToken() const; + +private: + const TokenList& tokens; + + TokenPtr last, current; + TokenList::const_iterator cursor; + std::unique_ptr root; + + const bool is_binary; +}; + + +/* token parsing - this happens when building the DOM out of the parse-tree*/ +uint64_t ParseTokenAsID(const Token& t, const char*& err_out); +size_t ParseTokenAsDim(const Token& t, const char*& err_out); + +float ParseTokenAsFloat(const Token& t, const char*& err_out); +int ParseTokenAsInt(const Token& t, const char*& err_out); +int64_t ParseTokenAsInt64(const Token& t, const char*& err_out); +std::string ParseTokenAsString(const Token& t, const char*& err_out); + +/* wrapper around ParseTokenAsXXX() with DOMError handling */ +uint64_t ParseTokenAsID(const Token& t); +size_t ParseTokenAsDim(const Token& t); +float ParseTokenAsFloat(const Token& t); +int ParseTokenAsInt(const Token& t); +int64_t ParseTokenAsInt64(const Token& t); +std::string ParseTokenAsString(const Token& t); + +/* read data arrays */ +void ParseVectorDataArray(std::vector& out, const Element& el); +void ParseVectorDataArray(std::vector& out, const Element& el); +void ParseVectorDataArray(std::vector& out, const Element& el); +void ParseVectorDataArray(std::vector& out, const Element& el); +void ParseVectorDataArray(std::vector& out, const Element& el); +void ParseVectorDataArray(std::vector& out, const Element& el); +void ParseVectorDataArray(std::vector& out, const Element& e); +void ParseVectorDataArray(std::vector& out, const Element& el); + +bool HasElement( const Scope& sc, const std::string& index ); + +// extract a required element from a scope, abort if the element cannot be found +const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element = NULL); + +// extract required compound scope +const Scope& GetRequiredScope(const Element& el); +// get token at a particular index +const Token& GetRequiredToken(const Element& el, unsigned int index); + +// read a 4x4 matrix from an array of 16 floats +aiMatrix4x4 ReadMatrix(const Element& element); + +} // ! FBX +} // ! Assimp + +#endif // ! INCLUDED_AI_FBX_PARSER_H diff --git a/thirdparty/assimp/code/FBX/FBXProperties.cpp b/thirdparty/assimp/code/FBX/FBXProperties.cpp new file mode 100644 index 0000000000..8d7036b6a9 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXProperties.cpp @@ -0,0 +1,235 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXProperties.cpp + * @brief Implementation of the FBX dynamic properties system + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXTokenizer.h" +#include "FBXParser.h" +#include "FBXDocument.h" +#include "FBXDocumentUtil.h" +#include "FBXProperties.h" + +namespace Assimp { +namespace FBX { + + using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Property::Property() +{ +} + +// ------------------------------------------------------------------------------------------------ +Property::~Property() +{ +} + +namespace { + +// ------------------------------------------------------------------------------------------------ +// read a typed property out of a FBX element. The return value is NULL if the property cannot be read. +Property* ReadTypedProperty(const Element& element) +{ + ai_assert(element.KeyToken().StringContents() == "P"); + + const TokenList& tok = element.Tokens(); + ai_assert(tok.size() >= 5); + + const std::string& s = ParseTokenAsString(*tok[1]); + const char* const cs = s.c_str(); + if (!strcmp(cs,"KString")) { + return new TypedProperty(ParseTokenAsString(*tok[4])); + } + else if (!strcmp(cs,"bool") || !strcmp(cs,"Bool")) { + return new TypedProperty(ParseTokenAsInt(*tok[4]) != 0); + } + else if (!strcmp(cs, "int") || !strcmp(cs, "Int") || !strcmp(cs, "enum") || !strcmp(cs, "Enum")) { + return new TypedProperty(ParseTokenAsInt(*tok[4])); + } + else if (!strcmp(cs, "ULongLong")) { + return new TypedProperty(ParseTokenAsID(*tok[4])); + } + else if (!strcmp(cs, "KTime")) { + return new TypedProperty(ParseTokenAsInt64(*tok[4])); + } + else if (!strcmp(cs,"Vector3D") || + !strcmp(cs,"ColorRGB") || + !strcmp(cs,"Vector") || + !strcmp(cs,"Color") || + !strcmp(cs,"Lcl Translation") || + !strcmp(cs,"Lcl Rotation") || + !strcmp(cs,"Lcl Scaling") + ) { + return new TypedProperty(aiVector3D( + ParseTokenAsFloat(*tok[4]), + ParseTokenAsFloat(*tok[5]), + ParseTokenAsFloat(*tok[6])) + ); + } + else if (!strcmp(cs,"double") || !strcmp(cs,"Number") || !strcmp(cs,"Float") || !strcmp(cs,"FieldOfView") || !strcmp( cs, "UnitScaleFactor" ) ) { + return new TypedProperty(ParseTokenAsFloat(*tok[4])); + } + return NULL; +} + + +// ------------------------------------------------------------------------------------------------ +// peek into an element and check if it contains a FBX property, if so return its name. +std::string PeekPropertyName(const Element& element) +{ + ai_assert(element.KeyToken().StringContents() == "P"); + const TokenList& tok = element.Tokens(); + if(tok.size() < 4) { + return ""; + } + + return ParseTokenAsString(*tok[0]); +} + +} //! anon + + +// ------------------------------------------------------------------------------------------------ +PropertyTable::PropertyTable() +: templateProps() +, element() +{ +} + +// ------------------------------------------------------------------------------------------------ +PropertyTable::PropertyTable(const Element& element, std::shared_ptr templateProps) +: templateProps(templateProps) +, element(&element) +{ + const Scope& scope = GetRequiredScope(element); + for(const ElementMap::value_type& v : scope.Elements()) { + if(v.first != "P") { + DOMWarning("expected only P elements in property table",v.second); + continue; + } + + const std::string& name = PeekPropertyName(*v.second); + if(!name.length()) { + DOMWarning("could not read property name",v.second); + continue; + } + + LazyPropertyMap::const_iterator it = lazyProps.find(name); + if (it != lazyProps.end()) { + DOMWarning("duplicate property name, will hide previous value: " + name,v.second); + continue; + } + + lazyProps[name] = v.second; + } +} + + +// ------------------------------------------------------------------------------------------------ +PropertyTable::~PropertyTable() +{ + for(PropertyMap::value_type& v : props) { + delete v.second; + } +} + + +// ------------------------------------------------------------------------------------------------ +const Property* PropertyTable::Get(const std::string& name) const +{ + PropertyMap::const_iterator it = props.find(name); + if (it == props.end()) { + // hasn't been parsed yet? + LazyPropertyMap::const_iterator lit = lazyProps.find(name); + if(lit != lazyProps.end()) { + props[name] = ReadTypedProperty(*(*lit).second); + it = props.find(name); + + ai_assert(it != props.end()); + } + + if (it == props.end()) { + // check property template + if(templateProps) { + return templateProps->Get(name); + } + + return NULL; + } + } + + return (*it).second; +} + +DirectPropertyMap PropertyTable::GetUnparsedProperties() const +{ + DirectPropertyMap result; + + // Loop through all the lazy properties (which is all the properties) + for(const LazyPropertyMap::value_type& element : lazyProps) { + + // Skip parsed properties + if (props.end() != props.find(element.first)) continue; + + // Read the element's value. + // Wrap the naked pointer (since the call site is required to acquire ownership) + // std::unique_ptr from C++11 would be preferred both as a wrapper and a return value. + std::shared_ptr prop = std::shared_ptr(ReadTypedProperty(*element.second)); + + // Element could not be read. Skip it. + if (!prop) continue; + + // Add to result + result[element.first] = prop; + } + + return result; +} + +} //! FBX +} //! Assimp + +#endif diff --git a/thirdparty/assimp/code/FBX/FBXProperties.h b/thirdparty/assimp/code/FBX/FBXProperties.h new file mode 100644 index 0000000000..58755542fc --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXProperties.h @@ -0,0 +1,185 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXProperties.h + * @brief FBX dynamic properties + */ +#ifndef INCLUDED_AI_FBX_PROPERTIES_H +#define INCLUDED_AI_FBX_PROPERTIES_H + +#include "FBXCompileConfig.h" +#include +#include + +namespace Assimp { +namespace FBX { + +// Forward declarations +class Element; + +/** Represents a dynamic property. Type info added by deriving classes, + * see #TypedProperty. + Example: + @verbatim + P: "ShininessExponent", "double", "Number", "",0.5 + @endvebatim +*/ +class Property { +protected: + Property(); + +public: + virtual ~Property(); + +public: + template + const T* As() const { + return dynamic_cast(this); + } +}; + +template +class TypedProperty : public Property { +public: + explicit TypedProperty(const T& value) + : value(value) { + // empty + } + + const T& Value() const { + return value; + } + +private: + T value; +}; + + +typedef std::fbx_unordered_map > DirectPropertyMap; +typedef std::fbx_unordered_map PropertyMap; +typedef std::fbx_unordered_map LazyPropertyMap; + +/** + * Represents a property table as can be found in the newer FBX files (Properties60, Properties70) + */ +class PropertyTable { +public: + // in-memory property table with no source element + PropertyTable(); + PropertyTable(const Element& element, std::shared_ptr templateProps); + ~PropertyTable(); + + const Property* Get(const std::string& name) const; + + // PropertyTable's need not be coupled with FBX elements so this can be NULL + const Element* GetElement() const { + return element; + } + + const PropertyTable* TemplateProps() const { + return templateProps.get(); + } + + DirectPropertyMap GetUnparsedProperties() const; + +private: + LazyPropertyMap lazyProps; + mutable PropertyMap props; + const std::shared_ptr templateProps; + const Element* const element; +}; + +// ------------------------------------------------------------------------------------------------ +template +inline +T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) { + const Property* const prop = in.Get(name); + if( nullptr == prop) { + return defaultValue; + } + + // strong typing, no need to be lenient + const TypedProperty* const tprop = prop->As< TypedProperty >(); + if( nullptr == tprop) { + return defaultValue; + } + + return tprop->Value(); +} + +// ------------------------------------------------------------------------------------------------ +template +inline +T PropertyGet(const PropertyTable& in, const std::string& name, bool& result, bool useTemplate=false ) { + const Property* prop = in.Get(name); + if( nullptr == prop) { + if ( ! useTemplate ) { + result = false; + return T(); + } + const PropertyTable* templ = in.TemplateProps(); + if ( nullptr == templ ) { + result = false; + return T(); + } + prop = templ->Get(name); + if ( nullptr == prop ) { + result = false; + return T(); + } + } + + // strong typing, no need to be lenient + const TypedProperty* const tprop = prop->As< TypedProperty >(); + if( nullptr == tprop) { + result = false; + return T(); + } + + result = true; + return tprop->Value(); +} + +} //! FBX +} //! Assimp + +#endif // INCLUDED_AI_FBX_PROPERTIES_H diff --git a/thirdparty/assimp/code/FBX/FBXTokenizer.cpp b/thirdparty/assimp/code/FBX/FBXTokenizer.cpp new file mode 100644 index 0000000000..252cce3557 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXTokenizer.cpp @@ -0,0 +1,248 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXTokenizer.cpp + * @brief Implementation of the FBX broadphase lexer + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +// tab width for logging columns +#define ASSIMP_FBX_TAB_WIDTH 4 + +#include + +#include "FBXTokenizer.h" +#include "FBXUtil.h" +#include + +namespace Assimp { +namespace FBX { + +// ------------------------------------------------------------------------------------------------ +Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int line, unsigned int column) + : +#ifdef DEBUG + contents(sbegin, static_cast(send-sbegin)), +#endif + sbegin(sbegin) + , send(send) + , type(type) + , line(line) + , column(column) +{ + ai_assert(sbegin); + ai_assert(send); + + // tokens must be of non-zero length + ai_assert(static_cast(send-sbegin) > 0); +} + +// ------------------------------------------------------------------------------------------------ +Token::~Token() +{ +} + +namespace { + +// ------------------------------------------------------------------------------------------------ +// signal tokenization error, this is always unrecoverable. Throws DeadlyImportError. +AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) AI_WONT_RETURN_SUFFIX; +AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) +{ + throw DeadlyImportError(Util::AddLineAndColumn("FBX-Tokenize",message,line,column)); +} + + +// process a potential data token up to 'cur', adding it to 'output_tokens'. +// ------------------------------------------------------------------------------------------------ +void ProcessDataToken( TokenList& output_tokens, const char*& start, const char*& end, + unsigned int line, + unsigned int column, + TokenType type = TokenType_DATA, + bool must_have_token = false) +{ + if (start && end) { + // sanity check: + // tokens should have no whitespace outside quoted text and [start,end] should + // properly delimit the valid range. + bool in_double_quotes = false; + for (const char* c = start; c != end + 1; ++c) { + if (*c == '\"') { + in_double_quotes = !in_double_quotes; + } + + if (!in_double_quotes && IsSpaceOrNewLine(*c)) { + TokenizeError("unexpected whitespace in token", line, column); + } + } + + if (in_double_quotes) { + TokenizeError("non-terminated double quotes", line, column); + } + + output_tokens.push_back(new_Token(start,end + 1,type,line,column)); + } + else if (must_have_token) { + TokenizeError("unexpected character, expected data token", line, column); + } + + start = end = NULL; +} + +} + +// ------------------------------------------------------------------------------------------------ +void Tokenize(TokenList& output_tokens, const char* input) +{ + ai_assert(input); + + // line and column numbers numbers are one-based + unsigned int line = 1; + unsigned int column = 1; + + bool comment = false; + bool in_double_quotes = false; + bool pending_data_token = false; + + const char* token_begin = NULL, *token_end = NULL; + for (const char* cur = input;*cur;column += (*cur == '\t' ? ASSIMP_FBX_TAB_WIDTH : 1), ++cur) { + const char c = *cur; + + if (IsLineEnd(c)) { + comment = false; + + column = 0; + ++line; + } + + if(comment) { + continue; + } + + if(in_double_quotes) { + if (c == '\"') { + in_double_quotes = false; + token_end = cur; + + ProcessDataToken(output_tokens,token_begin,token_end,line,column); + pending_data_token = false; + } + continue; + } + + switch(c) + { + case '\"': + if (token_begin) { + TokenizeError("unexpected double-quote", line, column); + } + token_begin = cur; + in_double_quotes = true; + continue; + + case ';': + ProcessDataToken(output_tokens,token_begin,token_end,line,column); + comment = true; + continue; + + case '{': + ProcessDataToken(output_tokens,token_begin,token_end, line, column); + output_tokens.push_back(new_Token(cur,cur+1,TokenType_OPEN_BRACKET,line,column)); + continue; + + case '}': + ProcessDataToken(output_tokens,token_begin,token_end,line,column); + output_tokens.push_back(new_Token(cur,cur+1,TokenType_CLOSE_BRACKET,line,column)); + continue; + + case ',': + if (pending_data_token) { + ProcessDataToken(output_tokens,token_begin,token_end,line,column,TokenType_DATA,true); + } + output_tokens.push_back(new_Token(cur,cur+1,TokenType_COMMA,line,column)); + continue; + + case ':': + if (pending_data_token) { + ProcessDataToken(output_tokens,token_begin,token_end,line,column,TokenType_KEY,true); + } + else { + TokenizeError("unexpected colon", line, column); + } + continue; + } + + if (IsSpaceOrNewLine(c)) { + + if (token_begin) { + // peek ahead and check if the next token is a colon in which + // case this counts as KEY token. + TokenType type = TokenType_DATA; + for (const char* peek = cur; *peek && IsSpaceOrNewLine(*peek); ++peek) { + if (*peek == ':') { + type = TokenType_KEY; + cur = peek; + break; + } + } + + ProcessDataToken(output_tokens,token_begin,token_end,line,column,type); + } + + pending_data_token = false; + } + else { + token_end = cur; + if (!token_begin) { + token_begin = cur; + } + + pending_data_token = true; + } + } +} + +} // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBX/FBXTokenizer.h b/thirdparty/assimp/code/FBX/FBXTokenizer.h new file mode 100644 index 0000000000..afa588a470 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXTokenizer.h @@ -0,0 +1,187 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXTokenizer.h + * @brief FBX lexer + */ +#ifndef INCLUDED_AI_FBX_TOKENIZER_H +#define INCLUDED_AI_FBX_TOKENIZER_H + +#include "FBXCompileConfig.h" +#include +#include +#include + +namespace Assimp { +namespace FBX { + +/** Rough classification for text FBX tokens used for constructing the + * basic scope hierarchy. */ +enum TokenType +{ + // { + TokenType_OPEN_BRACKET = 0, + + // } + TokenType_CLOSE_BRACKET, + + // '"blablubb"', '2', '*14' - very general token class, + // further processing happens at a later stage. + TokenType_DATA, + + // + TokenType_BINARY_DATA, + + // , + TokenType_COMMA, + + // blubb: + TokenType_KEY +}; + + +/** Represents a single token in a FBX file. Tokens are + * classified by the #TokenType enumerated types. + * + * Offers iterator protocol. Tokens are immutable. */ +class Token +{ +private: + static const unsigned int BINARY_MARKER = static_cast(-1); + +public: + /** construct a textual token */ + Token(const char* sbegin, const char* send, TokenType type, unsigned int line, unsigned int column); + + /** construct a binary token */ + Token(const char* sbegin, const char* send, TokenType type, size_t offset); + + ~Token(); + +public: + std::string StringContents() const { + return std::string(begin(),end()); + } + + bool IsBinary() const { + return column == BINARY_MARKER; + } + + const char* begin() const { + return sbegin; + } + + const char* end() const { + return send; + } + + TokenType Type() const { + return type; + } + + size_t Offset() const { + ai_assert(IsBinary()); + return offset; + } + + unsigned int Line() const { + ai_assert(!IsBinary()); + return static_cast(line); + } + + unsigned int Column() const { + ai_assert(!IsBinary()); + return column; + } + +private: + +#ifdef DEBUG + // full string copy for the sole purpose that it nicely appears + // in msvc's debugger window. + const std::string contents; +#endif + + + const char* const sbegin; + const char* const send; + const TokenType type; + + union { + size_t line; + size_t offset; + }; + const unsigned int column; +}; + +// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03 +typedef const Token* TokenPtr; +typedef std::vector< TokenPtr > TokenList; + +#define new_Token new Token + + +/** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens. + * + * Skips over comments and generates line and column numbers. + * + * @param output_tokens Receives a list of all tokens in the input data. + * @param input_buffer Textual input buffer to be processed, 0-terminated. + * @throw DeadlyImportError if something goes wrong */ +void Tokenize(TokenList& output_tokens, const char* input); + + +/** Tokenizer function for binary FBX files. + * + * Emits a token list suitable for direct parsing. + * + * @param output_tokens Receives a list of all tokens in the input data. + * @param input_buffer Binary input buffer to be processed. + * @param length Length of input buffer, in bytes. There is no 0-terminal. + * @throw DeadlyImportError if something goes wrong */ +void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length); + + +} // ! FBX +} // ! Assimp + +#endif // ! INCLUDED_AI_FBX_PARSER_H diff --git a/thirdparty/assimp/code/FBX/FBXUtil.cpp b/thirdparty/assimp/code/FBX/FBXUtil.cpp new file mode 100644 index 0000000000..c10e057c8c --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXUtil.cpp @@ -0,0 +1,243 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXUtil.cpp + * @brief Implementation of internal FBX utility functions + */ + +#include "FBXUtil.h" +#include "FBXTokenizer.h" + +#include +#include +#include + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +namespace Assimp { +namespace FBX { +namespace Util { + +// ------------------------------------------------------------------------------------------------ +const char* TokenTypeString(TokenType t) +{ + switch(t) { + case TokenType_OPEN_BRACKET: + return "TOK_OPEN_BRACKET"; + + case TokenType_CLOSE_BRACKET: + return "TOK_CLOSE_BRACKET"; + + case TokenType_DATA: + return "TOK_DATA"; + + case TokenType_COMMA: + return "TOK_COMMA"; + + case TokenType_KEY: + return "TOK_KEY"; + + case TokenType_BINARY_DATA: + return "TOK_BINARY_DATA"; + } + + ai_assert(false); + return ""; +} + + +// ------------------------------------------------------------------------------------------------ +std::string AddOffset(const std::string& prefix, const std::string& text, size_t offset) +{ + return static_cast( (Formatter::format() << prefix << " (offset 0x" << std::hex << offset << ") " << text) ); +} + +// ------------------------------------------------------------------------------------------------ +std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column) +{ + return static_cast( (Formatter::format() << prefix << " (line " << line << " << col " << column << ") " << text) ); +} + +// ------------------------------------------------------------------------------------------------ +std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok) +{ + if(tok->IsBinary()) { + return static_cast( (Formatter::format() << prefix << + " (" << TokenTypeString(tok->Type()) << + ", offset 0x" << std::hex << tok->Offset() << ") " << + text) ); + } + + return static_cast( (Formatter::format() << prefix << + " (" << TokenTypeString(tok->Type()) << + ", line " << tok->Line() << + ", col " << tok->Column() << ") " << + text) ); +} + +// Generated by this formula: T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; +static const uint8_t base64DecodeTable[128] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255, + 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, + 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255 +}; + +uint8_t DecodeBase64(char ch) +{ + const auto idx = static_cast(ch); + if (idx > 127) + return 255; + return base64DecodeTable[idx]; +} + +size_t ComputeDecodedSizeBase64(const char* in, size_t inLength) +{ + if (inLength < 2) + { + return 0; + } + const size_t equals = size_t(in[inLength - 1] == '=') + size_t(in[inLength - 2] == '='); + const size_t full_length = (inLength * 3) >> 2; // div by 4 + if (full_length < equals) + { + return 0; + } + return full_length - equals; +} + +size_t DecodeBase64(const char* in, size_t inLength, uint8_t* out, size_t maxOutLength) +{ + if (maxOutLength == 0 || inLength < 2) { + return 0; + } + const size_t realLength = inLength - size_t(in[inLength - 1] == '=') - size_t(in[inLength - 2] == '='); + size_t dst_offset = 0; + int val = 0, valb = -8; + for (size_t src_offset = 0; src_offset < realLength; ++src_offset) + { + const uint8_t table_value = Util::DecodeBase64(in[src_offset]); + if (table_value == 255) + { + return 0; + } + val = (val << 6) + table_value; + valb += 6; + if (valb >= 0) + { + out[dst_offset++] = static_cast((val >> valb) & 0xFF); + valb -= 8; + val &= 0xFFF; + } + } + return dst_offset; +} + +static const char to_base64_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +char EncodeBase64(char byte) +{ + return to_base64_string[(size_t)byte]; +} + +/** Encodes a block of 4 bytes to base64 encoding +* +* @param bytes Bytes to encode. +* @param out_string String to write encoded values to. +* @param string_pos Position in out_string.*/ +void EncodeByteBlock(const char* bytes, std::string& out_string, size_t string_pos) +{ + char b0 = (bytes[0] & 0xFC) >> 2; + char b1 = (bytes[0] & 0x03) << 4 | ((bytes[1] & 0xF0) >> 4); + char b2 = (bytes[1] & 0x0F) << 2 | ((bytes[2] & 0xC0) >> 6); + char b3 = (bytes[2] & 0x3F); + + out_string[string_pos + 0] = EncodeBase64(b0); + out_string[string_pos + 1] = EncodeBase64(b1); + out_string[string_pos + 2] = EncodeBase64(b2); + out_string[string_pos + 3] = EncodeBase64(b3); +} + +std::string EncodeBase64(const char* data, size_t length) +{ + // calculate extra bytes needed to get a multiple of 3 + size_t extraBytes = 3 - length % 3; + + // number of base64 bytes + size_t encodedBytes = 4 * (length + extraBytes) / 3; + + std::string encoded_string(encodedBytes, '='); + + // read blocks of 3 bytes + for (size_t ib3 = 0; ib3 < length / 3; ib3++) + { + const size_t iByte = ib3 * 3; + const size_t iEncodedByte = ib3 * 4; + const char* currData = &data[iByte]; + + EncodeByteBlock(currData, encoded_string, iEncodedByte); + } + + // if size of data is not a multiple of 3, also encode the final bytes (and add zeros where needed) + if (extraBytes > 0) + { + char finalBytes[4] = { 0,0,0,0 }; + memcpy(&finalBytes[0], &data[length - length % 3], length % 3); + + const size_t iEncodedByte = encodedBytes - 4; + EncodeByteBlock(&finalBytes[0], encoded_string, iEncodedByte); + + // add '=' at the end + for (size_t i = 0; i < 4 * extraBytes / 3; i++) + encoded_string[encodedBytes - i - 1] = '='; + } + return encoded_string; +} + +} // !Util +} // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBX/FBXUtil.h b/thirdparty/assimp/code/FBX/FBXUtil.h new file mode 100644 index 0000000000..b634418858 --- /dev/null +++ b/thirdparty/assimp/code/FBX/FBXUtil.h @@ -0,0 +1,137 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXUtil.h + * @brief FBX utility functions for internal use + */ +#ifndef INCLUDED_AI_FBX_UTIL_H +#define INCLUDED_AI_FBX_UTIL_H + +#include "FBXCompileConfig.h" +#include "FBXTokenizer.h" +#include + +namespace Assimp { +namespace FBX { + + +namespace Util { + + +/** helper for std::for_each to delete all heap-allocated items in a container */ +template +struct delete_fun +{ + void operator()(const volatile T* del) { + delete del; + } +}; + +/** Get a string representation for a #TokenType. */ +const char* TokenTypeString(TokenType t); + + + +/** Format log/error messages using a given offset in the source binary file + * + * @param prefix Message prefix to be preprended to the location info. + * @param text Message text + * @param line Line index, 1-based + * @param column Column index, 1-based + * @return A string of the following format: {prefix} (offset 0x{offset}) {text}*/ +std::string AddOffset(const std::string& prefix, const std::string& text, size_t offset); + + +/** Format log/error messages using a given line location in the source file. + * + * @param prefix Message prefix to be preprended to the location info. + * @param text Message text + * @param line Line index, 1-based + * @param column Column index, 1-based + * @return A string of the following format: {prefix} (line {line}, col {column}) {text}*/ +std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column); + + +/** Format log/error messages using a given cursor token. + * + * @param prefix Message prefix to be preprended to the location info. + * @param text Message text + * @param tok Token where parsing/processing stopped + * @return A string of the following format: {prefix} ({token-type}, line {line}, col {column}) {text}*/ +std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok); + +/** Decode a single Base64-encoded character. +* +* @param ch Character to decode (from base64 to binary). +* @return decoded byte value*/ +uint8_t DecodeBase64(char ch); + +/** Compute decoded size of a Base64-encoded string +* +* @param in Characters to decode. +* @param inLength Number of characters to decode. +* @return size of the decoded data (number of bytes)*/ +size_t ComputeDecodedSizeBase64(const char* in, size_t inLength); + +/** Decode a Base64-encoded string +* +* @param in Characters to decode. +* @param inLength Number of characters to decode. +* @param out Pointer where we will store the decoded data. +* @param maxOutLength Size of output buffer. +* @return size of the decoded data (number of bytes)*/ +size_t DecodeBase64(const char* in, size_t inLength, uint8_t* out, size_t maxOutLength); + +char EncodeBase64(char byte); + +/** Encode bytes in base64-encoding +* +* @param data Binary data to encode. +* @param inLength Number of bytes to encode. +* @return base64-encoded string*/ +std::string EncodeBase64(const char* data, size_t length); + +} +} +} + +#endif // ! INCLUDED_AI_FBX_UTIL_H diff --git a/thirdparty/assimp/code/FBXAnimation.cpp b/thirdparty/assimp/code/FBXAnimation.cpp deleted file mode 100644 index 874914431b..0000000000 --- a/thirdparty/assimp/code/FBXAnimation.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXAnimation.cpp - * @brief Assimp::FBX::AnimationCurve, Assimp::FBX::AnimationCurveNode, - * Assimp::FBX::AnimationLayer, Assimp::FBX::AnimationStack - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -#include "FBXParser.h" -#include "FBXDocument.h" -#include "FBXImporter.h" -#include "FBXDocumentUtil.h" - -namespace Assimp { -namespace FBX { - -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& /*doc*/) -: Object(id, element, name) -{ - const Scope& sc = GetRequiredScope(element); - const Element& KeyTime = GetRequiredElement(sc,"KeyTime"); - const Element& KeyValueFloat = GetRequiredElement(sc,"KeyValueFloat"); - - ParseVectorDataArray(keys, KeyTime); - ParseVectorDataArray(values, KeyValueFloat); - - if(keys.size() != values.size()) { - DOMError("the number of key times does not match the number of keyframe values",&KeyTime); - } - - // check if the key times are well-ordered - if(!std::equal(keys.begin(), keys.end() - 1, keys.begin() + 1, std::less())) { - DOMError("the keyframes are not in ascending order",&KeyTime); - } - - const Element* KeyAttrDataFloat = sc["KeyAttrDataFloat"]; - if(KeyAttrDataFloat) { - ParseVectorDataArray(attributes, *KeyAttrDataFloat); - } - - const Element* KeyAttrFlags = sc["KeyAttrFlags"]; - if(KeyAttrFlags) { - ParseVectorDataArray(flags, *KeyAttrFlags); - } -} - -// ------------------------------------------------------------------------------------------------ -AnimationCurve::~AnimationCurve() -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, - const Document& doc, const char* const * target_prop_whitelist /*= NULL*/, - size_t whitelist_size /*= 0*/) -: Object(id, element, name) -, target() -, doc(doc) -{ - const Scope& sc = GetRequiredScope(element); - - // find target node - const char* whitelist[] = {"Model","NodeAttribute","Deformer"}; - const std::vector& conns = doc.GetConnectionsBySourceSequenced(ID(),whitelist,3); - - for(const Connection* con : conns) { - - // link should go for a property - if (!con->PropertyName().length()) { - continue; - } - - if(target_prop_whitelist) { - const char* const s = con->PropertyName().c_str(); - bool ok = false; - for (size_t i = 0; i < whitelist_size; ++i) { - if (!strcmp(s, target_prop_whitelist[i])) { - ok = true; - break; - } - } - - if (!ok) { - throw std::range_error("AnimationCurveNode target property is not in whitelist"); - } - } - - const Object* const ob = con->DestinationObject(); - if(!ob) { - DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring",&element); - continue; - } - - // XXX support constraints as DOM class - //ai_assert(dynamic_cast(ob) || dynamic_cast(ob)); - target = ob; - if(!target) { - continue; - } - - prop = con->PropertyName(); - break; - } - - if(!target) { - DOMWarning("failed to resolve target Model/NodeAttribute/Constraint for AnimationCurveNode",&element); - } - - props = GetPropertyTable(doc,"AnimationCurveNode.FbxAnimCurveNode",element,sc,false); -} - -// ------------------------------------------------------------------------------------------------ -AnimationCurveNode::~AnimationCurveNode() -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -const AnimationCurveMap& AnimationCurveNode::Curves() const -{ - if ( curves.empty() ) { - // resolve attached animation curves - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurve"); - - for(const Connection* con : conns) { - - // link should go for a property - if (!con->PropertyName().length()) { - continue; - } - - const Object* const ob = con->SourceObject(); - if(!ob) { - DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring",&element); - continue; - } - - const AnimationCurve* const anim = dynamic_cast(ob); - if(!anim) { - DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve",&element); - continue; - } - - curves[con->PropertyName()] = anim; - } - } - - return curves; -} - -// ------------------------------------------------------------------------------------------------ -AnimationLayer::AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc) -: Object(id, element, name) -, doc(doc) -{ - const Scope& sc = GetRequiredScope(element); - - // note: the props table here bears little importance and is usually absent - props = GetPropertyTable(doc,"AnimationLayer.FbxAnimLayer",element,sc, true); -} - -// ------------------------------------------------------------------------------------------------ -AnimationLayer::~AnimationLayer() -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -AnimationCurveNodeList AnimationLayer::Nodes(const char* const * target_prop_whitelist /*= NULL*/, - size_t whitelist_size /*= 0*/) const -{ - AnimationCurveNodeList nodes; - - // resolve attached animation nodes - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurveNode"); - nodes.reserve(conns.size()); - - for(const Connection* con : conns) { - - // link should not go to a property - if (con->PropertyName().length()) { - continue; - } - - const Object* const ob = con->SourceObject(); - if(!ob) { - DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring",&element); - continue; - } - - const AnimationCurveNode* const anim = dynamic_cast(ob); - if(!anim) { - DOMWarning("source object for ->AnimationLayer link is not an AnimationCurveNode",&element); - continue; - } - - if(target_prop_whitelist) { - const char* s = anim->TargetProperty().c_str(); - bool ok = false; - for (size_t i = 0; i < whitelist_size; ++i) { - if (!strcmp(s, target_prop_whitelist[i])) { - ok = true; - break; - } - } - if(!ok) { - continue; - } - } - nodes.push_back(anim); - } - - return nodes; // pray for NRVO -} - -// ------------------------------------------------------------------------------------------------ -AnimationStack::AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc) -: Object(id, element, name) -{ - const Scope& sc = GetRequiredScope(element); - - // note: we don't currently use any of these properties so we shouldn't bother if it is missing - props = GetPropertyTable(doc,"AnimationStack.FbxAnimStack",element,sc, true); - - // resolve attached animation layers - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationLayer"); - layers.reserve(conns.size()); - - for(const Connection* con : conns) { - - // link should not go to a property - if (con->PropertyName().length()) { - continue; - } - - const Object* const ob = con->SourceObject(); - if(!ob) { - DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring",&element); - continue; - } - - const AnimationLayer* const anim = dynamic_cast(ob); - if(!anim) { - DOMWarning("source object for ->AnimationStack link is not an AnimationLayer",&element); - continue; - } - layers.push_back(anim); - } -} - -// ------------------------------------------------------------------------------------------------ -AnimationStack::~AnimationStack() -{ - // empty -} - -} //!FBX -} //!Assimp - -#endif // ASSIMP_BUILD_NO_FBX_IMPORTER diff --git a/thirdparty/assimp/code/FBXBinaryTokenizer.cpp b/thirdparty/assimp/code/FBXBinaryTokenizer.cpp deleted file mode 100644 index 7138df4315..0000000000 --- a/thirdparty/assimp/code/FBXBinaryTokenizer.cpp +++ /dev/null @@ -1,466 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -/** @file FBXBinaryTokenizer.cpp - * @brief Implementation of a fake lexer for binary fbx files - - * we emit tokens so the parser needs almost no special handling - * for binary files. - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -#include "FBXTokenizer.h" -#include "FBXUtil.h" -#include -#include -#include -#include - -namespace Assimp { -namespace FBX { - -//enum Flag -//{ -// e_unknown_0 = 1 << 0, -// e_unknown_1 = 1 << 1, -// e_unknown_2 = 1 << 2, -// e_unknown_3 = 1 << 3, -// e_unknown_4 = 1 << 4, -// e_unknown_5 = 1 << 5, -// e_unknown_6 = 1 << 6, -// e_unknown_7 = 1 << 7, -// e_unknown_8 = 1 << 8, -// e_unknown_9 = 1 << 9, -// e_unknown_10 = 1 << 10, -// e_unknown_11 = 1 << 11, -// e_unknown_12 = 1 << 12, -// e_unknown_13 = 1 << 13, -// e_unknown_14 = 1 << 14, -// e_unknown_15 = 1 << 15, -// e_unknown_16 = 1 << 16, -// e_unknown_17 = 1 << 17, -// e_unknown_18 = 1 << 18, -// e_unknown_19 = 1 << 19, -// e_unknown_20 = 1 << 20, -// e_unknown_21 = 1 << 21, -// e_unknown_22 = 1 << 22, -// e_unknown_23 = 1 << 23, -// e_flag_field_size_64_bit = 1 << 24, // Not sure what is -// e_unknown_25 = 1 << 25, -// e_unknown_26 = 1 << 26, -// e_unknown_27 = 1 << 27, -// e_unknown_28 = 1 << 28, -// e_unknown_29 = 1 << 29, -// e_unknown_30 = 1 << 30, -// e_unknown_31 = 1 << 31 -//}; -// -//bool check_flag(uint32_t flags, Flag to_check) -//{ -// return (flags & to_check) != 0; -//} -// ------------------------------------------------------------------------------------------------ -Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int offset) - : - #ifdef DEBUG - contents(sbegin, static_cast(send-sbegin)), - #endif - sbegin(sbegin) - , send(send) - , type(type) - , line(offset) - , column(BINARY_MARKER) -{ - ai_assert(sbegin); - ai_assert(send); - - // binary tokens may have zero length because they are sometimes dummies - // inserted by TokenizeBinary() - ai_assert(send >= sbegin); -} - - -namespace { - -// ------------------------------------------------------------------------------------------------ -// signal tokenization error, this is always unrecoverable. Throws DeadlyImportError. -AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset) AI_WONT_RETURN_SUFFIX; -AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset) -{ - throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset)); -} - - -// ------------------------------------------------------------------------------------------------ -uint32_t Offset(const char* begin, const char* cursor) { - ai_assert(begin <= cursor); - - return static_cast(cursor - begin); -} - -// ------------------------------------------------------------------------------------------------ -void TokenizeError(const std::string& message, const char* begin, const char* cursor) { - TokenizeError(message, Offset(begin, cursor)); -} - -// ------------------------------------------------------------------------------------------------ -uint32_t ReadWord(const char* input, const char*& cursor, const char* end) { - const size_t k_to_read = sizeof( uint32_t ); - if(Offset(cursor, end) < k_to_read ) { - TokenizeError("cannot ReadWord, out of bounds",input, cursor); - } - - uint32_t word; - ::memcpy(&word, cursor, 4); - AI_SWAP4(word); - - cursor += k_to_read; - - return word; -} - -// ------------------------------------------------------------------------------------------------ -uint64_t ReadDoubleWord(const char* input, const char*& cursor, const char* end) { - const size_t k_to_read = sizeof(uint64_t); - if(Offset(cursor, end) < k_to_read) { - TokenizeError("cannot ReadDoubleWord, out of bounds",input, cursor); - } - - uint64_t dword /*= *reinterpret_cast(cursor)*/; - ::memcpy( &dword, cursor, sizeof( uint64_t ) ); - AI_SWAP8(dword); - - cursor += k_to_read; - - return dword; -} - -// ------------------------------------------------------------------------------------------------ -uint8_t ReadByte(const char* input, const char*& cursor, const char* end) { - if(Offset(cursor, end) < sizeof( uint8_t ) ) { - TokenizeError("cannot ReadByte, out of bounds",input, cursor); - } - - uint8_t word;/* = *reinterpret_cast< const uint8_t* >( cursor )*/ - ::memcpy( &word, cursor, sizeof( uint8_t ) ); - ++cursor; - - return word; -} - -// ------------------------------------------------------------------------------------------------ -unsigned int ReadString(const char*& sbegin_out, const char*& send_out, const char* input, - const char*& cursor, const char* end, bool long_length = false, bool allow_null = false) { - const uint32_t len_len = long_length ? 4 : 1; - if(Offset(cursor, end) < len_len) { - TokenizeError("cannot ReadString, out of bounds reading length",input, cursor); - } - - const uint32_t length = long_length ? ReadWord(input, cursor, end) : ReadByte(input, cursor, end); - - if (Offset(cursor, end) < length) { - TokenizeError("cannot ReadString, length is out of bounds",input, cursor); - } - - sbegin_out = cursor; - cursor += length; - - send_out = cursor; - - if(!allow_null) { - for (unsigned int i = 0; i < length; ++i) { - if(sbegin_out[i] == '\0') { - TokenizeError("failed ReadString, unexpected NUL character in string",input, cursor); - } - } - } - - return length; -} - -// ------------------------------------------------------------------------------------------------ -void ReadData(const char*& sbegin_out, const char*& send_out, const char* input, const char*& cursor, const char* end) { - if(Offset(cursor, end) < 1) { - TokenizeError("cannot ReadData, out of bounds reading length",input, cursor); - } - - const char type = *cursor; - sbegin_out = cursor++; - - switch(type) - { - // 16 bit int - case 'Y': - cursor += 2; - break; - - // 1 bit bool flag (yes/no) - case 'C': - cursor += 1; - break; - - // 32 bit int - case 'I': - // <- fall through - - // float - case 'F': - cursor += 4; - break; - - // double - case 'D': - cursor += 8; - break; - - // 64 bit int - case 'L': - cursor += 8; - break; - - // note: do not write cursor += ReadWord(...cursor) as this would be UB - - // raw binary data - case 'R': - { - const uint32_t length = ReadWord(input, cursor, end); - cursor += length; - break; - } - - case 'b': - // TODO: what is the 'b' type code? Right now we just skip over it / - // take the full range we could get - cursor = end; - break; - - // array of * - case 'f': - case 'd': - case 'l': - case 'i': - case 'c': { - const uint32_t length = ReadWord(input, cursor, end); - const uint32_t encoding = ReadWord(input, cursor, end); - - const uint32_t comp_len = ReadWord(input, cursor, end); - - // compute length based on type and check against the stored value - if(encoding == 0) { - uint32_t stride = 0; - switch(type) - { - case 'f': - case 'i': - stride = 4; - break; - - case 'd': - case 'l': - stride = 8; - break; - - case 'c': - stride = 1; - break; - - default: - ai_assert(false); - }; - ai_assert(stride > 0); - if(length * stride != comp_len) { - TokenizeError("cannot ReadData, calculated data stride differs from what the file claims",input, cursor); - } - } - // zip/deflate algorithm (encoding==1)? take given length. anything else? die - else if (encoding != 1) { - TokenizeError("cannot ReadData, unknown encoding",input, cursor); - } - cursor += comp_len; - break; - } - - // string - case 'S': { - const char* sb, *se; - // 0 characters can legally happen in such strings - ReadString(sb, se, input, cursor, end, true, true); - break; - } - default: - TokenizeError("cannot ReadData, unexpected type code: " + std::string(&type, 1),input, cursor); - } - - if(cursor > end) { - TokenizeError("cannot ReadData, the remaining size is too small for the data type: " + std::string(&type, 1),input, cursor); - } - - // the type code is contained in the returned range - send_out = cursor; -} - - -// ------------------------------------------------------------------------------------------------ -bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor, const char* end, bool const is64bits) -{ - // the first word contains the offset at which this block ends - const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); - - // we may get 0 if reading reached the end of the file - - // fbx files have a mysterious extra footer which I don't know - // how to extract any information from, but at least it always - // starts with a 0. - if(!end_offset) { - return false; - } - - if(end_offset > Offset(input, end)) { - TokenizeError("block offset is out of range",input, cursor); - } - else if(end_offset < Offset(input, cursor)) { - TokenizeError("block offset is negative out of range",input, cursor); - } - - // the second data word contains the number of properties in the scope - const uint64_t prop_count = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); - - // the third data word contains the length of the property list - const uint64_t prop_length = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); - - // now comes the name of the scope/key - const char* sbeg, *send; - ReadString(sbeg, send, input, cursor, end); - - output_tokens.push_back(new_Token(sbeg, send, TokenType_KEY, Offset(input, cursor) )); - - // now come the individual properties - const char* begin_cursor = cursor; - for (unsigned int i = 0; i < prop_count; ++i) { - ReadData(sbeg, send, input, cursor, begin_cursor + prop_length); - - output_tokens.push_back(new_Token(sbeg, send, TokenType_DATA, Offset(input, cursor) )); - - if(i != prop_count-1) { - output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_COMMA, Offset(input, cursor) )); - } - } - - if (Offset(begin_cursor, cursor) != prop_length) { - TokenizeError("property length not reached, something is wrong",input, cursor); - } - - // at the end of each nested block, there is a NUL record to indicate - // that the sub-scope exists (i.e. to distinguish between P: and P : {}) - // this NUL record is 13 bytes long on 32 bit version and 25 bytes long on 64 bit. - const size_t sentinel_block_length = is64bits ? (sizeof(uint64_t)* 3 + 1) : (sizeof(uint32_t)* 3 + 1); - - if (Offset(input, cursor) < end_offset) { - if (end_offset - Offset(input, cursor) < sentinel_block_length) { - TokenizeError("insufficient padding bytes at block end",input, cursor); - } - - output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_OPEN_BRACKET, Offset(input, cursor) )); - - // XXX this is vulnerable to stack overflowing .. - while(Offset(input, cursor) < end_offset - sentinel_block_length) { - ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits); - } - output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor) )); - - for (unsigned int i = 0; i < sentinel_block_length; ++i) { - if(cursor[i] != '\0') { - TokenizeError("failed to read nested block sentinel, expected all bytes to be 0",input, cursor); - } - } - cursor += sentinel_block_length; - } - - if (Offset(input, cursor) != end_offset) { - TokenizeError("scope length not reached, something is wrong",input, cursor); - } - - return true; -} - -} // anonymous namespace - -// ------------------------------------------------------------------------------------------------ -// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent -void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int length) -{ - ai_assert(input); - - if(length < 0x1b) { - TokenizeError("file is too short",0); - } - - //uint32_t offset = 0x15; -/* const char* cursor = input + 0x15; - - const uint32_t flags = ReadWord(input, cursor, input + length); - - const uint8_t padding_0 = ReadByte(input, cursor, input + length); // unused - const uint8_t padding_1 = ReadByte(input, cursor, input + length); // unused*/ - - if (strncmp(input,"Kaydara FBX Binary",18)) { - TokenizeError("magic bytes not found",0); - } - - const char* cursor = input + 18; - /*Result ignored*/ ReadByte(input, cursor, input + length); - /*Result ignored*/ ReadByte(input, cursor, input + length); - /*Result ignored*/ ReadByte(input, cursor, input + length); - /*Result ignored*/ ReadByte(input, cursor, input + length); - /*Result ignored*/ ReadByte(input, cursor, input + length); - const uint32_t version = ReadWord(input, cursor, input + length); - const bool is64bits = version >= 7500; - const char *end = input + length; - while (cursor < end ) { - if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) { - break; - } - } -} - -} // !FBX -} // !Assimp - -#endif diff --git a/thirdparty/assimp/code/FBXCommon.h b/thirdparty/assimp/code/FBXCommon.h deleted file mode 100644 index fcb20a5cad..0000000000 --- a/thirdparty/assimp/code/FBXCommon.h +++ /dev/null @@ -1,86 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXCommon.h -* Some useful constants and enums for dealing with FBX files. -*/ -#ifndef AI_FBXCOMMON_H_INC -#define AI_FBXCOMMON_H_INC - -#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER - - -namespace FBX -{ - const std::string NULL_RECORD = { // 13 null bytes - '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0' - }; // who knows why - const std::string SEPARATOR = {'\x00', '\x01'}; // for use inside strings - const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import - const int64_t SECOND = 46186158000; // FBX's kTime unit - - // rotation order. We'll probably use EulerXYZ for everything - enum RotOrder { - RotOrder_EulerXYZ = 0, - RotOrder_EulerXZY, - RotOrder_EulerYZX, - RotOrder_EulerYXZ, - RotOrder_EulerZXY, - RotOrder_EulerZYX, - - RotOrder_SphericXYZ, - - RotOrder_MAX // end-of-enum sentinel - }; - - // transformation inheritance method. Most of the time RSrs - enum TransformInheritance { - TransformInheritance_RrSs = 0, - TransformInheritance_RSrs, - TransformInheritance_Rrs, - - TransformInheritance_MAX // end-of-enum sentinel - }; -} - -#endif // ASSIMP_BUILD_NO_FBX_EXPORTER - -#endif // AI_FBXCOMMON_H_INC diff --git a/thirdparty/assimp/code/FBXCompileConfig.h b/thirdparty/assimp/code/FBXCompileConfig.h deleted file mode 100644 index 3a3841fa5b..0000000000 --- a/thirdparty/assimp/code/FBXCompileConfig.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXCompileConfig.h - * @brief FBX importer compile-time switches - */ -#ifndef INCLUDED_AI_FBX_COMPILECONFIG_H -#define INCLUDED_AI_FBX_COMPILECONFIG_H - -#include - -// -#if _MSC_VER > 1500 || (defined __GNUC___) -# define ASSIMP_FBX_USE_UNORDERED_MULTIMAP -# else -# define fbx_unordered_map map -# define fbx_unordered_multimap multimap -#endif - -#ifdef ASSIMP_FBX_USE_UNORDERED_MULTIMAP -# include -# if _MSC_VER > 1600 -# define fbx_unordered_map unordered_map -# define fbx_unordered_multimap unordered_multimap -# else -# define fbx_unordered_map tr1::unordered_map -# define fbx_unordered_multimap tr1::unordered_multimap -# endif -#endif - -#endif // INCLUDED_AI_FBX_COMPILECONFIG_H diff --git a/thirdparty/assimp/code/FBXConverter.cpp b/thirdparty/assimp/code/FBXConverter.cpp deleted file mode 100644 index 09ae06a64f..0000000000 --- a/thirdparty/assimp/code/FBXConverter.cpp +++ /dev/null @@ -1,3536 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXConverter.cpp - * @brief Implementation of the FBX DOM -> aiScene converter - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -#include "FBXConverter.h" -#include "FBXParser.h" -#include "FBXMeshGeometry.h" -#include "FBXDocument.h" -#include "FBXUtil.h" -#include "FBXProperties.h" -#include "FBXImporter.h" - -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include - -namespace Assimp { - namespace FBX { - - using namespace Util; - -#define MAGIC_NODE_TAG "_$AssimpFbx$" - -#define CONVERT_FBX_TIME(time) static_cast(time) / 46186158000L - - FBXConverter::FBXConverter(aiScene* out, const Document& doc) - : defaultMaterialIndex() - , lights() - , cameras() - , textures() - , materials_converted() - , textures_converted() - , meshes_converted() - , node_anim_chain_bits() - , mNodeNameInstances() - , mNodeNames() - , anim_fps() - , out(out) - , doc(doc) { - // animations need to be converted first since this will - // populate the node_anim_chain_bits map, which is needed - // to determine which nodes need to be generated. - ConvertAnimations(); - ConvertRootNode(); - - if (doc.Settings().readAllMaterials) { - // unfortunately this means we have to evaluate all objects - for (const ObjectMap::value_type& v : doc.Objects()) { - - const Object* ob = v.second->Get(); - if (!ob) { - continue; - } - - const Material* mat = dynamic_cast(ob); - if (mat) { - - if (materials_converted.find(mat) == materials_converted.end()) { - ConvertMaterial(*mat, 0); - } - } - } - } - - ConvertGlobalSettings(); - TransferDataToScene(); - - // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE - // to make sure the scene passes assimp's validation. FBX files - // need not contain geometry (i.e. camera animations, raw armatures). - if (out->mNumMeshes == 0) { - out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; - } - } - - - FBXConverter::~FBXConverter() { - std::for_each(meshes.begin(), meshes.end(), Util::delete_fun()); - std::for_each(materials.begin(), materials.end(), Util::delete_fun()); - std::for_each(animations.begin(), animations.end(), Util::delete_fun()); - std::for_each(lights.begin(), lights.end(), Util::delete_fun()); - std::for_each(cameras.begin(), cameras.end(), Util::delete_fun()); - std::for_each(textures.begin(), textures.end(), Util::delete_fun()); - } - - void FBXConverter::ConvertRootNode() { - out->mRootNode = new aiNode(); - out->mRootNode->mName.Set("RootNode"); - - // root has ID 0 - ConvertNodes(0L, *out->mRootNode); - } - - void FBXConverter::ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform) { - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(id, "Model"); - - std::vector nodes; - nodes.reserve(conns.size()); - - std::vector nodes_chain; - std::vector post_nodes_chain; - - try { - for (const Connection* con : conns) { - - // ignore object-property links - if (con->PropertyName().length()) { - continue; - } - - const Object* const object = con->SourceObject(); - if (nullptr == object) { - FBXImporter::LogWarn("failed to convert source object for Model link"); - continue; - } - - const Model* const model = dynamic_cast(object); - - if (nullptr != model) { - nodes_chain.clear(); - post_nodes_chain.clear(); - - aiMatrix4x4 new_abs_transform = parent_transform; - - // even though there is only a single input node, the design of - // assimp (or rather: the complicated transformation chain that - // is employed by fbx) means that we may need multiple aiNode's - // to represent a fbx node's transformation. - GenerateTransformationNodeChain(*model, nodes_chain, post_nodes_chain); - - ai_assert(nodes_chain.size()); - - std::string original_name = FixNodeName(model->Name()); - - // check if any of the nodes in the chain has the name the fbx node - // is supposed to have. If there is none, add another node to - // preserve the name - people might have scripts etc. that rely - // on specific node names. - aiNode* name_carrier = NULL; - for (aiNode* prenode : nodes_chain) { - if (!strcmp(prenode->mName.C_Str(), original_name.c_str())) { - name_carrier = prenode; - break; - } - } - - if (!name_carrier) { - std::string old_original_name = original_name; - GetUniqueName(old_original_name, original_name); - nodes_chain.push_back(new aiNode(original_name)); - } - else { - original_name = nodes_chain.back()->mName.C_Str(); - } - - //setup metadata on newest node - SetupNodeMetadata(*model, *nodes_chain.back()); - - // link all nodes in a row - aiNode* last_parent = &parent; - for (aiNode* prenode : nodes_chain) { - ai_assert(prenode); - - if (last_parent != &parent) { - last_parent->mNumChildren = 1; - last_parent->mChildren = new aiNode*[1]; - last_parent->mChildren[0] = prenode; - } - - prenode->mParent = last_parent; - last_parent = prenode; - - new_abs_transform *= prenode->mTransformation; - } - - // attach geometry - ConvertModel(*model, *nodes_chain.back(), new_abs_transform); - - // check if there will be any child nodes - const std::vector& child_conns - = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model"); - - // if so, link the geometric transform inverse nodes - // before we attach any child nodes - if (child_conns.size()) { - for (aiNode* postnode : post_nodes_chain) { - ai_assert(postnode); - - if (last_parent != &parent) { - last_parent->mNumChildren = 1; - last_parent->mChildren = new aiNode*[1]; - last_parent->mChildren[0] = postnode; - } - - postnode->mParent = last_parent; - last_parent = postnode; - - new_abs_transform *= postnode->mTransformation; - } - } - else { - // free the nodes we allocated as we don't need them - Util::delete_fun deleter; - std::for_each( - post_nodes_chain.begin(), - post_nodes_chain.end(), - deleter - ); - } - - // attach sub-nodes (if any) - ConvertNodes(model->ID(), *last_parent, new_abs_transform); - - if (doc.Settings().readLights) { - ConvertLights(*model, original_name); - } - - if (doc.Settings().readCameras) { - ConvertCameras(*model, original_name); - } - - nodes.push_back(nodes_chain.front()); - nodes_chain.clear(); - } - } - - if (nodes.size()) { - parent.mChildren = new aiNode*[nodes.size()](); - parent.mNumChildren = static_cast(nodes.size()); - - std::swap_ranges(nodes.begin(), nodes.end(), parent.mChildren); - } - } - catch (std::exception&) { - Util::delete_fun deleter; - std::for_each(nodes.begin(), nodes.end(), deleter); - std::for_each(nodes_chain.begin(), nodes_chain.end(), deleter); - std::for_each(post_nodes_chain.begin(), post_nodes_chain.end(), deleter); - } - } - - - void FBXConverter::ConvertLights(const Model& model, const std::string &orig_name) { - const std::vector& node_attrs = model.GetAttributes(); - for (const NodeAttribute* attr : node_attrs) { - const Light* const light = dynamic_cast(attr); - if (light) { - ConvertLight(*light, orig_name); - } - } - } - - void FBXConverter::ConvertCameras(const Model& model, const std::string &orig_name) { - const std::vector& node_attrs = model.GetAttributes(); - for (const NodeAttribute* attr : node_attrs) { - const Camera* const cam = dynamic_cast(attr); - if (cam) { - ConvertCamera(*cam, orig_name); - } - } - } - - void FBXConverter::ConvertLight(const Light& light, const std::string &orig_name) { - lights.push_back(new aiLight()); - aiLight* const out_light = lights.back(); - - out_light->mName.Set(orig_name); - - const float intensity = light.Intensity() / 100.0f; - const aiVector3D& col = light.Color(); - - out_light->mColorDiffuse = aiColor3D(col.x, col.y, col.z); - out_light->mColorDiffuse.r *= intensity; - out_light->mColorDiffuse.g *= intensity; - out_light->mColorDiffuse.b *= intensity; - - out_light->mColorSpecular = out_light->mColorDiffuse; - - //lights are defined along negative y direction - out_light->mPosition = aiVector3D(0.0f); - out_light->mDirection = aiVector3D(0.0f, -1.0f, 0.0f); - out_light->mUp = aiVector3D(0.0f, 0.0f, -1.0f); - - switch (light.LightType()) - { - case Light::Type_Point: - out_light->mType = aiLightSource_POINT; - break; - - case Light::Type_Directional: - out_light->mType = aiLightSource_DIRECTIONAL; - break; - - case Light::Type_Spot: - out_light->mType = aiLightSource_SPOT; - out_light->mAngleOuterCone = AI_DEG_TO_RAD(light.OuterAngle()); - out_light->mAngleInnerCone = AI_DEG_TO_RAD(light.InnerAngle()); - break; - - case Light::Type_Area: - FBXImporter::LogWarn("cannot represent area light, set to UNDEFINED"); - out_light->mType = aiLightSource_UNDEFINED; - break; - - case Light::Type_Volume: - FBXImporter::LogWarn("cannot represent volume light, set to UNDEFINED"); - out_light->mType = aiLightSource_UNDEFINED; - break; - default: - ai_assert(false); - } - - float decay = light.DecayStart(); - switch (light.DecayType()) - { - case Light::Decay_None: - out_light->mAttenuationConstant = decay; - out_light->mAttenuationLinear = 0.0f; - out_light->mAttenuationQuadratic = 0.0f; - break; - case Light::Decay_Linear: - out_light->mAttenuationConstant = 0.0f; - out_light->mAttenuationLinear = 2.0f / decay; - out_light->mAttenuationQuadratic = 0.0f; - break; - case Light::Decay_Quadratic: - out_light->mAttenuationConstant = 0.0f; - out_light->mAttenuationLinear = 0.0f; - out_light->mAttenuationQuadratic = 2.0f / (decay * decay); - break; - case Light::Decay_Cubic: - FBXImporter::LogWarn("cannot represent cubic attenuation, set to Quadratic"); - out_light->mAttenuationQuadratic = 1.0f; - break; - default: - ai_assert(false); - } - } - - void FBXConverter::ConvertCamera(const Camera& cam, const std::string &orig_name) - { - cameras.push_back(new aiCamera()); - aiCamera* const out_camera = cameras.back(); - - out_camera->mName.Set(orig_name); - - out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight(); - - //cameras are defined along positive x direction - /*out_camera->mPosition = cam.Position(); - out_camera->mLookAt = (cam.InterestPosition() - out_camera->mPosition).Normalize(); - out_camera->mUp = cam.UpVector();*/ - - out_camera->mPosition = aiVector3D(0.0f); - out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f); - out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f); - - out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView()); - - out_camera->mClipPlaneNear = cam.NearPlane(); - out_camera->mClipPlaneFar = cam.FarPlane(); - - out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView()); - out_camera->mClipPlaneNear = cam.NearPlane(); - out_camera->mClipPlaneFar = cam.FarPlane(); - } - - void FBXConverter::GetUniqueName(const std::string &name, std::string &uniqueName) - { - uniqueName = name; - int i = 0; - auto it = mNodeNameInstances.find(name); // duplicate node name instance count - if (it != mNodeNameInstances.end()) - { - i = it->second; - while (mNodeNames.find(uniqueName) != mNodeNames.end()) - { - i++; - std::stringstream ext; - ext << name << std::setfill('0') << std::setw(3) << i; - uniqueName = ext.str(); - } - } - mNodeNameInstances[name] = i; - mNodeNames.insert(uniqueName); - } - - const char* FBXConverter::NameTransformationComp(TransformationComp comp) { - switch (comp) { - case TransformationComp_Translation: - return "Translation"; - case TransformationComp_RotationOffset: - return "RotationOffset"; - case TransformationComp_RotationPivot: - return "RotationPivot"; - case TransformationComp_PreRotation: - return "PreRotation"; - case TransformationComp_Rotation: - return "Rotation"; - case TransformationComp_PostRotation: - return "PostRotation"; - case TransformationComp_RotationPivotInverse: - return "RotationPivotInverse"; - case TransformationComp_ScalingOffset: - return "ScalingOffset"; - case TransformationComp_ScalingPivot: - return "ScalingPivot"; - case TransformationComp_Scaling: - return "Scaling"; - case TransformationComp_ScalingPivotInverse: - return "ScalingPivotInverse"; - case TransformationComp_GeometricScaling: - return "GeometricScaling"; - case TransformationComp_GeometricRotation: - return "GeometricRotation"; - case TransformationComp_GeometricTranslation: - return "GeometricTranslation"; - case TransformationComp_GeometricScalingInverse: - return "GeometricScalingInverse"; - case TransformationComp_GeometricRotationInverse: - return "GeometricRotationInverse"; - case TransformationComp_GeometricTranslationInverse: - return "GeometricTranslationInverse"; - case TransformationComp_MAXIMUM: // this is to silence compiler warnings - default: - break; - } - - ai_assert(false); - - return nullptr; - } - - const char* FBXConverter::NameTransformationCompProperty(TransformationComp comp) { - switch (comp) { - case TransformationComp_Translation: - return "Lcl Translation"; - case TransformationComp_RotationOffset: - return "RotationOffset"; - case TransformationComp_RotationPivot: - return "RotationPivot"; - case TransformationComp_PreRotation: - return "PreRotation"; - case TransformationComp_Rotation: - return "Lcl Rotation"; - case TransformationComp_PostRotation: - return "PostRotation"; - case TransformationComp_RotationPivotInverse: - return "RotationPivotInverse"; - case TransformationComp_ScalingOffset: - return "ScalingOffset"; - case TransformationComp_ScalingPivot: - return "ScalingPivot"; - case TransformationComp_Scaling: - return "Lcl Scaling"; - case TransformationComp_ScalingPivotInverse: - return "ScalingPivotInverse"; - case TransformationComp_GeometricScaling: - return "GeometricScaling"; - case TransformationComp_GeometricRotation: - return "GeometricRotation"; - case TransformationComp_GeometricTranslation: - return "GeometricTranslation"; - case TransformationComp_GeometricScalingInverse: - return "GeometricScalingInverse"; - case TransformationComp_GeometricRotationInverse: - return "GeometricRotationInverse"; - case TransformationComp_GeometricTranslationInverse: - return "GeometricTranslationInverse"; - case TransformationComp_MAXIMUM: // this is to silence compiler warnings - break; - } - - ai_assert(false); - - return nullptr; - } - - aiVector3D FBXConverter::TransformationCompDefaultValue(TransformationComp comp) - { - // XXX a neat way to solve the never-ending special cases for scaling - // would be to do everything in log space! - return comp == TransformationComp_Scaling ? aiVector3D(1.f, 1.f, 1.f) : aiVector3D(); - } - - void FBXConverter::GetRotationMatrix(Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out) - { - if (mode == Model::RotOrder_SphericXYZ) { - FBXImporter::LogError("Unsupported RotationMode: SphericXYZ"); - out = aiMatrix4x4(); - return; - } - - const float angle_epsilon = 1e-6f; - - out = aiMatrix4x4(); - - bool is_id[3] = { true, true, true }; - - aiMatrix4x4 temp[3]; - if (std::fabs(rotation.z) > angle_epsilon) { - aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(rotation.z), temp[2]); - is_id[2] = false; - } - if (std::fabs(rotation.y) > angle_epsilon) { - aiMatrix4x4::RotationY(AI_DEG_TO_RAD(rotation.y), temp[1]); - is_id[1] = false; - } - if (std::fabs(rotation.x) > angle_epsilon) { - aiMatrix4x4::RotationX(AI_DEG_TO_RAD(rotation.x), temp[0]); - is_id[0] = false; - } - - int order[3] = { -1, -1, -1 }; - - // note: rotation order is inverted since we're left multiplying as is usual in assimp - switch (mode) - { - case Model::RotOrder_EulerXYZ: - order[0] = 2; - order[1] = 1; - order[2] = 0; - break; - - case Model::RotOrder_EulerXZY: - order[0] = 1; - order[1] = 2; - order[2] = 0; - break; - - case Model::RotOrder_EulerYZX: - order[0] = 0; - order[1] = 2; - order[2] = 1; - break; - - case Model::RotOrder_EulerYXZ: - order[0] = 2; - order[1] = 0; - order[2] = 1; - break; - - case Model::RotOrder_EulerZXY: - order[0] = 1; - order[1] = 0; - order[2] = 2; - break; - - case Model::RotOrder_EulerZYX: - order[0] = 0; - order[1] = 1; - order[2] = 2; - break; - - default: - ai_assert(false); - break; - } - - ai_assert(order[0] >= 0); - ai_assert(order[0] <= 2); - ai_assert(order[1] >= 0); - ai_assert(order[1] <= 2); - ai_assert(order[2] >= 0); - ai_assert(order[2] <= 2); - - if (!is_id[order[0]]) { - out = temp[order[0]]; - } - - if (!is_id[order[1]]) { - out = out * temp[order[1]]; - } - - if (!is_id[order[2]]) { - out = out * temp[order[2]]; - } - } - - bool FBXConverter::NeedsComplexTransformationChain(const Model& model) - { - const PropertyTable& props = model.Props(); - bool ok; - - const float zero_epsilon = 1e-6f; - const aiVector3D all_ones(1.0f, 1.0f, 1.0f); - for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) { - const TransformationComp comp = static_cast(i); - - if (comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation) { - continue; - } - - bool scale_compare = (comp == TransformationComp_GeometricScaling || comp == TransformationComp_Scaling); - - const aiVector3D& v = PropertyGet(props, NameTransformationCompProperty(comp), ok); - if (ok && scale_compare) { - if ((v - all_ones).SquareLength() > zero_epsilon) { - return true; - } - } - else if (ok) { - if (v.SquareLength() > zero_epsilon) { - return true; - } - } - } - - return false; - } - - std::string FBXConverter::NameTransformationChainNode(const std::string& name, TransformationComp comp) - { - return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp); - } - - void FBXConverter::GenerateTransformationNodeChain(const Model& model, std::vector& output_nodes, - std::vector& post_output_nodes) { - const PropertyTable& props = model.Props(); - const Model::RotOrder rot = model.RotationOrder(); - - bool ok; - - aiMatrix4x4 chain[TransformationComp_MAXIMUM]; - std::fill_n(chain, static_cast(TransformationComp_MAXIMUM), aiMatrix4x4()); - - // generate transformation matrices for all the different transformation components - const float zero_epsilon = 1e-6f; - const aiVector3D all_ones(1.0f, 1.0f, 1.0f); - bool is_complex = false; - - const aiVector3D& PreRotation = PropertyGet(props, "PreRotation", ok); - if (ok && PreRotation.SquareLength() > zero_epsilon) { - is_complex = true; - - GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PreRotation, chain[TransformationComp_PreRotation]); - } - - const aiVector3D& PostRotation = PropertyGet(props, "PostRotation", ok); - if (ok && PostRotation.SquareLength() > zero_epsilon) { - is_complex = true; - - GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PostRotation, chain[TransformationComp_PostRotation]); - } - - const aiVector3D& RotationPivot = PropertyGet(props, "RotationPivot", ok); - if (ok && RotationPivot.SquareLength() > zero_epsilon) { - is_complex = true; - - aiMatrix4x4::Translation(RotationPivot, chain[TransformationComp_RotationPivot]); - aiMatrix4x4::Translation(-RotationPivot, chain[TransformationComp_RotationPivotInverse]); - } - - const aiVector3D& RotationOffset = PropertyGet(props, "RotationOffset", ok); - if (ok && RotationOffset.SquareLength() > zero_epsilon) { - is_complex = true; - - aiMatrix4x4::Translation(RotationOffset, chain[TransformationComp_RotationOffset]); - } - - const aiVector3D& ScalingOffset = PropertyGet(props, "ScalingOffset", ok); - if (ok && ScalingOffset.SquareLength() > zero_epsilon) { - is_complex = true; - - aiMatrix4x4::Translation(ScalingOffset, chain[TransformationComp_ScalingOffset]); - } - - const aiVector3D& ScalingPivot = PropertyGet(props, "ScalingPivot", ok); - if (ok && ScalingPivot.SquareLength() > zero_epsilon) { - is_complex = true; - - aiMatrix4x4::Translation(ScalingPivot, chain[TransformationComp_ScalingPivot]); - aiMatrix4x4::Translation(-ScalingPivot, chain[TransformationComp_ScalingPivotInverse]); - } - - const aiVector3D& Translation = PropertyGet(props, "Lcl Translation", ok); - if (ok && Translation.SquareLength() > zero_epsilon) { - aiMatrix4x4::Translation(Translation, chain[TransformationComp_Translation]); - } - - const aiVector3D& Scaling = PropertyGet(props, "Lcl Scaling", ok); - if (ok && (Scaling - all_ones).SquareLength() > zero_epsilon) { - aiMatrix4x4::Scaling(Scaling, chain[TransformationComp_Scaling]); - } - - const aiVector3D& Rotation = PropertyGet(props, "Lcl Rotation", ok); - if (ok && Rotation.SquareLength() > zero_epsilon) { - GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]); - } - - const aiVector3D& GeometricScaling = PropertyGet(props, "GeometricScaling", ok); - if (ok && (GeometricScaling - all_ones).SquareLength() > zero_epsilon) { - is_complex = true; - aiMatrix4x4::Scaling(GeometricScaling, chain[TransformationComp_GeometricScaling]); - aiVector3D GeometricScalingInverse = GeometricScaling; - bool canscale = true; - for (unsigned int i = 0; i < 3; ++i) { - if (std::fabs(GeometricScalingInverse[i]) > zero_epsilon) { - GeometricScalingInverse[i] = 1.0f / GeometricScaling[i]; - } - else { - FBXImporter::LogError("cannot invert geometric scaling matrix with a 0.0 scale component"); - canscale = false; - break; - } - } - if (canscale) { - aiMatrix4x4::Scaling(GeometricScalingInverse, chain[TransformationComp_GeometricScalingInverse]); - } - } - - const aiVector3D& GeometricRotation = PropertyGet(props, "GeometricRotation", ok); - if (ok && GeometricRotation.SquareLength() > zero_epsilon) { - is_complex = true; - GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotation]); - GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotationInverse]); - chain[TransformationComp_GeometricRotationInverse].Inverse(); - } - - const aiVector3D& GeometricTranslation = PropertyGet(props, "GeometricTranslation", ok); - if (ok && GeometricTranslation.SquareLength() > zero_epsilon) { - is_complex = true; - aiMatrix4x4::Translation(GeometricTranslation, chain[TransformationComp_GeometricTranslation]); - aiMatrix4x4::Translation(-GeometricTranslation, chain[TransformationComp_GeometricTranslationInverse]); - } - - // is_complex needs to be consistent with NeedsComplexTransformationChain() - // or the interplay between this code and the animation converter would - // not be guaranteed. - ai_assert(NeedsComplexTransformationChain(model) == is_complex); - - std::string name = FixNodeName(model.Name()); - - // now, if we have more than just Translation, Scaling and Rotation, - // we need to generate a full node chain to accommodate for assimp's - // lack to express pivots and offsets. - if (is_complex && doc.Settings().preservePivots) { - FBXImporter::LogInfo("generating full transformation chain for node: " + name); - - // query the anim_chain_bits dictionary to find out which chain elements - // have associated node animation channels. These can not be dropped - // even if they have identity transform in bind pose. - NodeAnimBitMap::const_iterator it = node_anim_chain_bits.find(name); - const unsigned int anim_chain_bitmask = (it == node_anim_chain_bits.end() ? 0 : (*it).second); - - unsigned int bit = 0x1; - for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) { - const TransformationComp comp = static_cast(i); - - if (chain[i].IsIdentity() && (anim_chain_bitmask & bit) == 0) { - continue; - } - - if (comp == TransformationComp_PostRotation) { - chain[i] = chain[i].Inverse(); - } - - aiNode* nd = new aiNode(); - nd->mName.Set(NameTransformationChainNode(name, comp)); - nd->mTransformation = chain[i]; - - // geometric inverses go in a post-node chain - if (comp == TransformationComp_GeometricScalingInverse || - comp == TransformationComp_GeometricRotationInverse || - comp == TransformationComp_GeometricTranslationInverse - ) { - post_output_nodes.push_back(nd); - } - else { - output_nodes.push_back(nd); - } - } - - ai_assert(output_nodes.size()); - return; - } - - // else, we can just multiply the matrices together - aiNode* nd = new aiNode(); - output_nodes.push_back(nd); - std::string uniqueName; - GetUniqueName(name, uniqueName); - - nd->mName.Set(uniqueName); - - for (const auto &transform : chain) { - nd->mTransformation = nd->mTransformation * transform; - } - } - - void FBXConverter::SetupNodeMetadata(const Model& model, aiNode& nd) - { - const PropertyTable& props = model.Props(); - DirectPropertyMap unparsedProperties = props.GetUnparsedProperties(); - - // create metadata on node - const std::size_t numStaticMetaData = 2; - aiMetadata* data = aiMetadata::Alloc(static_cast(unparsedProperties.size() + numStaticMetaData)); - nd.mMetaData = data; - int index = 0; - - // find user defined properties (3ds Max) - data->Set(index++, "UserProperties", aiString(PropertyGet(props, "UDP3DSMAX", ""))); - // preserve the info that a node was marked as Null node in the original file. - data->Set(index++, "IsNull", model.IsNull() ? true : false); - - // add unparsed properties to the node's metadata - for (const DirectPropertyMap::value_type& prop : unparsedProperties) { - // Interpret the property as a concrete type - if (const TypedProperty* interpreted = prop.second->As >()) { - data->Set(index++, prop.first, interpreted->Value()); - } - else if (const TypedProperty* interpreted = prop.second->As >()) { - data->Set(index++, prop.first, interpreted->Value()); - } - else if (const TypedProperty* interpreted = prop.second->As >()) { - data->Set(index++, prop.first, interpreted->Value()); - } - else if (const TypedProperty* interpreted = prop.second->As >()) { - data->Set(index++, prop.first, interpreted->Value()); - } - else if (const TypedProperty* interpreted = prop.second->As >()) { - data->Set(index++, prop.first, aiString(interpreted->Value())); - } - else if (const TypedProperty* interpreted = prop.second->As >()) { - data->Set(index++, prop.first, interpreted->Value()); - } - else { - ai_assert(false); - } - } - } - - void FBXConverter::ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform) - { - const std::vector& geos = model.GetGeometry(); - - std::vector meshes; - meshes.reserve(geos.size()); - - for (const Geometry* geo : geos) { - - const MeshGeometry* const mesh = dynamic_cast(geo); - const LineGeometry* const line = dynamic_cast(geo); - if (mesh) { - const std::vector& indices = ConvertMesh(*mesh, model, node_global_transform, nd); - std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); - } - else if (line) { - const std::vector& indices = ConvertLine(*line, model, node_global_transform, nd); - std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); - } - else { - FBXImporter::LogWarn("ignoring unrecognized geometry: " + geo->Name()); - } - } - - if (meshes.size()) { - nd.mMeshes = new unsigned int[meshes.size()](); - nd.mNumMeshes = static_cast(meshes.size()); - - std::swap_ranges(meshes.begin(), meshes.end(), nd.mMeshes); - } - } - - std::vector FBXConverter::ConvertMesh(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd) - { - std::vector temp; - - MeshMap::const_iterator it = meshes_converted.find(&mesh); - if (it != meshes_converted.end()) { - std::copy((*it).second.begin(), (*it).second.end(), std::back_inserter(temp)); - return temp; - } - - const std::vector& vertices = mesh.GetVertices(); - const std::vector& faces = mesh.GetFaceIndexCounts(); - if (vertices.empty() || faces.empty()) { - FBXImporter::LogWarn("ignoring empty geometry: " + mesh.Name()); - return temp; - } - - // one material per mesh maps easily to aiMesh. Multiple material - // meshes need to be split. - const MatIndexArray& mindices = mesh.GetMaterialIndices(); - if (doc.Settings().readMaterials && !mindices.empty()) { - const MatIndexArray::value_type base = mindices[0]; - for (MatIndexArray::value_type index : mindices) { - if (index != base) { - return ConvertMeshMultiMaterial(mesh, model, node_global_transform, nd); - } - } - } - - // faster code-path, just copy the data - temp.push_back(ConvertMeshSingleMaterial(mesh, model, node_global_transform, nd)); - return temp; - } - - std::vector FBXConverter::ConvertLine(const LineGeometry& line, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd) - { - std::vector temp; - - const std::vector& vertices = line.GetVertices(); - const std::vector& indices = line.GetIndices(); - if (vertices.empty() || indices.empty()) { - FBXImporter::LogWarn("ignoring empty line: " + line.Name()); - return temp; - } - - aiMesh* const out_mesh = SetupEmptyMesh(line, nd); - out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; - - // copy vertices - out_mesh->mNumVertices = static_cast(vertices.size()); - out_mesh->mVertices = new aiVector3D[out_mesh->mNumVertices]; - std::copy(vertices.begin(), vertices.end(), out_mesh->mVertices); - - //Number of line segments (faces) is "Number of Points - Number of Endpoints" - //N.B.: Endpoints in FbxLine are denoted by negative indices. - //If such an Index is encountered, add 1 and multiply by -1 to get the real index. - unsigned int epcount = 0; - for (unsigned i = 0; i < indices.size(); i++) - { - if (indices[i] < 0) epcount++; - } - unsigned int pcount = static_cast( indices.size() ); - unsigned int scount = out_mesh->mNumFaces = pcount - epcount; - - aiFace* fac = out_mesh->mFaces = new aiFace[scount](); - for (unsigned int i = 0; i < pcount; ++i) { - if (indices[i] < 0) continue; - aiFace& f = *fac++; - f.mNumIndices = 2; //2 == aiPrimitiveType_LINE - f.mIndices = new unsigned int[2]; - f.mIndices[0] = indices[i]; - int segid = indices[(i + 1 == pcount ? 0 : i + 1)]; //If we have reached he last point, wrap around - f.mIndices[1] = (segid < 0 ? (segid + 1)*-1 : segid); //Convert EndPoint Index to normal Index - } - temp.push_back(static_cast(meshes.size() - 1)); - return temp; - } - - aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode& nd) - { - aiMesh* const out_mesh = new aiMesh(); - meshes.push_back(out_mesh); - meshes_converted[&mesh].push_back(static_cast(meshes.size() - 1)); - - // set name - std::string name = mesh.Name(); - if (name.substr(0, 10) == "Geometry::") { - name = name.substr(10); - } - - if (name.length()) { - out_mesh->mName.Set(name); - } - else - { - out_mesh->mName = nd.mName; - } - - return out_mesh; - } - - unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd) - { - const MatIndexArray& mindices = mesh.GetMaterialIndices(); - aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); - - const std::vector& vertices = mesh.GetVertices(); - const std::vector& faces = mesh.GetFaceIndexCounts(); - - // copy vertices - out_mesh->mNumVertices = static_cast(vertices.size()); - out_mesh->mVertices = new aiVector3D[vertices.size()]; - - std::copy(vertices.begin(), vertices.end(), out_mesh->mVertices); - - // generate dummy faces - out_mesh->mNumFaces = static_cast(faces.size()); - aiFace* fac = out_mesh->mFaces = new aiFace[faces.size()](); - - unsigned int cursor = 0; - for (unsigned int pcount : faces) { - aiFace& f = *fac++; - f.mNumIndices = pcount; - f.mIndices = new unsigned int[pcount]; - switch (pcount) - { - case 1: - out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; - break; - case 2: - out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; - break; - case 3: - out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; - break; - default: - out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; - break; - } - for (unsigned int i = 0; i < pcount; ++i) { - f.mIndices[i] = cursor++; - } - } - - // copy normals - const std::vector& normals = mesh.GetNormals(); - if (normals.size()) { - ai_assert(normals.size() == vertices.size()); - - out_mesh->mNormals = new aiVector3D[vertices.size()]; - std::copy(normals.begin(), normals.end(), out_mesh->mNormals); - } - - // copy tangents - assimp requires both tangents and bitangents (binormals) - // to be present, or neither of them. Compute binormals from normals - // and tangents if needed. - const std::vector& tangents = mesh.GetTangents(); - const std::vector* binormals = &mesh.GetBinormals(); - - if (tangents.size()) { - std::vector tempBinormals; - if (!binormals->size()) { - if (normals.size()) { - tempBinormals.resize(normals.size()); - for (unsigned int i = 0; i < tangents.size(); ++i) { - tempBinormals[i] = normals[i] ^ tangents[i]; - } - - binormals = &tempBinormals; - } - else { - binormals = NULL; - } - } - - if (binormals) { - ai_assert(tangents.size() == vertices.size()); - ai_assert(binormals->size() == vertices.size()); - - out_mesh->mTangents = new aiVector3D[vertices.size()]; - std::copy(tangents.begin(), tangents.end(), out_mesh->mTangents); - - out_mesh->mBitangents = new aiVector3D[vertices.size()]; - std::copy(binormals->begin(), binormals->end(), out_mesh->mBitangents); - } - } - - // copy texture coords - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { - const std::vector& uvs = mesh.GetTextureCoords(i); - if (uvs.empty()) { - break; - } - - aiVector3D* out_uv = out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()]; - for (const aiVector2D& v : uvs) { - *out_uv++ = aiVector3D(v.x, v.y, 0.0f); - } - - out_mesh->mNumUVComponents[i] = 2; - } - - // copy vertex colors - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) { - const std::vector& colors = mesh.GetVertexColors(i); - if (colors.empty()) { - break; - } - - out_mesh->mColors[i] = new aiColor4D[vertices.size()]; - std::copy(colors.begin(), colors.end(), out_mesh->mColors[i]); - } - - if (!doc.Settings().readMaterials || mindices.empty()) { - FBXImporter::LogError("no material assigned to mesh, setting default material"); - out_mesh->mMaterialIndex = GetDefaultMaterial(); - } - else { - ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]); - } - - if (doc.Settings().readWeights && mesh.DeformerSkin() != NULL) { - ConvertWeights(out_mesh, model, mesh, node_global_transform, NO_MATERIAL_SEPARATION); - } - - std::vector animMeshes; - for (const BlendShape* blendShape : mesh.GetBlendShapes()) { - for (const BlendShapeChannel* blendShapeChannel : blendShape->BlendShapeChannels()) { - const std::vector& shapeGeometries = blendShapeChannel->GetShapeGeometries(); - for (size_t i = 0; i < shapeGeometries.size(); i++) { - aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh); - const ShapeGeometry* shapeGeometry = shapeGeometries.at(i); - const std::vector& vertices = shapeGeometry->GetVertices(); - const std::vector& normals = shapeGeometry->GetNormals(); - const std::vector& indices = shapeGeometry->GetIndices(); - animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name())); - for (size_t j = 0; j < indices.size(); j++) { - unsigned int index = indices.at(j); - aiVector3D vertex = vertices.at(j); - aiVector3D normal = normals.at(j); - unsigned int count = 0; - const unsigned int* outIndices = mesh.ToOutputVertexIndex(index, count); - for (unsigned int k = 0; k < count; k++) { - unsigned int index = outIndices[k]; - animMesh->mVertices[index] += vertex; - if (animMesh->mNormals != nullptr) { - animMesh->mNormals[index] += normal; - animMesh->mNormals[index].NormalizeSafe(); - } - } - } - animMesh->mWeight = shapeGeometries.size() > 1 ? blendShapeChannel->DeformPercent() / 100.0f : 1.0f; - animMeshes.push_back(animMesh); - } - } - } - const size_t numAnimMeshes = animMeshes.size(); - if (numAnimMeshes > 0) { - out_mesh->mNumAnimMeshes = static_cast(numAnimMeshes); - out_mesh->mAnimMeshes = new aiAnimMesh*[numAnimMeshes]; - for (size_t i = 0; i < numAnimMeshes; i++) { - out_mesh->mAnimMeshes[i] = animMeshes.at(i); - } - } - return static_cast(meshes.size() - 1); - } - - std::vector FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd) - { - const MatIndexArray& mindices = mesh.GetMaterialIndices(); - ai_assert(mindices.size()); - - std::set had; - std::vector indices; - - for (MatIndexArray::value_type index : mindices) { - if (had.find(index) == had.end()) { - - indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, node_global_transform, nd)); - had.insert(index); - } - } - - return indices; - } - - unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, - MatIndexArray::value_type index, - const aiMatrix4x4& node_global_transform, - aiNode& nd) - { - aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); - - const MatIndexArray& mindices = mesh.GetMaterialIndices(); - const std::vector& vertices = mesh.GetVertices(); - const std::vector& faces = mesh.GetFaceIndexCounts(); - - const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != NULL; - - unsigned int count_faces = 0; - unsigned int count_vertices = 0; - - // count faces - std::vector::const_iterator itf = faces.begin(); - for (MatIndexArray::const_iterator it = mindices.begin(), - end = mindices.end(); it != end; ++it, ++itf) - { - if ((*it) != index) { - continue; - } - ++count_faces; - count_vertices += *itf; - } - - ai_assert(count_faces); - ai_assert(count_vertices); - - // mapping from output indices to DOM indexing, needed to resolve weights - std::vector reverseMapping; - - if (process_weights) { - reverseMapping.resize(count_vertices); - } - - // allocate output data arrays, but don't fill them yet - out_mesh->mNumVertices = count_vertices; - out_mesh->mVertices = new aiVector3D[count_vertices]; - - out_mesh->mNumFaces = count_faces; - aiFace* fac = out_mesh->mFaces = new aiFace[count_faces](); - - - // allocate normals - const std::vector& normals = mesh.GetNormals(); - if (normals.size()) { - ai_assert(normals.size() == vertices.size()); - out_mesh->mNormals = new aiVector3D[vertices.size()]; - } - - // allocate tangents, binormals. - const std::vector& tangents = mesh.GetTangents(); - const std::vector* binormals = &mesh.GetBinormals(); - std::vector tempBinormals; - - if (tangents.size()) { - if (!binormals->size()) { - if (normals.size()) { - // XXX this computes the binormals for the entire mesh, not only - // the part for which we need them. - tempBinormals.resize(normals.size()); - for (unsigned int i = 0; i < tangents.size(); ++i) { - tempBinormals[i] = normals[i] ^ tangents[i]; - } - - binormals = &tempBinormals; - } - else { - binormals = NULL; - } - } - - if (binormals) { - ai_assert(tangents.size() == vertices.size() && binormals->size() == vertices.size()); - - out_mesh->mTangents = new aiVector3D[vertices.size()]; - out_mesh->mBitangents = new aiVector3D[vertices.size()]; - } - } - - // allocate texture coords - unsigned int num_uvs = 0; - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i, ++num_uvs) { - const std::vector& uvs = mesh.GetTextureCoords(i); - if (uvs.empty()) { - break; - } - - out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()]; - out_mesh->mNumUVComponents[i] = 2; - } - - // allocate vertex colors - unsigned int num_vcs = 0; - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i, ++num_vcs) { - const std::vector& colors = mesh.GetVertexColors(i); - if (colors.empty()) { - break; - } - - out_mesh->mColors[i] = new aiColor4D[vertices.size()]; - } - - unsigned int cursor = 0, in_cursor = 0; - - itf = faces.begin(); - for (MatIndexArray::const_iterator it = mindices.begin(), end = mindices.end(); it != end; ++it, ++itf) - { - const unsigned int pcount = *itf; - if ((*it) != index) { - in_cursor += pcount; - continue; - } - - aiFace& f = *fac++; - - f.mNumIndices = pcount; - f.mIndices = new unsigned int[pcount]; - switch (pcount) - { - case 1: - out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; - break; - case 2: - out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; - break; - case 3: - out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; - break; - default: - out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; - break; - } - for (unsigned int i = 0; i < pcount; ++i, ++cursor, ++in_cursor) { - f.mIndices[i] = cursor; - - if (reverseMapping.size()) { - reverseMapping[cursor] = in_cursor; - } - - out_mesh->mVertices[cursor] = vertices[in_cursor]; - - if (out_mesh->mNormals) { - out_mesh->mNormals[cursor] = normals[in_cursor]; - } - - if (out_mesh->mTangents) { - out_mesh->mTangents[cursor] = tangents[in_cursor]; - out_mesh->mBitangents[cursor] = (*binormals)[in_cursor]; - } - - for (unsigned int j = 0; j < num_uvs; ++j) { - const std::vector& uvs = mesh.GetTextureCoords(j); - out_mesh->mTextureCoords[j][cursor] = aiVector3D(uvs[in_cursor].x, uvs[in_cursor].y, 0.0f); - } - - for (unsigned int j = 0; j < num_vcs; ++j) { - const std::vector& cols = mesh.GetVertexColors(j); - out_mesh->mColors[j][cursor] = cols[in_cursor]; - } - } - } - - ConvertMaterialForMesh(out_mesh, model, mesh, index); - - if (process_weights) { - ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping); - } - - return static_cast(meshes.size() - 1); - } - - void FBXConverter::ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, - const aiMatrix4x4& node_global_transform, - unsigned int materialIndex, - std::vector* outputVertStartIndices) - { - ai_assert(geo.DeformerSkin()); - - std::vector out_indices; - std::vector index_out_indices; - std::vector count_out_indices; - - const Skin& sk = *geo.DeformerSkin(); - - std::vector bones; - bones.reserve(sk.Clusters().size()); - - const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION; - ai_assert(no_mat_check || outputVertStartIndices); - - try { - - for (const Cluster* cluster : sk.Clusters()) { - ai_assert(cluster); - - const WeightIndexArray& indices = cluster->GetIndices(); - - if (indices.empty()) { - continue; - } - - const MatIndexArray& mats = geo.GetMaterialIndices(); - - bool ok = false; - - const size_t no_index_sentinel = std::numeric_limits::max(); - - count_out_indices.clear(); - index_out_indices.clear(); - out_indices.clear(); - - // now check if *any* of these weights is contained in the output mesh, - // taking notes so we don't need to do it twice. - for (WeightIndexArray::value_type index : indices) { - - unsigned int count = 0; - const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count); - // ToOutputVertexIndex only returns NULL if index is out of bounds - // which should never happen - ai_assert(out_idx != NULL); - - index_out_indices.push_back(no_index_sentinel); - count_out_indices.push_back(0); - - for (unsigned int i = 0; i < count; ++i) { - if (no_mat_check || static_cast(mats[geo.FaceForVertexIndex(out_idx[i])]) == materialIndex) { - - if (index_out_indices.back() == no_index_sentinel) { - index_out_indices.back() = out_indices.size(); - - } - - if (no_mat_check) { - out_indices.push_back(out_idx[i]); - } - else { - // this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn) - const std::vector::iterator it = std::lower_bound( - outputVertStartIndices->begin(), - outputVertStartIndices->end(), - out_idx[i] - ); - - out_indices.push_back(std::distance(outputVertStartIndices->begin(), it)); - } - - ++count_out_indices.back(); - ok = true; - } - } - } - - // if we found at least one, generate the output bones - // XXX this could be heavily simplified by collecting the bone - // data in a single step. - if (ok) { - ConvertCluster(bones, model, *cluster, out_indices, index_out_indices, - count_out_indices, node_global_transform); - } - } - } - catch (std::exception&) { - std::for_each(bones.begin(), bones.end(), Util::delete_fun()); - throw; - } - - if (bones.empty()) { - return; - } - - out->mBones = new aiBone*[bones.size()](); - out->mNumBones = static_cast(bones.size()); - - std::swap_ranges(bones.begin(), bones.end(), out->mBones); - } - - void FBXConverter::ConvertCluster(std::vector& bones, const Model& /*model*/, const Cluster& cl, - std::vector& out_indices, - std::vector& index_out_indices, - std::vector& count_out_indices, - const aiMatrix4x4& node_global_transform) - { - - aiBone* const bone = new aiBone(); - bones.push_back(bone); - - bone->mName = FixNodeName(cl.TargetNode()->Name()); - - bone->mOffsetMatrix = cl.TransformLink(); - bone->mOffsetMatrix.Inverse(); - - bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform; - - bone->mNumWeights = static_cast(out_indices.size()); - aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()]; - - const size_t no_index_sentinel = std::numeric_limits::max(); - const WeightArray& weights = cl.GetWeights(); - - const size_t c = index_out_indices.size(); - for (size_t i = 0; i < c; ++i) { - const size_t index_index = index_out_indices[i]; - - if (index_index == no_index_sentinel) { - continue; - } - - const size_t cc = count_out_indices[i]; - for (size_t j = 0; j < cc; ++j) { - aiVertexWeight& out_weight = *cursor++; - - out_weight.mVertexId = static_cast(out_indices[index_index + j]); - out_weight.mWeight = weights[i]; - } - } - } - - void FBXConverter::ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, - MatIndexArray::value_type materialIndex) - { - // locate source materials for this mesh - const std::vector& mats = model.GetMaterials(); - if (static_cast(materialIndex) >= mats.size() || materialIndex < 0) { - FBXImporter::LogError("material index out of bounds, setting default material"); - out->mMaterialIndex = GetDefaultMaterial(); - return; - } - - const Material* const mat = mats[materialIndex]; - MaterialMap::const_iterator it = materials_converted.find(mat); - if (it != materials_converted.end()) { - out->mMaterialIndex = (*it).second; - return; - } - - out->mMaterialIndex = ConvertMaterial(*mat, &geo); - materials_converted[mat] = out->mMaterialIndex; - } - - unsigned int FBXConverter::GetDefaultMaterial() - { - if (defaultMaterialIndex) { - return defaultMaterialIndex - 1; - } - - aiMaterial* out_mat = new aiMaterial(); - materials.push_back(out_mat); - - const aiColor3D diffuse = aiColor3D(0.8f, 0.8f, 0.8f); - out_mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - - aiString s; - s.Set(AI_DEFAULT_MATERIAL_NAME); - - out_mat->AddProperty(&s, AI_MATKEY_NAME); - - defaultMaterialIndex = static_cast(materials.size()); - return defaultMaterialIndex - 1; - } - - - unsigned int FBXConverter::ConvertMaterial(const Material& material, const MeshGeometry* const mesh) - { - const PropertyTable& props = material.Props(); - - // generate empty output material - aiMaterial* out_mat = new aiMaterial(); - materials_converted[&material] = static_cast(materials.size()); - - materials.push_back(out_mat); - - aiString str; - - // strip Material:: prefix - std::string name = material.Name(); - if (name.substr(0, 10) == "Material::") { - name = name.substr(10); - } - - // set material name if not empty - this could happen - // and there should be no key for it in this case. - if (name.length()) { - str.Set(name); - out_mat->AddProperty(&str, AI_MATKEY_NAME); - } - - // shading stuff and colors - SetShadingPropertiesCommon(out_mat, props); - SetShadingPropertiesRaw( out_mat, props, material.Textures(), mesh ); - - // texture assignments - SetTextureProperties(out_mat, material.Textures(), mesh); - SetTextureProperties(out_mat, material.LayeredTextures(), mesh); - - return static_cast(materials.size() - 1); - } - - unsigned int FBXConverter::ConvertVideo(const Video& video) - { - // generate empty output texture - aiTexture* out_tex = new aiTexture(); - textures.push_back(out_tex); - - // assuming the texture is compressed - out_tex->mWidth = static_cast(video.ContentLength()); // total data size - out_tex->mHeight = 0; // fixed to 0 - - // steal the data from the Video to avoid an additional copy - out_tex->pcData = reinterpret_cast(const_cast(video).RelinquishContent()); - - // try to extract a hint from the file extension - const std::string& filename = video.FileName().empty() ? video.RelativeFilename() : video.FileName(); - std::string ext = BaseImporter::GetExtension(filename); - - if (ext == "jpeg") { - ext = "jpg"; - } - - if (ext.size() <= 3) { - memcpy(out_tex->achFormatHint, ext.c_str(), ext.size()); - } - - out_tex->mFilename.Set(video.FileName().c_str()); - - return static_cast(textures.size() - 1); - } - - aiString FBXConverter::GetTexturePath(const Texture* tex) - { - aiString path; - path.Set(tex->RelativeFilename()); - - const Video* media = tex->Media(); - if (media != nullptr) { - bool textureReady = false; //tells if our texture is ready (if it was loaded or if it was found) - unsigned int index; - - VideoMap::const_iterator it = textures_converted.find(media); - if (it != textures_converted.end()) { - index = (*it).second; - textureReady = true; - } - else { - if (media->ContentLength() > 0) { - index = ConvertVideo(*media); - textures_converted[media] = index; - textureReady = true; - } - } - - // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture), if the texture is ready - if (doc.Settings().useLegacyEmbeddedTextureNaming) { - if (textureReady) { - // TODO: check the possibility of using the flag "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING" - // In FBX files textures are now stored internally by Assimp with their filename included - // Now Assimp can lookup through the loaded textures after all data is processed - // We need to load all textures before referencing them, as FBX file format order may reference a texture before loading it - // This may occur on this case too, it has to be studied - path.data[0] = '*'; - path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index); - } - } - } - - return path; - } - - void FBXConverter::TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, - const std::string& propName, - aiTextureType target, const MeshGeometry* const mesh) - { - TextureMap::const_iterator it = textures.find(propName); - if (it == textures.end()) { - return; - } - - const Texture* const tex = (*it).second; - if (tex != 0) - { - aiString path = GetTexturePath(tex); - out_mat->AddProperty(&path, _AI_MATKEY_TEXTURE_BASE, target, 0); - - aiUVTransform uvTrafo; - // XXX handle all kinds of UV transformations - uvTrafo.mScaling = tex->UVScaling(); - uvTrafo.mTranslation = tex->UVTranslation(); - out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, 0); - - const PropertyTable& props = tex->Props(); - - int uvIndex = 0; - - bool ok; - const std::string& uvSet = PropertyGet(props, "UVSet", ok); - if (ok) { - // "default" is the name which usually appears in the FbxFileTexture template - if (uvSet != "default" && uvSet.length()) { - // this is a bit awkward - we need to find a mesh that uses this - // material and scan its UV channels for the given UV name because - // assimp references UV channels by index, not by name. - - // XXX: the case that UV channels may appear in different orders - // in meshes is unhandled. A possible solution would be to sort - // the UV channels alphabetically, but this would have the side - // effect that the primary (first) UV channel would sometimes - // be moved, causing trouble when users read only the first - // UV channel and ignore UV channel assignments altogether. - - const unsigned int matIndex = static_cast(std::distance(materials.begin(), - std::find(materials.begin(), materials.end(), out_mat) - )); - - - uvIndex = -1; - if (!mesh) - { - for (const MeshMap::value_type& v : meshes_converted) { - const MeshGeometry* const meshGeom = dynamic_cast (v.first); - if (!meshGeom) { - continue; - } - - const MatIndexArray& mats = meshGeom->GetMaterialIndices(); - if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) { - continue; - } - - int index = -1; - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { - if (meshGeom->GetTextureCoords(i).empty()) { - break; - } - const std::string& name = meshGeom->GetTextureCoordChannelName(i); - if (name == uvSet) { - index = static_cast(i); - break; - } - } - if (index == -1) { - FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); - continue; - } - - if (uvIndex == -1) { - uvIndex = index; - } - else { - FBXImporter::LogWarn("the UV channel named " + uvSet + - " appears at different positions in meshes, results will be wrong"); - } - } - } - else - { - int index = -1; - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { - if (mesh->GetTextureCoords(i).empty()) { - break; - } - const std::string& name = mesh->GetTextureCoordChannelName(i); - if (name == uvSet) { - index = static_cast(i); - break; - } - } - if (index == -1) { - FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); - } - - if (uvIndex == -1) { - uvIndex = index; - } - } - - if (uvIndex == -1) { - FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel"); - uvIndex = 0; - } - } - } - - out_mat->AddProperty(&uvIndex, 1, _AI_MATKEY_UVWSRC_BASE, target, 0); - } - } - - void FBXConverter::TrySetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, - const std::string& propName, - aiTextureType target, const MeshGeometry* const mesh) { - LayeredTextureMap::const_iterator it = layeredTextures.find(propName); - if (it == layeredTextures.end()) { - return; - } - - int texCount = (*it).second->textureCount(); - - // Set the blend mode for layered textures - int blendmode = (*it).second->GetBlendMode(); - out_mat->AddProperty(&blendmode, 1, _AI_MATKEY_TEXOP_BASE, target, 0); - - for (int texIndex = 0; texIndex < texCount; texIndex++) { - - const Texture* const tex = (*it).second->getTexture(texIndex); - - aiString path = GetTexturePath(tex); - out_mat->AddProperty(&path, _AI_MATKEY_TEXTURE_BASE, target, texIndex); - - aiUVTransform uvTrafo; - // XXX handle all kinds of UV transformations - uvTrafo.mScaling = tex->UVScaling(); - uvTrafo.mTranslation = tex->UVTranslation(); - out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, texIndex); - - const PropertyTable& props = tex->Props(); - - int uvIndex = 0; - - bool ok; - const std::string& uvSet = PropertyGet(props, "UVSet", ok); - if (ok) { - // "default" is the name which usually appears in the FbxFileTexture template - if (uvSet != "default" && uvSet.length()) { - // this is a bit awkward - we need to find a mesh that uses this - // material and scan its UV channels for the given UV name because - // assimp references UV channels by index, not by name. - - // XXX: the case that UV channels may appear in different orders - // in meshes is unhandled. A possible solution would be to sort - // the UV channels alphabetically, but this would have the side - // effect that the primary (first) UV channel would sometimes - // be moved, causing trouble when users read only the first - // UV channel and ignore UV channel assignments altogether. - - const unsigned int matIndex = static_cast(std::distance(materials.begin(), - std::find(materials.begin(), materials.end(), out_mat) - )); - - uvIndex = -1; - if (!mesh) - { - for (const MeshMap::value_type& v : meshes_converted) { - const MeshGeometry* const meshGeom = dynamic_cast (v.first); - if (!meshGeom) { - continue; - } - - const MatIndexArray& mats = meshGeom->GetMaterialIndices(); - if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) { - continue; - } - - int index = -1; - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { - if (meshGeom->GetTextureCoords(i).empty()) { - break; - } - const std::string& name = meshGeom->GetTextureCoordChannelName(i); - if (name == uvSet) { - index = static_cast(i); - break; - } - } - if (index == -1) { - FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); - continue; - } - - if (uvIndex == -1) { - uvIndex = index; - } - else { - FBXImporter::LogWarn("the UV channel named " + uvSet + - " appears at different positions in meshes, results will be wrong"); - } - } - } - else - { - int index = -1; - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { - if (mesh->GetTextureCoords(i).empty()) { - break; - } - const std::string& name = mesh->GetTextureCoordChannelName(i); - if (name == uvSet) { - index = static_cast(i); - break; - } - } - if (index == -1) { - FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); - } - - if (uvIndex == -1) { - uvIndex = index; - } - } - - if (uvIndex == -1) { - FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel"); - uvIndex = 0; - } - } - } - - out_mat->AddProperty(&uvIndex, 1, _AI_MATKEY_UVWSRC_BASE, target, texIndex); - } - } - - void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, const MeshGeometry* const mesh) - { - TrySetTextureProperties(out_mat, textures, "DiffuseColor", aiTextureType_DIFFUSE, mesh); - TrySetTextureProperties(out_mat, textures, "AmbientColor", aiTextureType_AMBIENT, mesh); - TrySetTextureProperties(out_mat, textures, "EmissiveColor", aiTextureType_EMISSIVE, mesh); - TrySetTextureProperties(out_mat, textures, "SpecularColor", aiTextureType_SPECULAR, mesh); - TrySetTextureProperties(out_mat, textures, "SpecularFactor", aiTextureType_SPECULAR, mesh); - TrySetTextureProperties(out_mat, textures, "TransparentColor", aiTextureType_OPACITY, mesh); - TrySetTextureProperties(out_mat, textures, "ReflectionColor", aiTextureType_REFLECTION, mesh); - TrySetTextureProperties(out_mat, textures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh); - TrySetTextureProperties(out_mat, textures, "NormalMap", aiTextureType_NORMALS, mesh); - TrySetTextureProperties(out_mat, textures, "Bump", aiTextureType_HEIGHT, mesh); - TrySetTextureProperties(out_mat, textures, "ShininessExponent", aiTextureType_SHININESS, mesh); - TrySetTextureProperties( out_mat, textures, "TransparencyFactor", aiTextureType_OPACITY, mesh ); - TrySetTextureProperties( out_mat, textures, "EmissiveFactor", aiTextureType_EMISSIVE, mesh ); - //Maya counterparts - TrySetTextureProperties(out_mat, textures, "Maya|DiffuseTexture", aiTextureType_DIFFUSE, mesh); - TrySetTextureProperties(out_mat, textures, "Maya|NormalTexture", aiTextureType_NORMALS, mesh); - TrySetTextureProperties(out_mat, textures, "Maya|SpecularTexture", aiTextureType_SPECULAR, mesh); - TrySetTextureProperties(out_mat, textures, "Maya|FalloffTexture", aiTextureType_OPACITY, mesh); - TrySetTextureProperties(out_mat, textures, "Maya|ReflectionMapTexture", aiTextureType_REFLECTION, mesh); - } - - void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh) - { - TrySetTextureProperties(out_mat, layeredTextures, "DiffuseColor", aiTextureType_DIFFUSE, mesh); - TrySetTextureProperties(out_mat, layeredTextures, "AmbientColor", aiTextureType_AMBIENT, mesh); - TrySetTextureProperties(out_mat, layeredTextures, "EmissiveColor", aiTextureType_EMISSIVE, mesh); - TrySetTextureProperties(out_mat, layeredTextures, "SpecularColor", aiTextureType_SPECULAR, mesh); - TrySetTextureProperties(out_mat, layeredTextures, "SpecularFactor", aiTextureType_SPECULAR, mesh); - TrySetTextureProperties(out_mat, layeredTextures, "TransparentColor", aiTextureType_OPACITY, mesh); - TrySetTextureProperties(out_mat, layeredTextures, "ReflectionColor", aiTextureType_REFLECTION, mesh); - TrySetTextureProperties(out_mat, layeredTextures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh); - TrySetTextureProperties(out_mat, layeredTextures, "NormalMap", aiTextureType_NORMALS, mesh); - TrySetTextureProperties(out_mat, layeredTextures, "Bump", aiTextureType_HEIGHT, mesh); - TrySetTextureProperties(out_mat, layeredTextures, "ShininessExponent", aiTextureType_SHININESS, mesh); - TrySetTextureProperties( out_mat, layeredTextures, "EmissiveFactor", aiTextureType_EMISSIVE, mesh ); - TrySetTextureProperties( out_mat, layeredTextures, "TransparencyFactor", aiTextureType_OPACITY, mesh ); - } - - aiColor3D FBXConverter::GetColorPropertyFactored(const PropertyTable& props, const std::string& colorName, - const std::string& factorName, bool& result, bool useTemplate) - { - result = true; - - bool ok; - aiVector3D BaseColor = PropertyGet(props, colorName, ok, useTemplate); - if (!ok) { - result = false; - return aiColor3D(0.0f, 0.0f, 0.0f); - } - - // if no factor name, return the colour as is - if (factorName.empty()) { - return aiColor3D(BaseColor.x, BaseColor.y, BaseColor.z); - } - - // otherwise it should be multiplied by the factor, if found. - float factor = PropertyGet(props, factorName, ok, useTemplate); - if (ok) { - BaseColor *= factor; - } - return aiColor3D(BaseColor.x, BaseColor.y, BaseColor.z); - } - - aiColor3D FBXConverter::GetColorPropertyFromMaterial(const PropertyTable& props, const std::string& baseName, - bool& result) - { - return GetColorPropertyFactored(props, baseName + "Color", baseName + "Factor", result, true); - } - - aiColor3D FBXConverter::GetColorProperty(const PropertyTable& props, const std::string& colorName, - bool& result, bool useTemplate) - { - result = true; - bool ok; - const aiVector3D& ColorVec = PropertyGet(props, colorName, ok, useTemplate); - if (!ok) { - result = false; - return aiColor3D(0.0f, 0.0f, 0.0f); - } - return aiColor3D(ColorVec.x, ColorVec.y, ColorVec.z); - } - - void FBXConverter::SetShadingPropertiesCommon(aiMaterial* out_mat, const PropertyTable& props) - { - // Set shading properties. - // Modern FBX Files have two separate systems for defining these, - // with only the more comprehensive one described in the property template. - // Likely the other values are a legacy system, - // which is still always exported by the official FBX SDK. - // - // Blender's FBX import and export mostly ignore this legacy system, - // and as we only support recent versions of FBX anyway, we can do the same. - bool ok; - - const aiColor3D& Diffuse = GetColorPropertyFromMaterial(props, "Diffuse", ok); - if (ok) { - out_mat->AddProperty(&Diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - } - - const aiColor3D& Emissive = GetColorPropertyFromMaterial(props, "Emissive", ok); - if (ok) { - out_mat->AddProperty(&Emissive, 1, AI_MATKEY_COLOR_EMISSIVE); - } - - const aiColor3D& Ambient = GetColorPropertyFromMaterial(props, "Ambient", ok); - if (ok) { - out_mat->AddProperty(&Ambient, 1, AI_MATKEY_COLOR_AMBIENT); - } - - // we store specular factor as SHININESS_STRENGTH, so just get the color - const aiColor3D& Specular = GetColorProperty(props, "SpecularColor", ok, true); - if (ok) { - out_mat->AddProperty(&Specular, 1, AI_MATKEY_COLOR_SPECULAR); - } - - // and also try to get SHININESS_STRENGTH - const float SpecularFactor = PropertyGet(props, "SpecularFactor", ok, true); - if (ok) { - out_mat->AddProperty(&SpecularFactor, 1, AI_MATKEY_SHININESS_STRENGTH); - } - - // and the specular exponent - const float ShininessExponent = PropertyGet(props, "ShininessExponent", ok); - if (ok) { - out_mat->AddProperty(&ShininessExponent, 1, AI_MATKEY_SHININESS); - } - - // TransparentColor / TransparencyFactor... gee thanks FBX :rolleyes: - const aiColor3D& Transparent = GetColorPropertyFactored(props, "TransparentColor", "TransparencyFactor", ok); - float CalculatedOpacity = 1.0f; - if (ok) { - out_mat->AddProperty(&Transparent, 1, AI_MATKEY_COLOR_TRANSPARENT); - // as calculated by FBX SDK 2017: - CalculatedOpacity = 1.0f - ((Transparent.r + Transparent.g + Transparent.b) / 3.0f); - } - - // try to get the transparency factor - const float TransparencyFactor = PropertyGet(props, "TransparencyFactor", ok); - if (ok) { - out_mat->AddProperty(&TransparencyFactor, 1, AI_MATKEY_TRANSPARENCYFACTOR); - } - - // use of TransparencyFactor is inconsistent. - // Maya always stores it as 1.0, - // so we can't use it to set AI_MATKEY_OPACITY. - // Blender is more sensible and stores it as the alpha value. - // However both the FBX SDK and Blender always write an additional - // legacy "Opacity" field, so we can try to use that. - // - // If we can't find it, - // we can fall back to the value which the FBX SDK calculates - // from transparency colour (RGB) and factor (F) as - // 1.0 - F*((R+G+B)/3). - // - // There's no consistent way to interpret this opacity value, - // so it's up to clients to do the correct thing. - const float Opacity = PropertyGet(props, "Opacity", ok); - if (ok) { - out_mat->AddProperty(&Opacity, 1, AI_MATKEY_OPACITY); - } - else if (CalculatedOpacity != 1.0) { - out_mat->AddProperty(&CalculatedOpacity, 1, AI_MATKEY_OPACITY); - } - - // reflection color and factor are stored separately - const aiColor3D& Reflection = GetColorProperty(props, "ReflectionColor", ok, true); - if (ok) { - out_mat->AddProperty(&Reflection, 1, AI_MATKEY_COLOR_REFLECTIVE); - } - - float ReflectionFactor = PropertyGet(props, "ReflectionFactor", ok, true); - if (ok) { - out_mat->AddProperty(&ReflectionFactor, 1, AI_MATKEY_REFLECTIVITY); - } - - const float BumpFactor = PropertyGet(props, "BumpFactor", ok); - if (ok) { - out_mat->AddProperty(&BumpFactor, 1, AI_MATKEY_BUMPSCALING); - } - - const float DispFactor = PropertyGet(props, "DisplacementFactor", ok); - if (ok) { - out_mat->AddProperty(&DispFactor, 1, "$mat.displacementscaling", 0, 0); - } -} - - -void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTable& props, const TextureMap& textures, const MeshGeometry* const mesh) -{ - // Add all the unparsed properties with a "$raw." prefix - - const std::string prefix = "$raw."; - - for (const DirectPropertyMap::value_type& prop : props.GetUnparsedProperties()) { - - std::string name = prefix + prop.first; - - if (const TypedProperty* interpreted = prop.second->As >()) - { - out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); - } - else if (const TypedProperty* interpreted = prop.second->As >()) - { - out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); - } - else if (const TypedProperty* interpreted = prop.second->As >()) - { - out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); - } - else if (const TypedProperty* interpreted = prop.second->As >()) - { - out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); - } - else if (const TypedProperty* interpreted = prop.second->As >()) - { - out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); - } - else if (const TypedProperty* interpreted = prop.second->As >()) - { - int value = interpreted->Value() ? 1 : 0; - out_mat->AddProperty(&value, 1, name.c_str(), 0, 0); - } - else if (const TypedProperty* interpreted = prop.second->As >()) - { - const aiString value = aiString(interpreted->Value()); - out_mat->AddProperty(&value, name.c_str(), 0, 0); - } - } - - // Add the textures' properties - - for (TextureMap::const_iterator it = textures.begin(); it != textures.end(); it++) { - - std::string name = prefix + it->first; - - const Texture* const tex = (*it).second; - if (tex != nullptr) - { - aiString path; - path.Set(tex->RelativeFilename()); - - const Video* media = tex->Media(); - if (media != nullptr && media->ContentLength() > 0) { - unsigned int index; - - VideoMap::const_iterator it = textures_converted.find(media); - if (it != textures_converted.end()) { - index = (*it).second; - } - else { - index = ConvertVideo(*media); - textures_converted[media] = index; - } - - // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture) - path.data[0] = '*'; - path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index); - } - - out_mat->AddProperty(&path, (name + "|file").c_str(), aiTextureType_UNKNOWN, 0); - - aiUVTransform uvTrafo; - // XXX handle all kinds of UV transformations - uvTrafo.mScaling = tex->UVScaling(); - uvTrafo.mTranslation = tex->UVTranslation(); - out_mat->AddProperty(&uvTrafo, 1, (name + "|uvtrafo").c_str(), aiTextureType_UNKNOWN, 0); - - int uvIndex = 0; - - bool uvFound = false; - const std::string& uvSet = PropertyGet(tex->Props(), "UVSet", uvFound); - if (uvFound) { - // "default" is the name which usually appears in the FbxFileTexture template - if (uvSet != "default" && uvSet.length()) { - // this is a bit awkward - we need to find a mesh that uses this - // material and scan its UV channels for the given UV name because - // assimp references UV channels by index, not by name. - - // XXX: the case that UV channels may appear in different orders - // in meshes is unhandled. A possible solution would be to sort - // the UV channels alphabetically, but this would have the side - // effect that the primary (first) UV channel would sometimes - // be moved, causing trouble when users read only the first - // UV channel and ignore UV channel assignments altogether. - - std::vector::iterator materialIt = std::find(materials.begin(), materials.end(), out_mat); - const unsigned int matIndex = static_cast(std::distance(materials.begin(), materialIt)); - - uvIndex = -1; - if (!mesh) - { - for (const MeshMap::value_type& v : meshes_converted) { - const MeshGeometry* const meshGeom = dynamic_cast(v.first); - if (!meshGeom) { - continue; - } - - const MatIndexArray& mats = meshGeom->GetMaterialIndices(); - if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) { - continue; - } - - int index = -1; - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { - if (meshGeom->GetTextureCoords(i).empty()) { - break; - } - const std::string& name = meshGeom->GetTextureCoordChannelName(i); - if (name == uvSet) { - index = static_cast(i); - break; - } - } - if (index == -1) { - FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); - continue; - } - - if (uvIndex == -1) { - uvIndex = index; - } - else { - FBXImporter::LogWarn("the UV channel named " + uvSet + " appears at different positions in meshes, results will be wrong"); - } - } - } - else - { - int index = -1; - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { - if (mesh->GetTextureCoords(i).empty()) { - break; - } - const std::string& name = mesh->GetTextureCoordChannelName(i); - if (name == uvSet) { - index = static_cast(i); - break; - } - } - if (index == -1) { - FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); - } - - if (uvIndex == -1) { - uvIndex = index; - } - } - - if (uvIndex == -1) { - FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel"); - uvIndex = 0; - } - } - } - - out_mat->AddProperty(&uvIndex, 1, (name + "|uvwsrc").c_str(), aiTextureType_UNKNOWN, 0); - } - } - } - - - double FBXConverter::FrameRateToDouble(FileGlobalSettings::FrameRate fp, double customFPSVal) { - switch (fp) { - case FileGlobalSettings::FrameRate_DEFAULT: - return 1.0; - - case FileGlobalSettings::FrameRate_120: - return 120.0; - - case FileGlobalSettings::FrameRate_100: - return 100.0; - - case FileGlobalSettings::FrameRate_60: - return 60.0; - - case FileGlobalSettings::FrameRate_50: - return 50.0; - - case FileGlobalSettings::FrameRate_48: - return 48.0; - - case FileGlobalSettings::FrameRate_30: - case FileGlobalSettings::FrameRate_30_DROP: - return 30.0; - - case FileGlobalSettings::FrameRate_NTSC_DROP_FRAME: - case FileGlobalSettings::FrameRate_NTSC_FULL_FRAME: - return 29.9700262; - - case FileGlobalSettings::FrameRate_PAL: - return 25.0; - - case FileGlobalSettings::FrameRate_CINEMA: - return 24.0; - - case FileGlobalSettings::FrameRate_1000: - return 1000.0; - - case FileGlobalSettings::FrameRate_CINEMA_ND: - return 23.976; - - case FileGlobalSettings::FrameRate_CUSTOM: - return customFPSVal; - - case FileGlobalSettings::FrameRate_MAX: // this is to silence compiler warnings - break; - } - - ai_assert(false); - - return -1.0f; - } - - - void FBXConverter::ConvertAnimations() - { - // first of all determine framerate - const FileGlobalSettings::FrameRate fps = doc.GlobalSettings().TimeMode(); - const float custom = doc.GlobalSettings().CustomFrameRate(); - anim_fps = FrameRateToDouble(fps, custom); - - const std::vector& animations = doc.AnimationStacks(); - for (const AnimationStack* stack : animations) { - ConvertAnimationStack(*stack); - } - } - - std::string FBXConverter::FixNodeName(const std::string& name) { - // strip Model:: prefix, avoiding ambiguities (i.e. don't strip if - // this causes ambiguities, well possible between empty identifiers, - // such as "Model::" and ""). Make sure the behaviour is consistent - // across multiple calls to FixNodeName(). - if (name.substr(0, 7) == "Model::") { - std::string temp = name.substr(7); - return temp; - } - - return name; - } - - std::string FBXConverter::FixAnimMeshName(const std::string& name) { - if (name.length()) { - size_t indexOf = name.find_first_of("::"); - if (indexOf != std::string::npos && indexOf < name.size() - 2) { - return name.substr(indexOf + 2); - } - } - return name.length() ? name : "AnimMesh"; - } - - void FBXConverter::ConvertAnimationStack(const AnimationStack& st) - { - const AnimationLayerList& layers = st.Layers(); - if (layers.empty()) { - return; - } - - aiAnimation* const anim = new aiAnimation(); - animations.push_back(anim); - - // strip AnimationStack:: prefix - std::string name = st.Name(); - if (name.substr(0, 16) == "AnimationStack::") { - name = name.substr(16); - } - else if (name.substr(0, 11) == "AnimStack::") { - name = name.substr(11); - } - - anim->mName.Set(name); - - // need to find all nodes for which we need to generate node animations - - // it may happen that we need to merge multiple layers, though. - NodeMap node_map; - - // reverse mapping from curves to layers, much faster than querying - // the FBX DOM for it. - LayerMap layer_map; - - const char* prop_whitelist[] = { - "Lcl Scaling", - "Lcl Rotation", - "Lcl Translation", - "DeformPercent" - }; - - std::map morphAnimDatas; - - for (const AnimationLayer* layer : layers) { - ai_assert(layer); - const AnimationCurveNodeList& nodes = layer->Nodes(prop_whitelist, 4); - for (const AnimationCurveNode* node : nodes) { - ai_assert(node); - const Model* const model = dynamic_cast(node->Target()); - if (model) { - const std::string& name = FixNodeName(model->Name()); - node_map[name].push_back(node); - layer_map[node] = layer; - continue; - } - const BlendShapeChannel* const bsc = dynamic_cast(node->Target()); - if (bsc) { - ProcessMorphAnimDatas(&morphAnimDatas, bsc, node); - } - } - } - - // generate node animations - std::vector node_anims; - - double min_time = 1e10; - double max_time = -1e10; - - int64_t start_time = st.LocalStart(); - int64_t stop_time = st.LocalStop(); - bool has_local_startstop = start_time != 0 || stop_time != 0; - if (!has_local_startstop) { - // no time range given, so accept every keyframe and use the actual min/max time - // the numbers are INT64_MIN/MAX, the 20000 is for safety because GenerateNodeAnimations uses an epsilon of 10000 - start_time = -9223372036854775807ll + 20000; - stop_time = 9223372036854775807ll - 20000; - } - - try { - for (const NodeMap::value_type& kv : node_map) { - GenerateNodeAnimations(node_anims, - kv.first, - kv.second, - layer_map, - start_time, stop_time, - max_time, - min_time); - } - } - catch (std::exception&) { - std::for_each(node_anims.begin(), node_anims.end(), Util::delete_fun()); - throw; - } - - if (node_anims.size() || morphAnimDatas.size()) { - if (node_anims.size()) { - anim->mChannels = new aiNodeAnim*[node_anims.size()](); - anim->mNumChannels = static_cast(node_anims.size()); - std::swap_ranges(node_anims.begin(), node_anims.end(), anim->mChannels); - } - if (morphAnimDatas.size()) { - unsigned int numMorphMeshChannels = static_cast(morphAnimDatas.size()); - anim->mMorphMeshChannels = new aiMeshMorphAnim*[numMorphMeshChannels]; - anim->mNumMorphMeshChannels = numMorphMeshChannels; - unsigned int i = 0; - for (auto morphAnimIt : morphAnimDatas) { - morphAnimData* animData = morphAnimIt.second; - unsigned int numKeys = static_cast(animData->size()); - aiMeshMorphAnim* meshMorphAnim = new aiMeshMorphAnim(); - meshMorphAnim->mName.Set(morphAnimIt.first); - meshMorphAnim->mNumKeys = numKeys; - meshMorphAnim->mKeys = new aiMeshMorphKey[numKeys]; - unsigned int j = 0; - for (auto animIt : *animData) { - morphKeyData* keyData = animIt.second; - unsigned int numValuesAndWeights = static_cast(keyData->values.size()); - meshMorphAnim->mKeys[j].mNumValuesAndWeights = numValuesAndWeights; - meshMorphAnim->mKeys[j].mValues = new unsigned int[numValuesAndWeights]; - meshMorphAnim->mKeys[j].mWeights = new double[numValuesAndWeights]; - meshMorphAnim->mKeys[j].mTime = CONVERT_FBX_TIME(animIt.first) * anim_fps; - for (unsigned int k = 0; k < numValuesAndWeights; k++) { - meshMorphAnim->mKeys[j].mValues[k] = keyData->values.at(k); - meshMorphAnim->mKeys[j].mWeights[k] = keyData->weights.at(k); - } - j++; - } - anim->mMorphMeshChannels[i++] = meshMorphAnim; - } - } - } - else { - // empty animations would fail validation, so drop them - delete anim; - animations.pop_back(); - FBXImporter::LogInfo("ignoring empty AnimationStack (using IK?): " + name); - return; - } - - double start_time_fps = has_local_startstop ? (CONVERT_FBX_TIME(start_time) * anim_fps) : min_time; - double stop_time_fps = has_local_startstop ? (CONVERT_FBX_TIME(stop_time) * anim_fps) : max_time; - - // adjust relative timing for animation - for (unsigned int c = 0; c < anim->mNumChannels; c++) { - aiNodeAnim* channel = anim->mChannels[c]; - for (uint32_t i = 0; i < channel->mNumPositionKeys; i++) { - channel->mPositionKeys[i].mTime -= start_time_fps; - } - for (uint32_t i = 0; i < channel->mNumRotationKeys; i++) { - channel->mRotationKeys[i].mTime -= start_time_fps; - } - for (uint32_t i = 0; i < channel->mNumScalingKeys; i++) { - channel->mScalingKeys[i].mTime -= start_time_fps; - } - } - for (unsigned int c = 0; c < anim->mNumMorphMeshChannels; c++) { - aiMeshMorphAnim* channel = anim->mMorphMeshChannels[c]; - for (uint32_t i = 0; i < channel->mNumKeys; i++) { - channel->mKeys[i].mTime -= start_time_fps; - } - } - - // for some mysterious reason, mDuration is simply the maximum key -- the - // validator always assumes animations to start at zero. - anim->mDuration = stop_time_fps - start_time_fps; - anim->mTicksPerSecond = anim_fps; - } - - // ------------------------------------------------------------------------------------------------ - void FBXConverter::ProcessMorphAnimDatas(std::map* morphAnimDatas, const BlendShapeChannel* bsc, const AnimationCurveNode* node) { - std::vector bscConnections = doc.GetConnectionsBySourceSequenced(bsc->ID(), "Deformer"); - for (const Connection* bscConnection : bscConnections) { - auto bs = dynamic_cast(bscConnection->DestinationObject()); - if (bs) { - auto channelIt = std::find(bs->BlendShapeChannels().begin(), bs->BlendShapeChannels().end(), bsc); - if (channelIt != bs->BlendShapeChannels().end()) { - auto channelIndex = static_cast(std::distance(bs->BlendShapeChannels().begin(), channelIt)); - std::vector bsConnections = doc.GetConnectionsBySourceSequenced(bs->ID(), "Geometry"); - for (const Connection* bsConnection : bsConnections) { - auto geo = dynamic_cast(bsConnection->DestinationObject()); - if (geo) { - std::vector geoConnections = doc.GetConnectionsBySourceSequenced(geo->ID(), "Model"); - for (const Connection* geoConnection : geoConnections) { - auto model = dynamic_cast(geoConnection->DestinationObject()); - if (model) { - auto geoIt = std::find(model->GetGeometry().begin(), model->GetGeometry().end(), geo); - auto geoIndex = static_cast(std::distance(model->GetGeometry().begin(), geoIt)); - auto name = aiString(FixNodeName(model->Name() + "*")); - name.length = 1 + ASSIMP_itoa10(name.data + name.length, MAXLEN - 1, geoIndex); - morphAnimData* animData; - auto animIt = morphAnimDatas->find(name.C_Str()); - if (animIt == morphAnimDatas->end()) { - animData = new morphAnimData(); - morphAnimDatas->insert(std::make_pair(name.C_Str(), animData)); - } - else { - animData = animIt->second; - } - for (std::pair curvesIt : node->Curves()) { - if (curvesIt.first == "d|DeformPercent") { - const AnimationCurve* animationCurve = curvesIt.second; - const KeyTimeList& keys = animationCurve->GetKeys(); - const KeyValueList& values = animationCurve->GetValues(); - unsigned int k = 0; - for (auto key : keys) { - morphKeyData* keyData; - auto keyIt = animData->find(key); - if (keyIt == animData->end()) { - keyData = new morphKeyData(); - animData->insert(std::make_pair(key, keyData)); - } - else { - keyData = keyIt->second; - } - keyData->values.push_back(channelIndex); - keyData->weights.push_back(values.at(k) / 100.0f); - k++; - } - } - } - } - } - } - } - } - } - } - } - - // ------------------------------------------------------------------------------------------------ -#ifdef ASSIMP_BUILD_DEBUG - // ------------------------------------------------------------------------------------------------ - // sanity check whether the input is ok - static void validateAnimCurveNodes(const std::vector& curves, - bool strictMode) { - const Object* target(NULL); - for (const AnimationCurveNode* node : curves) { - if (!target) { - target = node->Target(); - } - if (node->Target() != target) { - FBXImporter::LogWarn("Node target is nullptr type."); - } - if (strictMode) { - ai_assert(node->Target() == target); - } - } - } -#endif // ASSIMP_BUILD_DEBUG - - // ------------------------------------------------------------------------------------------------ - void FBXConverter::GenerateNodeAnimations(std::vector& node_anims, - const std::string& fixed_name, - const std::vector& curves, - const LayerMap& layer_map, - int64_t start, int64_t stop, - double& max_time, - double& min_time) - { - - NodeMap node_property_map; - ai_assert(curves.size()); - -#ifdef ASSIMP_BUILD_DEBUG - validateAnimCurveNodes(curves, doc.Settings().strictMode); -#endif - const AnimationCurveNode* curve_node = NULL; - for (const AnimationCurveNode* node : curves) { - ai_assert(node); - - if (node->TargetProperty().empty()) { - FBXImporter::LogWarn("target property for animation curve not set: " + node->Name()); - continue; - } - - curve_node = node; - if (node->Curves().empty()) { - FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode: " + node->Name()); - continue; - } - - node_property_map[node->TargetProperty()].push_back(node); - } - - ai_assert(curve_node); - ai_assert(curve_node->TargetAsModel()); - - const Model& target = *curve_node->TargetAsModel(); - - // check for all possible transformation components - NodeMap::const_iterator chain[TransformationComp_MAXIMUM]; - - bool has_any = false; - bool has_complex = false; - - for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) { - const TransformationComp comp = static_cast(i); - - // inverse pivots don't exist in the input, we just generate them - if (comp == TransformationComp_RotationPivotInverse || comp == TransformationComp_ScalingPivotInverse) { - chain[i] = node_property_map.end(); - continue; - } - - chain[i] = node_property_map.find(NameTransformationCompProperty(comp)); - if (chain[i] != node_property_map.end()) { - - // check if this curves contains redundant information by looking - // up the corresponding node's transformation chain. - if (doc.Settings().optimizeEmptyAnimationCurves && - IsRedundantAnimationData(target, comp, (*chain[i]).second)) { - - FBXImporter::LogDebug("dropping redundant animation channel for node " + target.Name()); - continue; - } - - has_any = true; - - if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling && comp != TransformationComp_Translation) - { - has_complex = true; - } - } - } - - if (!has_any) { - FBXImporter::LogWarn("ignoring node animation, did not find any transformation key frames"); - return; - } - - // this needs to play nicely with GenerateTransformationNodeChain() which will - // be invoked _later_ (animations come first). If this node has only rotation, - // scaling and translation _and_ there are no animated other components either, - // we can use a single node and also a single node animation channel. - if (!has_complex && !NeedsComplexTransformationChain(target)) { - - aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain, - node_property_map.end(), - layer_map, - start, stop, - max_time, - min_time, - true // input is TRS order, assimp is SRT - ); - - ai_assert(nd); - if (nd->mNumPositionKeys == 0 && nd->mNumRotationKeys == 0 && nd->mNumScalingKeys == 0) { - delete nd; - } - else { - node_anims.push_back(nd); - } - return; - } - - // otherwise, things get gruesome and we need separate animation channels - // for each part of the transformation chain. Remember which channels - // we generated and pass this information to the node conversion - // code to avoid nodes that have identity transform, but non-identity - // animations, being dropped. - unsigned int flags = 0, bit = 0x1; - for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) { - const TransformationComp comp = static_cast(i); - - if (chain[i] != node_property_map.end()) { - flags |= bit; - - ai_assert(comp != TransformationComp_RotationPivotInverse); - ai_assert(comp != TransformationComp_ScalingPivotInverse); - - const std::string& chain_name = NameTransformationChainNode(fixed_name, comp); - - aiNodeAnim* na = nullptr; - switch (comp) - { - case TransformationComp_Rotation: - case TransformationComp_PreRotation: - case TransformationComp_PostRotation: - case TransformationComp_GeometricRotation: - na = GenerateRotationNodeAnim(chain_name, - target, - (*chain[i]).second, - layer_map, - start, stop, - max_time, - min_time); - - break; - - case TransformationComp_RotationOffset: - case TransformationComp_RotationPivot: - case TransformationComp_ScalingOffset: - case TransformationComp_ScalingPivot: - case TransformationComp_Translation: - case TransformationComp_GeometricTranslation: - na = GenerateTranslationNodeAnim(chain_name, - target, - (*chain[i]).second, - layer_map, - start, stop, - max_time, - min_time); - - // pivoting requires us to generate an implicit inverse channel to undo the pivot translation - if (comp == TransformationComp_RotationPivot) { - const std::string& invName = NameTransformationChainNode(fixed_name, - TransformationComp_RotationPivotInverse); - - aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName, - target, - (*chain[i]).second, - layer_map, - start, stop, - max_time, - min_time, - true); - - ai_assert(inv); - if (inv->mNumPositionKeys == 0 && inv->mNumRotationKeys == 0 && inv->mNumScalingKeys == 0) { - delete inv; - } - else { - node_anims.push_back(inv); - } - - ai_assert(TransformationComp_RotationPivotInverse > i); - flags |= bit << (TransformationComp_RotationPivotInverse - i); - } - else if (comp == TransformationComp_ScalingPivot) { - const std::string& invName = NameTransformationChainNode(fixed_name, - TransformationComp_ScalingPivotInverse); - - aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName, - target, - (*chain[i]).second, - layer_map, - start, stop, - max_time, - min_time, - true); - - ai_assert(inv); - if (inv->mNumPositionKeys == 0 && inv->mNumRotationKeys == 0 && inv->mNumScalingKeys == 0) { - delete inv; - } - else { - node_anims.push_back(inv); - } - - ai_assert(TransformationComp_RotationPivotInverse > i); - flags |= bit << (TransformationComp_RotationPivotInverse - i); - } - - break; - - case TransformationComp_Scaling: - case TransformationComp_GeometricScaling: - na = GenerateScalingNodeAnim(chain_name, - target, - (*chain[i]).second, - layer_map, - start, stop, - max_time, - min_time); - - break; - - default: - ai_assert(false); - } - - ai_assert(na); - if (na->mNumPositionKeys == 0 && na->mNumRotationKeys == 0 && na->mNumScalingKeys == 0) { - delete na; - } - else { - node_anims.push_back(na); - } - continue; - } - } - - node_anim_chain_bits[fixed_name] = flags; - } - - - bool FBXConverter::IsRedundantAnimationData(const Model& target, - TransformationComp comp, - const std::vector& curves) { - ai_assert(curves.size()); - - // look for animation nodes with - // * sub channels for all relevant components set - // * one key/value pair per component - // * combined values match up the corresponding value in the bind pose node transformation - // only such nodes are 'redundant' for this function. - - if (curves.size() > 1) { - return false; - } - - const AnimationCurveNode& nd = *curves.front(); - const AnimationCurveMap& sub_curves = nd.Curves(); - - const AnimationCurveMap::const_iterator dx = sub_curves.find("d|X"); - const AnimationCurveMap::const_iterator dy = sub_curves.find("d|Y"); - const AnimationCurveMap::const_iterator dz = sub_curves.find("d|Z"); - - if (dx == sub_curves.end() || dy == sub_curves.end() || dz == sub_curves.end()) { - return false; - } - - const KeyValueList& vx = (*dx).second->GetValues(); - const KeyValueList& vy = (*dy).second->GetValues(); - const KeyValueList& vz = (*dz).second->GetValues(); - - if (vx.size() != 1 || vy.size() != 1 || vz.size() != 1) { - return false; - } - - const aiVector3D dyn_val = aiVector3D(vx[0], vy[0], vz[0]); - const aiVector3D& static_val = PropertyGet(target.Props(), - NameTransformationCompProperty(comp), - TransformationCompDefaultValue(comp) - ); - - const float epsilon = 1e-6f; - return (dyn_val - static_val).SquareLength() < epsilon; - } - - - aiNodeAnim* FBXConverter::GenerateRotationNodeAnim(const std::string& name, - const Model& target, - const std::vector& curves, - const LayerMap& layer_map, - int64_t start, int64_t stop, - double& max_time, - double& min_time) - { - std::unique_ptr na(new aiNodeAnim()); - na->mNodeName.Set(name); - - ConvertRotationKeys(na.get(), curves, layer_map, start, stop, max_time, min_time, target.RotationOrder()); - - // dummy scaling key - na->mScalingKeys = new aiVectorKey[1]; - na->mNumScalingKeys = 1; - - na->mScalingKeys[0].mTime = 0.; - na->mScalingKeys[0].mValue = aiVector3D(1.0f, 1.0f, 1.0f); - - // dummy position key - na->mPositionKeys = new aiVectorKey[1]; - na->mNumPositionKeys = 1; - - na->mPositionKeys[0].mTime = 0.; - na->mPositionKeys[0].mValue = aiVector3D(); - - return na.release(); - } - - aiNodeAnim* FBXConverter::GenerateScalingNodeAnim(const std::string& name, - const Model& /*target*/, - const std::vector& curves, - const LayerMap& layer_map, - int64_t start, int64_t stop, - double& max_time, - double& min_time) - { - std::unique_ptr na(new aiNodeAnim()); - na->mNodeName.Set(name); - - ConvertScaleKeys(na.get(), curves, layer_map, start, stop, max_time, min_time); - - // dummy rotation key - na->mRotationKeys = new aiQuatKey[1]; - na->mNumRotationKeys = 1; - - na->mRotationKeys[0].mTime = 0.; - na->mRotationKeys[0].mValue = aiQuaternion(); - - // dummy position key - na->mPositionKeys = new aiVectorKey[1]; - na->mNumPositionKeys = 1; - - na->mPositionKeys[0].mTime = 0.; - na->mPositionKeys[0].mValue = aiVector3D(); - - return na.release(); - } - - aiNodeAnim* FBXConverter::GenerateTranslationNodeAnim(const std::string& name, - const Model& /*target*/, - const std::vector& curves, - const LayerMap& layer_map, - int64_t start, int64_t stop, - double& max_time, - double& min_time, - bool inverse) { - std::unique_ptr na(new aiNodeAnim()); - na->mNodeName.Set(name); - - ConvertTranslationKeys(na.get(), curves, layer_map, start, stop, max_time, min_time); - - if (inverse) { - for (unsigned int i = 0; i < na->mNumPositionKeys; ++i) { - na->mPositionKeys[i].mValue *= -1.0f; - } - } - - // dummy scaling key - na->mScalingKeys = new aiVectorKey[1]; - na->mNumScalingKeys = 1; - - na->mScalingKeys[0].mTime = 0.; - na->mScalingKeys[0].mValue = aiVector3D(1.0f, 1.0f, 1.0f); - - // dummy rotation key - na->mRotationKeys = new aiQuatKey[1]; - na->mNumRotationKeys = 1; - - na->mRotationKeys[0].mTime = 0.; - na->mRotationKeys[0].mValue = aiQuaternion(); - - return na.release(); - } - - aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name, - const Model& target, - NodeMap::const_iterator chain[TransformationComp_MAXIMUM], - NodeMap::const_iterator iter_end, - const LayerMap& layer_map, - int64_t start, int64_t stop, - double& max_time, - double& min_time, - bool reverse_order) - - { - std::unique_ptr na(new aiNodeAnim()); - na->mNodeName.Set(name); - - const PropertyTable& props = target.Props(); - - // need to convert from TRS order to SRT? - if (reverse_order) { - - aiVector3D def_scale = PropertyGet(props, "Lcl Scaling", aiVector3D(1.f, 1.f, 1.f)); - aiVector3D def_translate = PropertyGet(props, "Lcl Translation", aiVector3D(0.f, 0.f, 0.f)); - aiVector3D def_rot = PropertyGet(props, "Lcl Rotation", aiVector3D(0.f, 0.f, 0.f)); - - KeyFrameListList scaling; - KeyFrameListList translation; - KeyFrameListList rotation; - - if (chain[TransformationComp_Scaling] != iter_end) { - scaling = GetKeyframeList((*chain[TransformationComp_Scaling]).second, start, stop); - } - - if (chain[TransformationComp_Translation] != iter_end) { - translation = GetKeyframeList((*chain[TransformationComp_Translation]).second, start, stop); - } - - if (chain[TransformationComp_Rotation] != iter_end) { - rotation = GetKeyframeList((*chain[TransformationComp_Rotation]).second, start, stop); - } - - KeyFrameListList joined; - joined.insert(joined.end(), scaling.begin(), scaling.end()); - joined.insert(joined.end(), translation.begin(), translation.end()); - joined.insert(joined.end(), rotation.begin(), rotation.end()); - - const KeyTimeList& times = GetKeyTimeList(joined); - - aiQuatKey* out_quat = new aiQuatKey[times.size()]; - aiVectorKey* out_scale = new aiVectorKey[times.size()]; - aiVectorKey* out_translation = new aiVectorKey[times.size()]; - - if (times.size()) - { - ConvertTransformOrder_TRStoSRT(out_quat, out_scale, out_translation, - scaling, - translation, - rotation, - times, - max_time, - min_time, - target.RotationOrder(), - def_scale, - def_translate, - def_rot); - } - - // XXX remove duplicates / redundant keys which this operation did - // likely produce if not all three channels were equally dense. - - na->mNumScalingKeys = static_cast(times.size()); - na->mNumRotationKeys = na->mNumScalingKeys; - na->mNumPositionKeys = na->mNumScalingKeys; - - na->mScalingKeys = out_scale; - na->mRotationKeys = out_quat; - na->mPositionKeys = out_translation; - } - else { - - // if a particular transformation is not given, grab it from - // the corresponding node to meet the semantics of aiNodeAnim, - // which requires all of rotation, scaling and translation - // to be set. - if (chain[TransformationComp_Scaling] != iter_end) { - ConvertScaleKeys(na.get(), (*chain[TransformationComp_Scaling]).second, - layer_map, - start, stop, - max_time, - min_time); - } - else { - na->mScalingKeys = new aiVectorKey[1]; - na->mNumScalingKeys = 1; - - na->mScalingKeys[0].mTime = 0.; - na->mScalingKeys[0].mValue = PropertyGet(props, "Lcl Scaling", - aiVector3D(1.f, 1.f, 1.f)); - } - - if (chain[TransformationComp_Rotation] != iter_end) { - ConvertRotationKeys(na.get(), (*chain[TransformationComp_Rotation]).second, - layer_map, - start, stop, - max_time, - min_time, - target.RotationOrder()); - } - else { - na->mRotationKeys = new aiQuatKey[1]; - na->mNumRotationKeys = 1; - - na->mRotationKeys[0].mTime = 0.; - na->mRotationKeys[0].mValue = EulerToQuaternion( - PropertyGet(props, "Lcl Rotation", aiVector3D(0.f, 0.f, 0.f)), - target.RotationOrder()); - } - - if (chain[TransformationComp_Translation] != iter_end) { - ConvertTranslationKeys(na.get(), (*chain[TransformationComp_Translation]).second, - layer_map, - start, stop, - max_time, - min_time); - } - else { - na->mPositionKeys = new aiVectorKey[1]; - na->mNumPositionKeys = 1; - - na->mPositionKeys[0].mTime = 0.; - na->mPositionKeys[0].mValue = PropertyGet(props, "Lcl Translation", - aiVector3D(0.f, 0.f, 0.f)); - } - - } - return na.release(); - } - - FBXConverter::KeyFrameListList FBXConverter::GetKeyframeList(const std::vector& nodes, int64_t start, int64_t stop) - { - KeyFrameListList inputs; - inputs.reserve(nodes.size() * 3); - - //give some breathing room for rounding errors - int64_t adj_start = start - 10000; - int64_t adj_stop = stop + 10000; - - for (const AnimationCurveNode* node : nodes) { - ai_assert(node); - - const AnimationCurveMap& curves = node->Curves(); - for (const AnimationCurveMap::value_type& kv : curves) { - - unsigned int mapto; - if (kv.first == "d|X") { - mapto = 0; - } - else if (kv.first == "d|Y") { - mapto = 1; - } - else if (kv.first == "d|Z") { - mapto = 2; - } - else { - FBXImporter::LogWarn("ignoring scale animation curve, did not recognize target component"); - continue; - } - - const AnimationCurve* const curve = kv.second; - ai_assert(curve->GetKeys().size() == curve->GetValues().size() && curve->GetKeys().size()); - - //get values within the start/stop time window - std::shared_ptr Keys(new KeyTimeList()); - std::shared_ptr Values(new KeyValueList()); - const size_t count = curve->GetKeys().size(); - Keys->reserve(count); - Values->reserve(count); - for (size_t n = 0; n < count; n++) - { - int64_t k = curve->GetKeys().at(n); - if (k >= adj_start && k <= adj_stop) - { - Keys->push_back(k); - Values->push_back(curve->GetValues().at(n)); - } - } - - inputs.push_back(std::make_tuple(Keys, Values, mapto)); - } - } - return inputs; // pray for NRVO :-) - } - - - KeyTimeList FBXConverter::GetKeyTimeList(const KeyFrameListList& inputs) { - ai_assert(!inputs.empty()); - - // reserve some space upfront - it is likely that the key-frame lists - // have matching time values, so max(of all key-frame lists) should - // be a good estimate. - KeyTimeList keys; - - size_t estimate = 0; - for (const KeyFrameList& kfl : inputs) { - estimate = std::max(estimate, std::get<0>(kfl)->size()); - } - - keys.reserve(estimate); - - std::vector next_pos; - next_pos.resize(inputs.size(), 0); - - const size_t count = inputs.size(); - while (true) { - - int64_t min_tick = std::numeric_limits::max(); - for (size_t i = 0; i < count; ++i) { - const KeyFrameList& kfl = inputs[i]; - - if (std::get<0>(kfl)->size() > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) < min_tick) { - min_tick = std::get<0>(kfl)->at(next_pos[i]); - } - } - - if (min_tick == std::numeric_limits::max()) { - break; - } - keys.push_back(min_tick); - - for (size_t i = 0; i < count; ++i) { - const KeyFrameList& kfl = inputs[i]; - - - while (std::get<0>(kfl)->size() > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) == min_tick) { - ++next_pos[i]; - } - } - } - - return keys; - } - - void FBXConverter::InterpolateKeys(aiVectorKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs, - const aiVector3D& def_value, - double& max_time, - double& min_time) { - ai_assert(!keys.empty()); - ai_assert(nullptr != valOut); - - std::vector next_pos; - const size_t count(inputs.size()); - - next_pos.resize(inputs.size(), 0); - - for (KeyTimeList::value_type time : keys) { - ai_real result[3] = { def_value.x, def_value.y, def_value.z }; - - for (size_t i = 0; i < count; ++i) { - const KeyFrameList& kfl = inputs[i]; - - const size_t ksize = std::get<0>(kfl)->size(); - if (ksize == 0) { - continue; - } - if (ksize > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) == time) { - ++next_pos[i]; - } - - const size_t id0 = next_pos[i] > 0 ? next_pos[i] - 1 : 0; - const size_t id1 = next_pos[i] == ksize ? ksize - 1 : next_pos[i]; - - // use lerp for interpolation - const KeyValueList::value_type valueA = std::get<1>(kfl)->at(id0); - const KeyValueList::value_type valueB = std::get<1>(kfl)->at(id1); - - const KeyTimeList::value_type timeA = std::get<0>(kfl)->at(id0); - const KeyTimeList::value_type timeB = std::get<0>(kfl)->at(id1); - - const ai_real factor = timeB == timeA ? ai_real(0.) : static_cast((time - timeA)) / (timeB - timeA); - const ai_real interpValue = static_cast(valueA + (valueB - valueA) * factor); - - result[std::get<2>(kfl)] = interpValue; - } - - // magic value to convert fbx times to seconds - valOut->mTime = CONVERT_FBX_TIME(time) * anim_fps; - - min_time = std::min(min_time, valOut->mTime); - max_time = std::max(max_time, valOut->mTime); - - valOut->mValue.x = result[0]; - valOut->mValue.y = result[1]; - valOut->mValue.z = result[2]; - - ++valOut; - } - } - - void FBXConverter::InterpolateKeys(aiQuatKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs, - const aiVector3D& def_value, - double& maxTime, - double& minTime, - Model::RotOrder order) - { - ai_assert(!keys.empty()); - ai_assert(nullptr != valOut); - - std::unique_ptr temp(new aiVectorKey[keys.size()]); - InterpolateKeys(temp.get(), keys, inputs, def_value, maxTime, minTime); - - aiMatrix4x4 m; - - aiQuaternion lastq; - - for (size_t i = 0, c = keys.size(); i < c; ++i) { - - valOut[i].mTime = temp[i].mTime; - - GetRotationMatrix(order, temp[i].mValue, m); - aiQuaternion quat = aiQuaternion(aiMatrix3x3(m)); - - // take shortest path by checking the inner product - // http://www.3dkingdoms.com/weekly/weekly.php?a=36 - if (quat.x * lastq.x + quat.y * lastq.y + quat.z * lastq.z + quat.w * lastq.w < 0) - { - quat.x = -quat.x; - quat.y = -quat.y; - quat.z = -quat.z; - quat.w = -quat.w; - } - lastq = quat; - - valOut[i].mValue = quat; - } - } - - void FBXConverter::ConvertTransformOrder_TRStoSRT(aiQuatKey* out_quat, aiVectorKey* out_scale, - aiVectorKey* out_translation, - const KeyFrameListList& scaling, - const KeyFrameListList& translation, - const KeyFrameListList& rotation, - const KeyTimeList& times, - double& maxTime, - double& minTime, - Model::RotOrder order, - const aiVector3D& def_scale, - const aiVector3D& def_translate, - const aiVector3D& def_rotation) - { - if (rotation.size()) { - InterpolateKeys(out_quat, times, rotation, def_rotation, maxTime, minTime, order); - } - else { - for (size_t i = 0; i < times.size(); ++i) { - out_quat[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps; - out_quat[i].mValue = EulerToQuaternion(def_rotation, order); - } - } - - if (scaling.size()) { - InterpolateKeys(out_scale, times, scaling, def_scale, maxTime, minTime); - } - else { - for (size_t i = 0; i < times.size(); ++i) { - out_scale[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps; - out_scale[i].mValue = def_scale; - } - } - - if (translation.size()) { - InterpolateKeys(out_translation, times, translation, def_translate, maxTime, minTime); - } - else { - for (size_t i = 0; i < times.size(); ++i) { - out_translation[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps; - out_translation[i].mValue = def_translate; - } - } - - const size_t count = times.size(); - for (size_t i = 0; i < count; ++i) { - aiQuaternion& r = out_quat[i].mValue; - aiVector3D& s = out_scale[i].mValue; - aiVector3D& t = out_translation[i].mValue; - - aiMatrix4x4 mat, temp; - aiMatrix4x4::Translation(t, mat); - mat *= aiMatrix4x4(r.GetMatrix()); - mat *= aiMatrix4x4::Scaling(s, temp); - - mat.Decompose(s, r, t); - } - } - - aiQuaternion FBXConverter::EulerToQuaternion(const aiVector3D& rot, Model::RotOrder order) - { - aiMatrix4x4 m; - GetRotationMatrix(order, rot, m); - - return aiQuaternion(aiMatrix3x3(m)); - } - - void FBXConverter::ConvertScaleKeys(aiNodeAnim* na, const std::vector& nodes, const LayerMap& /*layers*/, - int64_t start, int64_t stop, - double& maxTime, - double& minTime) - { - ai_assert(nodes.size()); - - // XXX for now, assume scale should be blended geometrically (i.e. two - // layers should be multiplied with each other). There is a FBX - // property in the layer to specify the behaviour, though. - - const KeyFrameListList& inputs = GetKeyframeList(nodes, start, stop); - const KeyTimeList& keys = GetKeyTimeList(inputs); - - na->mNumScalingKeys = static_cast(keys.size()); - na->mScalingKeys = new aiVectorKey[keys.size()]; - if (keys.size() > 0) - InterpolateKeys(na->mScalingKeys, keys, inputs, aiVector3D(1.0f, 1.0f, 1.0f), maxTime, minTime); - } - - void FBXConverter::ConvertTranslationKeys(aiNodeAnim* na, const std::vector& nodes, - const LayerMap& /*layers*/, - int64_t start, int64_t stop, - double& maxTime, - double& minTime) - { - ai_assert(nodes.size()); - - // XXX see notes in ConvertScaleKeys() - const KeyFrameListList& inputs = GetKeyframeList(nodes, start, stop); - const KeyTimeList& keys = GetKeyTimeList(inputs); - - na->mNumPositionKeys = static_cast(keys.size()); - na->mPositionKeys = new aiVectorKey[keys.size()]; - if (keys.size() > 0) - InterpolateKeys(na->mPositionKeys, keys, inputs, aiVector3D(0.0f, 0.0f, 0.0f), maxTime, minTime); - } - - void FBXConverter::ConvertRotationKeys(aiNodeAnim* na, const std::vector& nodes, - const LayerMap& /*layers*/, - int64_t start, int64_t stop, - double& maxTime, - double& minTime, - Model::RotOrder order) - { - ai_assert(nodes.size()); - - // XXX see notes in ConvertScaleKeys() - const std::vector< KeyFrameList >& inputs = GetKeyframeList(nodes, start, stop); - const KeyTimeList& keys = GetKeyTimeList(inputs); - - na->mNumRotationKeys = static_cast(keys.size()); - na->mRotationKeys = new aiQuatKey[keys.size()]; - if (!keys.empty()) { - InterpolateKeys(na->mRotationKeys, keys, inputs, aiVector3D(0.0f, 0.0f, 0.0f), maxTime, minTime, order); - } - } - - void FBXConverter::ConvertGlobalSettings() { - if (nullptr == out) { - return; - } - - out->mMetaData = aiMetadata::Alloc(15); - out->mMetaData->Set(0, "UpAxis", doc.GlobalSettings().UpAxis()); - out->mMetaData->Set(1, "UpAxisSign", doc.GlobalSettings().UpAxisSign()); - out->mMetaData->Set(2, "FrontAxis", doc.GlobalSettings().FrontAxis()); - out->mMetaData->Set(3, "FrontAxisSign", doc.GlobalSettings().FrontAxisSign()); - out->mMetaData->Set(4, "CoordAxis", doc.GlobalSettings().CoordAxis()); - out->mMetaData->Set(5, "CoordAxisSign", doc.GlobalSettings().CoordAxisSign()); - out->mMetaData->Set(6, "OriginalUpAxis", doc.GlobalSettings().OriginalUpAxis()); - out->mMetaData->Set(7, "OriginalUpAxisSign", doc.GlobalSettings().OriginalUpAxisSign()); - out->mMetaData->Set(8, "UnitScaleFactor", (double)doc.GlobalSettings().UnitScaleFactor()); - out->mMetaData->Set(9, "OriginalUnitScaleFactor", doc.GlobalSettings().OriginalUnitScaleFactor()); - out->mMetaData->Set(10, "AmbientColor", doc.GlobalSettings().AmbientColor()); - out->mMetaData->Set(11, "FrameRate", (int)doc.GlobalSettings().TimeMode()); - out->mMetaData->Set(12, "TimeSpanStart", doc.GlobalSettings().TimeSpanStart()); - out->mMetaData->Set(13, "TimeSpanStop", doc.GlobalSettings().TimeSpanStop()); - out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate()); - } - - void FBXConverter::TransferDataToScene() - { - ai_assert(!out->mMeshes); - ai_assert(!out->mNumMeshes); - - // note: the trailing () ensures initialization with NULL - not - // many C++ users seem to know this, so pointing it out to avoid - // confusion why this code works. - - if (meshes.size()) { - out->mMeshes = new aiMesh*[meshes.size()](); - out->mNumMeshes = static_cast(meshes.size()); - - std::swap_ranges(meshes.begin(), meshes.end(), out->mMeshes); - } - - if (materials.size()) { - out->mMaterials = new aiMaterial*[materials.size()](); - out->mNumMaterials = static_cast(materials.size()); - - std::swap_ranges(materials.begin(), materials.end(), out->mMaterials); - } - - if (animations.size()) { - out->mAnimations = new aiAnimation*[animations.size()](); - out->mNumAnimations = static_cast(animations.size()); - - std::swap_ranges(animations.begin(), animations.end(), out->mAnimations); - } - - if (lights.size()) { - out->mLights = new aiLight*[lights.size()](); - out->mNumLights = static_cast(lights.size()); - - std::swap_ranges(lights.begin(), lights.end(), out->mLights); - } - - if (cameras.size()) { - out->mCameras = new aiCamera*[cameras.size()](); - out->mNumCameras = static_cast(cameras.size()); - - std::swap_ranges(cameras.begin(), cameras.end(), out->mCameras); - } - - if (textures.size()) { - out->mTextures = new aiTexture*[textures.size()](); - out->mNumTextures = static_cast(textures.size()); - - std::swap_ranges(textures.begin(), textures.end(), out->mTextures); - } - } - - // ------------------------------------------------------------------------------------------------ - void ConvertToAssimpScene(aiScene* out, const Document& doc) - { - FBXConverter converter(out, doc); - } - - } // !FBX -} // !Assimp - -#endif diff --git a/thirdparty/assimp/code/FBXConverter.h b/thirdparty/assimp/code/FBXConverter.h deleted file mode 100644 index 50637468b9..0000000000 --- a/thirdparty/assimp/code/FBXConverter.h +++ /dev/null @@ -1,461 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXDConverter.h - * @brief FBX DOM to aiScene conversion - */ -#ifndef INCLUDED_AI_FBX_CONVERTER_H -#define INCLUDED_AI_FBX_CONVERTER_H - -#include "FBXParser.h" -#include "FBXMeshGeometry.h" -#include "FBXDocument.h" -#include "FBXUtil.h" -#include "FBXProperties.h" -#include "FBXImporter.h" -#include -#include -#include -#include -#include -#include -#include -#include - -struct aiScene; -struct aiNode; -struct aiMaterial; - -struct morphKeyData { - std::vector values; - std::vector weights; -}; -typedef std::map morphAnimData; - -namespace Assimp { -namespace FBX { - -class Document; - -/** - * Convert a FBX #Document to #aiScene - * @param out Empty scene to be populated - * @param doc Parsed FBX document - */ -void ConvertToAssimpScene(aiScene* out, const Document& doc); - -/** Dummy class to encapsulate the conversion process */ -class FBXConverter { -public: - /** - * The different parts that make up the final local transformation of a fbx-node - */ - enum TransformationComp { - TransformationComp_GeometricScalingInverse = 0, - TransformationComp_GeometricRotationInverse, - TransformationComp_GeometricTranslationInverse, - TransformationComp_Translation, - TransformationComp_RotationOffset, - TransformationComp_RotationPivot, - TransformationComp_PreRotation, - TransformationComp_Rotation, - TransformationComp_PostRotation, - TransformationComp_RotationPivotInverse, - TransformationComp_ScalingOffset, - TransformationComp_ScalingPivot, - TransformationComp_Scaling, - TransformationComp_ScalingPivotInverse, - TransformationComp_GeometricTranslation, - TransformationComp_GeometricRotation, - TransformationComp_GeometricScaling, - - TransformationComp_MAXIMUM - }; - -public: - FBXConverter(aiScene* out, const Document& doc); - ~FBXConverter(); - -private: - // ------------------------------------------------------------------------------------------------ - // find scene root and trigger recursive scene conversion - void ConvertRootNode(); - - // ------------------------------------------------------------------------------------------------ - // collect and assign child nodes - void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4()); - - // ------------------------------------------------------------------------------------------------ - void ConvertLights(const Model& model, const std::string &orig_name ); - - // ------------------------------------------------------------------------------------------------ - void ConvertCameras(const Model& model, const std::string &orig_name ); - - // ------------------------------------------------------------------------------------------------ - void ConvertLight( const Light& light, const std::string &orig_name ); - - // ------------------------------------------------------------------------------------------------ - void ConvertCamera( const Camera& cam, const std::string &orig_name ); - - // ------------------------------------------------------------------------------------------------ - void GetUniqueName( const std::string &name, std::string& uniqueName ); - - // ------------------------------------------------------------------------------------------------ - // this returns unified names usable within assimp identifiers (i.e. no space characters - - // while these would be allowed, they are a potential trouble spot so better not use them). - const char* NameTransformationComp(TransformationComp comp); - - // ------------------------------------------------------------------------------------------------ - // note: this returns the REAL fbx property names - const char* NameTransformationCompProperty(TransformationComp comp); - - // ------------------------------------------------------------------------------------------------ - aiVector3D TransformationCompDefaultValue(TransformationComp comp); - - // ------------------------------------------------------------------------------------------------ - void GetRotationMatrix(Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out); - // ------------------------------------------------------------------------------------------------ - /** - * checks if a node has more than just scaling, rotation and translation components - */ - bool NeedsComplexTransformationChain(const Model& model); - - // ------------------------------------------------------------------------------------------------ - // note: name must be a FixNodeName() result - std::string NameTransformationChainNode(const std::string& name, TransformationComp comp); - - // ------------------------------------------------------------------------------------------------ - /** - * note: memory for output_nodes will be managed by the caller - */ - void GenerateTransformationNodeChain(const Model& model, std::vector& output_nodes, std::vector& post_output_nodes); - - // ------------------------------------------------------------------------------------------------ - void SetupNodeMetadata(const Model& model, aiNode& nd); - - // ------------------------------------------------------------------------------------------------ - void ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform); - - // ------------------------------------------------------------------------------------------------ - // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed - std::vector ConvertMesh(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd); - - // ------------------------------------------------------------------------------------------------ - std::vector ConvertLine(const LineGeometry& line, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd); - - // ------------------------------------------------------------------------------------------------ - aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode& nd); - - // ------------------------------------------------------------------------------------------------ - unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd); - - // ------------------------------------------------------------------------------------------------ - std::vector ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd); - - // ------------------------------------------------------------------------------------------------ - unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, - MatIndexArray::value_type index, - const aiMatrix4x4& node_global_transform, aiNode& nd); - - // ------------------------------------------------------------------------------------------------ - static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits::max() */ - static_cast(-1); - - // ------------------------------------------------------------------------------------------------ - /** - * - if materialIndex == NO_MATERIAL_SEPARATION, materials are not taken into - * account when determining which weights to include. - * - outputVertStartIndices is only used when a material index is specified, it gives for - * each output vertex the DOM index it maps to. - */ - void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, - const aiMatrix4x4& node_global_transform = aiMatrix4x4(), - unsigned int materialIndex = NO_MATERIAL_SEPARATION, - std::vector* outputVertStartIndices = NULL); - - // ------------------------------------------------------------------------------------------------ - void ConvertCluster(std::vector& bones, const Model& /*model*/, const Cluster& cl, - std::vector& out_indices, - std::vector& index_out_indices, - std::vector& count_out_indices, - const aiMatrix4x4& node_global_transform); - - // ------------------------------------------------------------------------------------------------ - void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, - MatIndexArray::value_type materialIndex); - - // ------------------------------------------------------------------------------------------------ - unsigned int GetDefaultMaterial(); - - // ------------------------------------------------------------------------------------------------ - // Material -> aiMaterial - unsigned int ConvertMaterial(const Material& material, const MeshGeometry* const mesh); - - // ------------------------------------------------------------------------------------------------ - // Video -> aiTexture - unsigned int ConvertVideo(const Video& video); - - // ------------------------------------------------------------------------------------------------ - // convert embedded texture if necessary and return actual texture path - aiString GetTexturePath(const Texture* tex); - - // ------------------------------------------------------------------------------------------------ - void TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, - const std::string& propName, - aiTextureType target, const MeshGeometry* const mesh); - - // ------------------------------------------------------------------------------------------------ - void TrySetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, - const std::string& propName, - aiTextureType target, const MeshGeometry* const mesh); - - // ------------------------------------------------------------------------------------------------ - void SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, const MeshGeometry* const mesh); - - // ------------------------------------------------------------------------------------------------ - void SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh); - - // ------------------------------------------------------------------------------------------------ - aiColor3D GetColorPropertyFromMaterial(const PropertyTable& props, const std::string& baseName, - bool& result); - aiColor3D GetColorPropertyFactored(const PropertyTable& props, const std::string& colorName, - const std::string& factorName, bool& result, bool useTemplate = true); - aiColor3D GetColorProperty(const PropertyTable& props, const std::string& colorName, - bool& result, bool useTemplate = true); - - // ------------------------------------------------------------------------------------------------ - void SetShadingPropertiesCommon(aiMaterial* out_mat, const PropertyTable& props); - void SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTable& props, const TextureMap& textures, const MeshGeometry* const mesh); - - // ------------------------------------------------------------------------------------------------ - // get the number of fps for a FrameRate enumerated value - static double FrameRateToDouble(FileGlobalSettings::FrameRate fp, double customFPSVal = -1.0); - - // ------------------------------------------------------------------------------------------------ - // convert animation data to aiAnimation et al - void ConvertAnimations(); - - // ------------------------------------------------------------------------------------------------ - // takes a fbx node name and returns the identifier to be used in the assimp output scene. - // the function is guaranteed to provide consistent results over multiple invocations - // UNLESS RenameNode() is called for a particular node name. - std::string FixNodeName(const std::string& name); - std::string FixAnimMeshName(const std::string& name); - - typedef std::map LayerMap; - - // XXX: better use multi_map .. - typedef std::map > NodeMap; - - // ------------------------------------------------------------------------------------------------ - void ConvertAnimationStack(const AnimationStack& st); - - // ------------------------------------------------------------------------------------------------ - void ProcessMorphAnimDatas(std::map* morphAnimDatas, const BlendShapeChannel* bsc, const AnimationCurveNode* node); - - // ------------------------------------------------------------------------------------------------ - void GenerateNodeAnimations(std::vector& node_anims, - const std::string& fixed_name, - const std::vector& curves, - const LayerMap& layer_map, - int64_t start, int64_t stop, - double& max_time, - double& min_time); - - // ------------------------------------------------------------------------------------------------ - bool IsRedundantAnimationData(const Model& target, - TransformationComp comp, - const std::vector& curves); - - // ------------------------------------------------------------------------------------------------ - aiNodeAnim* GenerateRotationNodeAnim(const std::string& name, - const Model& target, - const std::vector& curves, - const LayerMap& layer_map, - int64_t start, int64_t stop, - double& max_time, - double& min_time); - - // ------------------------------------------------------------------------------------------------ - aiNodeAnim* GenerateScalingNodeAnim(const std::string& name, - const Model& /*target*/, - const std::vector& curves, - const LayerMap& layer_map, - int64_t start, int64_t stop, - double& max_time, - double& min_time); - - // ------------------------------------------------------------------------------------------------ - aiNodeAnim* GenerateTranslationNodeAnim(const std::string& name, - const Model& /*target*/, - const std::vector& curves, - const LayerMap& layer_map, - int64_t start, int64_t stop, - double& max_time, - double& min_time, - bool inverse = false); - - // ------------------------------------------------------------------------------------------------ - // generate node anim, extracting only Rotation, Scaling and Translation from the given chain - aiNodeAnim* GenerateSimpleNodeAnim(const std::string& name, - const Model& target, - NodeMap::const_iterator chain[TransformationComp_MAXIMUM], - NodeMap::const_iterator iter_end, - const LayerMap& layer_map, - int64_t start, int64_t stop, - double& max_time, - double& min_time, - bool reverse_order = false); - - // key (time), value, mapto (component index) - typedef std::tuple, std::shared_ptr, unsigned int > KeyFrameList; - typedef std::vector KeyFrameListList; - - // ------------------------------------------------------------------------------------------------ - KeyFrameListList GetKeyframeList(const std::vector& nodes, int64_t start, int64_t stop); - - // ------------------------------------------------------------------------------------------------ - KeyTimeList GetKeyTimeList(const KeyFrameListList& inputs); - - // ------------------------------------------------------------------------------------------------ - void InterpolateKeys(aiVectorKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs, - const aiVector3D& def_value, - double& max_time, - double& min_time); - - // ------------------------------------------------------------------------------------------------ - void InterpolateKeys(aiQuatKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs, - const aiVector3D& def_value, - double& maxTime, - double& minTime, - Model::RotOrder order); - - // ------------------------------------------------------------------------------------------------ - void ConvertTransformOrder_TRStoSRT(aiQuatKey* out_quat, aiVectorKey* out_scale, - aiVectorKey* out_translation, - const KeyFrameListList& scaling, - const KeyFrameListList& translation, - const KeyFrameListList& rotation, - const KeyTimeList& times, - double& maxTime, - double& minTime, - Model::RotOrder order, - const aiVector3D& def_scale, - const aiVector3D& def_translate, - const aiVector3D& def_rotation); - - // ------------------------------------------------------------------------------------------------ - // euler xyz -> quat - aiQuaternion EulerToQuaternion(const aiVector3D& rot, Model::RotOrder order); - - // ------------------------------------------------------------------------------------------------ - void ConvertScaleKeys(aiNodeAnim* na, const std::vector& nodes, const LayerMap& /*layers*/, - int64_t start, int64_t stop, - double& maxTime, - double& minTime); - - // ------------------------------------------------------------------------------------------------ - void ConvertTranslationKeys(aiNodeAnim* na, const std::vector& nodes, - const LayerMap& /*layers*/, - int64_t start, int64_t stop, - double& maxTime, - double& minTime); - - // ------------------------------------------------------------------------------------------------ - void ConvertRotationKeys(aiNodeAnim* na, const std::vector& nodes, - const LayerMap& /*layers*/, - int64_t start, int64_t stop, - double& maxTime, - double& minTime, - Model::RotOrder order); - - void ConvertGlobalSettings(); - - // ------------------------------------------------------------------------------------------------ - // copy generated meshes, animations, lights, cameras and textures to the output scene - void TransferDataToScene(); - -private: - // 0: not assigned yet, others: index is value - 1 - unsigned int defaultMaterialIndex; - - std::vector meshes; - std::vector materials; - std::vector animations; - std::vector lights; - std::vector cameras; - std::vector textures; - - using MaterialMap = std::map; - MaterialMap materials_converted; - - using VideoMap = std::map; - VideoMap textures_converted; - - using MeshMap = std::map >; - MeshMap meshes_converted; - - // fixed node name -> which trafo chain components have animations? - using NodeAnimBitMap = std::map ; - NodeAnimBitMap node_anim_chain_bits; - - // number of nodes with the same name - using NodeAnimNameMap = std::unordered_map; - NodeAnimNameMap mNodeNameInstances; - - using NodeNameCache = std::unordered_set; - NodeNameCache mNodeNames; - - double anim_fps; - - aiScene* const out; - const FBX::Document& doc; -}; - -} -} - -#endif // INCLUDED_AI_FBX_CONVERTER_H diff --git a/thirdparty/assimp/code/FBXDeformer.cpp b/thirdparty/assimp/code/FBXDeformer.cpp deleted file mode 100644 index 6927553450..0000000000 --- a/thirdparty/assimp/code/FBXDeformer.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXNoteAttribute.cpp - * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -#include "FBXParser.h" -#include "FBXDocument.h" -#include "FBXMeshGeometry.h" -#include "FBXImporter.h" -#include "FBXDocumentUtil.h" - -namespace Assimp { -namespace FBX { - -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -Deformer::Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name) - : Object(id,element,name) -{ - const Scope& sc = GetRequiredScope(element); - - const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2)); - props = GetPropertyTable(doc,"Deformer.Fbx" + classname,element,sc,true); -} - - -// ------------------------------------------------------------------------------------------------ -Deformer::~Deformer() -{ - -} - - -// ------------------------------------------------------------------------------------------------ -Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: Deformer(id,element,doc,name) -, node() -{ - const Scope& sc = GetRequiredScope(element); - - const Element* const Indexes = sc["Indexes"]; - const Element* const Weights = sc["Weights"]; - - const Element& Transform = GetRequiredElement(sc,"Transform",&element); - const Element& TransformLink = GetRequiredElement(sc,"TransformLink",&element); - - transform = ReadMatrix(Transform); - transformLink = ReadMatrix(TransformLink); - - // it is actually possible that there be Deformer's with no weights - if (!!Indexes != !!Weights) { - DOMError("either Indexes or Weights are missing from Cluster",&element); - } - - if(Indexes) { - ParseVectorDataArray(indices,*Indexes); - ParseVectorDataArray(weights,*Weights); - } - - if(indices.size() != weights.size()) { - DOMError("sizes of index and weight array don't match up",&element); - } - - // read assigned node - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Model"); - for(const Connection* con : conns) { - const Model* const mod = ProcessSimpleConnection(*con, false, "Model -> Cluster", element); - if(mod) { - node = mod; - break; - } - } - - if (!node) { - DOMError("failed to read target Node for Cluster",&element); - } -} - - -// ------------------------------------------------------------------------------------------------ -Cluster::~Cluster() -{ - -} - - -// ------------------------------------------------------------------------------------------------ -Skin::Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: Deformer(id,element,doc,name) -, accuracy( 0.0f ) { - const Scope& sc = GetRequiredScope(element); - - const Element* const Link_DeformAcuracy = sc["Link_DeformAcuracy"]; - if(Link_DeformAcuracy) { - accuracy = ParseTokenAsFloat(GetRequiredToken(*Link_DeformAcuracy,0)); - } - - // resolve assigned clusters - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer"); - - clusters.reserve(conns.size()); - for(const Connection* con : conns) { - - const Cluster* const cluster = ProcessSimpleConnection(*con, false, "Cluster -> Skin", element); - if(cluster) { - clusters.push_back(cluster); - continue; - } - } -} - - -// ------------------------------------------------------------------------------------------------ -Skin::~Skin() -{ - -} -// ------------------------------------------------------------------------------------------------ -BlendShape::BlendShape(uint64_t id, const Element& element, const Document& doc, const std::string& name) - : Deformer(id, element, doc, name) -{ - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer"); - blendShapeChannels.reserve(conns.size()); - for (const Connection* con : conns) { - const BlendShapeChannel* const bspc = ProcessSimpleConnection(*con, false, "BlendShapeChannel -> BlendShape", element); - if (bspc) { - blendShapeChannels.push_back(bspc); - continue; - } - } -} -// ------------------------------------------------------------------------------------------------ -BlendShape::~BlendShape() -{ - -} -// ------------------------------------------------------------------------------------------------ -BlendShapeChannel::BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name) - : Deformer(id, element, doc, name) -{ - const Scope& sc = GetRequiredScope(element); - const Element* const DeformPercent = sc["DeformPercent"]; - if (DeformPercent) { - percent = ParseTokenAsFloat(GetRequiredToken(*DeformPercent, 0)); - } - const Element* const FullWeights = sc["FullWeights"]; - if (FullWeights) { - ParseVectorDataArray(fullWeights, *FullWeights); - } - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(), "Geometry"); - shapeGeometries.reserve(conns.size()); - for (const Connection* con : conns) { - const ShapeGeometry* const sg = ProcessSimpleConnection(*con, false, "Shape -> BlendShapeChannel", element); - if (sg) { - shapeGeometries.push_back(sg); - continue; - } - } -} -// ------------------------------------------------------------------------------------------------ -BlendShapeChannel::~BlendShapeChannel() -{ - -} -// ------------------------------------------------------------------------------------------------ -} -} -#endif - diff --git a/thirdparty/assimp/code/FBXDocument.cpp b/thirdparty/assimp/code/FBXDocument.cpp deleted file mode 100644 index 1af08fe6d8..0000000000 --- a/thirdparty/assimp/code/FBXDocument.cpp +++ /dev/null @@ -1,726 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the* - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXDocument.cpp - * @brief Implementation of the FBX DOM classes - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -#include "FBXDocument.h" -#include "FBXMeshGeometry.h" -#include "FBXParser.h" -#include "FBXUtil.h" -#include "FBXImporter.h" -#include "FBXImportSettings.h" -#include "FBXDocumentUtil.h" -#include "FBXProperties.h" - -#include -#include -#include - -namespace Assimp { -namespace FBX { - -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc) -: doc(doc) -, element(element) -, id(id) -, flags() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -LazyObject::~LazyObject() -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -const Object* LazyObject::Get(bool dieOnError) -{ - if(IsBeingConstructed() || FailedToConstruct()) { - return nullptr; - } - - if (object.get()) { - return object.get(); - } - - // if this is the root object, we return a dummy since there - // is no root object int he fbx file - it is just referenced - // with id 0. - if(id == 0L) { - object.reset(new Object(id, element, "Model::RootNode")); - return object.get(); - } - - const Token& key = element.KeyToken(); - const TokenList& tokens = element.Tokens(); - - if(tokens.size() < 3) { - DOMError("expected at least 3 tokens: id, name and class tag",&element); - } - - const char* err; - std::string name = ParseTokenAsString(*tokens[1],err); - if (err) { - DOMError(err,&element); - } - - // small fix for binary reading: binary fbx files don't use - // prefixes such as Model:: in front of their names. The - // loading code expects this at many places, though! - // so convert the binary representation (a 0x0001) to the - // double colon notation. - if(tokens[1]->IsBinary()) { - for (size_t i = 0; i < name.length(); ++i) { - if (name[i] == 0x0 && name[i+1] == 0x1) { - name = name.substr(i+2) + "::" + name.substr(0,i); - } - } - } - - const std::string classtag = ParseTokenAsString(*tokens[2],err); - if (err) { - DOMError(err,&element); - } - - // prevent recursive calls - flags |= BEING_CONSTRUCTED; - - try { - // this needs to be relatively fast since it happens a lot, - // so avoid constructing strings all the time. - const char* obtype = key.begin(); - const size_t length = static_cast(key.end()-key.begin()); - - // For debugging - //dumpObjectClassInfo( objtype, classtag ); - - if (!strncmp(obtype,"Geometry",length)) { - if (!strcmp(classtag.c_str(),"Mesh")) { - object.reset(new MeshGeometry(id,element,name,doc)); - } - if (!strcmp(classtag.c_str(), "Shape")) { - object.reset(new ShapeGeometry(id, element, name, doc)); - } - if (!strcmp(classtag.c_str(), "Line")) { - object.reset(new LineGeometry(id, element, name, doc)); - } - } - else if (!strncmp(obtype,"NodeAttribute",length)) { - if (!strcmp(classtag.c_str(),"Camera")) { - object.reset(new Camera(id,element,doc,name)); - } - else if (!strcmp(classtag.c_str(),"CameraSwitcher")) { - object.reset(new CameraSwitcher(id,element,doc,name)); - } - else if (!strcmp(classtag.c_str(),"Light")) { - object.reset(new Light(id,element,doc,name)); - } - else if (!strcmp(classtag.c_str(),"Null")) { - object.reset(new Null(id,element,doc,name)); - } - else if (!strcmp(classtag.c_str(),"LimbNode")) { - object.reset(new LimbNode(id,element,doc,name)); - } - } - else if (!strncmp(obtype,"Deformer",length)) { - if (!strcmp(classtag.c_str(),"Cluster")) { - object.reset(new Cluster(id,element,doc,name)); - } - else if (!strcmp(classtag.c_str(),"Skin")) { - object.reset(new Skin(id,element,doc,name)); - } - else if (!strcmp(classtag.c_str(), "BlendShape")) { - object.reset(new BlendShape(id, element, doc, name)); - } - else if (!strcmp(classtag.c_str(), "BlendShapeChannel")) { - object.reset(new BlendShapeChannel(id, element, doc, name)); - } - } - else if ( !strncmp( obtype, "Model", length ) ) { - // FK and IK effectors are not supported - if ( strcmp( classtag.c_str(), "IKEffector" ) && strcmp( classtag.c_str(), "FKEffector" ) ) { - object.reset( new Model( id, element, doc, name ) ); - } - } - else if (!strncmp(obtype,"Material",length)) { - object.reset(new Material(id,element,doc,name)); - } - else if (!strncmp(obtype,"Texture",length)) { - object.reset(new Texture(id,element,doc,name)); - } - else if (!strncmp(obtype,"LayeredTexture",length)) { - object.reset(new LayeredTexture(id,element,doc,name)); - } - else if (!strncmp(obtype,"Video",length)) { - object.reset(new Video(id,element,doc,name)); - } - else if (!strncmp(obtype,"AnimationStack",length)) { - object.reset(new AnimationStack(id,element,name,doc)); - } - else if (!strncmp(obtype,"AnimationLayer",length)) { - object.reset(new AnimationLayer(id,element,name,doc)); - } - // note: order matters for these two - else if (!strncmp(obtype,"AnimationCurve",length)) { - object.reset(new AnimationCurve(id,element,name,doc)); - } - else if (!strncmp(obtype,"AnimationCurveNode",length)) { - object.reset(new AnimationCurveNode(id,element,name,doc)); - } - } - catch(std::exception& ex) { - flags &= ~BEING_CONSTRUCTED; - flags |= FAILED_TO_CONSTRUCT; - - if(dieOnError || doc.Settings().strictMode) { - throw; - } - - // note: the error message is already formatted, so raw logging is ok - if(!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_ERROR(ex.what()); - } - return NULL; - } - - if (!object.get()) { - //DOMError("failed to convert element to DOM object, class: " + classtag + ", name: " + name,&element); - } - - flags &= ~BEING_CONSTRUCTED; - return object.get(); -} - -// ------------------------------------------------------------------------------------------------ -Object::Object(uint64_t id, const Element& element, const std::string& name) -: element(element) -, name(name) -, id(id) -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -Object::~Object() -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -FileGlobalSettings::FileGlobalSettings(const Document& doc, std::shared_ptr props) -: props(props) -, doc(doc) -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -FileGlobalSettings::~FileGlobalSettings() -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -Document::Document(const Parser& parser, const ImportSettings& settings) -: settings(settings) -, parser(parser) -{ - // Cannot use array default initialization syntax because vc8 fails on it - for (auto &timeStamp : creationTimeStamp) { - timeStamp = 0; - } - - ReadHeader(); - ReadPropertyTemplates(); - - ReadGlobalSettings(); - - // This order is important, connections need parsed objects to check - // whether connections are ok or not. Objects may not be evaluated yet, - // though, since this may require valid connections. - ReadObjects(); - ReadConnections(); -} - -// ------------------------------------------------------------------------------------------------ -Document::~Document() -{ - for(ObjectMap::value_type& v : objects) { - delete v.second; - } - - for(ConnectionMap::value_type& v : src_connections) { - delete v.second; - } - // |dest_connections| contain the same Connection objects as the |src_connections| -} - -// ------------------------------------------------------------------------------------------------ -static const unsigned int LowerSupportedVersion = 7100; -static const unsigned int UpperSupportedVersion = 7400; - -void Document::ReadHeader() { - // Read ID objects from "Objects" section - const Scope& sc = parser.GetRootScope(); - const Element* const ehead = sc["FBXHeaderExtension"]; - if(!ehead || !ehead->Compound()) { - DOMError("no FBXHeaderExtension dictionary found"); - } - - const Scope& shead = *ehead->Compound(); - fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead,"FBXVersion",ehead),0)); - - // While we may have some success with newer files, we don't support - // the older 6.n fbx format - if(fbxVersion < LowerSupportedVersion ) { - DOMError("unsupported, old format version, supported are only FBX 2011, FBX 2012 and FBX 2013"); - } - if(fbxVersion > UpperSupportedVersion ) { - if(Settings().strictMode) { - DOMError("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013" - " (turn off strict mode to try anyhow) "); - } - else { - DOMWarning("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013," - " trying to read it nevertheless"); - } - } - - const Element* const ecreator = shead["Creator"]; - if(ecreator) { - creator = ParseTokenAsString(GetRequiredToken(*ecreator,0)); - } - - const Element* const etimestamp = shead["CreationTimeStamp"]; - if(etimestamp && etimestamp->Compound()) { - const Scope& stimestamp = *etimestamp->Compound(); - creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Year"),0)); - creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Month"),0)); - creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Day"),0)); - creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Hour"),0)); - creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Minute"),0)); - creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Second"),0)); - creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Millisecond"),0)); - } -} - -// ------------------------------------------------------------------------------------------------ -void Document::ReadGlobalSettings() -{ - const Scope& sc = parser.GetRootScope(); - const Element* const ehead = sc["GlobalSettings"]; - if ( nullptr == ehead || !ehead->Compound() ) { - DOMWarning( "no GlobalSettings dictionary found" ); - globals.reset(new FileGlobalSettings(*this, std::make_shared())); - return; - } - - std::shared_ptr props = GetPropertyTable( *this, "", *ehead, *ehead->Compound(), true ); - - //double v = PropertyGet( *props.get(), std::string("UnitScaleFactor"), 1.0 ); - - if(!props) { - DOMError("GlobalSettings dictionary contains no property table"); - } - - globals.reset(new FileGlobalSettings(*this, props)); -} - -// ------------------------------------------------------------------------------------------------ -void Document::ReadObjects() -{ - // read ID objects from "Objects" section - const Scope& sc = parser.GetRootScope(); - const Element* const eobjects = sc["Objects"]; - if(!eobjects || !eobjects->Compound()) { - DOMError("no Objects dictionary found"); - } - - // add a dummy entry to represent the Model::RootNode object (id 0), - // which is only indirectly defined in the input file - objects[0] = new LazyObject(0L, *eobjects, *this); - - const Scope& sobjects = *eobjects->Compound(); - for(const ElementMap::value_type& el : sobjects.Elements()) { - - // extract ID - const TokenList& tok = el.second->Tokens(); - - if (tok.empty()) { - DOMError("expected ID after object key",el.second); - } - - const char* err; - const uint64_t id = ParseTokenAsID(*tok[0], err); - if(err) { - DOMError(err,el.second); - } - - // id=0 is normally implicit - if(id == 0L) { - DOMError("encountered object with implicitly defined id 0",el.second); - } - - if(objects.find(id) != objects.end()) { - DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second); - } - - objects[id] = new LazyObject(id, *el.second, *this); - - // grab all animation stacks upfront since there is no listing of them - if(!strcmp(el.first.c_str(),"AnimationStack")) { - animationStacks.push_back(id); - } - } -} - -// ------------------------------------------------------------------------------------------------ -void Document::ReadPropertyTemplates() -{ - const Scope& sc = parser.GetRootScope(); - // read property templates from "Definitions" section - const Element* const edefs = sc["Definitions"]; - if(!edefs || !edefs->Compound()) { - DOMWarning("no Definitions dictionary found"); - return; - } - - const Scope& sdefs = *edefs->Compound(); - const ElementCollection otypes = sdefs.GetCollection("ObjectType"); - for(ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) { - const Element& el = *(*it).second; - const Scope* sc = el.Compound(); - if(!sc) { - DOMWarning("expected nested scope in ObjectType, ignoring",&el); - continue; - } - - const TokenList& tok = el.Tokens(); - if(tok.empty()) { - DOMWarning("expected name for ObjectType element, ignoring",&el); - continue; - } - - const std::string& oname = ParseTokenAsString(*tok[0]); - - const ElementCollection templs = sc->GetCollection("PropertyTemplate"); - for(ElementMap::const_iterator it = templs.first; it != templs.second; ++it) { - const Element& el = *(*it).second; - const Scope* sc = el.Compound(); - if(!sc) { - DOMWarning("expected nested scope in PropertyTemplate, ignoring",&el); - continue; - } - - const TokenList& tok = el.Tokens(); - if(tok.empty()) { - DOMWarning("expected name for PropertyTemplate element, ignoring",&el); - continue; - } - - const std::string& pname = ParseTokenAsString(*tok[0]); - - const Element* Properties70 = (*sc)["Properties70"]; - if(Properties70) { - std::shared_ptr props = std::make_shared( - *Properties70,std::shared_ptr(static_cast(NULL)) - ); - - templates[oname+"."+pname] = props; - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -void Document::ReadConnections() -{ - const Scope& sc = parser.GetRootScope(); - // read property templates from "Definitions" section - const Element* const econns = sc["Connections"]; - if(!econns || !econns->Compound()) { - DOMError("no Connections dictionary found"); - } - - uint64_t insertionOrder = 0l; - const Scope& sconns = *econns->Compound(); - const ElementCollection conns = sconns.GetCollection("C"); - for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) { - const Element& el = *(*it).second; - const std::string& type = ParseTokenAsString(GetRequiredToken(el,0)); - - // PP = property-property connection, ignored for now - // (tokens: "PP", ID1, "Property1", ID2, "Property2") - if ( type == "PP" ) { - continue; - } - - const uint64_t src = ParseTokenAsID(GetRequiredToken(el,1)); - const uint64_t dest = ParseTokenAsID(GetRequiredToken(el,2)); - - // OO = object-object connection - // OP = object-property connection, in which case the destination property follows the object ID - const std::string& prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el,3)) : ""); - - if(objects.find(src) == objects.end()) { - DOMWarning("source object for connection does not exist",&el); - continue; - } - - // dest may be 0 (root node) but we added a dummy object before - if(objects.find(dest) == objects.end()) { - DOMWarning("destination object for connection does not exist",&el); - continue; - } - - // add new connection - const Connection* const c = new Connection(insertionOrder++,src,dest,prop,*this); - src_connections.insert(ConnectionMap::value_type(src,c)); - dest_connections.insert(ConnectionMap::value_type(dest,c)); - } -} - -// ------------------------------------------------------------------------------------------------ -const std::vector& Document::AnimationStacks() const -{ - if (!animationStacksResolved.empty() || animationStacks.empty()) { - return animationStacksResolved; - } - - animationStacksResolved.reserve(animationStacks.size()); - for(uint64_t id : animationStacks) { - LazyObject* const lazy = GetObject(id); - const AnimationStack* stack; - if(!lazy || !(stack = lazy->Get())) { - DOMWarning("failed to read AnimationStack object"); - continue; - } - animationStacksResolved.push_back(stack); - } - - return animationStacksResolved; -} - -// ------------------------------------------------------------------------------------------------ -LazyObject* Document::GetObject(uint64_t id) const -{ - ObjectMap::const_iterator it = objects.find(id); - return it == objects.end() ? nullptr : (*it).second; -} - -#define MAX_CLASSNAMES 6 - -// ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap& conns) const -{ - std::vector temp; - - const std::pair range = - conns.equal_range(id); - - temp.reserve(std::distance(range.first,range.second)); - for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) { - temp.push_back((*it).second); - } - - std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare)); - - return temp; // NRVO should handle this -} - -// ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsSequenced(uint64_t id, bool is_src, - const ConnectionMap& conns, - const char* const* classnames, - size_t count) const - -{ - ai_assert(classnames); - ai_assert( count != 0 ); - ai_assert( count <= MAX_CLASSNAMES); - - size_t lengths[MAX_CLASSNAMES]; - - const size_t c = count; - for (size_t i = 0; i < c; ++i) { - lengths[ i ] = strlen(classnames[i]); - } - - std::vector temp; - const std::pair range = - conns.equal_range(id); - - temp.reserve(std::distance(range.first,range.second)); - for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) { - const Token& key = (is_src - ? (*it).second->LazyDestinationObject() - : (*it).second->LazySourceObject() - ).GetElement().KeyToken(); - - const char* obtype = key.begin(); - - for (size_t i = 0; i < c; ++i) { - ai_assert(classnames[i]); - if(static_cast(std::distance(key.begin(),key.end())) == lengths[i] && !strncmp(classnames[i],obtype,lengths[i])) { - obtype = nullptr; - break; - } - } - - if(obtype) { - continue; - } - - temp.push_back((*it).second); - } - - std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare)); - return temp; // NRVO should handle this -} - -// ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsBySourceSequenced(uint64_t source) const -{ - return GetConnectionsSequenced(source, ConnectionsBySource()); -} - -// ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsBySourceSequenced(uint64_t src, const char* classname) const -{ - const char* arr[] = {classname}; - return GetConnectionsBySourceSequenced(src, arr,1); -} - -// ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsBySourceSequenced(uint64_t source, - const char* const* classnames, size_t count) const -{ - return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count); -} - -// ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsByDestinationSequenced(uint64_t dest, - const char* classname) const -{ - const char* arr[] = {classname}; - return GetConnectionsByDestinationSequenced(dest, arr,1); -} - -// ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsByDestinationSequenced(uint64_t dest) const -{ - return GetConnectionsSequenced(dest, ConnectionsByDestination()); -} - -// ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsByDestinationSequenced(uint64_t dest, - const char* const* classnames, size_t count) const - -{ - return GetConnectionsSequenced(dest, false, ConnectionsByDestination(),classnames, count); -} - -// ------------------------------------------------------------------------------------------------ -Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop, - const Document& doc) - -: insertionOrder(insertionOrder) -, prop(prop) -, src(src) -, dest(dest) -, doc(doc) -{ - ai_assert(doc.Objects().find(src) != doc.Objects().end()); - // dest may be 0 (root node) - ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end()); -} - -// ------------------------------------------------------------------------------------------------ -Connection::~Connection() -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -LazyObject& Connection::LazySourceObject() const -{ - LazyObject* const lazy = doc.GetObject(src); - ai_assert(lazy); - return *lazy; -} - -// ------------------------------------------------------------------------------------------------ -LazyObject& Connection::LazyDestinationObject() const -{ - LazyObject* const lazy = doc.GetObject(dest); - ai_assert(lazy); - return *lazy; -} - -// ------------------------------------------------------------------------------------------------ -const Object* Connection::SourceObject() const -{ - LazyObject* const lazy = doc.GetObject(src); - ai_assert(lazy); - return lazy->Get(); -} - -// ------------------------------------------------------------------------------------------------ -const Object* Connection::DestinationObject() const -{ - LazyObject* const lazy = doc.GetObject(dest); - ai_assert(lazy); - return lazy->Get(); -} - -} // !FBX -} // !Assimp - -#endif diff --git a/thirdparty/assimp/code/FBXDocument.h b/thirdparty/assimp/code/FBXDocument.h deleted file mode 100644 index c849defdcd..0000000000 --- a/thirdparty/assimp/code/FBXDocument.h +++ /dev/null @@ -1,1180 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXDocument.h - * @brief FBX DOM - */ -#ifndef INCLUDED_AI_FBX_DOCUMENT_H -#define INCLUDED_AI_FBX_DOCUMENT_H - -#include -#include -#include -#include "FBXProperties.h" -#include "FBXParser.h" - -#define _AI_CONCAT(a,b) a ## b -#define AI_CONCAT(a,b) _AI_CONCAT(a,b) - -namespace Assimp { -namespace FBX { - -class Parser; -class Object; -struct ImportSettings; - -class PropertyTable; -class Document; -class Material; -class ShapeGeometry; -class LineGeometry; -class Geometry; - -class Video; - -class AnimationCurve; -class AnimationCurveNode; -class AnimationLayer; -class AnimationStack; - -class BlendShapeChannel; -class BlendShape; -class Skin; -class Cluster; - - -/** Represents a delay-parsed FBX objects. Many objects in the scene - * are not needed by assimp, so it makes no sense to parse them - * upfront. */ -class LazyObject { -public: - LazyObject(uint64_t id, const Element& element, const Document& doc); - - ~LazyObject(); - - const Object* Get(bool dieOnError = false); - - template - const T* Get(bool dieOnError = false) { - const Object* const ob = Get(dieOnError); - return ob ? dynamic_cast(ob) : NULL; - } - - uint64_t ID() const { - return id; - } - - bool IsBeingConstructed() const { - return (flags & BEING_CONSTRUCTED) != 0; - } - - bool FailedToConstruct() const { - return (flags & FAILED_TO_CONSTRUCT) != 0; - } - - const Element& GetElement() const { - return element; - } - - const Document& GetDocument() const { - return doc; - } - -private: - const Document& doc; - const Element& element; - std::unique_ptr object; - - const uint64_t id; - - enum Flags { - BEING_CONSTRUCTED = 0x1, - FAILED_TO_CONSTRUCT = 0x2 - }; - - unsigned int flags; -}; - -/** Base class for in-memory (DOM) representations of FBX objects */ -class Object { -public: - Object(uint64_t id, const Element& element, const std::string& name); - - virtual ~Object(); - - const Element& SourceElement() const { - return element; - } - - const std::string& Name() const { - return name; - } - - uint64_t ID() const { - return id; - } - -protected: - const Element& element; - const std::string name; - const uint64_t id; -}; - -/** DOM class for generic FBX NoteAttribute blocks. NoteAttribute's just hold a property table, - * fixed members are added by deriving classes. */ -class NodeAttribute : public Object { -public: - NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name); - - virtual ~NodeAttribute(); - - const PropertyTable& Props() const { - ai_assert(props.get()); - return *props.get(); - } - -private: - std::shared_ptr props; -}; - -/** DOM base class for FBX camera settings attached to a node */ -class CameraSwitcher : public NodeAttribute { -public: - CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name); - - virtual ~CameraSwitcher(); - - int CameraID() const { - return cameraId; - } - - const std::string& CameraName() const { - return cameraName; - } - - const std::string& CameraIndexName() const { - return cameraIndexName; - } - -private: - int cameraId; - std::string cameraName; - std::string cameraIndexName; -}; - -#define fbx_stringize(a) #a - -#define fbx_simple_property(name, type, default_value) \ - type name() const { \ - return PropertyGet(Props(), fbx_stringize(name), (default_value)); \ - } - -// XXX improve logging -#define fbx_simple_enum_property(name, type, default_value) \ - type name() const { \ - const int ival = PropertyGet(Props(), fbx_stringize(name), static_cast(default_value)); \ - if (ival < 0 || ival >= AI_CONCAT(type, _MAX)) { \ - ai_assert(static_cast(default_value) >= 0 && static_cast(default_value) < AI_CONCAT(type, _MAX)); \ - return static_cast(default_value); \ - } \ - return static_cast(ival); \ -} - - -/** DOM base class for FBX cameras attached to a node */ -class Camera : public NodeAttribute { -public: - Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name); - - virtual ~Camera(); - - fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0)) - fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0)) - fbx_simple_property(InterestPosition, aiVector3D, aiVector3D(0,0,0)) - - fbx_simple_property(AspectWidth, float, 1.0f) - fbx_simple_property(AspectHeight, float, 1.0f) - fbx_simple_property(FilmWidth, float, 1.0f) - fbx_simple_property(FilmHeight, float, 1.0f) - - fbx_simple_property(NearPlane, float, 0.1f) - fbx_simple_property(FarPlane, float, 100.0f) - - fbx_simple_property(FilmAspectRatio, float, 1.0f) - fbx_simple_property(ApertureMode, int, 0) - - fbx_simple_property(FieldOfView, float, 1.0f) - fbx_simple_property(FocalLength, float, 1.0f) -}; - -/** DOM base class for FBX null markers attached to a node */ -class Null : public NodeAttribute { -public: - Null(uint64_t id, const Element& element, const Document& doc, const std::string& name); - virtual ~Null(); -}; - -/** DOM base class for FBX limb node markers attached to a node */ -class LimbNode : public NodeAttribute { -public: - LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name); - virtual ~LimbNode(); -}; - -/** DOM base class for FBX lights attached to a node */ -class Light : public NodeAttribute { -public: - Light(uint64_t id, const Element& element, const Document& doc, const std::string& name); - virtual ~Light(); - - enum Type - { - Type_Point, - Type_Directional, - Type_Spot, - Type_Area, - Type_Volume, - - Type_MAX // end-of-enum sentinel - }; - - enum Decay - { - Decay_None, - Decay_Linear, - Decay_Quadratic, - Decay_Cubic, - - Decay_MAX // end-of-enum sentinel - }; - - fbx_simple_property(Color, aiVector3D, aiVector3D(1,1,1)) - fbx_simple_enum_property(LightType, Type, 0) - fbx_simple_property(CastLightOnObject, bool, false) - fbx_simple_property(DrawVolumetricLight, bool, true) - fbx_simple_property(DrawGroundProjection, bool, true) - fbx_simple_property(DrawFrontFacingVolumetricLight, bool, false) - fbx_simple_property(Intensity, float, 100.0f) - fbx_simple_property(InnerAngle, float, 0.0f) - fbx_simple_property(OuterAngle, float, 45.0f) - fbx_simple_property(Fog, int, 50) - fbx_simple_enum_property(DecayType, Decay, 2) - fbx_simple_property(DecayStart, float, 1.0f) - fbx_simple_property(FileName, std::string, "") - - fbx_simple_property(EnableNearAttenuation, bool, false) - fbx_simple_property(NearAttenuationStart, float, 0.0f) - fbx_simple_property(NearAttenuationEnd, float, 0.0f) - fbx_simple_property(EnableFarAttenuation, bool, false) - fbx_simple_property(FarAttenuationStart, float, 0.0f) - fbx_simple_property(FarAttenuationEnd, float, 0.0f) - - fbx_simple_property(CastShadows, bool, true) - fbx_simple_property(ShadowColor, aiVector3D, aiVector3D(0,0,0)) - - fbx_simple_property(AreaLightShape, int, 0) - - fbx_simple_property(LeftBarnDoor, float, 20.0f) - fbx_simple_property(RightBarnDoor, float, 20.0f) - fbx_simple_property(TopBarnDoor, float, 20.0f) - fbx_simple_property(BottomBarnDoor, float, 20.0f) - fbx_simple_property(EnableBarnDoor, bool, true) -}; - -/** DOM base class for FBX models (even though its semantics are more "node" than "model" */ -class Model : public Object { -public: - enum RotOrder { - RotOrder_EulerXYZ = 0, - RotOrder_EulerXZY, - RotOrder_EulerYZX, - RotOrder_EulerYXZ, - RotOrder_EulerZXY, - RotOrder_EulerZYX, - - RotOrder_SphericXYZ, - - RotOrder_MAX // end-of-enum sentinel - }; - - enum TransformInheritance { - TransformInheritance_RrSs = 0, - TransformInheritance_RSrs, - TransformInheritance_Rrs, - - TransformInheritance_MAX // end-of-enum sentinel - }; - - Model(uint64_t id, const Element& element, const Document& doc, const std::string& name); - - virtual ~Model(); - - fbx_simple_property(QuaternionInterpolate, int, 0) - - fbx_simple_property(RotationOffset, aiVector3D, aiVector3D()) - fbx_simple_property(RotationPivot, aiVector3D, aiVector3D()) - fbx_simple_property(ScalingOffset, aiVector3D, aiVector3D()) - fbx_simple_property(ScalingPivot, aiVector3D, aiVector3D()) - fbx_simple_property(TranslationActive, bool, false) - - fbx_simple_property(TranslationMin, aiVector3D, aiVector3D()) - fbx_simple_property(TranslationMax, aiVector3D, aiVector3D()) - - fbx_simple_property(TranslationMinX, bool, false) - fbx_simple_property(TranslationMaxX, bool, false) - fbx_simple_property(TranslationMinY, bool, false) - fbx_simple_property(TranslationMaxY, bool, false) - fbx_simple_property(TranslationMinZ, bool, false) - fbx_simple_property(TranslationMaxZ, bool, false) - - fbx_simple_enum_property(RotationOrder, RotOrder, 0) - fbx_simple_property(RotationSpaceForLimitOnly, bool, false) - fbx_simple_property(RotationStiffnessX, float, 0.0f) - fbx_simple_property(RotationStiffnessY, float, 0.0f) - fbx_simple_property(RotationStiffnessZ, float, 0.0f) - fbx_simple_property(AxisLen, float, 0.0f) - - fbx_simple_property(PreRotation, aiVector3D, aiVector3D()) - fbx_simple_property(PostRotation, aiVector3D, aiVector3D()) - fbx_simple_property(RotationActive, bool, false) - - fbx_simple_property(RotationMin, aiVector3D, aiVector3D()) - fbx_simple_property(RotationMax, aiVector3D, aiVector3D()) - - fbx_simple_property(RotationMinX, bool, false) - fbx_simple_property(RotationMaxX, bool, false) - fbx_simple_property(RotationMinY, bool, false) - fbx_simple_property(RotationMaxY, bool, false) - fbx_simple_property(RotationMinZ, bool, false) - fbx_simple_property(RotationMaxZ, bool, false) - fbx_simple_enum_property(InheritType, TransformInheritance, 0) - - fbx_simple_property(ScalingActive, bool, false) - fbx_simple_property(ScalingMin, aiVector3D, aiVector3D()) - fbx_simple_property(ScalingMax, aiVector3D, aiVector3D(1.f,1.f,1.f)) - fbx_simple_property(ScalingMinX, bool, false) - fbx_simple_property(ScalingMaxX, bool, false) - fbx_simple_property(ScalingMinY, bool, false) - fbx_simple_property(ScalingMaxY, bool, false) - fbx_simple_property(ScalingMinZ, bool, false) - fbx_simple_property(ScalingMaxZ, bool, false) - - fbx_simple_property(GeometricTranslation, aiVector3D, aiVector3D()) - fbx_simple_property(GeometricRotation, aiVector3D, aiVector3D()) - fbx_simple_property(GeometricScaling, aiVector3D, aiVector3D(1.f, 1.f, 1.f)) - - fbx_simple_property(MinDampRangeX, float, 0.0f) - fbx_simple_property(MinDampRangeY, float, 0.0f) - fbx_simple_property(MinDampRangeZ, float, 0.0f) - fbx_simple_property(MaxDampRangeX, float, 0.0f) - fbx_simple_property(MaxDampRangeY, float, 0.0f) - fbx_simple_property(MaxDampRangeZ, float, 0.0f) - - fbx_simple_property(MinDampStrengthX, float, 0.0f) - fbx_simple_property(MinDampStrengthY, float, 0.0f) - fbx_simple_property(MinDampStrengthZ, float, 0.0f) - fbx_simple_property(MaxDampStrengthX, float, 0.0f) - fbx_simple_property(MaxDampStrengthY, float, 0.0f) - fbx_simple_property(MaxDampStrengthZ, float, 0.0f) - - fbx_simple_property(PreferredAngleX, float, 0.0f) - fbx_simple_property(PreferredAngleY, float, 0.0f) - fbx_simple_property(PreferredAngleZ, float, 0.0f) - - fbx_simple_property(Show, bool, true) - fbx_simple_property(LODBox, bool, false) - fbx_simple_property(Freeze, bool, false) - - const std::string& Shading() const { - return shading; - } - - const std::string& Culling() const { - return culling; - } - - const PropertyTable& Props() const { - ai_assert(props.get()); - return *props.get(); - } - - /** Get material links */ - const std::vector& GetMaterials() const { - return materials; - } - - /** Get geometry links */ - const std::vector& GetGeometry() const { - return geometry; - } - - /** Get node attachments */ - const std::vector& GetAttributes() const { - return attributes; - } - - /** convenience method to check if the node has a Null node marker */ - bool IsNull() const; - -private: - void ResolveLinks(const Element& element, const Document& doc); - -private: - std::vector materials; - std::vector geometry; - std::vector attributes; - - std::string shading; - std::string culling; - std::shared_ptr props; -}; - -/** DOM class for generic FBX textures */ -class Texture : public Object { -public: - Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name); - - virtual ~Texture(); - - const std::string& Type() const { - return type; - } - - const std::string& FileName() const { - return fileName; - } - - const std::string& RelativeFilename() const { - return relativeFileName; - } - - const std::string& AlphaSource() const { - return alphaSource; - } - - const aiVector2D& UVTranslation() const { - return uvTrans; - } - - const aiVector2D& UVScaling() const { - return uvScaling; - } - - const PropertyTable& Props() const { - ai_assert(props.get()); - return *props.get(); - } - - // return a 4-tuple - const unsigned int* Crop() const { - return crop; - } - - const Video* Media() const { - return media; - } - -private: - aiVector2D uvTrans; - aiVector2D uvScaling; - - std::string type; - std::string relativeFileName; - std::string fileName; - std::string alphaSource; - std::shared_ptr props; - - unsigned int crop[4]; - - const Video* media; -}; - -/** DOM class for layered FBX textures */ -class LayeredTexture : public Object { -public: - LayeredTexture(uint64_t id, const Element& element, const Document& doc, const std::string& name); - virtual ~LayeredTexture(); - - // Can only be called after construction of the layered texture object due to construction flag. - void fillTexture(const Document& doc); - - enum BlendMode { - BlendMode_Translucent, - BlendMode_Additive, - BlendMode_Modulate, - BlendMode_Modulate2, - BlendMode_Over, - BlendMode_Normal, - BlendMode_Dissolve, - BlendMode_Darken, - BlendMode_ColorBurn, - BlendMode_LinearBurn, - BlendMode_DarkerColor, - BlendMode_Lighten, - BlendMode_Screen, - BlendMode_ColorDodge, - BlendMode_LinearDodge, - BlendMode_LighterColor, - BlendMode_SoftLight, - BlendMode_HardLight, - BlendMode_VividLight, - BlendMode_LinearLight, - BlendMode_PinLight, - BlendMode_HardMix, - BlendMode_Difference, - BlendMode_Exclusion, - BlendMode_Subtract, - BlendMode_Divide, - BlendMode_Hue, - BlendMode_Saturation, - BlendMode_Color, - BlendMode_Luminosity, - BlendMode_Overlay, - BlendMode_BlendModeCount - }; - - const Texture* getTexture(int index=0) const - { - return textures[index]; - - } - int textureCount() const { - return static_cast(textures.size()); - } - BlendMode GetBlendMode() const - { - return blendMode; - } - float Alpha() - { - return alpha; - } -private: - std::vector textures; - BlendMode blendMode; - float alpha; -}; - -typedef std::fbx_unordered_map TextureMap; -typedef std::fbx_unordered_map LayeredTextureMap; - - -/** DOM class for generic FBX videos */ -class Video : public Object { -public: - Video(uint64_t id, const Element& element, const Document& doc, const std::string& name); - - virtual ~Video(); - - const std::string& Type() const { - return type; - } - - const std::string& FileName() const { - return fileName; - } - - const std::string& RelativeFilename() const { - return relativeFileName; - } - - const PropertyTable& Props() const { - ai_assert(props.get()); - return *props.get(); - } - - const uint8_t* Content() const { - ai_assert(content); - return content; - } - - uint32_t ContentLength() const { - return contentLength; - } - - uint8_t* RelinquishContent() { - uint8_t* ptr = content; - content = 0; - return ptr; - } - -private: - std::string type; - std::string relativeFileName; - std::string fileName; - std::shared_ptr props; - - uint32_t contentLength; - uint8_t* content; -}; - -/** DOM class for generic FBX materials */ -class Material : public Object { -public: - Material(uint64_t id, const Element& element, const Document& doc, const std::string& name); - - virtual ~Material(); - - const std::string& GetShadingModel() const { - return shading; - } - - bool IsMultilayer() const { - return multilayer; - } - - const PropertyTable& Props() const { - ai_assert(props.get()); - return *props.get(); - } - - const TextureMap& Textures() const { - return textures; - } - - const LayeredTextureMap& LayeredTextures() const { - return layeredTextures; - } - -private: - std::string shading; - bool multilayer; - std::shared_ptr props; - - TextureMap textures; - LayeredTextureMap layeredTextures; -}; - -typedef std::vector KeyTimeList; -typedef std::vector KeyValueList; - -/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefor) */ -class AnimationCurve : public Object { -public: - AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& doc); - virtual ~AnimationCurve(); - - /** get list of keyframe positions (time). - * Invariant: |GetKeys()| > 0 */ - const KeyTimeList& GetKeys() const { - return keys; - } - - /** get list of keyframe values. - * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/ - const KeyValueList& GetValues() const { - return values; - } - - const std::vector& GetAttributes() const { - return attributes; - } - - const std::vector& GetFlags() const { - return flags; - } - -private: - KeyTimeList keys; - KeyValueList values; - std::vector attributes; - std::vector flags; -}; - -// property-name -> animation curve -typedef std::map AnimationCurveMap; - -/** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */ -class AnimationCurveNode : public Object { -public: - /* the optional white list specifies a list of property names for which the caller - wants animations for. If the curve node does not match one of these, std::range_error - will be thrown. */ - AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, const Document& doc, - const char* const * target_prop_whitelist = NULL, size_t whitelist_size = 0); - - virtual ~AnimationCurveNode(); - - const PropertyTable& Props() const { - ai_assert(props.get()); - return *props.get(); - } - - - const AnimationCurveMap& Curves() const; - - /** Object the curve is assigned to, this can be NULL if the - * target object has no DOM representation or could not - * be read for other reasons.*/ - const Object* Target() const { - return target; - } - - const Model* TargetAsModel() const { - return dynamic_cast(target); - } - - const NodeAttribute* TargetAsNodeAttribute() const { - return dynamic_cast(target); - } - - /** Property of Target() that is being animated*/ - const std::string& TargetProperty() const { - return prop; - } - -private: - const Object* target; - std::shared_ptr props; - mutable AnimationCurveMap curves; - - std::string prop; - const Document& doc; -}; - -typedef std::vector AnimationCurveNodeList; - -/** Represents a FBX animation layer (i.e. a list of node animations) */ -class AnimationLayer : public Object { -public: - AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc); - virtual ~AnimationLayer(); - - const PropertyTable& Props() const { - ai_assert(props.get()); - return *props.get(); - } - - /* the optional white list specifies a list of property names for which the caller - wants animations for. Curves not matching this list will not be added to the - animation layer. */ - AnimationCurveNodeList Nodes(const char* const * target_prop_whitelist = nullptr, size_t whitelist_size = 0) const; - -private: - std::shared_ptr props; - const Document& doc; -}; - -typedef std::vector AnimationLayerList; - -/** Represents a FBX animation stack (i.e. a list of animation layers) */ -class AnimationStack : public Object { -public: - AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc); - virtual ~AnimationStack(); - - fbx_simple_property(LocalStart, int64_t, 0L) - fbx_simple_property(LocalStop, int64_t, 0L) - fbx_simple_property(ReferenceStart, int64_t, 0L) - fbx_simple_property(ReferenceStop, int64_t, 0L) - - const PropertyTable& Props() const { - ai_assert(props.get()); - return *props.get(); - } - - const AnimationLayerList& Layers() const { - return layers; - } - -private: - std::shared_ptr props; - AnimationLayerList layers; -}; - - -/** DOM class for deformers */ -class Deformer : public Object { -public: - Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name); - virtual ~Deformer(); - - const PropertyTable& Props() const { - ai_assert(props.get()); - return *props.get(); - } - -private: - std::shared_ptr props; -}; - -typedef std::vector WeightArray; -typedef std::vector WeightIndexArray; - - -/** DOM class for BlendShapeChannel deformers */ -class BlendShapeChannel : public Deformer { -public: - BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name); - - virtual ~BlendShapeChannel(); - - float DeformPercent() const { - return percent; - } - - const WeightArray& GetFullWeights() const { - return fullWeights; - } - - const std::vector& GetShapeGeometries() const { - return shapeGeometries; - } - -private: - float percent; - WeightArray fullWeights; - std::vector shapeGeometries; -}; - -/** DOM class for BlendShape deformers */ -class BlendShape : public Deformer { -public: - BlendShape(uint64_t id, const Element& element, const Document& doc, const std::string& name); - - virtual ~BlendShape(); - - const std::vector& BlendShapeChannels() const { - return blendShapeChannels; - } - -private: - std::vector blendShapeChannels; -}; - -/** DOM class for skin deformer clusters (aka sub-deformers) */ -class Cluster : public Deformer { -public: - Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name); - - virtual ~Cluster(); - - /** get the list of deformer weights associated with this cluster. - * Use #GetIndices() to get the associated vertices. Both arrays - * have the same size (and may also be empty). */ - const WeightArray& GetWeights() const { - return weights; - } - - /** get indices into the vertex data of the geometry associated - * with this cluster. Use #GetWeights() to get the associated weights. - * Both arrays have the same size (and may also be empty). */ - const WeightIndexArray& GetIndices() const { - return indices; - } - - /** */ - const aiMatrix4x4& Transform() const { - return transform; - } - - const aiMatrix4x4& TransformLink() const { - return transformLink; - } - - const Model* TargetNode() const { - return node; - } - -private: - WeightArray weights; - WeightIndexArray indices; - - aiMatrix4x4 transform; - aiMatrix4x4 transformLink; - - const Model* node; -}; - -/** DOM class for skin deformers */ -class Skin : public Deformer { -public: - Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name); - - virtual ~Skin(); - - float DeformAccuracy() const { - return accuracy; - } - - const std::vector& Clusters() const { - return clusters; - } - -private: - float accuracy; - std::vector clusters; -}; - -/** Represents a link between two FBX objects. */ -class Connection { -public: - Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop, const Document& doc); - - ~Connection(); - - // note: a connection ensures that the source and dest objects exist, but - // not that they have DOM representations, so the return value of one of - // these functions can still be NULL. - const Object* SourceObject() const; - const Object* DestinationObject() const; - - // these, however, are always guaranteed to be valid - LazyObject& LazySourceObject() const; - LazyObject& LazyDestinationObject() const; - - - /** return the name of the property the connection is attached to. - * this is an empty string for object to object (OO) connections. */ - const std::string& PropertyName() const { - return prop; - } - - uint64_t InsertionOrder() const { - return insertionOrder; - } - - int CompareTo(const Connection* c) const { - ai_assert( nullptr != c ); - - // note: can't subtract because this would overflow uint64_t - if(InsertionOrder() > c->InsertionOrder()) { - return 1; - } - else if(InsertionOrder() < c->InsertionOrder()) { - return -1; - } - return 0; - } - - bool Compare(const Connection* c) const { - ai_assert( nullptr != c ); - - return InsertionOrder() < c->InsertionOrder(); - } - -public: - uint64_t insertionOrder; - const std::string prop; - - uint64_t src, dest; - const Document& doc; -}; - -// XXX again, unique_ptr would be useful. shared_ptr is too -// bloated since the objects have a well-defined single owner -// during their entire lifetime (Document). FBX files have -// up to many thousands of objects (most of which we never use), -// so the memory overhead for them should be kept at a minimum. -typedef std::map ObjectMap; -typedef std::fbx_unordered_map > PropertyTemplateMap; - -typedef std::multimap ConnectionMap; - -/** DOM class for global document settings, a single instance per document can - * be accessed via Document.Globals(). */ -class FileGlobalSettings { -public: - FileGlobalSettings(const Document& doc, std::shared_ptr props); - - ~FileGlobalSettings(); - - const PropertyTable& Props() const { - ai_assert(props.get()); - return *props.get(); - } - - const Document& GetDocument() const { - return doc; - } - - fbx_simple_property(UpAxis, int, 1) - fbx_simple_property(UpAxisSign, int, 1) - fbx_simple_property(FrontAxis, int, 2) - fbx_simple_property(FrontAxisSign, int, 1) - fbx_simple_property(CoordAxis, int, 0) - fbx_simple_property(CoordAxisSign, int, 1) - fbx_simple_property(OriginalUpAxis, int, 0) - fbx_simple_property(OriginalUpAxisSign, int, 1) - fbx_simple_property(UnitScaleFactor, float, 1) - fbx_simple_property(OriginalUnitScaleFactor, float, 1) - fbx_simple_property(AmbientColor, aiVector3D, aiVector3D(0,0,0)) - fbx_simple_property(DefaultCamera, std::string, "") - - - enum FrameRate { - FrameRate_DEFAULT = 0, - FrameRate_120 = 1, - FrameRate_100 = 2, - FrameRate_60 = 3, - FrameRate_50 = 4, - FrameRate_48 = 5, - FrameRate_30 = 6, - FrameRate_30_DROP = 7, - FrameRate_NTSC_DROP_FRAME = 8, - FrameRate_NTSC_FULL_FRAME = 9, - FrameRate_PAL = 10, - FrameRate_CINEMA = 11, - FrameRate_1000 = 12, - FrameRate_CINEMA_ND = 13, - FrameRate_CUSTOM = 14, - - FrameRate_MAX// end-of-enum sentinel - }; - - fbx_simple_enum_property(TimeMode, FrameRate, FrameRate_DEFAULT) - fbx_simple_property(TimeSpanStart, uint64_t, 0L) - fbx_simple_property(TimeSpanStop, uint64_t, 0L) - fbx_simple_property(CustomFrameRate, float, -1.0f) - -private: - std::shared_ptr props; - const Document& doc; -}; - -/** DOM root for a FBX file */ -class Document { -public: - Document(const Parser& parser, const ImportSettings& settings); - - ~Document(); - - LazyObject* GetObject(uint64_t id) const; - - bool IsBinary() const { - return parser.IsBinary(); - } - - unsigned int FBXVersion() const { - return fbxVersion; - } - - const std::string& Creator() const { - return creator; - } - - // elements (in this order): Year, Month, Day, Hour, Second, Millisecond - const unsigned int* CreationTimeStamp() const { - return creationTimeStamp; - } - - const FileGlobalSettings& GlobalSettings() const { - ai_assert(globals.get()); - return *globals.get(); - } - - const PropertyTemplateMap& Templates() const { - return templates; - } - - const ObjectMap& Objects() const { - return objects; - } - - const ImportSettings& Settings() const { - return settings; - } - - const ConnectionMap& ConnectionsBySource() const { - return src_connections; - } - - const ConnectionMap& ConnectionsByDestination() const { - return dest_connections; - } - - // note: the implicit rule in all DOM classes is to always resolve - // from destination to source (since the FBX object hierarchy is, - // with very few exceptions, a DAG, this avoids cycles). In all - // cases that may involve back-facing edges in the object graph, - // use LazyObject::IsBeingConstructed() to check. - - std::vector GetConnectionsBySourceSequenced(uint64_t source) const; - std::vector GetConnectionsByDestinationSequenced(uint64_t dest) const; - - std::vector GetConnectionsBySourceSequenced(uint64_t source, const char* classname) const; - std::vector GetConnectionsByDestinationSequenced(uint64_t dest, const char* classname) const; - - std::vector GetConnectionsBySourceSequenced(uint64_t source, - const char* const* classnames, size_t count) const; - std::vector GetConnectionsByDestinationSequenced(uint64_t dest, - const char* const* classnames, - size_t count) const; - - const std::vector& AnimationStacks() const; - -private: - std::vector GetConnectionsSequenced(uint64_t id, const ConnectionMap&) const; - std::vector GetConnectionsSequenced(uint64_t id, bool is_src, - const ConnectionMap&, - const char* const* classnames, - size_t count) const; - void ReadHeader(); - void ReadObjects(); - void ReadPropertyTemplates(); - void ReadConnections(); - void ReadGlobalSettings(); - -private: - const ImportSettings& settings; - - ObjectMap objects; - const Parser& parser; - - PropertyTemplateMap templates; - ConnectionMap src_connections; - ConnectionMap dest_connections; - - unsigned int fbxVersion; - std::string creator; - unsigned int creationTimeStamp[7]; - - std::vector animationStacks; - mutable std::vector animationStacksResolved; - - std::unique_ptr globals; -}; - -} // Namespace FBX -} // Namespace Assimp - -#endif // INCLUDED_AI_FBX_DOCUMENT_H diff --git a/thirdparty/assimp/code/FBXDocumentUtil.cpp b/thirdparty/assimp/code/FBXDocumentUtil.cpp deleted file mode 100644 index f84691479a..0000000000 --- a/thirdparty/assimp/code/FBXDocumentUtil.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXDocumentUtil.cpp - * @brief Implementation of the FBX DOM utility functions declared in FBXDocumentUtil.h - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -#include "FBXParser.h" -#include "FBXDocument.h" -#include "FBXUtil.h" -#include "FBXDocumentUtil.h" -#include "FBXProperties.h" - - -namespace Assimp { -namespace FBX { -namespace Util { - -// ------------------------------------------------------------------------------------------------ -// signal DOM construction error, this is always unrecoverable. Throws DeadlyImportError. -void DOMError(const std::string& message, const Token& token) -{ - throw DeadlyImportError(Util::AddTokenText("FBX-DOM",message,&token)); -} - -// ------------------------------------------------------------------------------------------------ -void DOMError(const std::string& message, const Element* element /*= NULL*/) -{ - if(element) { - DOMError(message,element->KeyToken()); - } - throw DeadlyImportError("FBX-DOM " + message); -} - - -// ------------------------------------------------------------------------------------------------ -// print warning, do return -void DOMWarning(const std::string& message, const Token& token) -{ - if(DefaultLogger::get()) { - ASSIMP_LOG_WARN(Util::AddTokenText("FBX-DOM",message,&token)); - } -} - -// ------------------------------------------------------------------------------------------------ -void DOMWarning(const std::string& message, const Element* element /*= NULL*/) -{ - if(element) { - DOMWarning(message,element->KeyToken()); - return; - } - if(DefaultLogger::get()) { - ASSIMP_LOG_WARN("FBX-DOM: " + message); - } -} - - -// ------------------------------------------------------------------------------------------------ -// fetch a property table and the corresponding property template -std::shared_ptr GetPropertyTable(const Document& doc, - const std::string& templateName, - const Element &element, - const Scope& sc, - bool no_warn /*= false*/) -{ - const Element* const Properties70 = sc["Properties70"]; - std::shared_ptr templateProps = std::shared_ptr( - static_cast(NULL)); - - if(templateName.length()) { - PropertyTemplateMap::const_iterator it = doc.Templates().find(templateName); - if(it != doc.Templates().end()) { - templateProps = (*it).second; - } - } - - if(!Properties70 || !Properties70->Compound()) { - if(!no_warn) { - DOMWarning("property table (Properties70) not found",&element); - } - if(templateProps) { - return templateProps; - } - else { - return std::make_shared(); - } - } - return std::make_shared(*Properties70,templateProps); -} -} // !Util -} // !FBX -} // !Assimp - -#endif diff --git a/thirdparty/assimp/code/FBXDocumentUtil.h b/thirdparty/assimp/code/FBXDocumentUtil.h deleted file mode 100644 index 2450109e59..0000000000 --- a/thirdparty/assimp/code/FBXDocumentUtil.h +++ /dev/null @@ -1,120 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2012, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXDocumentUtil.h - * @brief FBX internal utilities used by the DOM reading code - */ -#ifndef INCLUDED_AI_FBX_DOCUMENT_UTIL_H -#define INCLUDED_AI_FBX_DOCUMENT_UTIL_H - -#include -#include -#include -#include "FBXDocument.h" - -struct Token; -struct Element; - -namespace Assimp { -namespace FBX { -namespace Util { - -/* DOM/Parse error reporting - does not return */ -AI_WONT_RETURN void DOMError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX; -AI_WONT_RETURN void DOMError(const std::string& message, const Element* element = NULL) AI_WONT_RETURN_SUFFIX; - -// does return -void DOMWarning(const std::string& message, const Token& token); -void DOMWarning(const std::string& message, const Element* element = NULL); - - -// fetch a property table and the corresponding property template -std::shared_ptr GetPropertyTable(const Document& doc, - const std::string& templateName, - const Element &element, - const Scope& sc, - bool no_warn = false); - -// ------------------------------------------------------------------------------------------------ -template -inline -const T* ProcessSimpleConnection(const Connection& con, - bool is_object_property_conn, - const char* name, - const Element& element, - const char** propNameOut = nullptr) -{ - if (is_object_property_conn && !con.PropertyName().length()) { - DOMWarning("expected incoming " + std::string(name) + - " link to be an object-object connection, ignoring", - &element - ); - return nullptr; - } - else if (!is_object_property_conn && con.PropertyName().length()) { - DOMWarning("expected incoming " + std::string(name) + - " link to be an object-property connection, ignoring", - &element - ); - return nullptr; - } - - if(is_object_property_conn && propNameOut) { - // note: this is ok, the return value of PropertyValue() is guaranteed to - // remain valid and unchanged as long as the document exists. - *propNameOut = con.PropertyName().c_str(); - } - - const Object* const ob = con.SourceObject(); - if(!ob) { - DOMWarning("failed to read source object for incoming " + std::string(name) + - " link, ignoring", - &element); - return nullptr; - } - - return dynamic_cast(ob); -} - -} //!Util -} //!FBX -} //!Assimp - -#endif diff --git a/thirdparty/assimp/code/FBXExportNode.cpp b/thirdparty/assimp/code/FBXExportNode.cpp deleted file mode 100644 index e5215466a1..0000000000 --- a/thirdparty/assimp/code/FBXExportNode.cpp +++ /dev/null @@ -1,568 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -#ifndef ASSIMP_BUILD_NO_EXPORT -#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER - -#include "FBXExportNode.h" -#include "FBXCommon.h" - -#include // StreamWriterLE -#include // DeadlyExportError -#include -#include // ai_snprintf - -#include -#include -#include // ostringstream -#include // shared_ptr - -// AddP70 helpers... there's no usable pattern here, -// so all are defined as separate functions. -// Even "animatable" properties are often completely different -// from the standard (nonanimated) property definition, -// so they are specified with an 'A' suffix. - -void FBX::Node::AddP70int( - const std::string& name, int32_t value -) { - FBX::Node n("P"); - n.AddProperties(name, "int", "Integer", "", value); - AddChild(n); -} - -void FBX::Node::AddP70bool( - const std::string& name, bool value -) { - FBX::Node n("P"); - n.AddProperties(name, "bool", "", "", int32_t(value)); - AddChild(n); -} - -void FBX::Node::AddP70double( - const std::string& name, double value -) { - FBX::Node n("P"); - n.AddProperties(name, "double", "Number", "", value); - AddChild(n); -} - -void FBX::Node::AddP70numberA( - const std::string& name, double value -) { - FBX::Node n("P"); - n.AddProperties(name, "Number", "", "A", value); - AddChild(n); -} - -void FBX::Node::AddP70color( - const std::string& name, double r, double g, double b -) { - FBX::Node n("P"); - n.AddProperties(name, "ColorRGB", "Color", "", r, g, b); - AddChild(n); -} - -void FBX::Node::AddP70colorA( - const std::string& name, double r, double g, double b -) { - FBX::Node n("P"); - n.AddProperties(name, "Color", "", "A", r, g, b); - AddChild(n); -} - -void FBX::Node::AddP70vector( - const std::string& name, double x, double y, double z -) { - FBX::Node n("P"); - n.AddProperties(name, "Vector3D", "Vector", "", x, y, z); - AddChild(n); -} - -void FBX::Node::AddP70vectorA( - const std::string& name, double x, double y, double z -) { - FBX::Node n("P"); - n.AddProperties(name, "Vector", "", "A", x, y, z); - AddChild(n); -} - -void FBX::Node::AddP70string( - const std::string& name, const std::string& value -) { - FBX::Node n("P"); - n.AddProperties(name, "KString", "", "", value); - AddChild(n); -} - -void FBX::Node::AddP70enum( - const std::string& name, int32_t value -) { - FBX::Node n("P"); - n.AddProperties(name, "enum", "", "", value); - AddChild(n); -} - -void FBX::Node::AddP70time( - const std::string& name, int64_t value -) { - FBX::Node n("P"); - n.AddProperties(name, "KTime", "Time", "", value); - AddChild(n); -} - - -// public member functions for writing nodes to stream - -void FBX::Node::Dump( - std::shared_ptr outfile, - bool binary, int indent -) { - if (binary) { - Assimp::StreamWriterLE outstream(outfile); - DumpBinary(outstream); - } else { - std::ostringstream ss; - DumpAscii(ss, indent); - std::string s = ss.str(); - outfile->Write(s.c_str(), s.size(), 1); - } -} - -void FBX::Node::Dump( - Assimp::StreamWriterLE &outstream, - bool binary, int indent -) { - if (binary) { - DumpBinary(outstream); - } else { - std::ostringstream ss; - DumpAscii(ss, indent); - outstream.PutString(ss.str()); - } -} - - -// public member functions for low-level writing - -void FBX::Node::Begin( - Assimp::StreamWriterLE &s, - bool binary, int indent -) { - if (binary) { - BeginBinary(s); - } else { - // assume we're at the correct place to start already - (void)indent; - std::ostringstream ss; - BeginAscii(ss, indent); - s.PutString(ss.str()); - } -} - -void FBX::Node::DumpProperties( - Assimp::StreamWriterLE& s, - bool binary, int indent -) { - if (binary) { - DumpPropertiesBinary(s); - } else { - std::ostringstream ss; - DumpPropertiesAscii(ss, indent); - s.PutString(ss.str()); - } -} - -void FBX::Node::EndProperties( - Assimp::StreamWriterLE &s, - bool binary, int indent -) { - EndProperties(s, binary, indent, properties.size()); -} - -void FBX::Node::EndProperties( - Assimp::StreamWriterLE &s, - bool binary, int indent, - size_t num_properties -) { - if (binary) { - EndPropertiesBinary(s, num_properties); - } else { - // nothing to do - (void)indent; - } -} - -void FBX::Node::BeginChildren( - Assimp::StreamWriterLE &s, - bool binary, int indent -) { - if (binary) { - // nothing to do - } else { - std::ostringstream ss; - BeginChildrenAscii(ss, indent); - s.PutString(ss.str()); - } -} - -void FBX::Node::DumpChildren( - Assimp::StreamWriterLE& s, - bool binary, int indent -) { - if (binary) { - DumpChildrenBinary(s); - } else { - std::ostringstream ss; - DumpChildrenAscii(ss, indent); - s.PutString(ss.str()); - } -} - -void FBX::Node::End( - Assimp::StreamWriterLE &s, - bool binary, int indent, - bool has_children -) { - if (binary) { - EndBinary(s, has_children); - } else { - std::ostringstream ss; - EndAscii(ss, indent, has_children); - s.PutString(ss.str()); - } -} - - -// public member functions for writing to binary fbx - -void FBX::Node::DumpBinary(Assimp::StreamWriterLE &s) -{ - // write header section (with placeholders for some things) - BeginBinary(s); - - // write properties - DumpPropertiesBinary(s); - - // go back and fill in property related placeholders - EndPropertiesBinary(s, properties.size()); - - // write children - DumpChildrenBinary(s); - - // finish, filling in end offset placeholder - EndBinary(s, force_has_children || !children.empty()); -} - - -// public member functions for writing to ascii fbx - -void FBX::Node::DumpAscii(std::ostream &s, int indent) -{ - // write name - BeginAscii(s, indent); - - // write properties - DumpPropertiesAscii(s, indent); - - if (force_has_children || !children.empty()) { - // begin children (with a '{') - BeginChildrenAscii(s, indent + 1); - // write children - DumpChildrenAscii(s, indent + 1); - } - - // finish (also closing the children bracket '}') - EndAscii(s, indent, force_has_children || !children.empty()); -} - - -// private member functions for low-level writing to fbx - -void FBX::Node::BeginBinary(Assimp::StreamWriterLE &s) -{ - // remember start pos so we can come back and write the end pos - this->start_pos = s.Tell(); - - // placeholders for end pos and property section info - s.PutU4(0); // end pos - s.PutU4(0); // number of properties - s.PutU4(0); // total property section length - - // node name - s.PutU1(uint8_t(name.size())); // length of node name - s.PutString(name); // node name as raw bytes - - // property data comes after here - this->property_start = s.Tell(); -} - -void FBX::Node::DumpPropertiesBinary(Assimp::StreamWriterLE& s) -{ - for (auto &p : properties) { - p.DumpBinary(s); - } -} - -void FBX::Node::EndPropertiesBinary( - Assimp::StreamWriterLE &s, - size_t num_properties -) { - if (num_properties == 0) { return; } - size_t pos = s.Tell(); - ai_assert(pos > property_start); - size_t property_section_size = pos - property_start; - s.Seek(start_pos + 4); - s.PutU4(uint32_t(num_properties)); - s.PutU4(uint32_t(property_section_size)); - s.Seek(pos); -} - -void FBX::Node::DumpChildrenBinary(Assimp::StreamWriterLE& s) -{ - for (FBX::Node& child : children) { - child.DumpBinary(s); - } -} - -void FBX::Node::EndBinary( - Assimp::StreamWriterLE &s, - bool has_children -) { - // if there were children, add a null record - if (has_children) { s.PutString(FBX::NULL_RECORD); } - - // now go back and write initial pos - this->end_pos = s.Tell(); - s.Seek(start_pos); - s.PutU4(uint32_t(end_pos)); - s.Seek(end_pos); -} - - -void FBX::Node::BeginAscii(std::ostream& s, int indent) -{ - s << '\n'; - for (int i = 0; i < indent; ++i) { s << '\t'; } - s << name << ": "; -} - -void FBX::Node::DumpPropertiesAscii(std::ostream &s, int indent) -{ - for (size_t i = 0; i < properties.size(); ++i) { - if (i > 0) { s << ", "; } - properties[i].DumpAscii(s, indent); - } -} - -void FBX::Node::BeginChildrenAscii(std::ostream& s, int indent) -{ - // only call this if there are actually children - s << " {"; - (void)indent; -} - -void FBX::Node::DumpChildrenAscii(std::ostream& s, int indent) -{ - // children will need a lot of padding and corralling - if (children.size() || force_has_children) { - for (size_t i = 0; i < children.size(); ++i) { - // no compression in ascii files, so skip this node if it exists - if (children[i].name == "EncryptionType") { continue; } - // the child can dump itself - children[i].DumpAscii(s, indent); - } - } -} - -void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children) -{ - if (!has_children) { return; } // nothing to do - s << '\n'; - for (int i = 0; i < indent; ++i) { s << '\t'; } - s << "}"; -} - -// private helpers for static member functions - -// ascii property node from vector of doubles -void FBX::Node::WritePropertyNodeAscii( - const std::string& name, - const std::vector& v, - Assimp::StreamWriterLE& s, - int indent -){ - char buffer[32]; - FBX::Node node(name); - node.Begin(s, false, indent); - std::string vsize = to_string(v.size()); - // * { - s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n"); - // indent + 1 - for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); } - // a: value,value,value,... - s.PutString("a: "); - int count = 0; - for (size_t i = 0; i < v.size(); ++i) { - if (i > 0) { s.PutChar(','); } - int len = ai_snprintf(buffer, sizeof(buffer), "%f", v[i]); - count += len; - if (count > 2048) { s.PutChar('\n'); count = 0; } - if (len < 0 || len > 31) { - // this should never happen - throw DeadlyExportError("failed to convert double to string"); - } - for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); } - } - // } - s.PutChar('\n'); - for (int i = 0; i < indent; ++i) { s.PutChar('\t'); } - s.PutChar('}'); s.PutChar(' '); - node.End(s, false, indent, false); -} - -// ascii property node from vector of int32_t -void FBX::Node::WritePropertyNodeAscii( - const std::string& name, - const std::vector& v, - Assimp::StreamWriterLE& s, - int indent -){ - char buffer[32]; - FBX::Node node(name); - node.Begin(s, false, indent); - std::string vsize = to_string(v.size()); - // * { - s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n"); - // indent + 1 - for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); } - // a: value,value,value,... - s.PutString("a: "); - int count = 0; - for (size_t i = 0; i < v.size(); ++i) { - if (i > 0) { s.PutChar(','); } - int len = ai_snprintf(buffer, sizeof(buffer), "%d", v[i]); - count += len; - if (count > 2048) { s.PutChar('\n'); count = 0; } - if (len < 0 || len > 31) { - // this should never happen - throw DeadlyExportError("failed to convert double to string"); - } - for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); } - } - // } - s.PutChar('\n'); - for (int i = 0; i < indent; ++i) { s.PutChar('\t'); } - s.PutChar('}'); s.PutChar(' '); - node.End(s, false, indent, false); -} - -// binary property node from vector of doubles -// TODO: optional zip compression! -void FBX::Node::WritePropertyNodeBinary( - const std::string& name, - const std::vector& v, - Assimp::StreamWriterLE& s -){ - FBX::Node node(name); - node.BeginBinary(s); - s.PutU1('d'); - s.PutU4(uint32_t(v.size())); // number of elements - s.PutU4(0); // no encoding (1 would be zip-compressed) - s.PutU4(uint32_t(v.size()) * 8); // data size - for (auto it = v.begin(); it != v.end(); ++it) { s.PutF8(*it); } - node.EndPropertiesBinary(s, 1); - node.EndBinary(s, false); -} - -// binary property node from vector of int32_t -// TODO: optional zip compression! -void FBX::Node::WritePropertyNodeBinary( - const std::string& name, - const std::vector& v, - Assimp::StreamWriterLE& s -){ - FBX::Node node(name); - node.BeginBinary(s); - s.PutU1('i'); - s.PutU4(uint32_t(v.size())); // number of elements - s.PutU4(0); // no encoding (1 would be zip-compressed) - s.PutU4(uint32_t(v.size()) * 4); // data size - for (auto it = v.begin(); it != v.end(); ++it) { s.PutI4(*it); } - node.EndPropertiesBinary(s, 1); - node.EndBinary(s, false); -} - -// public static member functions - -// convenience function to create and write a property node, -// holding a single property which is an array of values. -// does not copy the data, so is efficient for large arrays. -void FBX::Node::WritePropertyNode( - const std::string& name, - const std::vector& v, - Assimp::StreamWriterLE& s, - bool binary, int indent -){ - if (binary) { - FBX::Node::WritePropertyNodeBinary(name, v, s); - } else { - FBX::Node::WritePropertyNodeAscii(name, v, s, indent); - } -} - -// convenience function to create and write a property node, -// holding a single property which is an array of values. -// does not copy the data, so is efficient for large arrays. -void FBX::Node::WritePropertyNode( - const std::string& name, - const std::vector& v, - Assimp::StreamWriterLE& s, - bool binary, int indent -){ - if (binary) { - FBX::Node::WritePropertyNodeBinary(name, v, s); - } else { - FBX::Node::WritePropertyNodeAscii(name, v, s, indent); - } -} - -#endif // ASSIMP_BUILD_NO_FBX_EXPORTER -#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/thirdparty/assimp/code/FBXExportNode.h b/thirdparty/assimp/code/FBXExportNode.h deleted file mode 100644 index e1ebc36969..0000000000 --- a/thirdparty/assimp/code/FBXExportNode.h +++ /dev/null @@ -1,271 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXExportNode.h -* Declares the FBX::Node helper class for fbx export. -*/ -#ifndef AI_FBXEXPORTNODE_H_INC -#define AI_FBXEXPORTNODE_H_INC - -#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER - -#include "FBXExportProperty.h" - -#include // StreamWriterLE - -#include -#include - -namespace FBX { - class Node; -} - -class FBX::Node -{ -public: // public data members - // TODO: accessors - std::string name; // node name - std::vector properties; // node properties - std::vector children; // child nodes - - // some nodes always pretend they have children... - bool force_has_children = false; - -public: // constructors - /// The default class constructor. - Node() = default; - - /// The class constructor with the name. - Node(const std::string& n) - : name(n) - , properties() - , children() - , force_has_children( false ) { - // empty - } - - // convenience template to construct with properties directly - template - Node(const std::string& n, const More... more) - : name(n) - , properties() - , children() - , force_has_children(false) { - AddProperties(more...); - } - -public: // functions to add properties or children - // add a single property to the node - template - void AddProperty(T value) { - properties.emplace_back(value); - } - - // convenience function to add multiple properties at once - template - void AddProperties(T value, More... more) { - properties.emplace_back(value); - AddProperties(more...); - } - void AddProperties() {} - - // add a child node directly - void AddChild(const Node& node) { children.push_back(node); } - - // convenience function to add a child node with a single property - template - void AddChild( - const std::string& name, - More... more - ) { - FBX::Node c(name); - c.AddProperties(more...); - children.push_back(c); - } - -public: // support specifically for dealing with Properties70 nodes - - // it really is simpler to make these all separate functions. - // the versions with 'A' suffixes are for animatable properties. - // those often follow a completely different format internally in FBX. - void AddP70int(const std::string& name, int32_t value); - void AddP70bool(const std::string& name, bool value); - void AddP70double(const std::string& name, double value); - void AddP70numberA(const std::string& name, double value); - void AddP70color(const std::string& name, double r, double g, double b); - void AddP70colorA(const std::string& name, double r, double g, double b); - void AddP70vector(const std::string& name, double x, double y, double z); - void AddP70vectorA(const std::string& name, double x, double y, double z); - void AddP70string(const std::string& name, const std::string& value); - void AddP70enum(const std::string& name, int32_t value); - void AddP70time(const std::string& name, int64_t value); - - // template for custom P70 nodes. - // anything that doesn't fit in the above can be created manually. - template - void AddP70( - const std::string& name, - const std::string& type, - const std::string& type2, - const std::string& flags, - More... more - ) { - Node n("P"); - n.AddProperties(name, type, type2, flags, more...); - AddChild(n); - } - -public: // member functions for writing data to a file or stream - - // write the full node to the given file or stream - void Dump( - std::shared_ptr outfile, - bool binary, int indent - ); - void Dump(Assimp::StreamWriterLE &s, bool binary, int indent); - - // these other functions are for writing data piece by piece. - // they must be used carefully. - // for usage examples see FBXExporter.cpp. - void Begin(Assimp::StreamWriterLE &s, bool binary, int indent); - void DumpProperties(Assimp::StreamWriterLE& s, bool binary, int indent); - void EndProperties(Assimp::StreamWriterLE &s, bool binary, int indent); - void EndProperties( - Assimp::StreamWriterLE &s, bool binary, int indent, - size_t num_properties - ); - void BeginChildren(Assimp::StreamWriterLE &s, bool binary, int indent); - void DumpChildren(Assimp::StreamWriterLE& s, bool binary, int indent); - void End( - Assimp::StreamWriterLE &s, bool binary, int indent, - bool has_children - ); - -private: // internal functions used for writing - - void DumpBinary(Assimp::StreamWriterLE &s); - void DumpAscii(Assimp::StreamWriterLE &s, int indent); - void DumpAscii(std::ostream &s, int indent); - - void BeginBinary(Assimp::StreamWriterLE &s); - void DumpPropertiesBinary(Assimp::StreamWriterLE& s); - void EndPropertiesBinary(Assimp::StreamWriterLE &s); - void EndPropertiesBinary(Assimp::StreamWriterLE &s, size_t num_properties); - void DumpChildrenBinary(Assimp::StreamWriterLE& s); - void EndBinary(Assimp::StreamWriterLE &s, bool has_children); - - void BeginAscii(std::ostream &s, int indent); - void DumpPropertiesAscii(std::ostream &s, int indent); - void BeginChildrenAscii(std::ostream &s, int indent); - void DumpChildrenAscii(std::ostream &s, int indent); - void EndAscii(std::ostream &s, int indent, bool has_children); - -private: // data used for binary dumps - size_t start_pos; // starting position in stream - size_t end_pos; // ending position in stream - size_t property_start; // starting position of property section - -public: // static member functions - - // convenience function to create a node with a single property, - // and write it to the stream. - template - static void WritePropertyNode( - const std::string& name, - const T value, - Assimp::StreamWriterLE& s, - bool binary, int indent - ) { - FBX::Property p(value); - FBX::Node node(name, p); - node.Dump(s, binary, indent); - } - - // convenience function to create and write a property node, - // holding a single property which is an array of values. - // does not copy the data, so is efficient for large arrays. - static void WritePropertyNode( - const std::string& name, - const std::vector& v, - Assimp::StreamWriterLE& s, - bool binary, int indent - ); - - // convenience function to create and write a property node, - // holding a single property which is an array of values. - // does not copy the data, so is efficient for large arrays. - static void WritePropertyNode( - const std::string& name, - const std::vector& v, - Assimp::StreamWriterLE& s, - bool binary, int indent - ); - -private: // static helper functions - static void WritePropertyNodeAscii( - const std::string& name, - const std::vector& v, - Assimp::StreamWriterLE& s, - int indent - ); - static void WritePropertyNodeAscii( - const std::string& name, - const std::vector& v, - Assimp::StreamWriterLE& s, - int indent - ); - static void WritePropertyNodeBinary( - const std::string& name, - const std::vector& v, - Assimp::StreamWriterLE& s - ); - static void WritePropertyNodeBinary( - const std::string& name, - const std::vector& v, - Assimp::StreamWriterLE& s - ); - -}; - - -#endif // ASSIMP_BUILD_NO_FBX_EXPORTER - -#endif // AI_FBXEXPORTNODE_H_INC diff --git a/thirdparty/assimp/code/FBXExportProperty.cpp b/thirdparty/assimp/code/FBXExportProperty.cpp deleted file mode 100644 index 9981d6b1c6..0000000000 --- a/thirdparty/assimp/code/FBXExportProperty.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -#ifndef ASSIMP_BUILD_NO_EXPORT -#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER - -#include "FBXExportProperty.h" - -#include // StreamWriterLE -#include // DeadlyExportError - -#include -#include -#include -#include -#include // ostringstream - - -// constructors for single element properties - -FBX::Property::Property(bool v) - : type('C'), data(1) -{ - data = {uint8_t(v)}; -} - -FBX::Property::Property(int16_t v) : type('Y'), data(2) -{ - uint8_t* d = data.data(); - (reinterpret_cast(d))[0] = v; -} - -FBX::Property::Property(int32_t v) : type('I'), data(4) -{ - uint8_t* d = data.data(); - (reinterpret_cast(d))[0] = v; -} - -FBX::Property::Property(float v) : type('F'), data(4) -{ - uint8_t* d = data.data(); - (reinterpret_cast(d))[0] = v; -} - -FBX::Property::Property(double v) : type('D'), data(8) -{ - uint8_t* d = data.data(); - (reinterpret_cast(d))[0] = v; -} - -FBX::Property::Property(int64_t v) : type('L'), data(8) -{ - uint8_t* d = data.data(); - (reinterpret_cast(d))[0] = v; -} - - -// constructors for array-type properties - -FBX::Property::Property(const char* c, bool raw) - : Property(std::string(c), raw) -{} - -// strings can either be saved as "raw" (R) data, or "string" (S) data -FBX::Property::Property(const std::string& s, bool raw) - : type(raw ? 'R' : 'S'), data(s.size()) -{ - for (size_t i = 0; i < s.size(); ++i) { - data[i] = uint8_t(s[i]); - } -} - -FBX::Property::Property(const std::vector& r) - : type('R'), data(r) -{} - -FBX::Property::Property(const std::vector& va) - : type('i'), data(4*va.size()) -{ - int32_t* d = reinterpret_cast(data.data()); - for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } -} - -FBX::Property::Property(const std::vector& va) - : type('l'), data(8*va.size()) -{ - int64_t* d = reinterpret_cast(data.data()); - for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } -} - -FBX::Property::Property(const std::vector& va) - : type('f'), data(4*va.size()) -{ - float* d = reinterpret_cast(data.data()); - for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } -} - -FBX::Property::Property(const std::vector& va) - : type('d'), data(8*va.size()) -{ - double* d = reinterpret_cast(data.data()); - for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } -} - -FBX::Property::Property(const aiMatrix4x4& vm) - : type('d'), data(8*16) -{ - double* d = reinterpret_cast(data.data()); - for (unsigned int c = 0; c < 4; ++c) { - for (unsigned int r = 0; r < 4; ++r) { - d[4*c+r] = vm[r][c]; - } - } -} - -// public member functions - -size_t FBX::Property::size() -{ - switch (type) { - case 'C': case 'Y': case 'I': case 'F': case 'D': case 'L': - return data.size() + 1; - case 'S': case 'R': - return data.size() + 5; - case 'i': case 'd': - return data.size() + 13; - default: - throw DeadlyExportError("Requested size on property of unknown type"); - } -} - -void FBX::Property::DumpBinary(Assimp::StreamWriterLE &s) -{ - s.PutU1(type); - uint8_t* d = data.data(); - size_t N; - switch (type) { - case 'C': s.PutU1(*(reinterpret_cast(d))); return; - case 'Y': s.PutI2(*(reinterpret_cast(d))); return; - case 'I': s.PutI4(*(reinterpret_cast(d))); return; - case 'F': s.PutF4(*(reinterpret_cast(d))); return; - case 'D': s.PutF8(*(reinterpret_cast(d))); return; - case 'L': s.PutI8(*(reinterpret_cast(d))); return; - case 'S': - case 'R': - s.PutU4(uint32_t(data.size())); - for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); } - return; - case 'i': - N = data.size() / 4; - s.PutU4(uint32_t(N)); // number of elements - s.PutU4(0); // no encoding (1 would be zip-compressed) - // TODO: compress if large? - s.PutU4(uint32_t(data.size())); // data size - for (size_t i = 0; i < N; ++i) { - s.PutI4((reinterpret_cast(d))[i]); - } - return; - case 'l': - N = data.size() / 8; - s.PutU4(uint32_t(N)); // number of elements - s.PutU4(0); // no encoding (1 would be zip-compressed) - // TODO: compress if large? - s.PutU4(uint32_t(data.size())); // data size - for (size_t i = 0; i < N; ++i) { - s.PutI8((reinterpret_cast(d))[i]); - } - return; - case 'f': - N = data.size() / 4; - s.PutU4(uint32_t(N)); // number of elements - s.PutU4(0); // no encoding (1 would be zip-compressed) - // TODO: compress if large? - s.PutU4(uint32_t(data.size())); // data size - for (size_t i = 0; i < N; ++i) { - s.PutF4((reinterpret_cast(d))[i]); - } - return; - case 'd': - N = data.size() / 8; - s.PutU4(uint32_t(N)); // number of elements - s.PutU4(0); // no encoding (1 would be zip-compressed) - // TODO: compress if large? - s.PutU4(uint32_t(data.size())); // data size - for (size_t i = 0; i < N; ++i) { - s.PutF8((reinterpret_cast(d))[i]); - } - return; - default: - std::ostringstream err; - err << "Tried to dump property with invalid type '"; - err << type << "'!"; - throw DeadlyExportError(err.str()); - } -} - -void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent) -{ - std::ostringstream ss; - ss.imbue(std::locale::classic()); - ss.precision(15); // this seems to match official FBX SDK exports - DumpAscii(ss, indent); - outstream.PutString(ss.str()); -} - -void FBX::Property::DumpAscii(std::ostream& s, int indent) -{ - // no writing type... or anything. just shove it into the stream. - uint8_t* d = data.data(); - size_t N; - size_t swap = data.size(); - size_t count = 0; - switch (type) { - case 'C': - if (*(reinterpret_cast(d))) { s << 'T'; } - else { s << 'F'; } - return; - case 'Y': s << *(reinterpret_cast(d)); return; - case 'I': s << *(reinterpret_cast(d)); return; - case 'F': s << *(reinterpret_cast(d)); return; - case 'D': s << *(reinterpret_cast(d)); return; - case 'L': s << *(reinterpret_cast(d)); return; - case 'S': - // first search to see if it has "\x00\x01" in it - - // which separates fields which are reversed in the ascii version. - // yeah. - // FBX, yeah. - for (size_t i = 0; i < data.size(); ++i) { - if (data[i] == '\0') { - swap = i; - break; - } - } - case 'R': - s << '"'; - // we might as well check this now, - // probably it will never happen - for (size_t i = 0; i < data.size(); ++i) { - char c = data[i]; - if (c == '"') { - throw runtime_error("can't handle quotes in property string"); - } - } - // first write the SWAPPED member (if any) - for (size_t i = swap + 2; i < data.size(); ++i) { - char c = data[i]; - s << c; - } - // then a separator - if (swap != data.size()) { - s << "::"; - } - // then the initial member - for (size_t i = 0; i < swap; ++i) { - char c = data[i]; - s << c; - } - s << '"'; - return; - case 'i': - N = data.size() / 4; // number of elements - s << '*' << N << " {\n"; - for (int i = 0; i < indent + 1; ++i) { s << '\t'; } - s << "a: "; - for (size_t i = 0; i < N; ++i) { - if (i > 0) { s << ','; } - if (count++ > 120) { s << '\n'; count = 0; } - s << (reinterpret_cast(d))[i]; - } - s << '\n'; - for (int i = 0; i < indent; ++i) { s << '\t'; } - s << "} "; - return; - case 'l': - N = data.size() / 8; - s << '*' << N << " {\n"; - for (int i = 0; i < indent + 1; ++i) { s << '\t'; } - s << "a: "; - for (size_t i = 0; i < N; ++i) { - if (i > 0) { s << ','; } - if (count++ > 120) { s << '\n'; count = 0; } - s << (reinterpret_cast(d))[i]; - } - s << '\n'; - for (int i = 0; i < indent; ++i) { s << '\t'; } - s << "} "; - return; - case 'f': - N = data.size() / 4; - s << '*' << N << " {\n"; - for (int i = 0; i < indent + 1; ++i) { s << '\t'; } - s << "a: "; - for (size_t i = 0; i < N; ++i) { - if (i > 0) { s << ','; } - if (count++ > 120) { s << '\n'; count = 0; } - s << (reinterpret_cast(d))[i]; - } - s << '\n'; - for (int i = 0; i < indent; ++i) { s << '\t'; } - s << "} "; - return; - case 'd': - N = data.size() / 8; - s << '*' << N << " {\n"; - for (int i = 0; i < indent + 1; ++i) { s << '\t'; } - s << "a: "; - // set precision to something that can handle doubles - s.precision(15); - for (size_t i = 0; i < N; ++i) { - if (i > 0) { s << ','; } - if (count++ > 120) { s << '\n'; count = 0; } - s << (reinterpret_cast(d))[i]; - } - s << '\n'; - for (int i = 0; i < indent; ++i) { s << '\t'; } - s << "} "; - return; - default: - std::ostringstream err; - err << "Tried to dump property with invalid type '"; - err << type << "'!"; - throw runtime_error(err.str()); - } -} - -#endif // ASSIMP_BUILD_NO_FBX_EXPORTER -#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/thirdparty/assimp/code/FBXExportProperty.h b/thirdparty/assimp/code/FBXExportProperty.h deleted file mode 100644 index 9c9d37c362..0000000000 --- a/thirdparty/assimp/code/FBXExportProperty.h +++ /dev/null @@ -1,129 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXExportProperty.h -* Declares the FBX::Property helper class for fbx export. -*/ -#ifndef AI_FBXEXPORTPROPERTY_H_INC -#define AI_FBXEXPORTPROPERTY_H_INC - -#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER - - -#include // aiMatrix4x4 -#include // StreamWriterLE - -#include -#include -#include -#include // is_void - -namespace FBX { - class Property; -} - -/** FBX::Property - * - * Holds a value of any of FBX's recognized types, - * each represented by a particular one-character code. - * C : 1-byte uint8, usually 0x00 or 0x01 to represent boolean false and true - * Y : 2-byte int16 - * I : 4-byte int32 - * F : 4-byte float - * D : 8-byte double - * L : 8-byte int64 - * i : array of int32 - * f : array of float - * d : array of double - * l : array of int64 - * b : array of 1-byte booleans (0x00 or 0x01) - * S : string (array of 1-byte char) - * R : raw data (array of bytes) - */ -class FBX::Property -{ -public: - // constructors for basic types. - // all explicit to avoid accidental typecasting - explicit Property(bool v); - // TODO: determine if there is actually a byte type, - // or if this always means . 'C' seems to imply , - // so possibly the above was intended to represent both. - explicit Property(int16_t v); - explicit Property(int32_t v); - explicit Property(float v); - explicit Property(double v); - explicit Property(int64_t v); - // strings can either be stored as 'R' (raw) or 'S' (string) type - explicit Property(const char* c, bool raw=false); - explicit Property(const std::string& s, bool raw=false); - explicit Property(const std::vector& r); - explicit Property(const std::vector& va); - explicit Property(const std::vector& va); - explicit Property(const std::vector& va); - explicit Property(const std::vector& va); - explicit Property(const aiMatrix4x4& vm); - - // this will catch any type not defined above, - // so that we don't accidentally convert something we don't want. - // for example (const char*) --> (bool)... seriously wtf C++ - template - explicit Property(T v) : type('X') { - static_assert(std::is_void::value, "TRIED TO CREATE FBX PROPERTY WITH UNSUPPORTED TYPE, CHECK YOUR PROPERTY INSTANTIATION"); - } // note: no line wrap so it appears verbatim on the compiler error - - // the size of this property node in a binary file, in bytes - size_t size(); - - // write this property node as binary data to the given stream - void DumpBinary(Assimp::StreamWriterLE &s); - void DumpAscii(Assimp::StreamWriterLE &s, int indent=0); - void DumpAscii(std::ostream &s, int indent=0); - // note: make sure the ostream is in classic "C" locale - -private: - char type; - std::vector data; -}; - -#endif // ASSIMP_BUILD_NO_FBX_EXPORTER - -#endif // AI_FBXEXPORTPROPERTY_H_INC diff --git a/thirdparty/assimp/code/FBXExporter.cpp b/thirdparty/assimp/code/FBXExporter.cpp deleted file mode 100644 index acb1227144..0000000000 --- a/thirdparty/assimp/code/FBXExporter.cpp +++ /dev/null @@ -1,2480 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -#ifndef ASSIMP_BUILD_NO_EXPORT -#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER - -#include "FBXExporter.h" -#include "FBXExportNode.h" -#include "FBXExportProperty.h" -#include "FBXCommon.h" - -#include // aiGetVersion -#include -#include -#include -#include // StreamWriterLE -#include // DeadlyExportError -#include // aiTextureType -#include -#include - -// Header files, standard library. -#include // shared_ptr -#include -#include // stringstream -#include // localtime, tm_* -#include -#include -#include -#include -#include - -// RESOURCES: -// https://code.blender.org/2013/08/fbx-binary-file-format-specification/ -// https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure - -const ai_real DEG = ai_real( 57.29577951308232087679815481 ); // degrees per radian - -// some constants that we'll use for writing metadata -namespace FBX { - const std::string EXPORT_VERSION_STR = "7.4.0"; - const uint32_t EXPORT_VERSION_INT = 7400; // 7.4 == 2014/2015 - // FBX files have some hashed values that depend on the creation time field, - // but for now we don't actually know how to generate these. - // what we can do is set them to a known-working version. - // this is the data that Blender uses in their FBX export process. - const std::string GENERIC_CTIME = "1970-01-01 10:00:00:000"; - const std::string GENERIC_FILEID = - "\x28\xb3\x2a\xeb\xb6\x24\xcc\xc2\xbf\xc8\xb0\x2a\xa9\x2b\xfc\xf1"; - const std::string GENERIC_FOOTID = - "\xfa\xbc\xab\x09\xd0\xc8\xd4\x66\xb1\x76\xfb\x83\x1c\xf7\x26\x7e"; - const std::string FOOT_MAGIC = - "\xf8\x5a\x8c\x6a\xde\xf5\xd9\x7e\xec\xe9\x0c\xe3\x75\x8f\x29\x0b"; - const std::string COMMENT_UNDERLINE = - ";------------------------------------------------------------------"; -} - -using namespace Assimp; -using namespace FBX; - -namespace Assimp { - - // --------------------------------------------------------------------- - // Worker function for exporting a scene to binary FBX. - // Prototyped and registered in Exporter.cpp - void ExportSceneFBX ( - const char* pFile, - IOSystem* pIOSystem, - const aiScene* pScene, - const ExportProperties* pProperties - ){ - // initialize the exporter - FBXExporter exporter(pScene, pProperties); - - // perform binary export - exporter.ExportBinary(pFile, pIOSystem); - } - - // --------------------------------------------------------------------- - // Worker function for exporting a scene to ASCII FBX. - // Prototyped and registered in Exporter.cpp - void ExportSceneFBXA ( - const char* pFile, - IOSystem* pIOSystem, - const aiScene* pScene, - const ExportProperties* pProperties - ){ - // initialize the exporter - FBXExporter exporter(pScene, pProperties); - - // perform ascii export - exporter.ExportAscii(pFile, pIOSystem); - } - -} // end of namespace Assimp - -FBXExporter::FBXExporter ( const aiScene* pScene, const ExportProperties* pProperties ) -: binary(false) -, mScene(pScene) -, mProperties(pProperties) -, outfile() -, connections() -, mesh_uids() -, material_uids() -, node_uids() { - // will probably need to determine UIDs, connections, etc here. - // basically anything that needs to be known - // before we start writing sections to the stream. -} - -void FBXExporter::ExportBinary ( - const char* pFile, - IOSystem* pIOSystem -){ - // remember that we're exporting in binary mode - binary = true; - - // we're not currently using these preferences, - // but clang will cry about it if we never touch it. - // TODO: some of these might be relevant to export - (void)mProperties; - - // open the indicated file for writing (in binary mode) - outfile.reset(pIOSystem->Open(pFile,"wb")); - if (!outfile) { - throw DeadlyExportError( - "could not open output .fbx file: " + std::string(pFile) - ); - } - - // first a binary-specific file header - WriteBinaryHeader(); - - // the rest of the file is in node entries. - // we have to serialize each entry before we write to the output, - // as the first thing we write is the byte offset of the _next_ entry. - // Either that or we can skip back to write the offset when we finish. - WriteAllNodes(); - - // finally we have a binary footer to the file - WriteBinaryFooter(); - - // explicitly release file pointer, - // so we don't have to rely on class destruction. - outfile.reset(); -} - -void FBXExporter::ExportAscii ( - const char* pFile, - IOSystem* pIOSystem -){ - // remember that we're exporting in ascii mode - binary = false; - - // open the indicated file for writing in text mode - outfile.reset(pIOSystem->Open(pFile,"wt")); - if (!outfile) { - throw DeadlyExportError( - "could not open output .fbx file: " + std::string(pFile) - ); - } - - // write the ascii header - WriteAsciiHeader(); - - // write all the sections - WriteAllNodes(); - - // make sure the file ends with a newline. - // note: if the file is opened in text mode, - // this should do the right cross-platform thing. - outfile->Write("\n", 1, 1); - - // explicitly release file pointer, - // so we don't have to rely on class destruction. - outfile.reset(); -} - -void FBXExporter::WriteAsciiHeader() -{ - // basically just a comment at the top of the file - std::stringstream head; - head << "; FBX " << EXPORT_VERSION_STR << " project file\n"; - head << "; Created by the Open Asset Import Library (Assimp)\n"; - head << "; http://assimp.org\n"; - head << "; -------------------------------------------------\n"; - const std::string ascii_header = head.str(); - outfile->Write(ascii_header.c_str(), ascii_header.size(), 1); -} - -void FBXExporter::WriteAsciiSectionHeader(const std::string& title) -{ - StreamWriterLE outstream(outfile); - std::stringstream s; - s << "\n\n; " << title << '\n'; - s << FBX::COMMENT_UNDERLINE << "\n"; - outstream.PutString(s.str()); -} - -void FBXExporter::WriteBinaryHeader() -{ - // first a specific sequence of 23 bytes, always the same - const char binary_header[24] = "Kaydara FBX Binary\x20\x20\x00\x1a\x00"; - outfile->Write(binary_header, 1, 23); - - // then FBX version number, "multiplied" by 1000, as little-endian uint32. - // so 7.3 becomes 7300 == 0x841C0000, 7.4 becomes 7400 == 0xE81C0000, etc - { - StreamWriterLE outstream(outfile); - outstream.PutU4(EXPORT_VERSION_INT); - } // StreamWriter destructor writes the data to the file - - // after this the node data starts immediately - // (probably with the FBXHEaderExtension node) -} - -void FBXExporter::WriteBinaryFooter() -{ - outfile->Write(NULL_RECORD.c_str(), NULL_RECORD.size(), 1); - - outfile->Write(GENERIC_FOOTID.c_str(), GENERIC_FOOTID.size(), 1); - - // here some padding is added for alignment to 16 bytes. - // if already aligned, the full 16 bytes is added. - size_t pos = outfile->Tell(); - size_t pad = 16 - (pos % 16); - for (size_t i = 0; i < pad; ++i) { - outfile->Write("\x00", 1, 1); - } - - // not sure what this is, but it seems to always be 0 in modern files - for (size_t i = 0; i < 4; ++i) { - outfile->Write("\x00", 1, 1); - } - - // now the file version again - { - StreamWriterLE outstream(outfile); - outstream.PutU4(EXPORT_VERSION_INT); - } // StreamWriter destructor writes the data to the file - - // and finally some binary footer added to all files - for (size_t i = 0; i < 120; ++i) { - outfile->Write("\x00", 1, 1); - } - outfile->Write(FOOT_MAGIC.c_str(), FOOT_MAGIC.size(), 1); -} - -void FBXExporter::WriteAllNodes () -{ - // header - // (and fileid, creation time, creator, if binary) - WriteHeaderExtension(); - - // global settings - WriteGlobalSettings(); - - // documents - WriteDocuments(); - - // references - WriteReferences(); - - // definitions - WriteDefinitions(); - - // objects - WriteObjects(); - - // connections - WriteConnections(); - - // WriteTakes? (deprecated since at least 2015 (fbx 7.4)) -} - -//FBXHeaderExtension top-level node -void FBXExporter::WriteHeaderExtension () -{ - if (!binary) { - // no title, follows directly from the top comment - } - FBX::Node n("FBXHeaderExtension"); - StreamWriterLE outstream(outfile); - int indent = 0; - - // begin node - n.Begin(outstream, binary, indent); - - // write properties - // (none) - - // finish properties - n.EndProperties(outstream, binary, indent, 0); - - // begin children - n.BeginChildren(outstream, binary, indent); - - indent = 1; - - // write child nodes - FBX::Node::WritePropertyNode( - "FBXHeaderVersion", int32_t(1003), outstream, binary, indent - ); - FBX::Node::WritePropertyNode( - "FBXVersion", int32_t(EXPORT_VERSION_INT), outstream, binary, indent - ); - if (binary) { - FBX::Node::WritePropertyNode( - "EncryptionType", int32_t(0), outstream, binary, indent - ); - } - - FBX::Node CreationTimeStamp("CreationTimeStamp"); - time_t rawtime; - time(&rawtime); - struct tm * now = localtime(&rawtime); - CreationTimeStamp.AddChild("Version", int32_t(1000)); - CreationTimeStamp.AddChild("Year", int32_t(now->tm_year + 1900)); - CreationTimeStamp.AddChild("Month", int32_t(now->tm_mon + 1)); - CreationTimeStamp.AddChild("Day", int32_t(now->tm_mday)); - CreationTimeStamp.AddChild("Hour", int32_t(now->tm_hour)); - CreationTimeStamp.AddChild("Minute", int32_t(now->tm_min)); - CreationTimeStamp.AddChild("Second", int32_t(now->tm_sec)); - CreationTimeStamp.AddChild("Millisecond", int32_t(0)); - CreationTimeStamp.Dump(outstream, binary, indent); - - std::stringstream creator; - creator << "Open Asset Import Library (Assimp) " << aiGetVersionMajor() - << "." << aiGetVersionMinor() << "." << aiGetVersionRevision(); - FBX::Node::WritePropertyNode( - "Creator", creator.str(), outstream, binary, indent - ); - - //FBX::Node sceneinfo("SceneInfo"); - //sceneinfo.AddProperty("GlobalInfo" + FBX::SEPARATOR + "SceneInfo"); - // not sure if any of this is actually needed, - // so just write an empty node for now. - //sceneinfo.Dump(outstream, binary, indent); - - indent = 0; - - // finish node - n.End(outstream, binary, indent, true); - - // that's it for FBXHeaderExtension... - if (!binary) { return; } - - // but binary files also need top-level FileID, CreationTime, Creator: - std::vector raw(GENERIC_FILEID.size()); - for (size_t i = 0; i < GENERIC_FILEID.size(); ++i) { - raw[i] = uint8_t(GENERIC_FILEID[i]); - } - FBX::Node::WritePropertyNode( - "FileId", raw, outstream, binary, indent - ); - FBX::Node::WritePropertyNode( - "CreationTime", GENERIC_CTIME, outstream, binary, indent - ); - FBX::Node::WritePropertyNode( - "Creator", creator.str(), outstream, binary, indent - ); -} - -void FBXExporter::WriteGlobalSettings () -{ - if (!binary) { - // no title, follows directly from the header extension - } - FBX::Node gs("GlobalSettings"); - gs.AddChild("Version", int32_t(1000)); - - FBX::Node p("Properties70"); - p.AddP70int("UpAxis", 1); - p.AddP70int("UpAxisSign", 1); - p.AddP70int("FrontAxis", 2); - p.AddP70int("FrontAxisSign", 1); - p.AddP70int("CoordAxis", 0); - p.AddP70int("CoordAxisSign", 1); - p.AddP70int("OriginalUpAxis", 1); - p.AddP70int("OriginalUpAxisSign", 1); - p.AddP70double("UnitScaleFactor", 1.0); - p.AddP70double("OriginalUnitScaleFactor", 1.0); - p.AddP70color("AmbientColor", 0.0, 0.0, 0.0); - p.AddP70string("DefaultCamera", "Producer Perspective"); - p.AddP70enum("TimeMode", 11); - p.AddP70enum("TimeProtocol", 2); - p.AddP70enum("SnapOnFrameMode", 0); - p.AddP70time("TimeSpanStart", 0); // TODO: animation support - p.AddP70time("TimeSpanStop", FBX::SECOND); // TODO: animation support - p.AddP70double("CustomFrameRate", -1.0); - p.AddP70("TimeMarker", "Compound", "", ""); // not sure what this is - p.AddP70int("CurrentTimeMarker", -1); - gs.AddChild(p); - - gs.Dump(outfile, binary, 0); -} - -void FBXExporter::WriteDocuments () -{ - if (!binary) { - WriteAsciiSectionHeader("Documents Description"); - } - - // not sure what the use of multiple documents would be, - // or whether any end-application supports it - FBX::Node docs("Documents"); - docs.AddChild("Count", int32_t(1)); - FBX::Node doc("Document"); - - // generate uid - int64_t uid = generate_uid(); - doc.AddProperties(uid, "", "Scene"); - FBX::Node p("Properties70"); - p.AddP70("SourceObject", "object", "", ""); // what is this even for? - p.AddP70string("ActiveAnimStackName", ""); // should do this properly? - doc.AddChild(p); - - // UID for root node in scene hierarchy. - // always set to 0 in the case of a single document. - // not sure what happens if more than one document exists, - // but that won't matter to us as we're exporting a single scene. - doc.AddChild("RootNode", int64_t(0)); - - docs.AddChild(doc); - docs.Dump(outfile, binary, 0); -} - -void FBXExporter::WriteReferences () -{ - if (!binary) { - WriteAsciiSectionHeader("Document References"); - } - // always empty for now. - // not really sure what this is for. - FBX::Node n("References"); - n.force_has_children = true; - n.Dump(outfile, binary, 0); -} - - -// --------------------------------------------------------------- -// some internal helper functions used for writing the definitions -// (before any actual data is written) -// --------------------------------------------------------------- - -size_t count_nodes(const aiNode* n) { - size_t count = 1; - for (size_t i = 0; i < n->mNumChildren; ++i) { - count += count_nodes(n->mChildren[i]); - } - return count; -} - -bool has_phong_mat(const aiScene* scene) -{ - // just search for any material with a shininess exponent - for (size_t i = 0; i < scene->mNumMaterials; ++i) { - aiMaterial* mat = scene->mMaterials[i]; - float shininess = 0; - mat->Get(AI_MATKEY_SHININESS, shininess); - if (shininess > 0) { - return true; - } - } - return false; -} - -size_t count_images(const aiScene* scene) { - std::unordered_set images; - aiString texpath; - for (size_t i = 0; i < scene->mNumMaterials; ++i) { - aiMaterial* mat = scene->mMaterials[i]; - for ( - size_t tt = aiTextureType_DIFFUSE; - tt < aiTextureType_UNKNOWN; - ++tt - ){ - const aiTextureType textype = static_cast(tt); - const size_t texcount = mat->GetTextureCount(textype); - for (unsigned int j = 0; j < texcount; ++j) { - mat->GetTexture(textype, j, &texpath); - images.insert(std::string(texpath.C_Str())); - } - } - } - return images.size(); -} - -size_t count_textures(const aiScene* scene) { - size_t count = 0; - for (size_t i = 0; i < scene->mNumMaterials; ++i) { - aiMaterial* mat = scene->mMaterials[i]; - for ( - size_t tt = aiTextureType_DIFFUSE; - tt < aiTextureType_UNKNOWN; - ++tt - ){ - // TODO: handle layered textures - if (mat->GetTextureCount(static_cast(tt)) > 0) { - count += 1; - } - } - } - return count; -} - -size_t count_deformers(const aiScene* scene) { - size_t count = 0; - for (size_t i = 0; i < scene->mNumMeshes; ++i) { - const size_t n = scene->mMeshes[i]->mNumBones; - if (n) { - // 1 main deformer, 1 subdeformer per bone - count += n + 1; - } - } - return count; -} - -void FBXExporter::WriteDefinitions () -{ - // basically this is just bookkeeping: - // determining how many of each type of object there are - // and specifying the base properties to use when otherwise unspecified. - - // ascii section header - if (!binary) { - WriteAsciiSectionHeader("Object definitions"); - } - - // we need to count the objects - int32_t count; - int32_t total_count = 0; - - // and store them - std::vector object_nodes; - FBX::Node n, pt, p; - - // GlobalSettings - // this seems to always be here in Maya exports - n = FBX::Node("ObjectType", "GlobalSettings"); - count = 1; - n.AddChild("Count", count); - object_nodes.push_back(n); - total_count += count; - - // AnimationStack / FbxAnimStack - // this seems to always be here in Maya exports, - // but no harm seems to come of leaving it out. - count = mScene->mNumAnimations; - if (count) { - n = FBX::Node("ObjectType", "AnimationStack"); - n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", "FbxAnimStack"); - p = FBX::Node("Properties70"); - p.AddP70string("Description", ""); - p.AddP70time("LocalStart", 0); - p.AddP70time("LocalStop", 0); - p.AddP70time("ReferenceStart", 0); - p.AddP70time("ReferenceStop", 0); - pt.AddChild(p); - n.AddChild(pt); - object_nodes.push_back(n); - total_count += count; - } - - // AnimationLayer / FbxAnimLayer - // this seems to always be here in Maya exports, - // but no harm seems to come of leaving it out. - // Assimp doesn't support animation layers, - // so there will be one per aiAnimation - count = mScene->mNumAnimations; - if (count) { - n = FBX::Node("ObjectType", "AnimationLayer"); - n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", "FBXAnimLayer"); - p = FBX::Node("Properties70"); - p.AddP70("Weight", "Number", "", "A", double(100)); - p.AddP70bool("Mute", 0); - p.AddP70bool("Solo", 0); - p.AddP70bool("Lock", 0); - p.AddP70color("Color", 0.8, 0.8, 0.8); - p.AddP70("BlendMode", "enum", "", "", int32_t(0)); - p.AddP70("RotationAccumulationMode", "enum", "", "", int32_t(0)); - p.AddP70("ScaleAccumulationMode", "enum", "", "", int32_t(0)); - p.AddP70("BlendModeBypass", "ULongLong", "", "", int64_t(0)); - pt.AddChild(p); - n.AddChild(pt); - object_nodes.push_back(n); - total_count += count; - } - - // NodeAttribute - // this is completely absurd. - // there can only be one "NodeAttribute" template, - // but FbxSkeleton, FbxCamera, FbxLight all are "NodeAttributes". - // so if only one exists we should set the template for that, - // otherwise... we just pick one :/. - // the others have to set all their properties every instance, - // because there's no template. - count = 1; // TODO: select properly - if (count) { - // FbxSkeleton - n = FBX::Node("ObjectType", "NodeAttribute"); - n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", "FbxSkeleton"); - p = FBX::Node("Properties70"); - p.AddP70color("Color", 0.8, 0.8, 0.8); - p.AddP70double("Size", 33.333333333333); - p.AddP70("LimbLength", "double", "Number", "H", double(1)); - // note: not sure what the "H" flag is for - hidden? - pt.AddChild(p); - n.AddChild(pt); - object_nodes.push_back(n); - total_count += count; - } - - // Model / FbxNode - // <~~ node hierarchy - count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node) - if (count) { - n = FBX::Node("ObjectType", "Model"); - n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", "FbxNode"); - p = FBX::Node("Properties70"); - p.AddP70enum("QuaternionInterpolate", 0); - p.AddP70vector("RotationOffset", 0.0, 0.0, 0.0); - p.AddP70vector("RotationPivot", 0.0, 0.0, 0.0); - p.AddP70vector("ScalingOffset", 0.0, 0.0, 0.0); - p.AddP70vector("ScalingPivot", 0.0, 0.0, 0.0); - p.AddP70bool("TranslationActive", 0); - p.AddP70vector("TranslationMin", 0.0, 0.0, 0.0); - p.AddP70vector("TranslationMax", 0.0, 0.0, 0.0); - p.AddP70bool("TranslationMinX", 0); - p.AddP70bool("TranslationMinY", 0); - p.AddP70bool("TranslationMinZ", 0); - p.AddP70bool("TranslationMaxX", 0); - p.AddP70bool("TranslationMaxY", 0); - p.AddP70bool("TranslationMaxZ", 0); - p.AddP70enum("RotationOrder", 0); - p.AddP70bool("RotationSpaceForLimitOnly", 0); - p.AddP70double("RotationStiffnessX", 0.0); - p.AddP70double("RotationStiffnessY", 0.0); - p.AddP70double("RotationStiffnessZ", 0.0); - p.AddP70double("AxisLen", 10.0); - p.AddP70vector("PreRotation", 0.0, 0.0, 0.0); - p.AddP70vector("PostRotation", 0.0, 0.0, 0.0); - p.AddP70bool("RotationActive", 0); - p.AddP70vector("RotationMin", 0.0, 0.0, 0.0); - p.AddP70vector("RotationMax", 0.0, 0.0, 0.0); - p.AddP70bool("RotationMinX", 0); - p.AddP70bool("RotationMinY", 0); - p.AddP70bool("RotationMinZ", 0); - p.AddP70bool("RotationMaxX", 0); - p.AddP70bool("RotationMaxY", 0); - p.AddP70bool("RotationMaxZ", 0); - p.AddP70enum("InheritType", 0); - p.AddP70bool("ScalingActive", 0); - p.AddP70vector("ScalingMin", 0.0, 0.0, 0.0); - p.AddP70vector("ScalingMax", 1.0, 1.0, 1.0); - p.AddP70bool("ScalingMinX", 0); - p.AddP70bool("ScalingMinY", 0); - p.AddP70bool("ScalingMinZ", 0); - p.AddP70bool("ScalingMaxX", 0); - p.AddP70bool("ScalingMaxY", 0); - p.AddP70bool("ScalingMaxZ", 0); - p.AddP70vector("GeometricTranslation", 0.0, 0.0, 0.0); - p.AddP70vector("GeometricRotation", 0.0, 0.0, 0.0); - p.AddP70vector("GeometricScaling", 1.0, 1.0, 1.0); - p.AddP70double("MinDampRangeX", 0.0); - p.AddP70double("MinDampRangeY", 0.0); - p.AddP70double("MinDampRangeZ", 0.0); - p.AddP70double("MaxDampRangeX", 0.0); - p.AddP70double("MaxDampRangeY", 0.0); - p.AddP70double("MaxDampRangeZ", 0.0); - p.AddP70double("MinDampStrengthX", 0.0); - p.AddP70double("MinDampStrengthY", 0.0); - p.AddP70double("MinDampStrengthZ", 0.0); - p.AddP70double("MaxDampStrengthX", 0.0); - p.AddP70double("MaxDampStrengthY", 0.0); - p.AddP70double("MaxDampStrengthZ", 0.0); - p.AddP70double("PreferedAngleX", 0.0); - p.AddP70double("PreferedAngleY", 0.0); - p.AddP70double("PreferedAngleZ", 0.0); - p.AddP70("LookAtProperty", "object", "", ""); - p.AddP70("UpVectorProperty", "object", "", ""); - p.AddP70bool("Show", 1); - p.AddP70bool("NegativePercentShapeSupport", 1); - p.AddP70int("DefaultAttributeIndex", -1); - p.AddP70bool("Freeze", 0); - p.AddP70bool("LODBox", 0); - p.AddP70( - "Lcl Translation", "Lcl Translation", "", "A", - double(0), double(0), double(0) - ); - p.AddP70( - "Lcl Rotation", "Lcl Rotation", "", "A", - double(0), double(0), double(0) - ); - p.AddP70( - "Lcl Scaling", "Lcl Scaling", "", "A", - double(1), double(1), double(1) - ); - p.AddP70("Visibility", "Visibility", "", "A", double(1)); - p.AddP70( - "Visibility Inheritance", "Visibility Inheritance", "", "", - int32_t(1) - ); - pt.AddChild(p); - n.AddChild(pt); - object_nodes.push_back(n); - total_count += count; - } - - // Geometry / FbxMesh - // <~~ aiMesh - count = mScene->mNumMeshes; - if (count) { - n = FBX::Node("ObjectType", "Geometry"); - n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", "FbxMesh"); - p = FBX::Node("Properties70"); - p.AddP70color("Color", 0, 0, 0); - p.AddP70vector("BBoxMin", 0, 0, 0); - p.AddP70vector("BBoxMax", 0, 0, 0); - p.AddP70bool("Primary Visibility", 1); - p.AddP70bool("Casts Shadows", 1); - p.AddP70bool("Receive Shadows", 1); - pt.AddChild(p); - n.AddChild(pt); - object_nodes.push_back(n); - total_count += count; - } - - // Material / FbxSurfacePhong, FbxSurfaceLambert, FbxSurfaceMaterial - // <~~ aiMaterial - // basically if there's any phong material this is defined as phong, - // and otherwise lambert. - // More complex materials cause a bare-bones FbxSurfaceMaterial definition - // and are treated specially, as they're not really supported by FBX. - // TODO: support Maya's Stingray PBS material - count = mScene->mNumMaterials; - if (count) { - bool has_phong = has_phong_mat(mScene); - n = FBX::Node("ObjectType", "Material"); - n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate"); - if (has_phong) { - pt.AddProperty("FbxSurfacePhong"); - } else { - pt.AddProperty("FbxSurfaceLambert"); - } - p = FBX::Node("Properties70"); - if (has_phong) { - p.AddP70string("ShadingModel", "Phong"); - } else { - p.AddP70string("ShadingModel", "Lambert"); - } - p.AddP70bool("MultiLayer", 0); - p.AddP70colorA("EmissiveColor", 0.0, 0.0, 0.0); - p.AddP70numberA("EmissiveFactor", 1.0); - p.AddP70colorA("AmbientColor", 0.2, 0.2, 0.2); - p.AddP70numberA("AmbientFactor", 1.0); - p.AddP70colorA("DiffuseColor", 0.8, 0.8, 0.8); - p.AddP70numberA("DiffuseFactor", 1.0); - p.AddP70vector("Bump", 0.0, 0.0, 0.0); - p.AddP70vector("NormalMap", 0.0, 0.0, 0.0); - p.AddP70double("BumpFactor", 1.0); - p.AddP70colorA("TransparentColor", 0.0, 0.0, 0.0); - p.AddP70numberA("TransparencyFactor", 0.0); - p.AddP70color("DisplacementColor", 0.0, 0.0, 0.0); - p.AddP70double("DisplacementFactor", 1.0); - p.AddP70color("VectorDisplacementColor", 0.0, 0.0, 0.0); - p.AddP70double("VectorDisplacementFactor", 1.0); - if (has_phong) { - p.AddP70colorA("SpecularColor", 0.2, 0.2, 0.2); - p.AddP70numberA("SpecularFactor", 1.0); - p.AddP70numberA("ShininessExponent", 20.0); - p.AddP70colorA("ReflectionColor", 0.0, 0.0, 0.0); - p.AddP70numberA("ReflectionFactor", 1.0); - } - pt.AddChild(p); - n.AddChild(pt); - object_nodes.push_back(n); - total_count += count; - } - - // Video / FbxVideo - // one for each image file. - count = int32_t(count_images(mScene)); - if (count) { - n = FBX::Node("ObjectType", "Video"); - n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", "FbxVideo"); - p = FBX::Node("Properties70"); - p.AddP70bool("ImageSequence", 0); - p.AddP70int("ImageSequenceOffset", 0); - p.AddP70double("FrameRate", 0.0); - p.AddP70int("LastFrame", 0); - p.AddP70int("Width", 0); - p.AddP70int("Height", 0); - p.AddP70("Path", "KString", "XRefUrl", "", ""); - p.AddP70int("StartFrame", 0); - p.AddP70int("StopFrame", 0); - p.AddP70double("PlaySpeed", 0.0); - p.AddP70time("Offset", 0); - p.AddP70enum("InterlaceMode", 0); - p.AddP70bool("FreeRunning", 0); - p.AddP70bool("Loop", 0); - p.AddP70enum("AccessMode", 0); - pt.AddChild(p); - n.AddChild(pt); - object_nodes.push_back(n); - total_count += count; - } - - // Texture / FbxFileTexture - // <~~ aiTexture - count = int32_t(count_textures(mScene)); - if (count) { - n = FBX::Node("ObjectType", "Texture"); - n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", "FbxFileTexture"); - p = FBX::Node("Properties70"); - p.AddP70enum("TextureTypeUse", 0); - p.AddP70numberA("Texture alpha", 1.0); - p.AddP70enum("CurrentMappingType", 0); - p.AddP70enum("WrapModeU", 0); - p.AddP70enum("WrapModeV", 0); - p.AddP70bool("UVSwap", 0); - p.AddP70bool("PremultiplyAlpha", 1); - p.AddP70vectorA("Translation", 0.0, 0.0, 0.0); - p.AddP70vectorA("Rotation", 0.0, 0.0, 0.0); - p.AddP70vectorA("Scaling", 1.0, 1.0, 1.0); - p.AddP70vector("TextureRotationPivot", 0.0, 0.0, 0.0); - p.AddP70vector("TextureScalingPivot", 0.0, 0.0, 0.0); - p.AddP70enum("CurrentTextureBlendMode", 1); - p.AddP70string("UVSet", "default"); - p.AddP70bool("UseMaterial", 0); - p.AddP70bool("UseMipMap", 0); - pt.AddChild(p); - n.AddChild(pt); - object_nodes.push_back(n); - total_count += count; - } - - // AnimationCurveNode / FbxAnimCurveNode - count = mScene->mNumAnimations * 3; - if (count) { - n = FBX::Node("ObjectType", "AnimationCurveNode"); - n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", "FbxAnimCurveNode"); - p = FBX::Node("Properties70"); - p.AddP70("d", "Compound", "", ""); - pt.AddChild(p); - n.AddChild(pt); - object_nodes.push_back(n); - total_count += count; - } - - // AnimationCurve / FbxAnimCurve - count = mScene->mNumAnimations * 9; - if (count) { - n = FBX::Node("ObjectType", "AnimationCurve"); - n.AddChild("Count", count); - object_nodes.push_back(n); - total_count += count; - } - - // Pose - count = 0; - for (size_t i = 0; i < mScene->mNumMeshes; ++i) { - aiMesh* mesh = mScene->mMeshes[i]; - if (mesh->HasBones()) { ++count; } - } - if (count) { - n = FBX::Node("ObjectType", "Pose"); - n.AddChild("Count", count); - object_nodes.push_back(n); - total_count += count; - } - - // Deformer - count = int32_t(count_deformers(mScene)); - if (count) { - n = FBX::Node("ObjectType", "Deformer"); - n.AddChild("Count", count); - object_nodes.push_back(n); - total_count += count; - } - - // (template) - count = 0; - if (count) { - n = FBX::Node("ObjectType", ""); - n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", ""); - p = FBX::Node("Properties70"); - pt.AddChild(p); - n.AddChild(pt); - object_nodes.push_back(n); - total_count += count; - } - - // now write it all - FBX::Node defs("Definitions"); - defs.AddChild("Version", int32_t(100)); - defs.AddChild("Count", int32_t(total_count)); - for (auto &n : object_nodes) { defs.AddChild(n); } - defs.Dump(outfile, binary, 0); -} - - -// ------------------------------------------------------------------- -// some internal helper functions used for writing the objects section -// (which holds the actual data) -// ------------------------------------------------------------------- - -aiNode* get_node_for_mesh(unsigned int meshIndex, aiNode* node) -{ - for (size_t i = 0; i < node->mNumMeshes; ++i) { - if (node->mMeshes[i] == meshIndex) { - return node; - } - } - for (size_t i = 0; i < node->mNumChildren; ++i) { - aiNode* ret = get_node_for_mesh(meshIndex, node->mChildren[i]); - if (ret) { return ret; } - } - return nullptr; -} - -aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene) -{ - std::vector node_chain; - while (node != scene->mRootNode) { - node_chain.push_back(node); - node = node->mParent; - } - aiMatrix4x4 transform; - for (auto n = node_chain.rbegin(); n != node_chain.rend(); ++n) { - transform *= (*n)->mTransformation; - } - return transform; -} - -int64_t to_ktime(double ticks, const aiAnimation* anim) { - if (anim->mTicksPerSecond <= 0) { - return static_cast(ticks) * FBX::SECOND; - } - return (static_cast(ticks) / static_cast(anim->mTicksPerSecond)) * FBX::SECOND; -} - -int64_t to_ktime(double time) { - return (static_cast(time * FBX::SECOND)); -} - -void FBXExporter::WriteObjects () -{ - if (!binary) { - WriteAsciiSectionHeader("Object properties"); - } - // numbers should match those given in definitions! make sure to check - StreamWriterLE outstream(outfile); - FBX::Node object_node("Objects"); - int indent = 0; - object_node.Begin(outstream, binary, indent); - object_node.EndProperties(outstream, binary, indent); - object_node.BeginChildren(outstream, binary, indent); - - // geometry (aiMesh) - mesh_uids.clear(); - indent = 1; - for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { - // it's all about this mesh - aiMesh* m = mScene->mMeshes[mi]; - - // start the node record - FBX::Node n("Geometry"); - int64_t uid = generate_uid(); - mesh_uids.push_back(uid); - n.AddProperty(uid); - n.AddProperty(FBX::SEPARATOR + "Geometry"); - n.AddProperty("Mesh"); - n.Begin(outstream, binary, indent); - n.DumpProperties(outstream, binary, indent); - n.EndProperties(outstream, binary, indent); - n.BeginChildren(outstream, binary, indent); - indent = 2; - - // output vertex data - each vertex should be unique (probably) - std::vector flattened_vertices; - // index of original vertex in vertex data vector - std::vector vertex_indices; - // map of vertex value to its index in the data vector - std::map index_by_vertex_value; - int32_t index = 0; - for (size_t vi = 0; vi < m->mNumVertices; ++vi) { - aiVector3D vtx = m->mVertices[vi]; - auto elem = index_by_vertex_value.find(vtx); - if (elem == index_by_vertex_value.end()) { - vertex_indices.push_back(index); - index_by_vertex_value[vtx] = index; - flattened_vertices.push_back(vtx[0]); - flattened_vertices.push_back(vtx[1]); - flattened_vertices.push_back(vtx[2]); - ++index; - } else { - vertex_indices.push_back(int32_t(elem->second)); - } - } - FBX::Node::WritePropertyNode( - "Vertices", flattened_vertices, outstream, binary, indent - ); - - // output polygon data as a flattened array of vertex indices. - // the last vertex index of each polygon is negated and - 1 - std::vector polygon_data; - for (size_t fi = 0; fi < m->mNumFaces; ++fi) { - const aiFace &f = m->mFaces[fi]; - for (size_t pvi = 0; pvi < f.mNumIndices - 1; ++pvi) { - polygon_data.push_back(vertex_indices[f.mIndices[pvi]]); - } - polygon_data.push_back( - -1 - vertex_indices[f.mIndices[f.mNumIndices-1]] - ); - } - FBX::Node::WritePropertyNode( - "PolygonVertexIndex", polygon_data, outstream, binary, indent - ); - - // here could be edges but they're insane. - // it's optional anyway, so let's ignore it. - - FBX::Node::WritePropertyNode( - "GeometryVersion", int32_t(124), outstream, binary, indent - ); - - // normals, if any - if (m->HasNormals()) { - FBX::Node normals("LayerElementNormal", int32_t(0)); - normals.Begin(outstream, binary, indent); - normals.DumpProperties(outstream, binary, indent); - normals.EndProperties(outstream, binary, indent); - normals.BeginChildren(outstream, binary, indent); - indent = 3; - FBX::Node::WritePropertyNode( - "Version", int32_t(101), outstream, binary, indent - ); - FBX::Node::WritePropertyNode( - "Name", "", outstream, binary, indent - ); - FBX::Node::WritePropertyNode( - "MappingInformationType", "ByPolygonVertex", - outstream, binary, indent - ); - // TODO: vertex-normals or indexed normals when appropriate - FBX::Node::WritePropertyNode( - "ReferenceInformationType", "Direct", - outstream, binary, indent - ); - std::vector normal_data; - normal_data.reserve(3 * polygon_data.size()); - for (size_t fi = 0; fi < m->mNumFaces; ++fi) { - const aiFace &f = m->mFaces[fi]; - for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) { - const aiVector3D &n = m->mNormals[f.mIndices[pvi]]; - normal_data.push_back(n.x); - normal_data.push_back(n.y); - normal_data.push_back(n.z); - } - } - FBX::Node::WritePropertyNode( - "Normals", normal_data, outstream, binary, indent - ); - // note: version 102 has a NormalsW also... not sure what it is, - // so we can stick with version 101 for now. - indent = 2; - normals.End(outstream, binary, indent, true); - } - - // uvs, if any - for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) { - if (m->mNumUVComponents[uvi] > 2) { - // FBX only supports 2-channel UV maps... - // or at least i'm not sure how to indicate a different number - std::stringstream err; - err << "Only 2-channel UV maps supported by FBX,"; - err << " but mesh " << mi; - if (m->mName.length) { - err << " (" << m->mName.C_Str() << ")"; - } - err << " UV map " << uvi; - err << " has " << m->mNumUVComponents[uvi]; - err << " components! Data will be preserved,"; - err << " but may be incorrectly interpreted on load."; - ASSIMP_LOG_WARN(err.str()); - } - FBX::Node uv("LayerElementUV", int32_t(uvi)); - uv.Begin(outstream, binary, indent); - uv.DumpProperties(outstream, binary, indent); - uv.EndProperties(outstream, binary, indent); - uv.BeginChildren(outstream, binary, indent); - indent = 3; - FBX::Node::WritePropertyNode( - "Version", int32_t(101), outstream, binary, indent - ); - // it doesn't seem like assimp keeps the uv map name, - // so just leave it blank. - FBX::Node::WritePropertyNode( - "Name", "", outstream, binary, indent - ); - FBX::Node::WritePropertyNode( - "MappingInformationType", "ByPolygonVertex", - outstream, binary, indent - ); - FBX::Node::WritePropertyNode( - "ReferenceInformationType", "IndexToDirect", - outstream, binary, indent - ); - - std::vector uv_data; - std::vector uv_indices; - std::map index_by_uv; - int32_t index = 0; - for (size_t fi = 0; fi < m->mNumFaces; ++fi) { - const aiFace &f = m->mFaces[fi]; - for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) { - const aiVector3D &uv = - m->mTextureCoords[uvi][f.mIndices[pvi]]; - auto elem = index_by_uv.find(uv); - if (elem == index_by_uv.end()) { - index_by_uv[uv] = index; - uv_indices.push_back(index); - for (unsigned int x = 0; x < m->mNumUVComponents[uvi]; ++x) { - uv_data.push_back(uv[x]); - } - ++index; - } else { - uv_indices.push_back(elem->second); - } - } - } - FBX::Node::WritePropertyNode( - "UV", uv_data, outstream, binary, indent - ); - FBX::Node::WritePropertyNode( - "UVIndex", uv_indices, outstream, binary, indent - ); - indent = 2; - uv.End(outstream, binary, indent, true); - } - - // i'm not really sure why this material section exists, - // as the material is linked via "Connections". - // it seems to always have the same "0" value. - FBX::Node mat("LayerElementMaterial", int32_t(0)); - mat.AddChild("Version", int32_t(101)); - mat.AddChild("Name", ""); - mat.AddChild("MappingInformationType", "AllSame"); - mat.AddChild("ReferenceInformationType", "IndexToDirect"); - std::vector mat_indices = {0}; - mat.AddChild("Materials", mat_indices); - mat.Dump(outstream, binary, indent); - - // finally we have the layer specifications, - // which select the normals / UV set / etc to use. - // TODO: handle multiple uv sets correctly? - FBX::Node layer("Layer", int32_t(0)); - layer.AddChild("Version", int32_t(100)); - FBX::Node le("LayerElement"); - le.AddChild("Type", "LayerElementNormal"); - le.AddChild("TypedIndex", int32_t(0)); - layer.AddChild(le); - le = FBX::Node("LayerElement"); - le.AddChild("Type", "LayerElementMaterial"); - le.AddChild("TypedIndex", int32_t(0)); - layer.AddChild(le); - le = FBX::Node("LayerElement"); - le.AddChild("Type", "LayerElementUV"); - le.AddChild("TypedIndex", int32_t(0)); - layer.AddChild(le); - layer.Dump(outstream, binary, indent); - - // finish the node record - indent = 1; - n.End(outstream, binary, indent, true); - } - - // aiMaterial - material_uids.clear(); - for (size_t i = 0; i < mScene->mNumMaterials; ++i) { - // it's all about this material - aiMaterial* m = mScene->mMaterials[i]; - - // these are used to receive material data - float f; aiColor3D c; - - // start the node record - FBX::Node n("Material"); - - int64_t uid = generate_uid(); - material_uids.push_back(uid); - n.AddProperty(uid); - - aiString name; - m->Get(AI_MATKEY_NAME, name); - n.AddProperty(name.C_Str() + FBX::SEPARATOR + "Material"); - - n.AddProperty(""); - - n.AddChild("Version", int32_t(102)); - f = 0; - m->Get(AI_MATKEY_SHININESS, f); - bool phong = (f > 0); - if (phong) { - n.AddChild("ShadingModel", "phong"); - } else { - n.AddChild("ShadingModel", "lambert"); - } - n.AddChild("MultiLayer", int32_t(0)); - - FBX::Node p("Properties70"); - - // materials exported using the FBX SDK have two sets of fields. - // there are the properties specified in the PropertyTemplate, - // which are those supported by the modernFBX SDK, - // and an extra set of properties with simpler names. - // The extra properties are a legacy material system from pre-2009. - // - // In the modern system, each property has "color" and "factor". - // Generally the interpretation of these seems to be - // that the colour is multiplied by the factor before use, - // but this is not always clear-cut. - // - // Usually assimp only stores the colour, - // so we can just leave the factors at the default "1.0". - - // first we can export the "standard" properties - if (m->Get(AI_MATKEY_COLOR_AMBIENT, c) == aiReturn_SUCCESS) { - p.AddP70colorA("AmbientColor", c.r, c.g, c.b); - //p.AddP70numberA("AmbientFactor", 1.0); - } - if (m->Get(AI_MATKEY_COLOR_DIFFUSE, c) == aiReturn_SUCCESS) { - p.AddP70colorA("DiffuseColor", c.r, c.g, c.b); - //p.AddP70numberA("DiffuseFactor", 1.0); - } - if (m->Get(AI_MATKEY_COLOR_TRANSPARENT, c) == aiReturn_SUCCESS) { - // "TransparentColor" / "TransparencyFactor"... - // thanks FBX, for your insightful interpretation of consistency - p.AddP70colorA("TransparentColor", c.r, c.g, c.b); - // TransparencyFactor defaults to 0.0, so set it to 1.0. - // note: Maya always sets this to 1.0, - // so we can't use it sensibly as "Opacity". - // In stead we rely on the legacy "Opacity" value, below. - // Blender also relies on "Opacity" not "TransparencyFactor", - // probably for a similar reason. - p.AddP70numberA("TransparencyFactor", 1.0); - } - if (m->Get(AI_MATKEY_COLOR_REFLECTIVE, c) == aiReturn_SUCCESS) { - p.AddP70colorA("ReflectionColor", c.r, c.g, c.b); - } - if (m->Get(AI_MATKEY_REFLECTIVITY, f) == aiReturn_SUCCESS) { - p.AddP70numberA("ReflectionFactor", f); - } - if (phong) { - if (m->Get(AI_MATKEY_COLOR_SPECULAR, c) == aiReturn_SUCCESS) { - p.AddP70colorA("SpecularColor", c.r, c.g, c.b); - } - if (m->Get(AI_MATKEY_SHININESS_STRENGTH, f) == aiReturn_SUCCESS) { - p.AddP70numberA("ShininessFactor", f); - } - if (m->Get(AI_MATKEY_SHININESS, f) == aiReturn_SUCCESS) { - p.AddP70numberA("ShininessExponent", f); - } - if (m->Get(AI_MATKEY_REFLECTIVITY, f) == aiReturn_SUCCESS) { - p.AddP70numberA("ReflectionFactor", f); - } - } - - // Now the legacy system. - // For safety let's include it. - // thrse values don't exist in the property template, - // and usually are completely ignored when loading. - // One notable exception is the "Opacity" property, - // which Blender uses as (1.0 - alpha). - c.r = 0.0f; c.g = 0.0f; c.b = 0.0f; - m->Get(AI_MATKEY_COLOR_EMISSIVE, c); - p.AddP70vector("Emissive", c.r, c.g, c.b); - c.r = 0.2f; c.g = 0.2f; c.b = 0.2f; - m->Get(AI_MATKEY_COLOR_AMBIENT, c); - p.AddP70vector("Ambient", c.r, c.g, c.b); - c.r = 0.8f; c.g = 0.8f; c.b = 0.8f; - m->Get(AI_MATKEY_COLOR_DIFFUSE, c); - p.AddP70vector("Diffuse", c.r, c.g, c.b); - // The FBX SDK determines "Opacity" from transparency colour (RGB) - // and factor (F) as: O = (1.0 - F * ((R + G + B) / 3)). - // However we actually have an opacity value, - // so we should take it from AI_MATKEY_OPACITY if possible. - // It might make more sense to use TransparencyFactor, - // but Blender actually loads "Opacity" correctly, so let's use it. - f = 1.0f; - if (m->Get(AI_MATKEY_COLOR_TRANSPARENT, c) == aiReturn_SUCCESS) { - f = 1.0f - ((c.r + c.g + c.b) / 3.0f); - } - m->Get(AI_MATKEY_OPACITY, f); - p.AddP70double("Opacity", f); - if (phong) { - // specular color is multiplied by shininess_strength - c.r = 0.2f; c.g = 0.2f; c.b = 0.2f; - m->Get(AI_MATKEY_COLOR_SPECULAR, c); - f = 1.0f; - m->Get(AI_MATKEY_SHININESS_STRENGTH, f); - p.AddP70vector("Specular", f*c.r, f*c.g, f*c.b); - f = 20.0f; - m->Get(AI_MATKEY_SHININESS, f); - p.AddP70double("Shininess", f); - // Legacy "Reflectivity" is F*F*((R+G+B)/3), - // where F is the proportion of light reflected (AKA reflectivity), - // and RGB is the reflective colour of the material. - // No idea why, but we might as well set it the same way. - f = 0.0f; - m->Get(AI_MATKEY_REFLECTIVITY, f); - c.r = 1.0f, c.g = 1.0f, c.b = 1.0f; - m->Get(AI_MATKEY_COLOR_REFLECTIVE, c); - p.AddP70double("Reflectivity", f*f*((c.r+c.g+c.b)/3.0)); - } - - n.AddChild(p); - - n.Dump(outstream, binary, indent); - } - - // we need to look up all the images we're using, - // so we can generate uids, and eliminate duplicates. - std::map uid_by_image; - for (size_t i = 0; i < mScene->mNumMaterials; ++i) { - aiString texpath; - aiMaterial* mat = mScene->mMaterials[i]; - for ( - size_t tt = aiTextureType_DIFFUSE; - tt < aiTextureType_UNKNOWN; - ++tt - ){ - const aiTextureType textype = static_cast(tt); - const size_t texcount = mat->GetTextureCount(textype); - for (size_t j = 0; j < texcount; ++j) { - mat->GetTexture(textype, (unsigned int)j, &texpath); - const std::string texstring = texpath.C_Str(); - auto elem = uid_by_image.find(texstring); - if (elem == uid_by_image.end()) { - uid_by_image[texstring] = generate_uid(); - } - } - } - } - - // FbxVideo - stores images used by textures. - for (const auto &it : uid_by_image) { - if (it.first.compare(0, 1, "*") == 0) { - // TODO: embedded textures - continue; - } - FBX::Node n("Video"); - const int64_t& uid = it.second; - const std::string name = ""; // TODO: ... name??? - n.AddProperties(uid, name + FBX::SEPARATOR + "Video", "Clip"); - n.AddChild("Type", "Clip"); - FBX::Node p("Properties70"); - // TODO: get full path... relative path... etc... ugh... - // for now just use the same path for everything, - // and hopefully one of them will work out. - const std::string& path = it.first; - p.AddP70("Path", "KString", "XRefUrl", "", path); - n.AddChild(p); - n.AddChild("UseMipMap", int32_t(0)); - n.AddChild("Filename", path); - n.AddChild("RelativeFilename", path); - n.Dump(outstream, binary, indent); - } - - // Textures - // referenced by material_index/texture_type pairs. - std::map,int64_t> texture_uids; - const std::map prop_name_by_tt = { - {aiTextureType_DIFFUSE, "DiffuseColor"}, - {aiTextureType_SPECULAR, "SpecularColor"}, - {aiTextureType_AMBIENT, "AmbientColor"}, - {aiTextureType_EMISSIVE, "EmissiveColor"}, - {aiTextureType_HEIGHT, "Bump"}, - {aiTextureType_NORMALS, "NormalMap"}, - {aiTextureType_SHININESS, "ShininessExponent"}, - {aiTextureType_OPACITY, "TransparentColor"}, - {aiTextureType_DISPLACEMENT, "DisplacementColor"}, - //{aiTextureType_LIGHTMAP, "???"}, - {aiTextureType_REFLECTION, "ReflectionColor"} - //{aiTextureType_UNKNOWN, ""} - }; - for (size_t i = 0; i < mScene->mNumMaterials; ++i) { - // textures are attached to materials - aiMaterial* mat = mScene->mMaterials[i]; - int64_t material_uid = material_uids[i]; - - for ( - size_t j = aiTextureType_DIFFUSE; - j < aiTextureType_UNKNOWN; - ++j - ) { - const aiTextureType tt = static_cast(j); - size_t n = mat->GetTextureCount(tt); - - if (n < 1) { // no texture of this type - continue; - } - - if (n > 1) { - // TODO: multilayer textures - std::stringstream err; - err << "Multilayer textures not supported (for now),"; - err << " skipping texture type " << j; - err << " of material " << i; - ASSIMP_LOG_WARN(err.str()); - } - - // get image path for this (single-image) texture - aiString tpath; - if (mat->GetTexture(tt, 0, &tpath) != aiReturn_SUCCESS) { - std::stringstream err; - err << "Failed to get texture 0 for texture of type " << tt; - err << " on material " << i; - err << ", however GetTextureCount returned 1."; - throw DeadlyExportError(err.str()); - } - const std::string texture_path(tpath.C_Str()); - - // get connected image uid - auto elem = uid_by_image.find(texture_path); - if (elem == uid_by_image.end()) { - // this should never happen - std::stringstream err; - err << "Failed to find video element for texture with path"; - err << " \"" << texture_path << "\""; - err << ", type " << j << ", material " << i; - throw DeadlyExportError(err.str()); - } - const int64_t image_uid = elem->second; - - // get the name of the material property to connect to - auto elem2 = prop_name_by_tt.find(tt); - if (elem2 == prop_name_by_tt.end()) { - // don't know how to handle this type of texture, - // so skip it. - std::stringstream err; - err << "Not sure how to handle texture of type " << j; - err << " on material " << i; - err << ", skipping..."; - ASSIMP_LOG_WARN(err.str()); - continue; - } - const std::string& prop_name = elem2->second; - - // generate a uid for this texture - const int64_t texture_uid = generate_uid(); - - // link the texture to the material - connections.emplace_back( - "C", "OP", texture_uid, material_uid, prop_name - ); - - // link the image data to the texture - connections.emplace_back("C", "OO", image_uid, texture_uid); - - // now write the actual texture node - FBX::Node tnode("Texture"); - // TODO: some way to determine texture name? - const std::string texture_name = "" + FBX::SEPARATOR + "Texture"; - tnode.AddProperties(texture_uid, texture_name, ""); - // there really doesn't seem to be a better type than this: - tnode.AddChild("Type", "TextureVideoClip"); - tnode.AddChild("Version", int32_t(202)); - tnode.AddChild("TextureName", texture_name); - FBX::Node p("Properties70"); - p.AddP70enum("CurrentTextureBlendMode", 0); // TODO: verify - //p.AddP70string("UVSet", ""); // TODO: how should this work? - p.AddP70bool("UseMaterial", 1); - tnode.AddChild(p); - // can't easily detrmine which texture path will be correct, - // so just store what we have in every field. - // these being incorrect is a common problem with FBX anyway. - tnode.AddChild("FileName", texture_path); - tnode.AddChild("RelativeFilename", texture_path); - tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0)); - tnode.AddChild("ModelUVScaling", double(1.0), double(1.0)); - tnode.AddChild("Texture_Alpha_Source", "None"); - tnode.AddChild( - "Cropping", int32_t(0), int32_t(0), int32_t(0), int32_t(0) - ); - tnode.Dump(outstream, binary, indent); - } - } - - // bones. - // - // output structure: - // subset of node hierarchy that are "skeleton", - // i.e. do not have meshes but only bones. - // but.. i'm not sure how anyone could guarantee that... - // - // input... - // well, for each mesh it has "bones", - // and the bone names correspond to nodes. - // of course we also need the parent nodes, - // as they give some of the transform........ - // - // well. we can assume a sane input, i suppose. - // - // so input is the bone node hierarchy, - // with an extra thing for the transformation of the MESH in BONE space. - // - // output is a set of bone nodes, - // a "bindpose" which indicates the default local transform of all bones, - // and a set of "deformers". - // each deformer is parented to a mesh geometry, - // and has one or more "subdeformer"s as children. - // each subdeformer has one bone node as a child, - // and represents the influence of that bone on the grandparent mesh. - // the subdeformer has a list of indices, and weights, - // with indices specifying vertex indices, - // and weights specifying the corresponding influence of this bone. - // it also has Transform and TransformLink elements, - // specifying the transform of the MESH in BONE space, - // and the transformation of the BONE in WORLD space, - // likely in the bindpose. - // - // the input bone structure is different but similar, - // storing the number of weights for this bone, - // and an array of (vertex index, weight) pairs. - // - // one sticky point is that the number of vertices may not match, - // because assimp splits vertices by normal, uv, etc. - - // first we should mark the skeleton for each mesh. - // the skeleton must include not only the aiBones, - // but also all their parent nodes. - // anything that affects the position of any bone node must be included. - std::vector> skeleton_by_mesh(mScene->mNumMeshes); - // at the same time we can build a list of all the skeleton nodes, - // which will be used later to mark them as type "limbNode". - std::unordered_set limbnodes; - // and a map of nodes by bone name, as finding them is annoying. - std::map node_by_bone; - for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { - const aiMesh* m = mScene->mMeshes[mi]; - std::set skeleton; - for (size_t bi =0; bi < m->mNumBones; ++bi) { - const aiBone* b = m->mBones[bi]; - const std::string name(b->mName.C_Str()); - auto elem = node_by_bone.find(name); - aiNode* n; - if (elem != node_by_bone.end()) { - n = elem->second; - } else { - n = mScene->mRootNode->FindNode(b->mName); - if (!n) { - // this should never happen - std::stringstream err; - err << "Failed to find node for bone: \"" << name << "\""; - throw DeadlyExportError(err.str()); - } - node_by_bone[name] = n; - limbnodes.insert(n); - } - skeleton.insert(n); - // mark all parent nodes as skeleton as well, - // up until we find the root node, - // or else the node containing the mesh, - // or else the parent of a node containig the mesh. - for ( - const aiNode* parent = n->mParent; - parent && parent != mScene->mRootNode; - parent = parent->mParent - ) { - // if we've already done this node we can skip it all - if (skeleton.count(parent)) { - break; - } - // ignore fbx transform nodes as these will be collapsed later - // TODO: cache this by aiNode* - const std::string node_name(parent->mName.C_Str()); - if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) { - continue; - } - // otherwise check if this is the root of the skeleton - bool end = false; - // is the mesh part of this node? - for (size_t i = 0; i < parent->mNumMeshes; ++i) { - if (parent->mMeshes[i] == mi) { - end = true; - break; - } - } - // is the mesh in one of the children of this node? - for (size_t j = 0; j < parent->mNumChildren; ++j) { - aiNode* child = parent->mChildren[j]; - for (size_t i = 0; i < child->mNumMeshes; ++i) { - if (child->mMeshes[i] == mi) { - end = true; - break; - } - } - if (end) { break; } - } - limbnodes.insert(parent); - skeleton.insert(parent); - // if it was the skeleton root we can finish here - if (end) { break; } - } - } - skeleton_by_mesh[mi] = skeleton; - } - - // we'll need the uids for the bone nodes, so generate them now - for (size_t i = 0; i < mScene->mNumMeshes; ++i) { - auto &s = skeleton_by_mesh[i]; - for (const aiNode* n : s) { - auto elem = node_uids.find(n); - if (elem == node_uids.end()) { - node_uids[n] = generate_uid(); - } - } - } - - // now, for each aiMesh, we need to export a deformer, - // and for each aiBone a subdeformer, - // which should have all the skinning info. - // these will need to be connected properly to the mesh, - // and we can do that all now. - for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { - const aiMesh* m = mScene->mMeshes[mi]; - if (!m->HasBones()) { - continue; - } - // make a deformer for this mesh - int64_t deformer_uid = generate_uid(); - FBX::Node dnode("Deformer"); - dnode.AddProperties(deformer_uid, FBX::SEPARATOR + "Deformer", "Skin"); - dnode.AddChild("Version", int32_t(101)); - // "acuracy"... this is not a typo.... - dnode.AddChild("Link_DeformAcuracy", double(50)); - dnode.AddChild("SkinningType", "Linear"); // TODO: other modes? - dnode.Dump(outstream, binary, indent); - - // connect it - connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]); - - // we will be indexing by vertex... - // but there might be a different number of "vertices" - // between assimp and our output FBX. - // this code is cut-and-pasted from the geometry section above... - // ideally this should not be so. - // --- - // index of original vertex in vertex data vector - std::vector vertex_indices; - // map of vertex value to its index in the data vector - std::map index_by_vertex_value; - int32_t index = 0; - for (size_t vi = 0; vi < m->mNumVertices; ++vi) { - aiVector3D vtx = m->mVertices[vi]; - auto elem = index_by_vertex_value.find(vtx); - if (elem == index_by_vertex_value.end()) { - vertex_indices.push_back(index); - index_by_vertex_value[vtx] = index; - ++index; - } else { - vertex_indices.push_back(int32_t(elem->second)); - } - } - - // TODO, FIXME: this won't work if anything is not in the bind pose. - // for now if such a situation is detected, we throw an exception. - std::set not_in_bind_pose; - std::set no_offset_matrix; - - // first get this mesh's position in world space, - // as we'll need it for each subdeformer. - // - // ...of course taking the position of the MESH doesn't make sense, - // as it can be instanced to many nodes. - // All we can do is assume no instancing, - // and take the first node we find that contains the mesh. - aiNode* mesh_node = get_node_for_mesh((unsigned int)mi, mScene->mRootNode); - aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene); - - // now make a subdeformer for each bone in the skeleton - const std::set &skeleton = skeleton_by_mesh[mi]; - for (const aiNode* bone_node : skeleton) { - // if there's a bone for this node, find it - const aiBone* b = nullptr; - for (size_t bi = 0; bi < m->mNumBones; ++bi) { - // TODO: this probably should index by something else - const std::string name(m->mBones[bi]->mName.C_Str()); - if (node_by_bone[name] == bone_node) { - b = m->mBones[bi]; - break; - } - } - if (!b) { - no_offset_matrix.insert(bone_node); - } - - // start the subdeformer node - const int64_t subdeformer_uid = generate_uid(); - FBX::Node sdnode("Deformer"); - sdnode.AddProperties( - subdeformer_uid, FBX::SEPARATOR + "SubDeformer", "Cluster" - ); - sdnode.AddChild("Version", int32_t(100)); - sdnode.AddChild("UserData", "", ""); - - // add indices and weights, if any - if (b) { - std::vector subdef_indices; - std::vector subdef_weights; - int32_t last_index = -1; - for (size_t wi = 0; wi < b->mNumWeights; ++wi) { - int32_t vi = vertex_indices[b->mWeights[wi].mVertexId]; - if (vi == last_index) { - // only for vertices we exported to fbx - // TODO, FIXME: this assumes identically-located vertices - // will always deform in the same way. - // as assimp doesn't store a separate list of "positions", - // there's not much that can be done about this - // other than assuming that identical position means - // identical vertex. - continue; - } - subdef_indices.push_back(vi); - subdef_weights.push_back(b->mWeights[wi].mWeight); - last_index = vi; - } - // yes, "indexes" - sdnode.AddChild("Indexes", subdef_indices); - sdnode.AddChild("Weights", subdef_weights); - } - - // transform is the transform of the mesh, but in bone space. - // if the skeleton is in the bind pose, - // we can take the inverse of the world-space bone transform - // and multiply by the world-space transform of the mesh. - aiMatrix4x4 bone_xform = get_world_transform(bone_node, mScene); - aiMatrix4x4 inverse_bone_xform = bone_xform; - inverse_bone_xform.Inverse(); - aiMatrix4x4 tr = inverse_bone_xform * mesh_xform; - - // this should be the same as the bone's mOffsetMatrix. - // if it's not the same, the skeleton isn't in the bind pose. - const float epsilon = 1e-4f; // some error is to be expected - bool bone_xform_okay = true; - if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) { - not_in_bind_pose.insert(b); - bone_xform_okay = false; - } - - // if we have a bone we should use the mOffsetMatrix, - // otherwise try to just use the calculated transform. - if (b) { - sdnode.AddChild("Transform", b->mOffsetMatrix); - } else { - sdnode.AddChild("Transform", tr); - } - // note: it doesn't matter if we mix these, - // because if they disagree we'll throw an exception later. - // it could be that the skeleton is not in the bone pose - // but all bones are still defined, - // in which case this would use the mOffsetMatrix for everything - // and a correct skeleton would still be output. - - // transformlink should be the position of the bone in world space. - // if the bone is in the bind pose (or nonexistent), - // we can just use the matrix we already calculated - if (bone_xform_okay) { - sdnode.AddChild("TransformLink", bone_xform); - // otherwise we can only work it out using the mesh position. - } else { - aiMatrix4x4 trl = b->mOffsetMatrix; - trl.Inverse(); - trl *= mesh_xform; - sdnode.AddChild("TransformLink", trl); - } - // note: this means we ALWAYS rely on the mesh node transform - // being unchanged from the time the skeleton was bound. - // there's not really any way around this at the moment. - - // done - sdnode.Dump(outstream, binary, indent); - - // lastly, connect to the parent deformer - connections.emplace_back( - "C", "OO", subdeformer_uid, deformer_uid - ); - - // we also need to connect the limb node to the subdeformer. - connections.emplace_back( - "C", "OO", node_uids[bone_node], subdeformer_uid - ); - } - - // if we cannot create a valid FBX file, simply die. - // this will both prevent unnecessary bug reports, - // and tell the user what they can do to fix the situation - // (i.e. export their model in the bind pose). - if (no_offset_matrix.size() && not_in_bind_pose.size()) { - std::stringstream err; - err << "Not enough information to construct bind pose"; - err << " for mesh " << mi << "!"; - err << " Transform matrix for bone \""; - err << (*not_in_bind_pose.begin())->mName.C_Str() << "\""; - if (not_in_bind_pose.size() > 1) { - err << " (and " << not_in_bind_pose.size() - 1 << " more)"; - } - err << " does not match mOffsetMatrix,"; - err << " and node \""; - err << (*no_offset_matrix.begin())->mName.C_Str() << "\""; - if (no_offset_matrix.size() > 1) { - err << " (and " << no_offset_matrix.size() - 1 << " more)"; - } - err << " has no offset matrix to rely on."; - err << " Please ensure bones are in the bind pose to export."; - throw DeadlyExportError(err.str()); - } - - } - - // BindPose - // - // This is a legacy system, which should be unnecessary. - // - // Somehow including it slows file loading by the official FBX SDK, - // and as it can reconstruct it from the deformers anyway, - // this is not currently included. - // - // The code is kept here in case it's useful in the future, - // but it's pretty much a hack anyway, - // as assimp doesn't store bindpose information for full skeletons. - // - /*for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { - aiMesh* mesh = mScene->mMeshes[mi]; - if (! mesh->HasBones()) { continue; } - int64_t bindpose_uid = generate_uid(); - FBX::Node bpnode("Pose"); - bpnode.AddProperty(bindpose_uid); - // note: this uid is never linked or connected to anything. - bpnode.AddProperty(FBX::SEPARATOR + "Pose"); // blank name - bpnode.AddProperty("BindPose"); - - bpnode.AddChild("Type", "BindPose"); - bpnode.AddChild("Version", int32_t(100)); - - aiNode* mesh_node = get_node_for_mesh(mi, mScene->mRootNode); - - // next get the whole skeleton for this mesh. - // we need it all to define the bindpose section. - // the FBX SDK will complain if it's missing, - // and also if parents of used bones don't have a subdeformer. - // order shouldn't matter. - std::set skeleton; - for (size_t bi = 0; bi < mesh->mNumBones; ++bi) { - // bone node should have already been indexed - const aiBone* b = mesh->mBones[bi]; - const std::string bone_name(b->mName.C_Str()); - aiNode* parent = node_by_bone[bone_name]; - // insert all nodes down to the root or mesh node - while ( - parent - && parent != mScene->mRootNode - && parent != mesh_node - ) { - skeleton.insert(parent); - parent = parent->mParent; - } - } - - // number of pose nodes. includes one for the mesh itself. - bpnode.AddChild("NbPoseNodes", int32_t(1 + skeleton.size())); - - // the first pose node is always the mesh itself - FBX::Node pose("PoseNode"); - pose.AddChild("Node", mesh_uids[mi]); - aiMatrix4x4 mesh_node_xform = get_world_transform(mesh_node, mScene); - pose.AddChild("Matrix", mesh_node_xform); - bpnode.AddChild(pose); - - for (aiNode* bonenode : skeleton) { - // does this node have a uid yet? - int64_t node_uid; - auto node_uid_iter = node_uids.find(bonenode); - if (node_uid_iter != node_uids.end()) { - node_uid = node_uid_iter->second; - } else { - node_uid = generate_uid(); - node_uids[bonenode] = node_uid; - } - - // make a pose thingy - pose = FBX::Node("PoseNode"); - pose.AddChild("Node", node_uid); - aiMatrix4x4 node_xform = get_world_transform(bonenode, mScene); - pose.AddChild("Matrix", node_xform); - bpnode.AddChild(pose); - } - - // now write it - bpnode.Dump(outstream, binary, indent); - }*/ - - // TODO: cameras, lights - - // write nodes (i.e. model hierarchy) - // start at root node - WriteModelNodes( - outstream, mScene->mRootNode, 0, limbnodes - ); - - // animations - // - // in FBX there are: - // * AnimationStack - corresponds to an aiAnimation - // * AnimationLayer - a combinable animation component - // * AnimationCurveNode - links the property to be animated - // * AnimationCurve - defines animation data for a single property value - // - // the CurveNode also provides the default value for a property, - // such as the X, Y, Z coordinates for animatable translation. - // - // the Curve only specifies values for one component of the property, - // so there will be a separate AnimationCurve for X, Y, and Z. - // - // Assimp has: - // * aiAnimation - basically corresponds to an AnimationStack - // * aiNodeAnim - defines all animation for one aiNode - // * aiVectorKey/aiQuatKey - define the keyframe data for T/R/S - // - // assimp has no equivalent for AnimationLayer, - // and these are flattened on FBX import. - // we can assume there will be one per AnimationStack. - // - // the aiNodeAnim contains all animation data for a single aiNode, - // which will correspond to three AnimationCurveNode's: - // one each for translation, rotation and scale. - // The data for each of these will be put in 9 AnimationCurve's, - // T.X, T.Y, T.Z, R.X, R.Y, R.Z, etc. - - // AnimationStack / aiAnimation - std::vector animation_stack_uids(mScene->mNumAnimations); - for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { - int64_t animstack_uid = generate_uid(); - animation_stack_uids[ai] = animstack_uid; - const aiAnimation* anim = mScene->mAnimations[ai]; - - FBX::Node asnode("AnimationStack"); - std::string name = anim->mName.C_Str() + FBX::SEPARATOR + "AnimStack"; - asnode.AddProperties(animstack_uid, name, ""); - FBX::Node p("Properties70"); - p.AddP70time("LocalStart", 0); // assimp doesn't store this - p.AddP70time("LocalStop", to_ktime(anim->mDuration, anim)); - p.AddP70time("ReferenceStart", 0); - p.AddP70time("ReferenceStop", to_ktime(anim->mDuration, anim)); - asnode.AddChild(p); - - // this node absurdly always pretends it has children - // (in this case it does, but just in case...) - asnode.force_has_children = true; - asnode.Dump(outstream, binary, indent); - - // note: animation stacks are not connected to anything - } - - // AnimationLayer - one per aiAnimation - std::vector animation_layer_uids(mScene->mNumAnimations); - for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { - int64_t animlayer_uid = generate_uid(); - animation_layer_uids[ai] = animlayer_uid; - FBX::Node alnode("AnimationLayer"); - alnode.AddProperties(animlayer_uid, FBX::SEPARATOR + "AnimLayer", ""); - - // this node absurdly always pretends it has children - alnode.force_has_children = true; - alnode.Dump(outstream, binary, indent); - - // connect to the relevant animstack - connections.emplace_back( - "C", "OO", animlayer_uid, animation_stack_uids[ai] - ); - } - - // AnimCurveNode - three per aiNodeAnim - std::vector>> curve_node_uids; - for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { - const aiAnimation* anim = mScene->mAnimations[ai]; - const int64_t layer_uid = animation_layer_uids[ai]; - std::vector> nodeanim_uids; - for (size_t nai = 0; nai < anim->mNumChannels; ++nai) { - const aiNodeAnim* na = anim->mChannels[nai]; - // get the corresponding aiNode - const aiNode* node = mScene->mRootNode->FindNode(na->mNodeName); - // and its transform - const aiMatrix4x4 node_xfm = get_world_transform(node, mScene); - aiVector3D T, R, S; - node_xfm.Decompose(S, R, T); - - // AnimationCurveNode uids - std::array ids; - ids[0] = generate_uid(); // T - ids[1] = generate_uid(); // R - ids[2] = generate_uid(); // S - - // translation - WriteAnimationCurveNode(outstream, - ids[0], "T", T, "Lcl Translation", - layer_uid, node_uids[node] - ); - - // rotation - WriteAnimationCurveNode(outstream, - ids[1], "R", R, "Lcl Rotation", - layer_uid, node_uids[node] - ); - - // scale - WriteAnimationCurveNode(outstream, - ids[2], "S", S, "Lcl Scale", - layer_uid, node_uids[node] - ); - - // store the uids for later use - nodeanim_uids.push_back(ids); - } - curve_node_uids.push_back(nodeanim_uids); - } - - // AnimCurve - defines actual keyframe data. - // there's a separate curve for every component of every vector, - // for example a transform curvenode will have separate X/Y/Z AnimCurve's - for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { - const aiAnimation* anim = mScene->mAnimations[ai]; - for (size_t nai = 0; nai < anim->mNumChannels; ++nai) { - const aiNodeAnim* na = anim->mChannels[nai]; - // get the corresponding aiNode - const aiNode* node = mScene->mRootNode->FindNode(na->mNodeName); - // and its transform - const aiMatrix4x4 node_xfm = get_world_transform(node, mScene); - aiVector3D T, R, S; - node_xfm.Decompose(S, R, T); - const std::array& ids = curve_node_uids[ai][nai]; - - std::vector times; - std::vector xval, yval, zval; - - // position/translation - for (size_t ki = 0; ki < na->mNumPositionKeys; ++ki) { - const aiVectorKey& k = na->mPositionKeys[ki]; - times.push_back(to_ktime(k.mTime)); - xval.push_back(k.mValue.x); - yval.push_back(k.mValue.y); - zval.push_back(k.mValue.z); - } - // one curve each for X, Y, Z - WriteAnimationCurve(outstream, T.x, times, xval, ids[0], "d|X"); - WriteAnimationCurve(outstream, T.y, times, yval, ids[0], "d|Y"); - WriteAnimationCurve(outstream, T.z, times, zval, ids[0], "d|Z"); - - // rotation - times.clear(); xval.clear(); yval.clear(); zval.clear(); - for (size_t ki = 0; ki < na->mNumRotationKeys; ++ki) { - const aiQuatKey& k = na->mRotationKeys[ki]; - times.push_back(to_ktime(k.mTime)); - // TODO: aiQuaternion method to convert to Euler... - aiMatrix4x4 m(k.mValue.GetMatrix()); - aiVector3D qs, qr, qt; - m.Decompose(qs, qr, qt); - qr *= DEG; - xval.push_back(qr.x); - yval.push_back(qr.y); - zval.push_back(qr.z); - } - WriteAnimationCurve(outstream, R.x, times, xval, ids[1], "d|X"); - WriteAnimationCurve(outstream, R.y, times, yval, ids[1], "d|Y"); - WriteAnimationCurve(outstream, R.z, times, zval, ids[1], "d|Z"); - - // scaling/scale - times.clear(); xval.clear(); yval.clear(); zval.clear(); - for (size_t ki = 0; ki < na->mNumScalingKeys; ++ki) { - const aiVectorKey& k = na->mScalingKeys[ki]; - times.push_back(to_ktime(k.mTime)); - xval.push_back(k.mValue.x); - yval.push_back(k.mValue.y); - zval.push_back(k.mValue.z); - } - WriteAnimationCurve(outstream, S.x, times, xval, ids[2], "d|X"); - WriteAnimationCurve(outstream, S.y, times, yval, ids[2], "d|Y"); - WriteAnimationCurve(outstream, S.z, times, zval, ids[2], "d|Z"); - } - } - - indent = 0; - object_node.End(outstream, binary, indent, true); -} - -// convenience map of magic node name strings to FBX properties, -// including the expected type of transform. -const std::map> transform_types = { - {"Translation", {"Lcl Translation", 't'}}, - {"RotationOffset", {"RotationOffset", 't'}}, - {"RotationPivot", {"RotationPivot", 't'}}, - {"PreRotation", {"PreRotation", 'r'}}, - {"Rotation", {"Lcl Rotation", 'r'}}, - {"PostRotation", {"PostRotation", 'r'}}, - {"RotationPivotInverse", {"RotationPivotInverse", 'i'}}, - {"ScalingOffset", {"ScalingOffset", 't'}}, - {"ScalingPivot", {"ScalingPivot", 't'}}, - {"Scaling", {"Lcl Scaling", 's'}}, - {"ScalingPivotInverse", {"ScalingPivotInverse", 'i'}}, - {"GeometricScaling", {"GeometricScaling", 's'}}, - {"GeometricRotation", {"GeometricRotation", 'r'}}, - {"GeometricTranslation", {"GeometricTranslation", 't'}}, - {"GeometricTranslationInverse", {"GeometricTranslationInverse", 'i'}}, - {"GeometricRotationInverse", {"GeometricRotationInverse", 'i'}}, - {"GeometricScalingInverse", {"GeometricScalingInverse", 'i'}} -}; - -// write a single model node to the stream -void FBXExporter::WriteModelNode( - StreamWriterLE& outstream, - bool binary, - const aiNode* node, - int64_t node_uid, - const std::string& type, - const std::vector>& transform_chain, - TransformInheritance inherit_type -){ - const aiVector3D zero = {0, 0, 0}; - const aiVector3D one = {1, 1, 1}; - FBX::Node m("Model"); - std::string name = node->mName.C_Str() + FBX::SEPARATOR + "Model"; - m.AddProperties(node_uid, name, type); - m.AddChild("Version", int32_t(232)); - FBX::Node p("Properties70"); - p.AddP70bool("RotationActive", 1); - p.AddP70int("DefaultAttributeIndex", 0); - p.AddP70enum("InheritType", inherit_type); - if (transform_chain.empty()) { - // decompose 4x4 transform matrix into TRS - aiVector3D t, r, s; - node->mTransformation.Decompose(s, r, t); - if (t != zero) { - p.AddP70( - "Lcl Translation", "Lcl Translation", "", "A", - double(t.x), double(t.y), double(t.z) - ); - } - if (r != zero) { - p.AddP70( - "Lcl Rotation", "Lcl Rotation", "", "A", - double(DEG*r.x), double(DEG*r.y), double(DEG*r.z) - ); - } - if (s != one) { - p.AddP70( - "Lcl Scaling", "Lcl Scaling", "", "A", - double(s.x), double(s.y), double(s.z) - ); - } - } else { - // apply the transformation chain. - // these transformation elements are created when importing FBX, - // which has a complex transformation hierarchy for each node. - // as such we can bake the hierarchy back into the node on export. - for (auto &item : transform_chain) { - auto elem = transform_types.find(item.first); - if (elem == transform_types.end()) { - // then this is a bug - std::stringstream err; - err << "unrecognized FBX transformation type: "; - err << item.first; - throw DeadlyExportError(err.str()); - } - const std::string &name = elem->second.first; - const aiVector3D &v = item.second; - if (name.compare(0, 4, "Lcl ") == 0) { - // special handling for animatable properties - p.AddP70( - name, name, "", "A", - double(v.x), double(v.y), double(v.z) - ); - } else { - p.AddP70vector(name, v.x, v.y, v.z); - } - } - } - m.AddChild(p); - - // not sure what these are for, - // but they seem to be omnipresent - m.AddChild("Shading", Property(true)); - m.AddChild("Culling", Property("CullingOff")); - - m.Dump(outstream, binary, 1); -} - -// wrapper for WriteModelNodes to create and pass a blank transform chain -void FBXExporter::WriteModelNodes( - StreamWriterLE& s, - const aiNode* node, - int64_t parent_uid, - const std::unordered_set& limbnodes -) { - std::vector> chain; - WriteModelNodes(s, node, parent_uid, limbnodes, chain); -} - -void FBXExporter::WriteModelNodes( - StreamWriterLE& outstream, - const aiNode* node, - int64_t parent_uid, - const std::unordered_set& limbnodes, - std::vector>& transform_chain -) { - // first collapse any expanded transformation chains created by FBX import. - std::string node_name(node->mName.C_Str()); - if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) { - auto pos = node_name.find(MAGIC_NODE_TAG) + MAGIC_NODE_TAG.size() + 1; - std::string type_name = node_name.substr(pos); - auto elem = transform_types.find(type_name); - if (elem == transform_types.end()) { - // then this is a bug and should be fixed - std::stringstream err; - err << "unrecognized FBX transformation node"; - err << " of type " << type_name << " in node " << node_name; - throw DeadlyExportError(err.str()); - } - aiVector3D t, r, s; - node->mTransformation.Decompose(s, r, t); - switch (elem->second.second) { - case 'i': // inverse - // we don't need to worry about the inverse matrices - break; - case 't': // translation - transform_chain.emplace_back(elem->first, t); - break; - case 'r': // rotation - r *= float(DEG); - transform_chain.emplace_back(elem->first, r); - break; - case 's': // scale - transform_chain.emplace_back(elem->first, s); - break; - default: - // this should never happen - std::stringstream err; - err << "unrecognized FBX transformation type code: "; - err << elem->second.second; - throw DeadlyExportError(err.str()); - } - // now continue on to any child nodes - for (unsigned i = 0; i < node->mNumChildren; ++i) { - WriteModelNodes( - outstream, - node->mChildren[i], - parent_uid, - limbnodes, - transform_chain - ); - } - return; - } - - int64_t node_uid = 0; - // generate uid and connect to parent, if not the root node, - if (node != mScene->mRootNode) { - auto elem = node_uids.find(node); - if (elem != node_uids.end()) { - node_uid = elem->second; - } else { - node_uid = generate_uid(); - node_uids[node] = node_uid; - } - connections.emplace_back("C", "OO", node_uid, parent_uid); - } - - // what type of node is this? - if (node == mScene->mRootNode) { - // handled later - } else if (node->mNumMeshes == 1) { - // connect to child mesh, which should have been written previously - connections.emplace_back( - "C", "OO", mesh_uids[node->mMeshes[0]], node_uid - ); - // also connect to the material for the child mesh - connections.emplace_back( - "C", "OO", - material_uids[mScene->mMeshes[node->mMeshes[0]]->mMaterialIndex], - node_uid - ); - // write model node - WriteModelNode( - outstream, binary, node, node_uid, "Mesh", transform_chain - ); - } else if (limbnodes.count(node)) { - WriteModelNode( - outstream, binary, node, node_uid, "LimbNode", transform_chain - ); - // we also need to write a nodeattribute to mark it as a skeleton - int64_t node_attribute_uid = generate_uid(); - FBX::Node na("NodeAttribute"); - na.AddProperties( - node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode" - ); - na.AddChild("TypeFlags", Property("Skeleton")); - na.Dump(outstream, binary, 1); - // and connect them - connections.emplace_back("C", "OO", node_attribute_uid, node_uid); - } else { - // generate a null node so we can add children to it - WriteModelNode( - outstream, binary, node, node_uid, "Null", transform_chain - ); - } - - // if more than one child mesh, make nodes for each mesh - if (node->mNumMeshes > 1 || node == mScene->mRootNode) { - for (size_t i = 0; i < node->mNumMeshes; ++i) { - // make a new model node - int64_t new_node_uid = generate_uid(); - // connect to parent node - connections.emplace_back("C", "OO", new_node_uid, node_uid); - // connect to child mesh, which should have been written previously - connections.emplace_back( - "C", "OO", mesh_uids[node->mMeshes[i]], new_node_uid - ); - // also connect to the material for the child mesh - connections.emplace_back( - "C", "OO", - material_uids[ - mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex - ], - new_node_uid - ); - // write model node - FBX::Node m("Model"); - // take name from mesh name, if it exists - std::string name = mScene->mMeshes[node->mMeshes[i]]->mName.C_Str(); - name += FBX::SEPARATOR + "Model"; - m.AddProperties(new_node_uid, name, "Mesh"); - m.AddChild("Version", int32_t(232)); - FBX::Node p("Properties70"); - p.AddP70enum("InheritType", 1); - m.AddChild(p); - m.Dump(outstream, binary, 1); - } - } - - // now recurse into children - for (size_t i = 0; i < node->mNumChildren; ++i) { - WriteModelNodes( - outstream, node->mChildren[i], node_uid, limbnodes - ); - } -} - - -void FBXExporter::WriteAnimationCurveNode( - StreamWriterLE& outstream, - int64_t uid, - std::string name, // "T", "R", or "S" - aiVector3D default_value, - std::string property_name, // "Lcl Translation" etc - int64_t layer_uid, - int64_t node_uid -) { - FBX::Node n("AnimationCurveNode"); - n.AddProperties(uid, name + FBX::SEPARATOR + "AnimCurveNode", ""); - FBX::Node p("Properties70"); - p.AddP70numberA("d|X", default_value.x); - p.AddP70numberA("d|Y", default_value.y); - p.AddP70numberA("d|Z", default_value.z); - n.AddChild(p); - n.Dump(outstream, binary, 1); - // connect to layer - this->connections.emplace_back("C", "OO", uid, layer_uid); - // connect to bone - this->connections.emplace_back("C", "OP", uid, node_uid, property_name); -} - - -void FBXExporter::WriteAnimationCurve( - StreamWriterLE& outstream, - double default_value, - const std::vector& times, - const std::vector& values, - int64_t curvenode_uid, - const std::string& property_link // "d|X", "d|Y", etc -) { - FBX::Node n("AnimationCurve"); - int64_t curve_uid = generate_uid(); - n.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); - n.AddChild("Default", default_value); - n.AddChild("KeyVer", int32_t(4009)); - n.AddChild("KeyTime", times); - n.AddChild("KeyValueFloat", values); - // TODO: keyattr flags and data (STUB for now) - n.AddChild("KeyAttrFlags", std::vector{0}); - n.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); - n.AddChild( - "KeyAttrRefCount", - std::vector{static_cast(times.size())} - ); - n.Dump(outstream, binary, 1); - this->connections.emplace_back( - "C", "OP", curve_uid, curvenode_uid, property_link - ); -} - - -void FBXExporter::WriteConnections () -{ - // we should have completed the connection graph already, - // so basically just dump it here - if (!binary) { - WriteAsciiSectionHeader("Object connections"); - } - // TODO: comments with names in the ascii version - FBX::Node conn("Connections"); - StreamWriterLE outstream(outfile); - conn.Begin(outstream, binary, 0); - conn.BeginChildren(outstream, binary, 0); - for (auto &n : connections) { - n.Dump(outstream, binary, 1); - } - conn.End(outstream, binary, 0, !connections.empty()); - connections.clear(); -} - -#endif // ASSIMP_BUILD_NO_FBX_EXPORTER -#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/thirdparty/assimp/code/FBXExporter.h b/thirdparty/assimp/code/FBXExporter.h deleted file mode 100644 index 71fb55c57f..0000000000 --- a/thirdparty/assimp/code/FBXExporter.h +++ /dev/null @@ -1,178 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXExporter.h -* Declares the exporter class to write a scene to an fbx file -*/ -#ifndef AI_FBXEXPORTER_H_INC -#define AI_FBXEXPORTER_H_INC - -#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER - -#include "FBXExportNode.h" // FBX::Node -#include "FBXCommon.h" // FBX::TransformInheritance - -#include -//#include -#include // StreamWriterLE -#include // DeadlyExportError - -#include -#include -#include -#include // shared_ptr -#include // stringstream - -struct aiScene; -struct aiNode; -//struct aiMaterial; - -namespace Assimp -{ - class IOSystem; - class IOStream; - class ExportProperties; - - // --------------------------------------------------------------------- - /** Helper class to export a given scene to an FBX file. */ - // --------------------------------------------------------------------- - class FBXExporter - { - public: - /// Constructor for a specific scene to export - FBXExporter(const aiScene* pScene, const ExportProperties* pProperties); - - // call one of these methods to export - void ExportBinary(const char* pFile, IOSystem* pIOSystem); - void ExportAscii(const char* pFile, IOSystem* pIOSystem); - - private: - bool binary; // whether current export is in binary or ascii format - const aiScene* mScene; // the scene to export - const ExportProperties* mProperties; // currently unused - std::shared_ptr outfile; // file to write to - - std::vector connections; // connection storage - - std::vector mesh_uids; - std::vector material_uids; - std::map node_uids; - - // this crude unique-ID system is actually fine - int64_t last_uid = 999999; - int64_t generate_uid() { return ++last_uid; } - - // binary files have a specific header and footer, - // in addition to the actual data - void WriteBinaryHeader(); - void WriteBinaryFooter(); - - // ascii files have a comment at the top - void WriteAsciiHeader(); - - // WriteAllNodes does the actual export. - // It just calls all the Write
methods below in order. - void WriteAllNodes(); - - // Methods to write individual sections. - // The order here matches the order inside an FBX file. - // Each method corresponds to a top-level FBX section, - // except WriteHeader which also includes some binary-only sections - // and WriteFooter which is binary data only. - void WriteHeaderExtension(); - // WriteFileId(); // binary-only, included in WriteHeader - // WriteCreationTime(); // binary-only, included in WriteHeader - // WriteCreator(); // binary-only, included in WriteHeader - void WriteGlobalSettings(); - void WriteDocuments(); - void WriteReferences(); - void WriteDefinitions(); - void WriteObjects(); - void WriteConnections(); - // WriteTakes(); // deprecated since at least 2015 (fbx 7.4) - - // helpers - void WriteAsciiSectionHeader(const std::string& title); - void WriteModelNodes( - Assimp::StreamWriterLE& s, - const aiNode* node, - int64_t parent_uid, - const std::unordered_set& limbnodes - ); - void WriteModelNodes( // usually don't call this directly - StreamWriterLE& s, - const aiNode* node, - int64_t parent_uid, - const std::unordered_set& limbnodes, - std::vector>& transform_chain - ); - void WriteModelNode( // nor this - StreamWriterLE& s, - bool binary, - const aiNode* node, - int64_t node_uid, - const std::string& type, - const std::vector>& xfm_chain, - FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs - ); - void WriteAnimationCurveNode( - StreamWriterLE& outstream, - int64_t uid, - std::string name, // "T", "R", or "S" - aiVector3D default_value, - std::string property_name, // "Lcl Translation" etc - int64_t animation_layer_uid, - int64_t node_uid - ); - void WriteAnimationCurve( - StreamWriterLE& outstream, - double default_value, - const std::vector& times, - const std::vector& values, - int64_t curvenode_id, - const std::string& property_link // "d|X", "d|Y", etc - ); - }; -} - -#endif // ASSIMP_BUILD_NO_FBX_EXPORTER - -#endif // AI_FBXEXPORTER_H_INC diff --git a/thirdparty/assimp/code/FBXImportSettings.h b/thirdparty/assimp/code/FBXImportSettings.h deleted file mode 100644 index d5e1c20608..0000000000 --- a/thirdparty/assimp/code/FBXImportSettings.h +++ /dev/null @@ -1,153 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXImportSettings.h - * @brief FBX importer runtime configuration - */ -#ifndef INCLUDED_AI_FBX_IMPORTSETTINGS_H -#define INCLUDED_AI_FBX_IMPORTSETTINGS_H - -namespace Assimp { -namespace FBX { - -/** FBX import settings, parts of which are publicly accessible via their corresponding AI_CONFIG constants */ -struct ImportSettings -{ - ImportSettings() - : strictMode(true) - , readAllLayers(true) - , readAllMaterials(false) - , readMaterials(true) - , readTextures(true) - , readCameras(true) - , readLights(true) - , readAnimations(true) - , readWeights(true) - , preservePivots(true) - , optimizeEmptyAnimationCurves(true) - , useLegacyEmbeddedTextureNaming(false) - {} - - - /** enable strict mode: - * - only accept fbx 2012, 2013 files - * - on the slightest error, give up. - * - * Basically, strict mode means that the fbx file will actually - * be validated. Strict mode is off by default. */ - bool strictMode; - - /** specifies whether all geometry layers are read and scanned for - * usable data channels. The FBX spec indicates that many readers - * will only read the first channel and that this is in some way - * the recommended way- in reality, however, it happens a lot that - * vertex data is spread among multiple layers. The default - * value for this option is true.*/ - bool readAllLayers; - - /** specifies whether all materials are read, or only those that - * are referenced by at least one mesh. Reading all materials - * may make FBX reading a lot slower since all objects - * need to be processed . - * This bit is ignored unless readMaterials=true*/ - bool readAllMaterials; - - - /** import materials (true) or skip them and assign a default - * material. The default value is true.*/ - bool readMaterials; - - /** import embedded textures? Default value is true.*/ - bool readTextures; - - /** import cameras? Default value is true.*/ - bool readCameras; - - /** import light sources? Default value is true.*/ - bool readLights; - - /** import animations (i.e. animation curves, the node - * skeleton is always imported). Default value is true. */ - bool readAnimations; - - /** read bones (vertex weights and deform info). - * Default value is true. */ - bool readWeights; - - /** preserve transformation pivots and offsets. Since these can - * not directly be represented in assimp, additional dummy - * nodes will be generated. Note that settings this to false - * can make animation import a lot slower. The default value - * is true. - * - * The naming scheme for the generated nodes is: - * _$AssimpFbx$_ - * - * where is one of - * RotationPivot - * RotationOffset - * PreRotation - * PostRotation - * ScalingPivot - * ScalingOffset - * Translation - * Scaling - * Rotation - **/ - bool preservePivots; - - /** do not import animation curves that specify a constant - * values matching the corresponding node transformation. - * The default value is true. */ - bool optimizeEmptyAnimationCurves; - - /** use legacy naming for embedded textures eg: (*0, *1, *2) - **/ - bool useLegacyEmbeddedTextureNaming; -}; - - -} // !FBX -} // !Assimp - -#endif - diff --git a/thirdparty/assimp/code/FBXImporter.cpp b/thirdparty/assimp/code/FBXImporter.cpp deleted file mode 100644 index 2cc8bffc29..0000000000 --- a/thirdparty/assimp/code/FBXImporter.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. -r -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXImporter.cpp - * @brief Implementation of the FBX importer. - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -#include "FBXImporter.h" - -#include "FBXTokenizer.h" -#include "FBXParser.h" -#include "FBXUtil.h" -#include "FBXDocument.h" -#include "FBXConverter.h" - -#include -#include -#include -#include - -namespace Assimp { - -template<> -const char* LogFunctions::Prefix() { - static auto prefix = "FBX: "; - return prefix; -} - -} - -using namespace Assimp; -using namespace Assimp::Formatter; -using namespace Assimp::FBX; - -namespace { - -static const aiImporterDesc desc = { - "Autodesk FBX Importer", - "", - "", - "", - aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "fbx" -}; -} - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by #Importer -FBXImporter::FBXImporter() -{ -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -FBXImporter::~FBXImporter() -{ -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. -bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const -{ - const std::string& extension = GetExtension(pFile); - if (extension == std::string( desc.mFileExtensions ) ) { - return true; - } - - else if ((!extension.length() || checkSig) && pIOHandler) { - // at least ASCII-FBX files usually have a 'FBX' somewhere in their head - const char* tokens[] = {"fbx"}; - return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); - } - return false; -} - -// ------------------------------------------------------------------------------------------------ -// List all extensions handled by this loader -const aiImporterDesc* FBXImporter::GetInfo () const -{ - return &desc; -} - -// ------------------------------------------------------------------------------------------------ -// Setup configuration properties for the loader -void FBXImporter::SetupProperties(const Importer* pImp) -{ - settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true); - settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false); - settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true); - settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true); - settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true); - settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true); - settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true); - settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false); - settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true); - settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true); - settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false); -} - -// ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. -void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) -{ - std::unique_ptr stream(pIOHandler->Open(pFile,"rb")); - if (!stream) { - ThrowException("Could not open file for reading"); - } - - // read entire file into memory - no streaming for this, fbx - // files can grow large, but the assimp output data structure - // then becomes very large, too. Assimp doesn't support - // streaming for its output data structures so the net win with - // streaming input data would be very low. - std::vector contents; - contents.resize(stream->FileSize()+1); - stream->Read( &*contents.begin(), 1, contents.size()-1 ); - contents[ contents.size() - 1 ] = 0; - const char* const begin = &*contents.begin(); - - // broadphase tokenizing pass in which we identify the core - // syntax elements of FBX (brackets, commas, key:value mappings) - TokenList tokens; - try { - - bool is_binary = false; - if (!strncmp(begin,"Kaydara FBX Binary",18)) { - is_binary = true; - TokenizeBinary(tokens,begin,static_cast(contents.size())); - } - else { - Tokenize(tokens,begin); - } - - // use this information to construct a very rudimentary - // parse-tree representing the FBX scope structure - Parser parser(tokens, is_binary); - - // take the raw parse-tree and convert it to a FBX DOM - Document doc(parser,settings); - - // convert the FBX DOM to aiScene - ConvertToAssimpScene(pScene,doc); - - std::for_each(tokens.begin(),tokens.end(),Util::delete_fun()); - } - catch(std::exception&) { - std::for_each(tokens.begin(),tokens.end(),Util::delete_fun()); - throw; - } -} - -#endif // !ASSIMP_BUILD_NO_FBX_IMPORTER diff --git a/thirdparty/assimp/code/FBXImporter.h b/thirdparty/assimp/code/FBXImporter.h deleted file mode 100644 index c365b2cddf..0000000000 --- a/thirdparty/assimp/code/FBXImporter.h +++ /dev/null @@ -1,100 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXImporter.h - * @brief Declaration of the FBX main importer class - */ -#ifndef INCLUDED_AI_FBX_IMPORTER_H -#define INCLUDED_AI_FBX_IMPORTER_H - -#include -#include - -#include "FBXImportSettings.h" - -namespace Assimp { - -// TinyFormatter.h -namespace Formatter { - template class basic_formatter; - typedef class basic_formatter< char, std::char_traits, std::allocator > format; -} - -// ------------------------------------------------------------------------------------------- -/** Load the Autodesk FBX file format. - - See http://en.wikipedia.org/wiki/FBX -*/ -// ------------------------------------------------------------------------------------------- -class FBXImporter : public BaseImporter, public LogFunctions -{ -public: - FBXImporter(); - virtual ~FBXImporter(); - - // -------------------- - bool CanRead( const std::string& pFile, - IOSystem* pIOHandler, - bool checkSig - ) const; - -protected: - - // -------------------- - const aiImporterDesc* GetInfo () const; - - // -------------------- - void SetupProperties(const Importer* pImp); - - // -------------------- - void InternReadFile( const std::string& pFile, - aiScene* pScene, - IOSystem* pIOHandler - ); - -private: - FBX::ImportSettings settings; -}; // !class FBXImporter - -} // end of namespace Assimp -#endif // !INCLUDED_AI_FBX_IMPORTER_H - diff --git a/thirdparty/assimp/code/FBXMaterial.cpp b/thirdparty/assimp/code/FBXMaterial.cpp deleted file mode 100644 index f16f134404..0000000000 --- a/thirdparty/assimp/code/FBXMaterial.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXMaterial.cpp - * @brief Assimp::FBX::Material and Assimp::FBX::Texture implementation - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -#include "FBXParser.h" -#include "FBXDocument.h" -#include "FBXImporter.h" -#include "FBXImportSettings.h" -#include "FBXDocumentUtil.h" -#include "FBXProperties.h" -#include - -#include // std::transform -#include "FBXUtil.h" - -namespace Assimp { -namespace FBX { - - using namespace Util; - -// ------------------------------------------------------------------------------------------------ -Material::Material(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: Object(id,element,name) -{ - const Scope& sc = GetRequiredScope(element); - - const Element* const ShadingModel = sc["ShadingModel"]; - const Element* const MultiLayer = sc["MultiLayer"]; - - if(MultiLayer) { - multilayer = !!ParseTokenAsInt(GetRequiredToken(*MultiLayer,0)); - } - - if(ShadingModel) { - shading = ParseTokenAsString(GetRequiredToken(*ShadingModel,0)); - } - else { - DOMWarning("shading mode not specified, assuming phong",&element); - shading = "phong"; - } - - std::string templateName; - - // lower-case shading because Blender (for example) writes "Phong" - std::transform(shading.begin(), shading.end(), shading.begin(), ::tolower); - if(shading == "phong") { - templateName = "Material.FbxSurfacePhong"; - } - else if(shading == "lambert") { - templateName = "Material.FbxSurfaceLambert"; - } - else { - DOMWarning("shading mode not recognized: " + shading,&element); - } - - props = GetPropertyTable(doc,templateName,element,sc); - - // resolve texture links - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID()); - for(const Connection* con : conns) { - - // texture link to properties, not objects - if (!con->PropertyName().length()) { - continue; - } - - const Object* const ob = con->SourceObject(); - if(!ob) { - DOMWarning("failed to read source object for texture link, ignoring",&element); - continue; - } - - const Texture* const tex = dynamic_cast(ob); - if(!tex) { - const LayeredTexture* const layeredTexture = dynamic_cast(ob); - if(!layeredTexture) { - DOMWarning("source object for texture link is not a texture or layered texture, ignoring",&element); - continue; - } - const std::string& prop = con->PropertyName(); - if (layeredTextures.find(prop) != layeredTextures.end()) { - DOMWarning("duplicate layered texture link: " + prop,&element); - } - - layeredTextures[prop] = layeredTexture; - ((LayeredTexture*)layeredTexture)->fillTexture(doc); - } - else - { - const std::string& prop = con->PropertyName(); - if (textures.find(prop) != textures.end()) { - DOMWarning("duplicate texture link: " + prop,&element); - } - - textures[prop] = tex; - } - - } -} - - -// ------------------------------------------------------------------------------------------------ -Material::~Material() -{ -} - - -// ------------------------------------------------------------------------------------------------ -Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: Object(id,element,name) -, uvScaling(1.0f,1.0f) -, media(0) -{ - const Scope& sc = GetRequiredScope(element); - - const Element* const Type = sc["Type"]; - const Element* const FileName = sc["FileName"]; - const Element* const RelativeFilename = sc["RelativeFilename"]; - const Element* const ModelUVTranslation = sc["ModelUVTranslation"]; - const Element* const ModelUVScaling = sc["ModelUVScaling"]; - const Element* const Texture_Alpha_Source = sc["Texture_Alpha_Source"]; - const Element* const Cropping = sc["Cropping"]; - - if(Type) { - type = ParseTokenAsString(GetRequiredToken(*Type,0)); - } - - if(FileName) { - fileName = ParseTokenAsString(GetRequiredToken(*FileName,0)); - } - - if(RelativeFilename) { - relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0)); - } - - if(ModelUVTranslation) { - uvTrans = aiVector2D(ParseTokenAsFloat(GetRequiredToken(*ModelUVTranslation,0)), - ParseTokenAsFloat(GetRequiredToken(*ModelUVTranslation,1)) - ); - } - - if(ModelUVScaling) { - uvScaling = aiVector2D(ParseTokenAsFloat(GetRequiredToken(*ModelUVScaling,0)), - ParseTokenAsFloat(GetRequiredToken(*ModelUVScaling,1)) - ); - } - - if(Cropping) { - crop[0] = ParseTokenAsInt(GetRequiredToken(*Cropping,0)); - crop[1] = ParseTokenAsInt(GetRequiredToken(*Cropping,1)); - crop[2] = ParseTokenAsInt(GetRequiredToken(*Cropping,2)); - crop[3] = ParseTokenAsInt(GetRequiredToken(*Cropping,3)); - } - else { - // vc8 doesn't support the crop() syntax in initialization lists - // (and vc9 WARNS about the new (i.e. compliant) behaviour). - crop[0] = crop[1] = crop[2] = crop[3] = 0; - } - - if(Texture_Alpha_Source) { - alphaSource = ParseTokenAsString(GetRequiredToken(*Texture_Alpha_Source,0)); - } - - props = GetPropertyTable(doc,"Texture.FbxFileTexture",element,sc); - - // 3DS Max and FBX SDK use "Scaling" and "Translation" instead of "ModelUVScaling" and "ModelUVTranslation". Use these properties if available. - bool ok; - const aiVector3D& scaling = PropertyGet(*props, "Scaling", ok); - if (ok) { - uvScaling.x = scaling.x; - uvScaling.y = scaling.y; - } - - const aiVector3D& trans = PropertyGet(*props, "Translation", ok); - if (ok) { - uvTrans.x = trans.x; - uvTrans.y = trans.y; - } - - // resolve video links - if(doc.Settings().readTextures) { - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID()); - for(const Connection* con : conns) { - const Object* const ob = con->SourceObject(); - if(!ob) { - DOMWarning("failed to read source object for texture link, ignoring",&element); - continue; - } - - const Video* const video = dynamic_cast(ob); - if(video) { - media = video; - } - } - } -} - - -Texture::~Texture() -{ - -} - -LayeredTexture::LayeredTexture(uint64_t id, const Element& element, const Document& /*doc*/, const std::string& name) -: Object(id,element,name) -,blendMode(BlendMode_Modulate) -,alpha(1) -{ - const Scope& sc = GetRequiredScope(element); - - const Element* const BlendModes = sc["BlendModes"]; - const Element* const Alphas = sc["Alphas"]; - - - if(BlendModes!=0) - { - blendMode = (BlendMode)ParseTokenAsInt(GetRequiredToken(*BlendModes,0)); - } - if(Alphas!=0) - { - alpha = ParseTokenAsFloat(GetRequiredToken(*Alphas,0)); - } -} - -LayeredTexture::~LayeredTexture() -{ - -} - -void LayeredTexture::fillTexture(const Document& doc) -{ - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID()); - for(size_t i = 0; i < conns.size();++i) - { - const Connection* con = conns.at(i); - - const Object* const ob = con->SourceObject(); - if(!ob) { - DOMWarning("failed to read source object for texture link, ignoring",&element); - continue; - } - - const Texture* const tex = dynamic_cast(ob); - - textures.push_back(tex); - } -} - - -// ------------------------------------------------------------------------------------------------ -Video::Video(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: Object(id,element,name) -, contentLength(0) -, content(0) -{ - const Scope& sc = GetRequiredScope(element); - - const Element* const Type = sc["Type"]; - const Element* const FileName = sc.FindElementCaseInsensitive("FileName"); //some files retain the information as "Filename", others "FileName", who knows - const Element* const RelativeFilename = sc["RelativeFilename"]; - const Element* const Content = sc["Content"]; - - if(Type) { - type = ParseTokenAsString(GetRequiredToken(*Type,0)); - } - - if(FileName) { - fileName = ParseTokenAsString(GetRequiredToken(*FileName,0)); - } - - if(RelativeFilename) { - relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0)); - } - - if(Content) { - //this field is omitted when the embedded texture is already loaded, let's ignore if it's not found - try { - const Token& token = GetRequiredToken(*Content, 0); - const char* data = token.begin(); - if (!token.IsBinary()) { - if (*data != '"') { - DOMError("embedded content is not surrounded by quotation marks", &element); - } - else { - const char* encodedData = data + 1; - size_t encodedDataLen = static_cast(token.end() - token.begin()); - // search for last quotation mark - while (encodedDataLen > 1 && encodedData[encodedDataLen] != '"') - encodedDataLen--; - if (encodedDataLen % 4 != 0) { - DOMError("embedded content is invalid, needs to be in base64", &element); - } - else { - contentLength = Util::DecodeBase64(encodedData, encodedDataLen, content); - } - } - } - else if (static_cast(token.end() - data) < 5) { - DOMError("binary data array is too short, need five (5) bytes for type signature and element count", &element); - } - else if (*data != 'R') { - DOMWarning("video content is not raw binary data, ignoring", &element); - } - else { - // read number of elements - uint32_t len = 0; - ::memcpy(&len, data + 1, sizeof(len)); - AI_SWAP4(len); - - contentLength = len; - - content = new uint8_t[len]; - ::memcpy(content, data + 5, len); - } - } catch (const runtime_error& runtimeError) - { - //we don't need the content data for contents that has already been loaded - ASSIMP_LOG_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ", - runtimeError.what()); - } - } - - props = GetPropertyTable(doc,"Video.FbxVideo",element,sc); -} - - -Video::~Video() -{ - if(content) { - delete[] content; - } -} - -} //!FBX -} //!Assimp - -#endif diff --git a/thirdparty/assimp/code/FBXMeshGeometry.cpp b/thirdparty/assimp/code/FBXMeshGeometry.cpp deleted file mode 100644 index d75476b826..0000000000 --- a/thirdparty/assimp/code/FBXMeshGeometry.cpp +++ /dev/null @@ -1,711 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXMeshGeometry.cpp - * @brief Assimp::FBX::MeshGeometry implementation - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -#include - -#include "FBXMeshGeometry.h" -#include "FBXDocument.h" -#include "FBXImporter.h" -#include "FBXImportSettings.h" -#include "FBXDocumentUtil.h" - - -namespace Assimp { -namespace FBX { - -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) - : Object(id, element, name) - , skin() -{ - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer"); - for(const Connection* con : conns) { - const Skin* const sk = ProcessSimpleConnection(*con, false, "Skin -> Geometry", element); - if(sk) { - skin = sk; - } - const BlendShape* const bsp = ProcessSimpleConnection(*con, false, "BlendShape -> Geometry", element); - if (bsp) { - blendShapes.push_back(bsp); - } - } -} - -// ------------------------------------------------------------------------------------------------ -Geometry::~Geometry() -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -const std::vector& Geometry::GetBlendShapes() const { - return blendShapes; -} - -// ------------------------------------------------------------------------------------------------ -const Skin* Geometry::DeformerSkin() const { - return skin; -} - -// ------------------------------------------------------------------------------------------------ -MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) -: Geometry(id, element,name, doc) -{ - const Scope* sc = element.Compound(); - if (!sc) { - DOMError("failed to read Geometry object (class: Mesh), no data scope found"); - } - - // must have Mesh elements: - const Element& Vertices = GetRequiredElement(*sc,"Vertices",&element); - const Element& PolygonVertexIndex = GetRequiredElement(*sc,"PolygonVertexIndex",&element); - - // optional Mesh elements: - const ElementCollection& Layer = sc->GetCollection("Layer"); - - std::vector tempVerts; - ParseVectorDataArray(tempVerts,Vertices); - - if(tempVerts.empty()) { - FBXImporter::LogWarn("encountered mesh with no vertices"); - return; - } - - std::vector tempFaces; - ParseVectorDataArray(tempFaces,PolygonVertexIndex); - - if(tempFaces.empty()) { - FBXImporter::LogWarn("encountered mesh with no faces"); - return; - } - - m_vertices.reserve(tempFaces.size()); - m_faces.reserve(tempFaces.size() / 3); - - m_mapping_offsets.resize(tempVerts.size()); - m_mapping_counts.resize(tempVerts.size(),0); - m_mappings.resize(tempFaces.size()); - - const size_t vertex_count = tempVerts.size(); - - // generate output vertices, computing an adjacency table to - // preserve the mapping from fbx indices to *this* indexing. - unsigned int count = 0; - for(int index : tempFaces) { - const int absi = index < 0 ? (-index - 1) : index; - if(static_cast(absi) >= vertex_count) { - DOMError("polygon vertex index out of range",&PolygonVertexIndex); - } - - m_vertices.push_back(tempVerts[absi]); - ++count; - - ++m_mapping_counts[absi]; - - if (index < 0) { - m_faces.push_back(count); - count = 0; - } - } - - unsigned int cursor = 0; - for (size_t i = 0, e = tempVerts.size(); i < e; ++i) { - m_mapping_offsets[i] = cursor; - cursor += m_mapping_counts[i]; - - m_mapping_counts[i] = 0; - } - - cursor = 0; - for(int index : tempFaces) { - const int absi = index < 0 ? (-index - 1) : index; - m_mappings[m_mapping_offsets[absi] + m_mapping_counts[absi]++] = cursor++; - } - - // if settings.readAllLayers is true: - // * read all layers, try to load as many vertex channels as possible - // if settings.readAllLayers is false: - // * read only the layer with index 0, but warn about any further layers - for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) { - const TokenList& tokens = (*it).second->Tokens(); - - const char* err; - const int index = ParseTokenAsInt(*tokens[0], err); - if(err) { - DOMError(err,&element); - } - - if(doc.Settings().readAllLayers || index == 0) { - const Scope& layer = GetRequiredScope(*(*it).second); - ReadLayer(layer); - } - else { - FBXImporter::LogWarn("ignoring additional geometry layers"); - } - } -} - -// ------------------------------------------------------------------------------------------------ -MeshGeometry::~MeshGeometry() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -const std::vector& MeshGeometry::GetVertices() const { - return m_vertices; -} - -// ------------------------------------------------------------------------------------------------ -const std::vector& MeshGeometry::GetNormals() const { - return m_normals; -} - -// ------------------------------------------------------------------------------------------------ -const std::vector& MeshGeometry::GetTangents() const { - return m_tangents; -} - -// ------------------------------------------------------------------------------------------------ -const std::vector& MeshGeometry::GetBinormals() const { - return m_binormals; -} - -// ------------------------------------------------------------------------------------------------ -const std::vector& MeshGeometry::GetFaceIndexCounts() const { - return m_faces; -} - -// ------------------------------------------------------------------------------------------------ -const std::vector& MeshGeometry::GetTextureCoords( unsigned int index ) const { - static const std::vector empty; - return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : m_uvs[ index ]; -} - -std::string MeshGeometry::GetTextureCoordChannelName( unsigned int index ) const { - return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? "" : m_uvNames[ index ]; -} - -const std::vector& MeshGeometry::GetVertexColors( unsigned int index ) const { - static const std::vector empty; - return index >= AI_MAX_NUMBER_OF_COLOR_SETS ? empty : m_colors[ index ]; -} - -const MatIndexArray& MeshGeometry::GetMaterialIndices() const { - return m_materials; -} -// ------------------------------------------------------------------------------------------------ -const unsigned int* MeshGeometry::ToOutputVertexIndex( unsigned int in_index, unsigned int& count ) const { - if ( in_index >= m_mapping_counts.size() ) { - return NULL; - } - - ai_assert( m_mapping_counts.size() == m_mapping_offsets.size() ); - count = m_mapping_counts[ in_index ]; - - ai_assert( m_mapping_offsets[ in_index ] + count <= m_mappings.size() ); - - return &m_mappings[ m_mapping_offsets[ in_index ] ]; -} - -// ------------------------------------------------------------------------------------------------ -unsigned int MeshGeometry::FaceForVertexIndex( unsigned int in_index ) const { - ai_assert( in_index < m_vertices.size() ); - - // in the current conversion pattern this will only be needed if - // weights are present, so no need to always pre-compute this table - if ( m_facesVertexStartIndices.empty() ) { - m_facesVertexStartIndices.resize( m_faces.size() + 1, 0 ); - - std::partial_sum( m_faces.begin(), m_faces.end(), m_facesVertexStartIndices.begin() + 1 ); - m_facesVertexStartIndices.pop_back(); - } - - ai_assert( m_facesVertexStartIndices.size() == m_faces.size() ); - const std::vector::iterator it = std::upper_bound( - m_facesVertexStartIndices.begin(), - m_facesVertexStartIndices.end(), - in_index - ); - - return static_cast< unsigned int >( std::distance( m_facesVertexStartIndices.begin(), it - 1 ) ); -} - -// ------------------------------------------------------------------------------------------------ -void MeshGeometry::ReadLayer(const Scope& layer) -{ - const ElementCollection& LayerElement = layer.GetCollection("LayerElement"); - for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) { - const Scope& elayer = GetRequiredScope(*(*eit).second); - - ReadLayerElement(elayer); - } -} - - -// ------------------------------------------------------------------------------------------------ -void MeshGeometry::ReadLayerElement(const Scope& layerElement) -{ - const Element& Type = GetRequiredElement(layerElement,"Type"); - const Element& TypedIndex = GetRequiredElement(layerElement,"TypedIndex"); - - const std::string& type = ParseTokenAsString(GetRequiredToken(Type,0)); - const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex,0)); - - const Scope& top = GetRequiredScope(element); - const ElementCollection candidates = top.GetCollection(type); - - for (ElementMap::const_iterator it = candidates.first; it != candidates.second; ++it) { - const int index = ParseTokenAsInt(GetRequiredToken(*(*it).second,0)); - if(index == typedIndex) { - ReadVertexData(type,typedIndex,GetRequiredScope(*(*it).second)); - return; - } - } - - FBXImporter::LogError(Formatter::format("failed to resolve vertex layer element: ") - << type << ", index: " << typedIndex); -} - -// ------------------------------------------------------------------------------------------------ -void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scope& source) -{ - const std::string& MappingInformationType = ParseTokenAsString(GetRequiredToken( - GetRequiredElement(source,"MappingInformationType"),0) - ); - - const std::string& ReferenceInformationType = ParseTokenAsString(GetRequiredToken( - GetRequiredElement(source,"ReferenceInformationType"),0) - ); - - if (type == "LayerElementUV") { - if(index >= AI_MAX_NUMBER_OF_TEXTURECOORDS) { - FBXImporter::LogError(Formatter::format("ignoring UV layer, maximum number of UV channels exceeded: ") - << index << " (limit is " << AI_MAX_NUMBER_OF_TEXTURECOORDS << ")" ); - return; - } - - const Element* Name = source["Name"]; - m_uvNames[index] = ""; - if(Name) { - m_uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name,0)); - } - - ReadVertexDataUV(m_uvs[index],source, - MappingInformationType, - ReferenceInformationType - ); - } - else if (type == "LayerElementMaterial") { - if (m_materials.size() > 0) { - FBXImporter::LogError("ignoring additional material layer"); - return; - } - - std::vector temp_materials; - - ReadVertexDataMaterials(temp_materials,source, - MappingInformationType, - ReferenceInformationType - ); - - // sometimes, there will be only negative entries. Drop the material - // layer in such a case (I guess it means a default material should - // be used). This is what the converter would do anyway, and it - // avoids losing the material if there are more material layers - // coming of which at least one contains actual data (did observe - // that with one test file). - const size_t count_neg = std::count_if(temp_materials.begin(),temp_materials.end(),[](int n) { return n < 0; }); - if(count_neg == temp_materials.size()) { - FBXImporter::LogWarn("ignoring dummy material layer (all entries -1)"); - return; - } - - std::swap(temp_materials, m_materials); - } - else if (type == "LayerElementNormal") { - if (m_normals.size() > 0) { - FBXImporter::LogError("ignoring additional normal layer"); - return; - } - - ReadVertexDataNormals(m_normals,source, - MappingInformationType, - ReferenceInformationType - ); - } - else if (type == "LayerElementTangent") { - if (m_tangents.size() > 0) { - FBXImporter::LogError("ignoring additional tangent layer"); - return; - } - - ReadVertexDataTangents(m_tangents,source, - MappingInformationType, - ReferenceInformationType - ); - } - else if (type == "LayerElementBinormal") { - if (m_binormals.size() > 0) { - FBXImporter::LogError("ignoring additional binormal layer"); - return; - } - - ReadVertexDataBinormals(m_binormals,source, - MappingInformationType, - ReferenceInformationType - ); - } - else if (type == "LayerElementColor") { - if(index >= AI_MAX_NUMBER_OF_COLOR_SETS) { - FBXImporter::LogError(Formatter::format("ignoring vertex color layer, maximum number of color sets exceeded: ") - << index << " (limit is " << AI_MAX_NUMBER_OF_COLOR_SETS << ")" ); - return; - } - - ReadVertexDataColors(m_colors[index],source, - MappingInformationType, - ReferenceInformationType - ); - } -} - -// ------------------------------------------------------------------------------------------------ -// Lengthy utility function to read and resolve a FBX vertex data array - that is, the -// output is in polygon vertex order. This logic is used for reading normals, UVs, colors, -// tangents .. -template -void ResolveVertexDataArray(std::vector& data_out, const Scope& source, - const std::string& MappingInformationType, - const std::string& ReferenceInformationType, - const char* dataElementName, - const char* indexDataElementName, - size_t vertex_count, - const std::vector& mapping_counts, - const std::vector& mapping_offsets, - const std::vector& mappings) -{ - bool isDirect = ReferenceInformationType == "Direct"; - bool isIndexToDirect = ReferenceInformationType == "IndexToDirect"; - - // fall-back to direct data if there is no index data element - if ( isIndexToDirect && !HasElement( source, indexDataElementName ) ) { - isDirect = true; - isIndexToDirect = false; - } - - // handle permutations of Mapping and Reference type - it would be nice to - // deal with this more elegantly and with less redundancy, but right - // now it seems unavoidable. - if (MappingInformationType == "ByVertice" && isDirect) { - if (!HasElement(source, dataElementName)) { - return; - } - std::vector tempData; - ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); - - data_out.resize(vertex_count); - for (size_t i = 0, e = tempData.size(); i < e; ++i) { - - const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i]; - for (unsigned int j = istart; j < iend; ++j) { - data_out[mappings[j]] = tempData[i]; - } - } - } - else if (MappingInformationType == "ByVertice" && isIndexToDirect) { - std::vector tempData; - ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); - - data_out.resize(vertex_count); - - std::vector uvIndices; - ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName)); - for (size_t i = 0, e = uvIndices.size(); i < e; ++i) { - - const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i]; - for (unsigned int j = istart; j < iend; ++j) { - if (static_cast(uvIndices[i]) >= tempData.size()) { - DOMError("index out of range",&GetRequiredElement(source,indexDataElementName)); - } - data_out[mappings[j]] = tempData[uvIndices[i]]; - } - } - } - else if (MappingInformationType == "ByPolygonVertex" && isDirect) { - std::vector tempData; - ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); - - if (tempData.size() != vertex_count) { - FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ") - << tempData.size() << ", expected " << vertex_count - ); - return; - } - - data_out.swap(tempData); - } - else if (MappingInformationType == "ByPolygonVertex" && isIndexToDirect) { - std::vector tempData; - ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); - - data_out.resize(vertex_count); - - std::vector uvIndices; - ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName)); - - if (uvIndices.size() != vertex_count) { - FBXImporter::LogError("length of input data unexpected for ByPolygonVertex mapping"); - return; - } - - const T empty; - unsigned int next = 0; - for(int i : uvIndices) { - if ( -1 == i ) { - data_out[ next++ ] = empty; - continue; - } - if (static_cast(i) >= tempData.size()) { - DOMError("index out of range",&GetRequiredElement(source,indexDataElementName)); - } - - data_out[next++] = tempData[i]; - } - } - else { - FBXImporter::LogError(Formatter::format("ignoring vertex data channel, access type not implemented: ") - << MappingInformationType << "," << ReferenceInformationType); - } -} - -// ------------------------------------------------------------------------------------------------ -void MeshGeometry::ReadVertexDataNormals(std::vector& normals_out, const Scope& source, - const std::string& MappingInformationType, - const std::string& ReferenceInformationType) -{ - ResolveVertexDataArray(normals_out,source,MappingInformationType,ReferenceInformationType, - "Normals", - "NormalsIndex", - m_vertices.size(), - m_mapping_counts, - m_mapping_offsets, - m_mappings); -} - -// ------------------------------------------------------------------------------------------------ -void MeshGeometry::ReadVertexDataUV(std::vector& uv_out, const Scope& source, - const std::string& MappingInformationType, - const std::string& ReferenceInformationType) -{ - ResolveVertexDataArray(uv_out,source,MappingInformationType,ReferenceInformationType, - "UV", - "UVIndex", - m_vertices.size(), - m_mapping_counts, - m_mapping_offsets, - m_mappings); -} - -// ------------------------------------------------------------------------------------------------ -void MeshGeometry::ReadVertexDataColors(std::vector& colors_out, const Scope& source, - const std::string& MappingInformationType, - const std::string& ReferenceInformationType) -{ - ResolveVertexDataArray(colors_out,source,MappingInformationType,ReferenceInformationType, - "Colors", - "ColorIndex", - m_vertices.size(), - m_mapping_counts, - m_mapping_offsets, - m_mappings); -} - -// ------------------------------------------------------------------------------------------------ -static const std::string TangentIndexToken = "TangentIndex"; -static const std::string TangentsIndexToken = "TangentsIndex"; - -void MeshGeometry::ReadVertexDataTangents(std::vector& tangents_out, const Scope& source, - const std::string& MappingInformationType, - const std::string& ReferenceInformationType) -{ - const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent"; - const char * strIdx = source.Elements().count( "Tangents" ) > 0 ? TangentsIndexToken.c_str() : TangentIndexToken.c_str(); - ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType, - str, - strIdx, - m_vertices.size(), - m_mapping_counts, - m_mapping_offsets, - m_mappings); -} - -// ------------------------------------------------------------------------------------------------ -static const std::string BinormalIndexToken = "BinormalIndex"; -static const std::string BinormalsIndexToken = "BinormalsIndex"; - -void MeshGeometry::ReadVertexDataBinormals(std::vector& binormals_out, const Scope& source, - const std::string& MappingInformationType, - const std::string& ReferenceInformationType) -{ - const char * str = source.Elements().count( "Binormals" ) > 0 ? "Binormals" : "Binormal"; - const char * strIdx = source.Elements().count( "Binormals" ) > 0 ? BinormalsIndexToken.c_str() : BinormalIndexToken.c_str(); - ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType, - str, - strIdx, - m_vertices.size(), - m_mapping_counts, - m_mapping_offsets, - m_mappings); -} - - -// ------------------------------------------------------------------------------------------------ -void MeshGeometry::ReadVertexDataMaterials(std::vector& materials_out, const Scope& source, - const std::string& MappingInformationType, - const std::string& ReferenceInformationType) -{ - const size_t face_count = m_faces.size(); - ai_assert(face_count); - - // materials are handled separately. First of all, they are assigned per-face - // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect - // has a slightly different meaning for materials. - ParseVectorDataArray(materials_out,GetRequiredElement(source,"Materials")); - - if (MappingInformationType == "AllSame") { - // easy - same material for all faces - if (materials_out.empty()) { - FBXImporter::LogError(Formatter::format("expected material index, ignoring")); - return; - } - else if (materials_out.size() > 1) { - FBXImporter::LogWarn(Formatter::format("expected only a single material index, ignoring all except the first one")); - materials_out.clear(); - } - - m_materials.assign(m_vertices.size(),materials_out[0]); - } - else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") { - m_materials.resize(face_count); - - if(materials_out.size() != face_count) { - FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ") - << materials_out.size() << ", expected " << face_count - ); - return; - } - } - else { - FBXImporter::LogError(Formatter::format("ignoring material assignments, access type not implemented: ") - << MappingInformationType << "," << ReferenceInformationType); - } -} -// ------------------------------------------------------------------------------------------------ -ShapeGeometry::ShapeGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) - : Geometry(id, element, name, doc) -{ - const Scope* sc = element.Compound(); - if (!sc) { - DOMError("failed to read Geometry object (class: Shape), no data scope found"); - } - const Element& Indexes = GetRequiredElement(*sc, "Indexes", &element); - const Element& Normals = GetRequiredElement(*sc, "Normals", &element); - const Element& Vertices = GetRequiredElement(*sc, "Vertices", &element); - ParseVectorDataArray(m_indices, Indexes); - ParseVectorDataArray(m_vertices, Vertices); - ParseVectorDataArray(m_normals, Normals); -} - -// ------------------------------------------------------------------------------------------------ -ShapeGeometry::~ShapeGeometry() { - // empty -} -// ------------------------------------------------------------------------------------------------ -const std::vector& ShapeGeometry::GetVertices() const { - return m_vertices; -} -// ------------------------------------------------------------------------------------------------ -const std::vector& ShapeGeometry::GetNormals() const { - return m_normals; -} -// ------------------------------------------------------------------------------------------------ -const std::vector& ShapeGeometry::GetIndices() const { - return m_indices; -} -// ------------------------------------------------------------------------------------------------ -LineGeometry::LineGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) - : Geometry(id, element, name, doc) -{ - const Scope* sc = element.Compound(); - if (!sc) { - DOMError("failed to read Geometry object (class: Line), no data scope found"); - } - const Element& Points = GetRequiredElement(*sc, "Points", &element); - const Element& PointsIndex = GetRequiredElement(*sc, "PointsIndex", &element); - ParseVectorDataArray(m_vertices, Points); - ParseVectorDataArray(m_indices, PointsIndex); -} - -// ------------------------------------------------------------------------------------------------ -LineGeometry::~LineGeometry() { - // empty -} -// ------------------------------------------------------------------------------------------------ -const std::vector& LineGeometry::GetVertices() const { - return m_vertices; -} -// ------------------------------------------------------------------------------------------------ -const std::vector& LineGeometry::GetIndices() const { - return m_indices; -} -} // !FBX -} // !Assimp -#endif - diff --git a/thirdparty/assimp/code/FBXMeshGeometry.h b/thirdparty/assimp/code/FBXMeshGeometry.h deleted file mode 100644 index d6d4512177..0000000000 --- a/thirdparty/assimp/code/FBXMeshGeometry.h +++ /dev/null @@ -1,235 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXImporter.h -* @brief Declaration of the FBX main importer class -*/ -#ifndef INCLUDED_AI_FBX_MESHGEOMETRY_H -#define INCLUDED_AI_FBX_MESHGEOMETRY_H - -#include "FBXParser.h" -#include "FBXDocument.h" - -namespace Assimp { -namespace FBX { - -/** - * DOM base class for all kinds of FBX geometry - */ -class Geometry : public Object -{ -public: - Geometry( uint64_t id, const Element& element, const std::string& name, const Document& doc ); - virtual ~Geometry(); - - /** Get the Skin attached to this geometry or NULL */ - const Skin* DeformerSkin() const; - - /** Get the BlendShape attached to this geometry or NULL */ - const std::vector& GetBlendShapes() const; - -private: - const Skin* skin; - std::vector blendShapes; - -}; - -typedef std::vector MatIndexArray; - - -/** - * DOM class for FBX geometry of type "Mesh" - */ -class MeshGeometry : public Geometry -{ -public: - /** The class constructor */ - MeshGeometry( uint64_t id, const Element& element, const std::string& name, const Document& doc ); - - /** The class destructor */ - virtual ~MeshGeometry(); - - /** Get a list of all vertex points, non-unique*/ - const std::vector& GetVertices() const; - - /** Get a list of all vertex normals or an empty array if - * no normals are specified. */ - const std::vector& GetNormals() const; - - /** Get a list of all vertex tangents or an empty array - * if no tangents are specified */ - const std::vector& GetTangents() const; - - /** Get a list of all vertex bi-normals or an empty array - * if no bi-normals are specified */ - const std::vector& GetBinormals() const; - - /** Return list of faces - each entry denotes a face and specifies - * how many vertices it has. Vertices are taken from the - * vertex data arrays in sequential order. */ - const std::vector& GetFaceIndexCounts() const; - - /** Get a UV coordinate slot, returns an empty array if - * the requested slot does not exist. */ - const std::vector& GetTextureCoords( unsigned int index ) const; - - /** Get a UV coordinate slot, returns an empty array if - * the requested slot does not exist. */ - std::string GetTextureCoordChannelName( unsigned int index ) const; - - /** Get a vertex color coordinate slot, returns an empty array if - * the requested slot does not exist. */ - const std::vector& GetVertexColors( unsigned int index ) const; - - /** Get per-face-vertex material assignments */ - const MatIndexArray& GetMaterialIndices() const; - - /** Convert from a fbx file vertex index (for example from a #Cluster weight) or NULL - * if the vertex index is not valid. */ - const unsigned int* ToOutputVertexIndex( unsigned int in_index, unsigned int& count ) const; - - /** Determine the face to which a particular output vertex index belongs. - * This mapping is always unique. */ - unsigned int FaceForVertexIndex( unsigned int in_index ) const; -private: - void ReadLayer( const Scope& layer ); - void ReadLayerElement( const Scope& layerElement ); - void ReadVertexData( const std::string& type, int index, const Scope& source ); - - void ReadVertexDataUV( std::vector& uv_out, const Scope& source, - const std::string& MappingInformationType, - const std::string& ReferenceInformationType ); - - void ReadVertexDataNormals( std::vector& normals_out, const Scope& source, - const std::string& MappingInformationType, - const std::string& ReferenceInformationType ); - - void ReadVertexDataColors( std::vector& colors_out, const Scope& source, - const std::string& MappingInformationType, - const std::string& ReferenceInformationType ); - - void ReadVertexDataTangents( std::vector& tangents_out, const Scope& source, - const std::string& MappingInformationType, - const std::string& ReferenceInformationType ); - - void ReadVertexDataBinormals( std::vector& binormals_out, const Scope& source, - const std::string& MappingInformationType, - const std::string& ReferenceInformationType ); - - void ReadVertexDataMaterials( MatIndexArray& materials_out, const Scope& source, - const std::string& MappingInformationType, - const std::string& ReferenceInformationType ); - -private: - // cached data arrays - MatIndexArray m_materials; - std::vector m_vertices; - std::vector m_faces; - mutable std::vector m_facesVertexStartIndices; - std::vector m_tangents; - std::vector m_binormals; - std::vector m_normals; - - std::string m_uvNames[ AI_MAX_NUMBER_OF_TEXTURECOORDS ]; - std::vector m_uvs[ AI_MAX_NUMBER_OF_TEXTURECOORDS ]; - std::vector m_colors[ AI_MAX_NUMBER_OF_COLOR_SETS ]; - - std::vector m_mapping_counts; - std::vector m_mapping_offsets; - std::vector m_mappings; -}; - -/** -* DOM class for FBX geometry of type "Shape" -*/ -class ShapeGeometry : public Geometry -{ -public: - /** The class constructor */ - ShapeGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc); - - /** The class destructor */ - virtual ~ShapeGeometry(); - - /** Get a list of all vertex points, non-unique*/ - const std::vector& GetVertices() const; - - /** Get a list of all vertex normals or an empty array if - * no normals are specified. */ - const std::vector& GetNormals() const; - - /** Return list of vertex indices. */ - const std::vector& GetIndices() const; - -private: - std::vector m_vertices; - std::vector m_normals; - std::vector m_indices; -}; -/** -* DOM class for FBX geometry of type "Line" -*/ -class LineGeometry : public Geometry -{ -public: - /** The class constructor */ - LineGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc); - - /** The class destructor */ - virtual ~LineGeometry(); - - /** Get a list of all vertex points, non-unique*/ - const std::vector& GetVertices() const; - - /** Return list of vertex indices. */ - const std::vector& GetIndices() const; - -private: - std::vector m_vertices; - std::vector m_indices; -}; - -} -} - -#endif // INCLUDED_AI_FBX_MESHGEOMETRY_H - diff --git a/thirdparty/assimp/code/FBXModel.cpp b/thirdparty/assimp/code/FBXModel.cpp deleted file mode 100644 index 589af36ac7..0000000000 --- a/thirdparty/assimp/code/FBXModel.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXModel.cpp - * @brief Assimp::FBX::Model implementation - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -#include "FBXParser.h" -#include "FBXMeshGeometry.h" -#include "FBXDocument.h" -#include "FBXImporter.h" -#include "FBXDocumentUtil.h" - -namespace Assimp { -namespace FBX { - -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -Model::Model(uint64_t id, const Element& element, const Document& doc, const std::string& name) - : Object(id,element,name) - , shading("Y") -{ - const Scope& sc = GetRequiredScope(element); - const Element* const Shading = sc["Shading"]; - const Element* const Culling = sc["Culling"]; - - if(Shading) { - shading = GetRequiredToken(*Shading,0).StringContents(); - } - - if (Culling) { - culling = ParseTokenAsString(GetRequiredToken(*Culling,0)); - } - - props = GetPropertyTable(doc,"Model.FbxNode",element,sc); - ResolveLinks(element,doc); -} - -// ------------------------------------------------------------------------------------------------ -Model::~Model() -{ - -} - -// ------------------------------------------------------------------------------------------------ -void Model::ResolveLinks(const Element& element, const Document& doc) -{ - const char* const arr[] = {"Geometry","Material","NodeAttribute"}; - - // resolve material - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),arr, 3); - - materials.reserve(conns.size()); - geometry.reserve(conns.size()); - attributes.reserve(conns.size()); - for(const Connection* con : conns) { - - // material and geometry links should be Object-Object connections - if (con->PropertyName().length()) { - continue; - } - - const Object* const ob = con->SourceObject(); - if(!ob) { - DOMWarning("failed to read source object for incoming Model link, ignoring",&element); - continue; - } - - const Material* const mat = dynamic_cast(ob); - if(mat) { - materials.push_back(mat); - continue; - } - - const Geometry* const geo = dynamic_cast(ob); - if(geo) { - geometry.push_back(geo); - continue; - } - - const NodeAttribute* const att = dynamic_cast(ob); - if(att) { - attributes.push_back(att); - continue; - } - - DOMWarning("source object for model link is neither Material, NodeAttribute nor Geometry, ignoring",&element); - continue; - } -} - -// ------------------------------------------------------------------------------------------------ -bool Model::IsNull() const -{ - const std::vector& attrs = GetAttributes(); - for(const NodeAttribute* att : attrs) { - - const Null* null_tag = dynamic_cast(att); - if(null_tag) { - return true; - } - } - - return false; -} - - -} //!FBX -} //!Assimp - -#endif diff --git a/thirdparty/assimp/code/FBXNodeAttribute.cpp b/thirdparty/assimp/code/FBXNodeAttribute.cpp deleted file mode 100644 index b72e5637ee..0000000000 --- a/thirdparty/assimp/code/FBXNodeAttribute.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXNoteAttribute.cpp - * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -#include "FBXParser.h" -#include "FBXDocument.h" -#include "FBXImporter.h" -#include "FBXDocumentUtil.h" - -namespace Assimp { -namespace FBX { - -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -NodeAttribute::NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: Object(id,element,name) -, props() -{ - const Scope& sc = GetRequiredScope(element); - - const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2)); - - // hack on the deriving type but Null/LimbNode attributes are the only case in which - // the property table is by design absent and no warning should be generated - // for it. - const bool is_null_or_limb = !strcmp(classname.c_str(), "Null") || !strcmp(classname.c_str(), "LimbNode"); - props = GetPropertyTable(doc,"NodeAttribute.Fbx" + classname,element,sc, is_null_or_limb); -} - - -// ------------------------------------------------------------------------------------------------ -NodeAttribute::~NodeAttribute() -{ - // empty -} - - -// ------------------------------------------------------------------------------------------------ -CameraSwitcher::CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name) - : NodeAttribute(id,element,doc,name) -{ - const Scope& sc = GetRequiredScope(element); - const Element* const CameraId = sc["CameraId"]; - const Element* const CameraName = sc["CameraName"]; - const Element* const CameraIndexName = sc["CameraIndexName"]; - - if(CameraId) { - cameraId = ParseTokenAsInt(GetRequiredToken(*CameraId,0)); - } - - if(CameraName) { - cameraName = GetRequiredToken(*CameraName,0).StringContents(); - } - - if(CameraIndexName && CameraIndexName->Tokens().size()) { - cameraIndexName = GetRequiredToken(*CameraIndexName,0).StringContents(); - } -} - -// ------------------------------------------------------------------------------------------------ -CameraSwitcher::~CameraSwitcher() -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -Camera::Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: NodeAttribute(id,element,doc,name) -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -Camera::~Camera() -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -Light::Light(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: NodeAttribute(id,element,doc,name) -{ - // empty -} - - -// ------------------------------------------------------------------------------------------------ -Light::~Light() -{ -} - - -// ------------------------------------------------------------------------------------------------ -Null::Null(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: NodeAttribute(id,element,doc,name) -{ - -} - - -// ------------------------------------------------------------------------------------------------ -Null::~Null() -{ - -} - - -// ------------------------------------------------------------------------------------------------ -LimbNode::LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: NodeAttribute(id,element,doc,name) -{ - -} - - -// ------------------------------------------------------------------------------------------------ -LimbNode::~LimbNode() -{ - -} - -} -} - -#endif diff --git a/thirdparty/assimp/code/FBXParser.cpp b/thirdparty/assimp/code/FBXParser.cpp deleted file mode 100644 index b255c47347..0000000000 --- a/thirdparty/assimp/code/FBXParser.cpp +++ /dev/null @@ -1,1313 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXParser.cpp - * @brief Implementation of the FBX parser and the rudimentary DOM that we use - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -#ifdef ASSIMP_BUILD_NO_OWN_ZLIB -# include -#else -# include "../contrib/zlib/zlib.h" -#endif - -#include "FBXTokenizer.h" -#include "FBXParser.h" -#include "FBXUtil.h" - -#include -#include -#include - -#include - -using namespace Assimp; -using namespace Assimp::FBX; - -namespace { - - // ------------------------------------------------------------------------------------------------ - // signal parse error, this is always unrecoverable. Throws DeadlyImportError. - AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX; - AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) - { - throw DeadlyImportError(Util::AddTokenText("FBX-Parser",message,&token)); - } - - // ------------------------------------------------------------------------------------------------ - AI_WONT_RETURN void ParseError(const std::string& message, const Element* element = NULL) AI_WONT_RETURN_SUFFIX; - AI_WONT_RETURN void ParseError(const std::string& message, const Element* element) - { - if(element) { - ParseError(message,element->KeyToken()); - } - throw DeadlyImportError("FBX-Parser " + message); - } - - - // ------------------------------------------------------------------------------------------------ - void ParseError(const std::string& message, TokenPtr token) - { - if(token) { - ParseError(message, *token); - } - ParseError(message); - } - - // Initially, we did reinterpret_cast, breaking strict aliasing rules. - // This actually caused trouble on Android, so let's be safe this time. - // https://github.com/assimp/assimp/issues/24 - template - T SafeParse(const char* data, const char* end) { - // Actual size validation happens during Tokenization so - // this is valid as an assertion. - (void)(end); - ai_assert(static_cast(end - data) >= sizeof(T)); - T result = static_cast(0); - ::memcpy(&result, data, sizeof(T)); - return result; - } -} - -namespace Assimp { -namespace FBX { - -// ------------------------------------------------------------------------------------------------ -Element::Element(const Token& key_token, Parser& parser) -: key_token(key_token) -{ - TokenPtr n = NULL; - do { - n = parser.AdvanceToNextToken(); - if(!n) { - ParseError("unexpected end of file, expected closing bracket",parser.LastToken()); - } - - if (n->Type() == TokenType_DATA) { - tokens.push_back(n); - TokenPtr prev = n; - n = parser.AdvanceToNextToken(); - if(!n) { - ParseError("unexpected end of file, expected bracket, comma or key",parser.LastToken()); - } - - const TokenType ty = n->Type(); - - // some exporters are missing a comma on the next line - if (ty == TokenType_DATA && prev->Type() == TokenType_DATA && (n->Line() == prev->Line() + 1)) { - tokens.push_back(n); - continue; - } - - if (ty != TokenType_OPEN_BRACKET && ty != TokenType_CLOSE_BRACKET && ty != TokenType_COMMA && ty != TokenType_KEY) { - ParseError("unexpected token; expected bracket, comma or key",n); - } - } - - if (n->Type() == TokenType_OPEN_BRACKET) { - compound.reset(new Scope(parser)); - - // current token should be a TOK_CLOSE_BRACKET - n = parser.CurrentToken(); - ai_assert(n); - - if (n->Type() != TokenType_CLOSE_BRACKET) { - ParseError("expected closing bracket",n); - } - - parser.AdvanceToNextToken(); - return; - } - } - while(n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET); -} - -// ------------------------------------------------------------------------------------------------ -Element::~Element() -{ - // no need to delete tokens, they are owned by the parser -} - -// ------------------------------------------------------------------------------------------------ -Scope::Scope(Parser& parser,bool topLevel) -{ - if(!topLevel) { - TokenPtr t = parser.CurrentToken(); - if (t->Type() != TokenType_OPEN_BRACKET) { - ParseError("expected open bracket",t); - } - } - - TokenPtr n = parser.AdvanceToNextToken(); - if(n == NULL) { - ParseError("unexpected end of file"); - } - - // note: empty scopes are allowed - while(n->Type() != TokenType_CLOSE_BRACKET) { - if (n->Type() != TokenType_KEY) { - ParseError("unexpected token, expected TOK_KEY",n); - } - - const std::string& str = n->StringContents(); - elements.insert(ElementMap::value_type(str,new_Element(*n,parser))); - - // Element() should stop at the next Key token (or right after a Close token) - n = parser.CurrentToken(); - if(n == NULL) { - if (topLevel) { - return; - } - ParseError("unexpected end of file",parser.LastToken()); - } - } -} - -// ------------------------------------------------------------------------------------------------ -Scope::~Scope() -{ - for(ElementMap::value_type& v : elements) { - delete v.second; - } -} - -// ------------------------------------------------------------------------------------------------ -Parser::Parser (const TokenList& tokens, bool is_binary) -: tokens(tokens) -, last() -, current() -, cursor(tokens.begin()) -, is_binary(is_binary) -{ - root.reset(new Scope(*this,true)); -} - -// ------------------------------------------------------------------------------------------------ -Parser::~Parser() -{ - // empty -} - -// ------------------------------------------------------------------------------------------------ -TokenPtr Parser::AdvanceToNextToken() -{ - last = current; - if (cursor == tokens.end()) { - current = NULL; - } else { - current = *cursor++; - } - return current; -} - -// ------------------------------------------------------------------------------------------------ -TokenPtr Parser::CurrentToken() const -{ - return current; -} - -// ------------------------------------------------------------------------------------------------ -TokenPtr Parser::LastToken() const -{ - return last; -} - -// ------------------------------------------------------------------------------------------------ -uint64_t ParseTokenAsID(const Token& t, const char*& err_out) -{ - err_out = NULL; - - if (t.Type() != TokenType_DATA) { - err_out = "expected TOK_DATA token"; - return 0L; - } - - if(t.IsBinary()) - { - const char* data = t.begin(); - if (data[0] != 'L') { - err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)"; - return 0L; - } - - BE_NCONST uint64_t id = SafeParse(data+1, t.end()); - AI_SWAP8(id); - return id; - } - - // XXX: should use size_t here - unsigned int length = static_cast(t.end() - t.begin()); - ai_assert(length > 0); - - const char* out = nullptr; - const uint64_t id = strtoul10_64(t.begin(),&out,&length); - if (out > t.end()) { - err_out = "failed to parse ID (text)"; - return 0L; - } - - return id; -} - -// ------------------------------------------------------------------------------------------------ -size_t ParseTokenAsDim(const Token& t, const char*& err_out) -{ - // same as ID parsing, except there is a trailing asterisk - err_out = NULL; - - if (t.Type() != TokenType_DATA) { - err_out = "expected TOK_DATA token"; - return 0; - } - - if(t.IsBinary()) - { - const char* data = t.begin(); - if (data[0] != 'L') { - err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)"; - return 0; - } - - BE_NCONST uint64_t id = SafeParse(data+1, t.end()); - AI_SWAP8(id); - return static_cast(id); - } - - if(*t.begin() != '*') { - err_out = "expected asterisk before array dimension"; - return 0; - } - - // XXX: should use size_t here - unsigned int length = static_cast(t.end() - t.begin()); - if(length == 0) { - err_out = "expected valid integer number after asterisk"; - return 0; - } - - const char* out = nullptr; - const size_t id = static_cast(strtoul10_64(t.begin() + 1,&out,&length)); - if (out > t.end()) { - err_out = "failed to parse ID"; - return 0; - } - - return id; -} - - -// ------------------------------------------------------------------------------------------------ -float ParseTokenAsFloat(const Token& t, const char*& err_out) -{ - err_out = NULL; - - if (t.Type() != TokenType_DATA) { - err_out = "expected TOK_DATA token"; - return 0.0f; - } - - if(t.IsBinary()) - { - const char* data = t.begin(); - if (data[0] != 'F' && data[0] != 'D') { - err_out = "failed to parse F(loat) or D(ouble), unexpected data type (binary)"; - return 0.0f; - } - - if (data[0] == 'F') { - return SafeParse(data+1, t.end()); - } - else { - return static_cast( SafeParse(data+1, t.end()) ); - } - } - - // need to copy the input string to a temporary buffer - // first - next in the fbx token stream comes ',', - // which fast_atof could interpret as decimal point. -#define MAX_FLOAT_LENGTH 31 - char temp[MAX_FLOAT_LENGTH + 1]; - const size_t length = static_cast(t.end()-t.begin()); - std::copy(t.begin(),t.end(),temp); - temp[std::min(static_cast(MAX_FLOAT_LENGTH),length)] = '\0'; - - return fast_atof(temp); -} - - -// ------------------------------------------------------------------------------------------------ -int ParseTokenAsInt(const Token& t, const char*& err_out) -{ - err_out = NULL; - - if (t.Type() != TokenType_DATA) { - err_out = "expected TOK_DATA token"; - return 0; - } - - if(t.IsBinary()) - { - const char* data = t.begin(); - if (data[0] != 'I') { - err_out = "failed to parse I(nt), unexpected data type (binary)"; - return 0; - } - - BE_NCONST int32_t ival = SafeParse(data+1, t.end()); - AI_SWAP4(ival); - return static_cast(ival); - } - - ai_assert(static_cast(t.end() - t.begin()) > 0); - - const char* out; - const int intval = strtol10(t.begin(),&out); - if (out != t.end()) { - err_out = "failed to parse ID"; - return 0; - } - - return intval; -} - - -// ------------------------------------------------------------------------------------------------ -int64_t ParseTokenAsInt64(const Token& t, const char*& err_out) -{ - err_out = NULL; - - if (t.Type() != TokenType_DATA) { - err_out = "expected TOK_DATA token"; - return 0L; - } - - if (t.IsBinary()) - { - const char* data = t.begin(); - if (data[0] != 'L') { - err_out = "failed to parse Int64, unexpected data type"; - return 0L; - } - - BE_NCONST int64_t id = SafeParse(data + 1, t.end()); - AI_SWAP8(id); - return id; - } - - // XXX: should use size_t here - unsigned int length = static_cast(t.end() - t.begin()); - ai_assert(length > 0); - - const char* out = nullptr; - const int64_t id = strtol10_64(t.begin(), &out, &length); - if (out > t.end()) { - err_out = "failed to parse Int64 (text)"; - return 0L; - } - - return id; -} - -// ------------------------------------------------------------------------------------------------ -std::string ParseTokenAsString(const Token& t, const char*& err_out) -{ - err_out = NULL; - - if (t.Type() != TokenType_DATA) { - err_out = "expected TOK_DATA token"; - return ""; - } - - if(t.IsBinary()) - { - const char* data = t.begin(); - if (data[0] != 'S') { - err_out = "failed to parse S(tring), unexpected data type (binary)"; - return ""; - } - - // read string length - BE_NCONST int32_t len = SafeParse(data+1, t.end()); - AI_SWAP4(len); - - ai_assert(t.end() - data == 5 + len); - return std::string(data + 5, len); - } - - const size_t length = static_cast(t.end() - t.begin()); - if(length < 2) { - err_out = "token is too short to hold a string"; - return ""; - } - - const char* s = t.begin(), *e = t.end() - 1; - if (*s != '\"' || *e != '\"') { - err_out = "expected double quoted string"; - return ""; - } - - return std::string(s+1,length-2); -} - - -namespace { - -// ------------------------------------------------------------------------------------------------ -// read the type code and element count of a binary data array and stop there -void ReadBinaryDataArrayHead(const char*& data, const char* end, char& type, uint32_t& count, - const Element& el) -{ - if (static_cast(end-data) < 5) { - ParseError("binary data array is too short, need five (5) bytes for type signature and element count",&el); - } - - // data type - type = *data; - - // read number of elements - BE_NCONST uint32_t len = SafeParse(data+1, end); - AI_SWAP4(len); - - count = len; - data += 5; -} - - -// ------------------------------------------------------------------------------------------------ -// read binary data array, assume cursor points to the 'compression mode' field (i.e. behind the header) -void ReadBinaryDataArray(char type, uint32_t count, const char*& data, const char* end, - std::vector& buff, - const Element& /*el*/) -{ - BE_NCONST uint32_t encmode = SafeParse(data, end); - AI_SWAP4(encmode); - data += 4; - - // next comes the compressed length - BE_NCONST uint32_t comp_len = SafeParse(data, end); - AI_SWAP4(comp_len); - data += 4; - - ai_assert(data + comp_len == end); - - // determine the length of the uncompressed data by looking at the type signature - uint32_t stride = 0; - switch(type) - { - case 'f': - case 'i': - stride = 4; - break; - - case 'd': - case 'l': - stride = 8; - break; - - default: - ai_assert(false); - }; - - const uint32_t full_length = stride * count; - buff.resize(full_length); - - if(encmode == 0) { - ai_assert(full_length == comp_len); - - // plain data, no compression - std::copy(data, end, buff.begin()); - } - else if(encmode == 1) { - // zlib/deflate, next comes ZIP head (0x78 0x01) - // see http://www.ietf.org/rfc/rfc1950.txt - - z_stream zstream; - zstream.opaque = Z_NULL; - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.data_type = Z_BINARY; - - // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib - if(Z_OK != inflateInit(&zstream)) { - ParseError("failure initializing zlib"); - } - - zstream.next_in = reinterpret_cast( const_cast(data) ); - zstream.avail_in = comp_len; - - zstream.avail_out = static_cast(buff.size()); - zstream.next_out = reinterpret_cast(&*buff.begin()); - const int ret = inflate(&zstream, Z_FINISH); - - if (ret != Z_STREAM_END && ret != Z_OK) { - ParseError("failure decompressing compressed data section"); - } - - // terminate zlib - inflateEnd(&zstream); - } -#ifdef ASSIMP_BUILD_DEBUG - else { - // runtime check for this happens at tokenization stage - ai_assert(false); - } -#endif - - data += comp_len; - ai_assert(data == end); -} - -} // !anon - - -// ------------------------------------------------------------------------------------------------ -// read an array of float3 tuples -void ParseVectorDataArray(std::vector& out, const Element& el) -{ - out.resize( 0 ); - - const TokenList& tok = el.Tokens(); - if(tok.empty()) { - ParseError("unexpected empty element",&el); - } - - if(tok[0]->IsBinary()) { - const char* data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if(count % 3 != 0) { - ParseError("number of floats is not a multiple of three (3) (binary)",&el); - } - - if(!count) { - return; - } - - if (type != 'd' && type != 'f') { - ParseError("expected float or double array (binary)",&el); - } - - std::vector buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - ai_assert(data == end); - ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); - - const uint32_t count3 = count / 3; - out.reserve(count3); - - if (type == 'd') { - const double* d = reinterpret_cast(&buff[0]); - for (unsigned int i = 0; i < count3; ++i, d += 3) { - out.push_back(aiVector3D(static_cast(d[0]), - static_cast(d[1]), - static_cast(d[2]))); - } - // for debugging - /*for ( size_t i = 0; i < out.size(); i++ ) { - aiVector3D vec3( out[ i ] ); - std::stringstream stream; - stream << " vec3.x = " << vec3.x << " vec3.y = " << vec3.y << " vec3.z = " << vec3.z << std::endl; - DefaultLogger::get()->info( stream.str() ); - }*/ - } - else if (type == 'f') { - const float* f = reinterpret_cast(&buff[0]); - for (unsigned int i = 0; i < count3; ++i, f += 3) { - out.push_back(aiVector3D(f[0],f[1],f[2])); - } - } - - return; - } - - const size_t dim = ParseTokenAsDim(*tok[0]); - - // may throw bad_alloc if the input is rubbish, but this need - // not to be prevented - importing would fail but we wouldn't - // crash since assimp handles this case properly. - out.reserve(dim); - - const Scope& scope = GetRequiredScope(el); - const Element& a = GetRequiredElement(scope,"a",&el); - - if (a.Tokens().size() % 3 != 0) { - ParseError("number of floats is not a multiple of three (3)",&el); - } - for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { - aiVector3D v; - v.x = ParseTokenAsFloat(**it++); - v.y = ParseTokenAsFloat(**it++); - v.z = ParseTokenAsFloat(**it++); - - out.push_back(v); - } -} - - -// ------------------------------------------------------------------------------------------------ -// read an array of color4 tuples -void ParseVectorDataArray(std::vector& out, const Element& el) -{ - out.resize( 0 ); - const TokenList& tok = el.Tokens(); - if(tok.empty()) { - ParseError("unexpected empty element",&el); - } - - if(tok[0]->IsBinary()) { - const char* data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if(count % 4 != 0) { - ParseError("number of floats is not a multiple of four (4) (binary)",&el); - } - - if(!count) { - return; - } - - if (type != 'd' && type != 'f') { - ParseError("expected float or double array (binary)",&el); - } - - std::vector buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - ai_assert(data == end); - ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); - - const uint32_t count4 = count / 4; - out.reserve(count4); - - if (type == 'd') { - const double* d = reinterpret_cast(&buff[0]); - for (unsigned int i = 0; i < count4; ++i, d += 4) { - out.push_back(aiColor4D(static_cast(d[0]), - static_cast(d[1]), - static_cast(d[2]), - static_cast(d[3]))); - } - } - else if (type == 'f') { - const float* f = reinterpret_cast(&buff[0]); - for (unsigned int i = 0; i < count4; ++i, f += 4) { - out.push_back(aiColor4D(f[0],f[1],f[2],f[3])); - } - } - return; - } - - const size_t dim = ParseTokenAsDim(*tok[0]); - - // see notes in ParseVectorDataArray() above - out.reserve(dim); - - const Scope& scope = GetRequiredScope(el); - const Element& a = GetRequiredElement(scope,"a",&el); - - if (a.Tokens().size() % 4 != 0) { - ParseError("number of floats is not a multiple of four (4)",&el); - } - for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { - aiColor4D v; - v.r = ParseTokenAsFloat(**it++); - v.g = ParseTokenAsFloat(**it++); - v.b = ParseTokenAsFloat(**it++); - v.a = ParseTokenAsFloat(**it++); - - out.push_back(v); - } -} - - -// ------------------------------------------------------------------------------------------------ -// read an array of float2 tuples -void ParseVectorDataArray(std::vector& out, const Element& el) -{ - out.resize( 0 ); - const TokenList& tok = el.Tokens(); - if(tok.empty()) { - ParseError("unexpected empty element",&el); - } - - if(tok[0]->IsBinary()) { - const char* data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if(count % 2 != 0) { - ParseError("number of floats is not a multiple of two (2) (binary)",&el); - } - - if(!count) { - return; - } - - if (type != 'd' && type != 'f') { - ParseError("expected float or double array (binary)",&el); - } - - std::vector buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - ai_assert(data == end); - ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); - - const uint32_t count2 = count / 2; - out.reserve(count2); - - if (type == 'd') { - const double* d = reinterpret_cast(&buff[0]); - for (unsigned int i = 0; i < count2; ++i, d += 2) { - out.push_back(aiVector2D(static_cast(d[0]), - static_cast(d[1]))); - } - } - else if (type == 'f') { - const float* f = reinterpret_cast(&buff[0]); - for (unsigned int i = 0; i < count2; ++i, f += 2) { - out.push_back(aiVector2D(f[0],f[1])); - } - } - - return; - } - - const size_t dim = ParseTokenAsDim(*tok[0]); - - // see notes in ParseVectorDataArray() above - out.reserve(dim); - - const Scope& scope = GetRequiredScope(el); - const Element& a = GetRequiredElement(scope,"a",&el); - - if (a.Tokens().size() % 2 != 0) { - ParseError("number of floats is not a multiple of two (2)",&el); - } - for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { - aiVector2D v; - v.x = ParseTokenAsFloat(**it++); - v.y = ParseTokenAsFloat(**it++); - - out.push_back(v); - } -} - - -// ------------------------------------------------------------------------------------------------ -// read an array of ints -void ParseVectorDataArray(std::vector& out, const Element& el) -{ - out.resize( 0 ); - const TokenList& tok = el.Tokens(); - if(tok.empty()) { - ParseError("unexpected empty element",&el); - } - - if(tok[0]->IsBinary()) { - const char* data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if(!count) { - return; - } - - if (type != 'i') { - ParseError("expected int array (binary)",&el); - } - - std::vector buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - ai_assert(data == end); - ai_assert(buff.size() == count * 4); - - out.reserve(count); - - const int32_t* ip = reinterpret_cast(&buff[0]); - for (unsigned int i = 0; i < count; ++i, ++ip) { - BE_NCONST int32_t val = *ip; - AI_SWAP4(val); - out.push_back(val); - } - - return; - } - - const size_t dim = ParseTokenAsDim(*tok[0]); - - // see notes in ParseVectorDataArray() - out.reserve(dim); - - const Scope& scope = GetRequiredScope(el); - const Element& a = GetRequiredElement(scope,"a",&el); - - for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { - const int ival = ParseTokenAsInt(**it++); - out.push_back(ival); - } -} - - -// ------------------------------------------------------------------------------------------------ -// read an array of floats -void ParseVectorDataArray(std::vector& out, const Element& el) -{ - out.resize( 0 ); - const TokenList& tok = el.Tokens(); - if(tok.empty()) { - ParseError("unexpected empty element",&el); - } - - if(tok[0]->IsBinary()) { - const char* data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if(!count) { - return; - } - - if (type != 'd' && type != 'f') { - ParseError("expected float or double array (binary)",&el); - } - - std::vector buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - ai_assert(data == end); - ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); - - if (type == 'd') { - const double* d = reinterpret_cast(&buff[0]); - for (unsigned int i = 0; i < count; ++i, ++d) { - out.push_back(static_cast(*d)); - } - } - else if (type == 'f') { - const float* f = reinterpret_cast(&buff[0]); - for (unsigned int i = 0; i < count; ++i, ++f) { - out.push_back(*f); - } - } - - return; - } - - const size_t dim = ParseTokenAsDim(*tok[0]); - - // see notes in ParseVectorDataArray() - out.reserve(dim); - - const Scope& scope = GetRequiredScope(el); - const Element& a = GetRequiredElement(scope,"a",&el); - - for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { - const float ival = ParseTokenAsFloat(**it++); - out.push_back(ival); - } -} - - -// ------------------------------------------------------------------------------------------------ -// read an array of uints -void ParseVectorDataArray(std::vector& out, const Element& el) -{ - out.resize( 0 ); - const TokenList& tok = el.Tokens(); - if(tok.empty()) { - ParseError("unexpected empty element",&el); - } - - if(tok[0]->IsBinary()) { - const char* data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if(!count) { - return; - } - - if (type != 'i') { - ParseError("expected (u)int array (binary)",&el); - } - - std::vector buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - ai_assert(data == end); - ai_assert(buff.size() == count * 4); - - out.reserve(count); - - const int32_t* ip = reinterpret_cast(&buff[0]); - for (unsigned int i = 0; i < count; ++i, ++ip) { - BE_NCONST int32_t val = *ip; - if(val < 0) { - ParseError("encountered negative integer index (binary)"); - } - - AI_SWAP4(val); - out.push_back(val); - } - - return; - } - - const size_t dim = ParseTokenAsDim(*tok[0]); - - // see notes in ParseVectorDataArray() - out.reserve(dim); - - const Scope& scope = GetRequiredScope(el); - const Element& a = GetRequiredElement(scope,"a",&el); - - for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { - const int ival = ParseTokenAsInt(**it++); - if(ival < 0) { - ParseError("encountered negative integer index"); - } - out.push_back(static_cast(ival)); - } -} - - -// ------------------------------------------------------------------------------------------------ -// read an array of uint64_ts -void ParseVectorDataArray(std::vector& out, const Element& el) -{ - out.resize( 0 ); - const TokenList& tok = el.Tokens(); - if(tok.empty()) { - ParseError("unexpected empty element",&el); - } - - if(tok[0]->IsBinary()) { - const char* data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if(!count) { - return; - } - - if (type != 'l') { - ParseError("expected long array (binary)",&el); - } - - std::vector buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - ai_assert(data == end); - ai_assert(buff.size() == count * 8); - - out.reserve(count); - - const uint64_t* ip = reinterpret_cast(&buff[0]); - for (unsigned int i = 0; i < count; ++i, ++ip) { - BE_NCONST uint64_t val = *ip; - AI_SWAP8(val); - out.push_back(val); - } - - return; - } - - const size_t dim = ParseTokenAsDim(*tok[0]); - - // see notes in ParseVectorDataArray() - out.reserve(dim); - - const Scope& scope = GetRequiredScope(el); - const Element& a = GetRequiredElement(scope,"a",&el); - - for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { - const uint64_t ival = ParseTokenAsID(**it++); - - out.push_back(ival); - } -} - -// ------------------------------------------------------------------------------------------------ -// read an array of int64_ts -void ParseVectorDataArray(std::vector& out, const Element& el) -{ - out.resize( 0 ); - const TokenList& tok = el.Tokens(); - if (tok.empty()) { - ParseError("unexpected empty element", &el); - } - - if (tok[0]->IsBinary()) { - const char* data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if (!count) { - return; - } - - if (type != 'l') { - ParseError("expected long array (binary)", &el); - } - - std::vector buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - ai_assert(data == end); - ai_assert(buff.size() == count * 8); - - out.reserve(count); - - const int64_t* ip = reinterpret_cast(&buff[0]); - for (unsigned int i = 0; i < count; ++i, ++ip) { - BE_NCONST int64_t val = *ip; - AI_SWAP8(val); - out.push_back(val); - } - - return; - } - - const size_t dim = ParseTokenAsDim(*tok[0]); - - // see notes in ParseVectorDataArray() - out.reserve(dim); - - const Scope& scope = GetRequiredScope(el); - const Element& a = GetRequiredElement(scope, "a", &el); - - for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end;) { - const int64_t ival = ParseTokenAsInt64(**it++); - - out.push_back(ival); - } -} - -// ------------------------------------------------------------------------------------------------ -aiMatrix4x4 ReadMatrix(const Element& element) -{ - std::vector values; - ParseVectorDataArray(values,element); - - if(values.size() != 16) { - ParseError("expected 16 matrix elements"); - } - - aiMatrix4x4 result; - - - result.a1 = values[0]; - result.a2 = values[1]; - result.a3 = values[2]; - result.a4 = values[3]; - - result.b1 = values[4]; - result.b2 = values[5]; - result.b3 = values[6]; - result.b4 = values[7]; - - result.c1 = values[8]; - result.c2 = values[9]; - result.c3 = values[10]; - result.c4 = values[11]; - - result.d1 = values[12]; - result.d2 = values[13]; - result.d3 = values[14]; - result.d4 = values[15]; - - result.Transpose(); - return result; -} - - -// ------------------------------------------------------------------------------------------------ -// wrapper around ParseTokenAsString() with ParseError handling -std::string ParseTokenAsString(const Token& t) -{ - const char* err; - const std::string& i = ParseTokenAsString(t,err); - if(err) { - ParseError(err,t); - } - return i; -} - -bool HasElement( const Scope& sc, const std::string& index ) { - const Element* el = sc[ index ]; - if ( nullptr == el ) { - return false; - } - - return true; -} - -// ------------------------------------------------------------------------------------------------ -// extract a required element from a scope, abort if the element cannot be found -const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element /*= NULL*/) -{ - const Element* el = sc[index]; - if(!el) { - ParseError("did not find required element \"" + index + "\"",element); - } - return *el; -} - - -// ------------------------------------------------------------------------------------------------ -// extract required compound scope -const Scope& GetRequiredScope(const Element& el) -{ - const Scope* const s = el.Compound(); - if(!s) { - ParseError("expected compound scope",&el); - } - - return *s; -} - - -// ------------------------------------------------------------------------------------------------ -// get token at a particular index -const Token& GetRequiredToken(const Element& el, unsigned int index) -{ - const TokenList& t = el.Tokens(); - if(index >= t.size()) { - ParseError(Formatter::format( "missing token at index " ) << index,&el); - } - - return *t[index]; -} - - -// ------------------------------------------------------------------------------------------------ -// wrapper around ParseTokenAsID() with ParseError handling -uint64_t ParseTokenAsID(const Token& t) -{ - const char* err; - const uint64_t i = ParseTokenAsID(t,err); - if(err) { - ParseError(err,t); - } - return i; -} - - -// ------------------------------------------------------------------------------------------------ -// wrapper around ParseTokenAsDim() with ParseError handling -size_t ParseTokenAsDim(const Token& t) -{ - const char* err; - const size_t i = ParseTokenAsDim(t,err); - if(err) { - ParseError(err,t); - } - return i; -} - - -// ------------------------------------------------------------------------------------------------ -// wrapper around ParseTokenAsFloat() with ParseError handling -float ParseTokenAsFloat(const Token& t) -{ - const char* err; - const float i = ParseTokenAsFloat(t,err); - if(err) { - ParseError(err,t); - } - return i; -} - - -// ------------------------------------------------------------------------------------------------ -// wrapper around ParseTokenAsInt() with ParseError handling -int ParseTokenAsInt(const Token& t) -{ - const char* err; - const int i = ParseTokenAsInt(t,err); - if(err) { - ParseError(err,t); - } - return i; -} - - - -// ------------------------------------------------------------------------------------------------ -// wrapper around ParseTokenAsInt64() with ParseError handling -int64_t ParseTokenAsInt64(const Token& t) -{ - const char* err; - const int64_t i = ParseTokenAsInt64(t, err); - if (err) { - ParseError(err, t); - } - return i; -} - -} // !FBX -} // !Assimp - -#endif diff --git a/thirdparty/assimp/code/FBXParser.h b/thirdparty/assimp/code/FBXParser.h deleted file mode 100644 index 7b0cf72039..0000000000 --- a/thirdparty/assimp/code/FBXParser.h +++ /dev/null @@ -1,235 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXParser.h - * @brief FBX parsing code - */ -#ifndef INCLUDED_AI_FBX_PARSER_H -#define INCLUDED_AI_FBX_PARSER_H - -#include -#include -#include -#include -#include - -#include "FBXCompileConfig.h" -#include "FBXTokenizer.h" - -namespace Assimp { -namespace FBX { - -class Scope; -class Parser; -class Element; - -// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03 -typedef std::vector< Scope* > ScopeList; -typedef std::fbx_unordered_multimap< std::string, Element* > ElementMap; - -typedef std::pair ElementCollection; - -# define new_Scope new Scope -# define new_Element new Element - - -/** FBX data entity that consists of a key:value tuple. - * - * Example: - * @verbatim - * AnimationCurve: 23, "AnimCurve::", "" { - * [..] - * } - * @endverbatim - * - * As can be seen in this sample, elements can contain nested #Scope - * as their trailing member. **/ -class Element -{ -public: - Element(const Token& key_token, Parser& parser); - ~Element(); - - const Scope* Compound() const { - return compound.get(); - } - - const Token& KeyToken() const { - return key_token; - } - - const TokenList& Tokens() const { - return tokens; - } - -private: - const Token& key_token; - TokenList tokens; - std::unique_ptr compound; -}; - -/** FBX data entity that consists of a 'scope', a collection - * of not necessarily unique #Element instances. - * - * Example: - * @verbatim - * GlobalSettings: { - * Version: 1000 - * Properties70: - * [...] - * } - * @endverbatim */ -class Scope -{ -public: - Scope(Parser& parser, bool topLevel = false); - ~Scope(); - - const Element* operator[] (const std::string& index) const { - ElementMap::const_iterator it = elements.find(index); - return it == elements.end() ? NULL : (*it).second; - } - - const Element* FindElementCaseInsensitive(const std::string& elementName) const { - const char* elementNameCStr = elementName.c_str(); - for (auto element = elements.begin(); element != elements.end(); ++element) - { - if (!ASSIMP_strincmp(element->first.c_str(), elementNameCStr, MAXLEN)) { - return element->second; - } - } - return NULL; - } - - ElementCollection GetCollection(const std::string& index) const { - return elements.equal_range(index); - } - - const ElementMap& Elements() const { - return elements; - } - -private: - ElementMap elements; -}; - -/** FBX parsing class, takes a list of input tokens and generates a hierarchy - * of nested #Scope instances, representing the fbx DOM.*/ -class Parser -{ -public: - /** Parse given a token list. Does not take ownership of the tokens - - * the objects must persist during the entire parser lifetime */ - Parser (const TokenList& tokens,bool is_binary); - ~Parser(); - - const Scope& GetRootScope() const { - return *root.get(); - } - - bool IsBinary() const { - return is_binary; - } - -private: - friend class Scope; - friend class Element; - - TokenPtr AdvanceToNextToken(); - TokenPtr LastToken() const; - TokenPtr CurrentToken() const; - -private: - const TokenList& tokens; - - TokenPtr last, current; - TokenList::const_iterator cursor; - std::unique_ptr root; - - const bool is_binary; -}; - - -/* token parsing - this happens when building the DOM out of the parse-tree*/ -uint64_t ParseTokenAsID(const Token& t, const char*& err_out); -size_t ParseTokenAsDim(const Token& t, const char*& err_out); - -float ParseTokenAsFloat(const Token& t, const char*& err_out); -int ParseTokenAsInt(const Token& t, const char*& err_out); -int64_t ParseTokenAsInt64(const Token& t, const char*& err_out); -std::string ParseTokenAsString(const Token& t, const char*& err_out); - -/* wrapper around ParseTokenAsXXX() with DOMError handling */ -uint64_t ParseTokenAsID(const Token& t); -size_t ParseTokenAsDim(const Token& t); -float ParseTokenAsFloat(const Token& t); -int ParseTokenAsInt(const Token& t); -int64_t ParseTokenAsInt64(const Token& t); -std::string ParseTokenAsString(const Token& t); - -/* read data arrays */ -void ParseVectorDataArray(std::vector& out, const Element& el); -void ParseVectorDataArray(std::vector& out, const Element& el); -void ParseVectorDataArray(std::vector& out, const Element& el); -void ParseVectorDataArray(std::vector& out, const Element& el); -void ParseVectorDataArray(std::vector& out, const Element& el); -void ParseVectorDataArray(std::vector& out, const Element& el); -void ParseVectorDataArray(std::vector& out, const Element& e); -void ParseVectorDataArray(std::vector& out, const Element& el); - -bool HasElement( const Scope& sc, const std::string& index ); - -// extract a required element from a scope, abort if the element cannot be found -const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element = NULL); - -// extract required compound scope -const Scope& GetRequiredScope(const Element& el); -// get token at a particular index -const Token& GetRequiredToken(const Element& el, unsigned int index); - -// read a 4x4 matrix from an array of 16 floats -aiMatrix4x4 ReadMatrix(const Element& element); - -} // ! FBX -} // ! Assimp - -#endif // ! INCLUDED_AI_FBX_PARSER_H diff --git a/thirdparty/assimp/code/FBXProperties.cpp b/thirdparty/assimp/code/FBXProperties.cpp deleted file mode 100644 index 8d7036b6a9..0000000000 --- a/thirdparty/assimp/code/FBXProperties.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXProperties.cpp - * @brief Implementation of the FBX dynamic properties system - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -#include "FBXTokenizer.h" -#include "FBXParser.h" -#include "FBXDocument.h" -#include "FBXDocumentUtil.h" -#include "FBXProperties.h" - -namespace Assimp { -namespace FBX { - - using namespace Util; - -// ------------------------------------------------------------------------------------------------ -Property::Property() -{ -} - -// ------------------------------------------------------------------------------------------------ -Property::~Property() -{ -} - -namespace { - -// ------------------------------------------------------------------------------------------------ -// read a typed property out of a FBX element. The return value is NULL if the property cannot be read. -Property* ReadTypedProperty(const Element& element) -{ - ai_assert(element.KeyToken().StringContents() == "P"); - - const TokenList& tok = element.Tokens(); - ai_assert(tok.size() >= 5); - - const std::string& s = ParseTokenAsString(*tok[1]); - const char* const cs = s.c_str(); - if (!strcmp(cs,"KString")) { - return new TypedProperty(ParseTokenAsString(*tok[4])); - } - else if (!strcmp(cs,"bool") || !strcmp(cs,"Bool")) { - return new TypedProperty(ParseTokenAsInt(*tok[4]) != 0); - } - else if (!strcmp(cs, "int") || !strcmp(cs, "Int") || !strcmp(cs, "enum") || !strcmp(cs, "Enum")) { - return new TypedProperty(ParseTokenAsInt(*tok[4])); - } - else if (!strcmp(cs, "ULongLong")) { - return new TypedProperty(ParseTokenAsID(*tok[4])); - } - else if (!strcmp(cs, "KTime")) { - return new TypedProperty(ParseTokenAsInt64(*tok[4])); - } - else if (!strcmp(cs,"Vector3D") || - !strcmp(cs,"ColorRGB") || - !strcmp(cs,"Vector") || - !strcmp(cs,"Color") || - !strcmp(cs,"Lcl Translation") || - !strcmp(cs,"Lcl Rotation") || - !strcmp(cs,"Lcl Scaling") - ) { - return new TypedProperty(aiVector3D( - ParseTokenAsFloat(*tok[4]), - ParseTokenAsFloat(*tok[5]), - ParseTokenAsFloat(*tok[6])) - ); - } - else if (!strcmp(cs,"double") || !strcmp(cs,"Number") || !strcmp(cs,"Float") || !strcmp(cs,"FieldOfView") || !strcmp( cs, "UnitScaleFactor" ) ) { - return new TypedProperty(ParseTokenAsFloat(*tok[4])); - } - return NULL; -} - - -// ------------------------------------------------------------------------------------------------ -// peek into an element and check if it contains a FBX property, if so return its name. -std::string PeekPropertyName(const Element& element) -{ - ai_assert(element.KeyToken().StringContents() == "P"); - const TokenList& tok = element.Tokens(); - if(tok.size() < 4) { - return ""; - } - - return ParseTokenAsString(*tok[0]); -} - -} //! anon - - -// ------------------------------------------------------------------------------------------------ -PropertyTable::PropertyTable() -: templateProps() -, element() -{ -} - -// ------------------------------------------------------------------------------------------------ -PropertyTable::PropertyTable(const Element& element, std::shared_ptr templateProps) -: templateProps(templateProps) -, element(&element) -{ - const Scope& scope = GetRequiredScope(element); - for(const ElementMap::value_type& v : scope.Elements()) { - if(v.first != "P") { - DOMWarning("expected only P elements in property table",v.second); - continue; - } - - const std::string& name = PeekPropertyName(*v.second); - if(!name.length()) { - DOMWarning("could not read property name",v.second); - continue; - } - - LazyPropertyMap::const_iterator it = lazyProps.find(name); - if (it != lazyProps.end()) { - DOMWarning("duplicate property name, will hide previous value: " + name,v.second); - continue; - } - - lazyProps[name] = v.second; - } -} - - -// ------------------------------------------------------------------------------------------------ -PropertyTable::~PropertyTable() -{ - for(PropertyMap::value_type& v : props) { - delete v.second; - } -} - - -// ------------------------------------------------------------------------------------------------ -const Property* PropertyTable::Get(const std::string& name) const -{ - PropertyMap::const_iterator it = props.find(name); - if (it == props.end()) { - // hasn't been parsed yet? - LazyPropertyMap::const_iterator lit = lazyProps.find(name); - if(lit != lazyProps.end()) { - props[name] = ReadTypedProperty(*(*lit).second); - it = props.find(name); - - ai_assert(it != props.end()); - } - - if (it == props.end()) { - // check property template - if(templateProps) { - return templateProps->Get(name); - } - - return NULL; - } - } - - return (*it).second; -} - -DirectPropertyMap PropertyTable::GetUnparsedProperties() const -{ - DirectPropertyMap result; - - // Loop through all the lazy properties (which is all the properties) - for(const LazyPropertyMap::value_type& element : lazyProps) { - - // Skip parsed properties - if (props.end() != props.find(element.first)) continue; - - // Read the element's value. - // Wrap the naked pointer (since the call site is required to acquire ownership) - // std::unique_ptr from C++11 would be preferred both as a wrapper and a return value. - std::shared_ptr prop = std::shared_ptr(ReadTypedProperty(*element.second)); - - // Element could not be read. Skip it. - if (!prop) continue; - - // Add to result - result[element.first] = prop; - } - - return result; -} - -} //! FBX -} //! Assimp - -#endif diff --git a/thirdparty/assimp/code/FBXProperties.h b/thirdparty/assimp/code/FBXProperties.h deleted file mode 100644 index 58755542fc..0000000000 --- a/thirdparty/assimp/code/FBXProperties.h +++ /dev/null @@ -1,185 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXProperties.h - * @brief FBX dynamic properties - */ -#ifndef INCLUDED_AI_FBX_PROPERTIES_H -#define INCLUDED_AI_FBX_PROPERTIES_H - -#include "FBXCompileConfig.h" -#include -#include - -namespace Assimp { -namespace FBX { - -// Forward declarations -class Element; - -/** Represents a dynamic property. Type info added by deriving classes, - * see #TypedProperty. - Example: - @verbatim - P: "ShininessExponent", "double", "Number", "",0.5 - @endvebatim -*/ -class Property { -protected: - Property(); - -public: - virtual ~Property(); - -public: - template - const T* As() const { - return dynamic_cast(this); - } -}; - -template -class TypedProperty : public Property { -public: - explicit TypedProperty(const T& value) - : value(value) { - // empty - } - - const T& Value() const { - return value; - } - -private: - T value; -}; - - -typedef std::fbx_unordered_map > DirectPropertyMap; -typedef std::fbx_unordered_map PropertyMap; -typedef std::fbx_unordered_map LazyPropertyMap; - -/** - * Represents a property table as can be found in the newer FBX files (Properties60, Properties70) - */ -class PropertyTable { -public: - // in-memory property table with no source element - PropertyTable(); - PropertyTable(const Element& element, std::shared_ptr templateProps); - ~PropertyTable(); - - const Property* Get(const std::string& name) const; - - // PropertyTable's need not be coupled with FBX elements so this can be NULL - const Element* GetElement() const { - return element; - } - - const PropertyTable* TemplateProps() const { - return templateProps.get(); - } - - DirectPropertyMap GetUnparsedProperties() const; - -private: - LazyPropertyMap lazyProps; - mutable PropertyMap props; - const std::shared_ptr templateProps; - const Element* const element; -}; - -// ------------------------------------------------------------------------------------------------ -template -inline -T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) { - const Property* const prop = in.Get(name); - if( nullptr == prop) { - return defaultValue; - } - - // strong typing, no need to be lenient - const TypedProperty* const tprop = prop->As< TypedProperty >(); - if( nullptr == tprop) { - return defaultValue; - } - - return tprop->Value(); -} - -// ------------------------------------------------------------------------------------------------ -template -inline -T PropertyGet(const PropertyTable& in, const std::string& name, bool& result, bool useTemplate=false ) { - const Property* prop = in.Get(name); - if( nullptr == prop) { - if ( ! useTemplate ) { - result = false; - return T(); - } - const PropertyTable* templ = in.TemplateProps(); - if ( nullptr == templ ) { - result = false; - return T(); - } - prop = templ->Get(name); - if ( nullptr == prop ) { - result = false; - return T(); - } - } - - // strong typing, no need to be lenient - const TypedProperty* const tprop = prop->As< TypedProperty >(); - if( nullptr == tprop) { - result = false; - return T(); - } - - result = true; - return tprop->Value(); -} - -} //! FBX -} //! Assimp - -#endif // INCLUDED_AI_FBX_PROPERTIES_H diff --git a/thirdparty/assimp/code/FBXTokenizer.cpp b/thirdparty/assimp/code/FBXTokenizer.cpp deleted file mode 100644 index 252cce3557..0000000000 --- a/thirdparty/assimp/code/FBXTokenizer.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXTokenizer.cpp - * @brief Implementation of the FBX broadphase lexer - */ - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -// tab width for logging columns -#define ASSIMP_FBX_TAB_WIDTH 4 - -#include - -#include "FBXTokenizer.h" -#include "FBXUtil.h" -#include - -namespace Assimp { -namespace FBX { - -// ------------------------------------------------------------------------------------------------ -Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int line, unsigned int column) - : -#ifdef DEBUG - contents(sbegin, static_cast(send-sbegin)), -#endif - sbegin(sbegin) - , send(send) - , type(type) - , line(line) - , column(column) -{ - ai_assert(sbegin); - ai_assert(send); - - // tokens must be of non-zero length - ai_assert(static_cast(send-sbegin) > 0); -} - -// ------------------------------------------------------------------------------------------------ -Token::~Token() -{ -} - -namespace { - -// ------------------------------------------------------------------------------------------------ -// signal tokenization error, this is always unrecoverable. Throws DeadlyImportError. -AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) AI_WONT_RETURN_SUFFIX; -AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) -{ - throw DeadlyImportError(Util::AddLineAndColumn("FBX-Tokenize",message,line,column)); -} - - -// process a potential data token up to 'cur', adding it to 'output_tokens'. -// ------------------------------------------------------------------------------------------------ -void ProcessDataToken( TokenList& output_tokens, const char*& start, const char*& end, - unsigned int line, - unsigned int column, - TokenType type = TokenType_DATA, - bool must_have_token = false) -{ - if (start && end) { - // sanity check: - // tokens should have no whitespace outside quoted text and [start,end] should - // properly delimit the valid range. - bool in_double_quotes = false; - for (const char* c = start; c != end + 1; ++c) { - if (*c == '\"') { - in_double_quotes = !in_double_quotes; - } - - if (!in_double_quotes && IsSpaceOrNewLine(*c)) { - TokenizeError("unexpected whitespace in token", line, column); - } - } - - if (in_double_quotes) { - TokenizeError("non-terminated double quotes", line, column); - } - - output_tokens.push_back(new_Token(start,end + 1,type,line,column)); - } - else if (must_have_token) { - TokenizeError("unexpected character, expected data token", line, column); - } - - start = end = NULL; -} - -} - -// ------------------------------------------------------------------------------------------------ -void Tokenize(TokenList& output_tokens, const char* input) -{ - ai_assert(input); - - // line and column numbers numbers are one-based - unsigned int line = 1; - unsigned int column = 1; - - bool comment = false; - bool in_double_quotes = false; - bool pending_data_token = false; - - const char* token_begin = NULL, *token_end = NULL; - for (const char* cur = input;*cur;column += (*cur == '\t' ? ASSIMP_FBX_TAB_WIDTH : 1), ++cur) { - const char c = *cur; - - if (IsLineEnd(c)) { - comment = false; - - column = 0; - ++line; - } - - if(comment) { - continue; - } - - if(in_double_quotes) { - if (c == '\"') { - in_double_quotes = false; - token_end = cur; - - ProcessDataToken(output_tokens,token_begin,token_end,line,column); - pending_data_token = false; - } - continue; - } - - switch(c) - { - case '\"': - if (token_begin) { - TokenizeError("unexpected double-quote", line, column); - } - token_begin = cur; - in_double_quotes = true; - continue; - - case ';': - ProcessDataToken(output_tokens,token_begin,token_end,line,column); - comment = true; - continue; - - case '{': - ProcessDataToken(output_tokens,token_begin,token_end, line, column); - output_tokens.push_back(new_Token(cur,cur+1,TokenType_OPEN_BRACKET,line,column)); - continue; - - case '}': - ProcessDataToken(output_tokens,token_begin,token_end,line,column); - output_tokens.push_back(new_Token(cur,cur+1,TokenType_CLOSE_BRACKET,line,column)); - continue; - - case ',': - if (pending_data_token) { - ProcessDataToken(output_tokens,token_begin,token_end,line,column,TokenType_DATA,true); - } - output_tokens.push_back(new_Token(cur,cur+1,TokenType_COMMA,line,column)); - continue; - - case ':': - if (pending_data_token) { - ProcessDataToken(output_tokens,token_begin,token_end,line,column,TokenType_KEY,true); - } - else { - TokenizeError("unexpected colon", line, column); - } - continue; - } - - if (IsSpaceOrNewLine(c)) { - - if (token_begin) { - // peek ahead and check if the next token is a colon in which - // case this counts as KEY token. - TokenType type = TokenType_DATA; - for (const char* peek = cur; *peek && IsSpaceOrNewLine(*peek); ++peek) { - if (*peek == ':') { - type = TokenType_KEY; - cur = peek; - break; - } - } - - ProcessDataToken(output_tokens,token_begin,token_end,line,column,type); - } - - pending_data_token = false; - } - else { - token_end = cur; - if (!token_begin) { - token_begin = cur; - } - - pending_data_token = true; - } - } -} - -} // !FBX -} // !Assimp - -#endif diff --git a/thirdparty/assimp/code/FBXTokenizer.h b/thirdparty/assimp/code/FBXTokenizer.h deleted file mode 100644 index 2af29743f4..0000000000 --- a/thirdparty/assimp/code/FBXTokenizer.h +++ /dev/null @@ -1,187 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXTokenizer.h - * @brief FBX lexer - */ -#ifndef INCLUDED_AI_FBX_TOKENIZER_H -#define INCLUDED_AI_FBX_TOKENIZER_H - -#include "FBXCompileConfig.h" -#include -#include -#include - -namespace Assimp { -namespace FBX { - -/** Rough classification for text FBX tokens used for constructing the - * basic scope hierarchy. */ -enum TokenType -{ - // { - TokenType_OPEN_BRACKET = 0, - - // } - TokenType_CLOSE_BRACKET, - - // '"blablubb"', '2', '*14' - very general token class, - // further processing happens at a later stage. - TokenType_DATA, - - // - TokenType_BINARY_DATA, - - // , - TokenType_COMMA, - - // blubb: - TokenType_KEY -}; - - -/** Represents a single token in a FBX file. Tokens are - * classified by the #TokenType enumerated types. - * - * Offers iterator protocol. Tokens are immutable. */ -class Token -{ -private: - static const unsigned int BINARY_MARKER = static_cast(-1); - -public: - /** construct a textual token */ - Token(const char* sbegin, const char* send, TokenType type, unsigned int line, unsigned int column); - - /** construct a binary token */ - Token(const char* sbegin, const char* send, TokenType type, unsigned int offset); - - ~Token(); - -public: - std::string StringContents() const { - return std::string(begin(),end()); - } - - bool IsBinary() const { - return column == BINARY_MARKER; - } - - const char* begin() const { - return sbegin; - } - - const char* end() const { - return send; - } - - TokenType Type() const { - return type; - } - - unsigned int Offset() const { - ai_assert(IsBinary()); - return offset; - } - - unsigned int Line() const { - ai_assert(!IsBinary()); - return line; - } - - unsigned int Column() const { - ai_assert(!IsBinary()); - return column; - } - -private: - -#ifdef DEBUG - // full string copy for the sole purpose that it nicely appears - // in msvc's debugger window. - const std::string contents; -#endif - - - const char* const sbegin; - const char* const send; - const TokenType type; - - union { - const unsigned int line; - unsigned int offset; - }; - const unsigned int column; -}; - -// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03 -typedef const Token* TokenPtr; -typedef std::vector< TokenPtr > TokenList; - -#define new_Token new Token - - -/** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens. - * - * Skips over comments and generates line and column numbers. - * - * @param output_tokens Receives a list of all tokens in the input data. - * @param input_buffer Textual input buffer to be processed, 0-terminated. - * @throw DeadlyImportError if something goes wrong */ -void Tokenize(TokenList& output_tokens, const char* input); - - -/** Tokenizer function for binary FBX files. - * - * Emits a token list suitable for direct parsing. - * - * @param output_tokens Receives a list of all tokens in the input data. - * @param input_buffer Binary input buffer to be processed. - * @param length Length of input buffer, in bytes. There is no 0-terminal. - * @throw DeadlyImportError if something goes wrong */ -void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int length); - - -} // ! FBX -} // ! Assimp - -#endif // ! INCLUDED_AI_FBX_PARSER_H diff --git a/thirdparty/assimp/code/FBXUtil.cpp b/thirdparty/assimp/code/FBXUtil.cpp deleted file mode 100644 index fb483161b2..0000000000 --- a/thirdparty/assimp/code/FBXUtil.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXUtil.cpp - * @brief Implementation of internal FBX utility functions - */ - -#include "FBXUtil.h" -#include "FBXTokenizer.h" - -#include -#include -#include - -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER - -namespace Assimp { -namespace FBX { -namespace Util { - -// ------------------------------------------------------------------------------------------------ -const char* TokenTypeString(TokenType t) -{ - switch(t) { - case TokenType_OPEN_BRACKET: - return "TOK_OPEN_BRACKET"; - - case TokenType_CLOSE_BRACKET: - return "TOK_CLOSE_BRACKET"; - - case TokenType_DATA: - return "TOK_DATA"; - - case TokenType_COMMA: - return "TOK_COMMA"; - - case TokenType_KEY: - return "TOK_KEY"; - - case TokenType_BINARY_DATA: - return "TOK_BINARY_DATA"; - } - - ai_assert(false); - return ""; -} - - -// ------------------------------------------------------------------------------------------------ -std::string AddOffset(const std::string& prefix, const std::string& text, unsigned int offset) -{ - return static_cast( (Formatter::format() << prefix << " (offset 0x" << std::hex << offset << ") " << text) ); -} - -// ------------------------------------------------------------------------------------------------ -std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column) -{ - return static_cast( (Formatter::format() << prefix << " (line " << line << " << col " << column << ") " << text) ); -} - -// ------------------------------------------------------------------------------------------------ -std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok) -{ - if(tok->IsBinary()) { - return static_cast( (Formatter::format() << prefix << - " (" << TokenTypeString(tok->Type()) << - ", offset 0x" << std::hex << tok->Offset() << ") " << - text) ); - } - - return static_cast( (Formatter::format() << prefix << - " (" << TokenTypeString(tok->Type()) << - ", line " << tok->Line() << - ", col " << tok->Column() << ") " << - text) ); -} - -static const uint8_t base64DecodeTable[128] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 64, 0, 0, - 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, - 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0 -}; - -uint8_t DecodeBase64(char ch) -{ - return base64DecodeTable[size_t(ch)]; -} - -size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out) -{ - if (inLength < 4) { - out = 0; - return 0; - } - - const size_t outLength = (inLength * 3) / 4; - out = new uint8_t[outLength]; - memset(out, 0, outLength); - - size_t i = 0; - size_t j = 0; - for (i = 0; i < inLength - 4; i += 4) - { - uint8_t b0 = Util::DecodeBase64(in[i]); - uint8_t b1 = Util::DecodeBase64(in[i + 1]); - uint8_t b2 = Util::DecodeBase64(in[i + 2]); - uint8_t b3 = Util::DecodeBase64(in[i + 3]); - - out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4)); - out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2)); - out[j++] = (uint8_t)((b2 << 6) | b3); - } - return outLength; -} - -} // !Util -} // !FBX -} // !Assimp - -#endif diff --git a/thirdparty/assimp/code/FBXUtil.h b/thirdparty/assimp/code/FBXUtil.h deleted file mode 100644 index 6890e015ba..0000000000 --- a/thirdparty/assimp/code/FBXUtil.h +++ /dev/null @@ -1,120 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXUtil.h - * @brief FBX utility functions for internal use - */ -#ifndef INCLUDED_AI_FBX_UTIL_H -#define INCLUDED_AI_FBX_UTIL_H - -#include "FBXCompileConfig.h" -#include "FBXTokenizer.h" -#include - -namespace Assimp { -namespace FBX { - - -namespace Util { - - -/** helper for std::for_each to delete all heap-allocated items in a container */ -template -struct delete_fun -{ - void operator()(const volatile T* del) { - delete del; - } -}; - -/** Get a string representation for a #TokenType. */ -const char* TokenTypeString(TokenType t); - - - -/** Format log/error messages using a given offset in the source binary file - * - * @param prefix Message prefix to be preprended to the location info. - * @param text Message text - * @param line Line index, 1-based - * @param column Column index, 1-based - * @return A string of the following format: {prefix} (offset 0x{offset}) {text}*/ -std::string AddOffset(const std::string& prefix, const std::string& text, unsigned int offset); - - -/** Format log/error messages using a given line location in the source file. - * - * @param prefix Message prefix to be preprended to the location info. - * @param text Message text - * @param line Line index, 1-based - * @param column Column index, 1-based - * @return A string of the following format: {prefix} (line {line}, col {column}) {text}*/ -std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column); - - -/** Format log/error messages using a given cursor token. - * - * @param prefix Message prefix to be preprended to the location info. - * @param text Message text - * @param tok Token where parsing/processing stopped - * @return A string of the following format: {prefix} ({token-type}, line {line}, col {column}) {text}*/ -std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok); - -/** Decode a single Base64-encoded character. -* -* @param ch Character to decode (from base64 to binary). -* @return decoded byte value*/ -uint8_t DecodeBase64(char ch); - -/** Decode a Base64-encoded string -* -* @param in Characters to decode. -* @param inLength Number of characters to decode. -* @param out Reference to pointer where we will store the decoded data. -* @return size of the decoded data (number of bytes)*/ -size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out); - -} -} -} - -#endif // ! INCLUDED_AI_FBX_UTIL_H diff --git a/thirdparty/assimp/code/FIReader.cpp b/thirdparty/assimp/code/FIReader.cpp deleted file mode 100644 index 2116316ca3..0000000000 --- a/thirdparty/assimp/code/FIReader.cpp +++ /dev/null @@ -1,1834 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -/// \file FIReader.cpp -/// \brief Reader for Fast Infoset encoded binary XML files. -/// \date 2017 -/// \author Patrick Daehne - -#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER - -#include "FIReader.hpp" -#include - -// Workaround for issue #1361 -// https://github.com/assimp/assimp/issues/1361 -#ifdef __ANDROID__ -# define _GLIBCXX_USE_C99 1 -#endif - -#include -#include -#include -#include -#include -#include "../contrib/utf8cpp/source/utf8.h" -#include -#include -#include -#include -#include -#include - -namespace Assimp { - -static const std::string parseErrorMessage = "Fast Infoset parse error"; - -static const char *xmlDeclarations[] = { - "", - "", - "", - "", - "", - "", - "", - "", - "" -}; - -static size_t parseMagic(const uint8_t *data, const uint8_t *dataEnd) { - if (dataEnd - data < 4) { - return 0; - } - uint32_t magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - switch (magic) { - case 0xe0000001: - return 4; - case 0x3c3f786d: // "= xmlDeclarationLength) && (memcmp(xmlDeclaration, data, xmlDeclarationLength) == 0)) { - data += xmlDeclarationLength; - if (dataEnd - data < 4) { - return 0; - } - magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - return magic == 0xe0000001 ? xmlDeclarationLength + 4 : 0; - } - } - return 0; - } - default: - return 0; - } -} - -static std::string parseUTF8String(const uint8_t *data, size_t len) { - return std::string((char*)data, len); -} - -static std::string parseUTF16String(const uint8_t *data, size_t len) { - if (len & 1) { - throw DeadlyImportError(parseErrorMessage); - } - size_t numShorts = len / 2; - std::vector utf16; - utf16.reserve(numShorts); - for (size_t i = 0; i < numShorts; ++i) { - short v = (data[0] << 8) | data[1]; - utf16.push_back(v); - data += 2; - } - std::string result; - utf8::utf16to8(utf16.begin(), utf16.end(), back_inserter(result)); - return result; -} - -struct FIStringValueImpl: public FIStringValue { - inline FIStringValueImpl(std::string &&value_) { value = std::move(value_); } - virtual const std::string &toString() const /*override*/ { return value; } -}; - -std::shared_ptr FIStringValue::create(std::string &&value) { - return std::make_shared(std::move(value)); -} - -struct FIHexValueImpl: public FIHexValue { - mutable std::string strValue; - mutable bool strValueValid; - inline FIHexValueImpl(std::vector &&value_): strValueValid(false) { value = std::move(value_); } - virtual const std::string &toString() const /*override*/ { - if (!strValueValid) { - strValueValid = true; - std::ostringstream os; - os << std::hex << std::uppercase << std::setfill('0'); - std::for_each(value.begin(), value.end(), [&](uint8_t c) { os << std::setw(2) << static_cast(c); }); - strValue = os.str(); - } - return strValue; - }; -}; - -std::shared_ptr FIHexValue::create(std::vector &&value) { - return std::make_shared(std::move(value)); -} - -struct FIBase64ValueImpl: public FIBase64Value { - mutable std::string strValue; - mutable bool strValueValid; - inline FIBase64ValueImpl(std::vector &&value_): strValueValid(false) { value = std::move(value_); } - virtual const std::string &toString() const /*override*/ { - if (!strValueValid) { - strValueValid = true; - std::ostringstream os; - uint8_t c1 = 0, c2; - int imod3 = 0; - std::vector::size_type valueSize = value.size(); - for (std::vector::size_type i = 0; i < valueSize; ++i) { - c2 = value[i]; - switch (imod3) { - case 0: - os << basis_64[c2 >> 2]; - imod3 = 1; - break; - case 1: - os << basis_64[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)]; - imod3 = 2; - break; - case 2: - os << basis_64[((c1 & 0x0f) << 2) | ((c2 & 0xc0) >> 6)] << basis_64[c2 & 0x3f]; - imod3 = 0; - break; - } - c1 = c2; - } - switch (imod3) { - case 1: - os << basis_64[(c1 & 0x03) << 4] << "=="; - break; - case 2: - os << basis_64[(c1 & 0x0f) << 2] << '='; - break; - } - strValue = os.str(); - } - return strValue; - }; - static const char basis_64[]; -}; - -const char FIBase64ValueImpl::basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -std::shared_ptr FIBase64Value::create(std::vector &&value) { - return std::make_shared(std::move(value)); -} - -struct FIShortValueImpl: public FIShortValue { - mutable std::string strValue; - mutable bool strValueValid; - inline FIShortValueImpl(std::vector &&value_): strValueValid(false) { value = std::move(value_); } - virtual const std::string &toString() const /*override*/ { - if (!strValueValid) { - strValueValid = true; - std::ostringstream os; - int n = 0; - std::for_each(value.begin(), value.end(), [&](int16_t s) { if (++n > 1) os << ' '; os << s; }); - strValue = os.str(); - } - return strValue; - } -}; - -std::shared_ptr FIShortValue::create(std::vector &&value) { - return std::make_shared(std::move(value)); -} - -struct FIIntValueImpl: public FIIntValue { - mutable std::string strValue; - mutable bool strValueValid; - inline FIIntValueImpl(std::vector &&value_): strValueValid(false) { value = std::move(value_); } - virtual const std::string &toString() const /*override*/ { - if (!strValueValid) { - strValueValid = true; - std::ostringstream os; - int n = 0; - std::for_each(value.begin(), value.end(), [&](int32_t i) { if (++n > 1) os << ' '; os << i; }); - strValue = os.str(); - } - return strValue; - }; -}; - -std::shared_ptr FIIntValue::create(std::vector &&value) { - return std::make_shared(std::move(value)); -} - -struct FILongValueImpl: public FILongValue { - mutable std::string strValue; - mutable bool strValueValid; - inline FILongValueImpl(std::vector &&value_): strValueValid(false) { value = std::move(value_); } - virtual const std::string &toString() const /*override*/ { - if (!strValueValid) { - strValueValid = true; - std::ostringstream os; - int n = 0; - std::for_each(value.begin(), value.end(), [&](int64_t l) { if (++n > 1) os << ' '; os << l; }); - strValue = os.str(); - } - return strValue; - }; -}; - -std::shared_ptr FILongValue::create(std::vector &&value) { - return std::make_shared(std::move(value)); -} - -struct FIBoolValueImpl: public FIBoolValue { - mutable std::string strValue; - mutable bool strValueValid; - inline FIBoolValueImpl(std::vector &&value_): strValueValid(false) { value = std::move(value_); } - virtual const std::string &toString() const /*override*/ { - if (!strValueValid) { - strValueValid = true; - std::ostringstream os; - os << std::boolalpha; - int n = 0; - std::for_each(value.begin(), value.end(), [&](bool b) { if (++n > 1) os << ' '; os << b; }); - strValue = os.str(); - } - return strValue; - }; -}; - -std::shared_ptr FIBoolValue::create(std::vector &&value) { - return std::make_shared(std::move(value)); -} - -struct FIFloatValueImpl: public FIFloatValue { - mutable std::string strValue; - mutable bool strValueValid; - inline FIFloatValueImpl(std::vector &&value_): strValueValid(false) { value = std::move(value_); } - virtual const std::string &toString() const /*override*/ { - if (!strValueValid) { - strValueValid = true; - std::ostringstream os; - int n = 0; - std::for_each(value.begin(), value.end(), [&](float f) { if (++n > 1) os << ' '; os << f; }); - strValue = os.str(); - } - return strValue; - } -}; - -std::shared_ptr FIFloatValue::create(std::vector &&value) { - return std::make_shared(std::move(value)); -} - -struct FIDoubleValueImpl: public FIDoubleValue { - mutable std::string strValue; - mutable bool strValueValid; - inline FIDoubleValueImpl(std::vector &&value_): strValueValid(false) { value = std::move(value_); } - virtual const std::string &toString() const /*override*/ { - if (!strValueValid) { - strValueValid = true; - std::ostringstream os; - int n = 0; - std::for_each(value.begin(), value.end(), [&](double d) { if (++n > 1) os << ' '; os << d; }); - strValue = os.str(); - } - return strValue; - } -}; - -std::shared_ptr FIDoubleValue::create(std::vector &&value) { - return std::make_shared(std::move(value)); -} - -struct FIUUIDValueImpl: public FIUUIDValue { - mutable std::string strValue; - mutable bool strValueValid; - inline FIUUIDValueImpl(std::vector &&value_): strValueValid(false) { value = std::move(value_); } - virtual const std::string &toString() const /*override*/ { - if (!strValueValid) { - strValueValid = true; - std::ostringstream os; - os << std::hex << std::uppercase << std::setfill('0'); - std::vector::size_type valueSize = value.size(); - for (std::vector::size_type i = 0; i < valueSize; ++i) { - switch (i & 15) { - case 0: - if (i > 0) { - os << ' '; - } - os << std::setw(2) << static_cast(value[i]); - break; - case 4: - case 6: - case 8: - case 10: - os << '-'; - // intentionally fall through! - case 1: - case 2: - case 3: - case 5: - case 7: - case 9: - case 11: - case 12: - case 13: - case 14: - case 15: - os << std::setw(2) << static_cast(value[i]); - break; - } - } - strValue = os.str(); - } - return strValue; - }; -}; - -std::shared_ptr FIUUIDValue::create(std::vector &&value) { - return std::make_shared(std::move(value)); -} - -struct FICDATAValueImpl: public FICDATAValue { - inline FICDATAValueImpl(std::string &&value_) { value = std::move(value_); } - virtual const std::string &toString() const /*override*/ { return value; } -}; - -std::shared_ptr FICDATAValue::create(std::string &&value) { - return std::make_shared(std::move(value)); -} - -struct FIHexDecoder: public FIDecoder { - virtual std::shared_ptr decode(const uint8_t *data, size_t len) /*override*/ { - return FIHexValue::create(std::vector(data, data + len)); - } -}; - -struct FIBase64Decoder: public FIDecoder { - virtual std::shared_ptr decode(const uint8_t *data, size_t len) /*override*/ { - return FIBase64Value::create(std::vector(data, data + len)); - } -}; - -struct FIShortDecoder: public FIDecoder { - virtual std::shared_ptr decode(const uint8_t *data, size_t len) /*override*/ { - if (len & 1) { - throw DeadlyImportError(parseErrorMessage); - } - std::vector value; - size_t numShorts = len / 2; - value.reserve(numShorts); - for (size_t i = 0; i < numShorts; ++i) { - int16_t v = (data[0] << 8) | data[1]; - value.push_back(v); - data += 2; - } - return FIShortValue::create(std::move(value)); - } -}; - -struct FIIntDecoder: public FIDecoder { - virtual std::shared_ptr decode(const uint8_t *data, size_t len) /*override*/ { - if (len & 3) { - throw DeadlyImportError(parseErrorMessage); - } - std::vector value; - size_t numInts = len / 4; - value.reserve(numInts); - for (size_t i = 0; i < numInts; ++i) { - int32_t v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - value.push_back(v); - data += 4; - } - return FIIntValue::create(std::move(value)); - } -}; - -struct FILongDecoder: public FIDecoder { - virtual std::shared_ptr decode(const uint8_t *data, size_t len) /*override*/ { - if (len & 7) { - throw DeadlyImportError(parseErrorMessage); - } - std::vector value; - size_t numLongs = len / 8; - value.reserve(numLongs); - for (size_t i = 0; i < numLongs; ++i) { - int64_t b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7]; - int64_t v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; - value.push_back(v); - data += 8; - } - return FILongValue::create(std::move(value)); - } -}; - -struct FIBoolDecoder: public FIDecoder { - virtual std::shared_ptr decode(const uint8_t *data, size_t len) /*override*/ { - if (len < 1) { - throw DeadlyImportError(parseErrorMessage); - } - std::vector value; - uint8_t b = *data++; - size_t unusedBits = b >> 4; - size_t numBools = (len * 8) - 4 - unusedBits; - value.reserve(numBools); - uint8_t mask = 1 << 3; - for (size_t i = 0; i < numBools; ++i) { - if (!mask) { - mask = 1 << 7; - b = *data++; - } - value.push_back((b & mask) != 0); - } - return FIBoolValue::create(std::move(value)); - } -}; - -struct FIFloatDecoder: public FIDecoder { - virtual std::shared_ptr decode(const uint8_t *data, size_t len) /*override*/ { - if (len & 3) { - throw DeadlyImportError(parseErrorMessage); - } - std::vector value; - size_t numFloats = len / 4; - value.reserve(numFloats); - for (size_t i = 0; i < numFloats; ++i) { - int v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - float f; - memcpy(&f, &v, 4); - value.push_back(f); - data += 4; - } - return FIFloatValue::create(std::move(value)); - } -}; - -struct FIDoubleDecoder: public FIDecoder { - virtual std::shared_ptr decode(const uint8_t *data, size_t len) /*override*/ { - if (len & 7) { - throw DeadlyImportError(parseErrorMessage); - } - std::vector value; - size_t numDoubles = len / 8; - value.reserve(numDoubles); - for (size_t i = 0; i < numDoubles; ++i) { - long long b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7]; - long long v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; - double f; - memcpy(&f, &v, 8); - value.push_back(f); - data += 8; - } - return FIDoubleValue::create(std::move(value)); - } -}; - -struct FIUUIDDecoder: public FIDecoder { - virtual std::shared_ptr decode(const uint8_t *data, size_t len) /*override*/ { - if (len & 15) { - throw DeadlyImportError(parseErrorMessage); - } - return FIUUIDValue::create(std::vector(data, data + len)); - } -}; - -struct FICDATADecoder: public FIDecoder { - virtual std::shared_ptr decode(const uint8_t *data, size_t len) /*override*/ { - return FICDATAValue::create(parseUTF8String(data, len)); - } -}; - -class CFIReaderImpl: public FIReader { -public: - - CFIReaderImpl(std::unique_ptr data_, size_t size): - data(std::move(data_)), dataP(data.get()), dataEnd(data.get() + size), currentNodeType(irr::io::EXN_NONE), - emptyElement(false), headerPending(true), terminatorPending(false) - {} - - virtual ~CFIReaderImpl() {} - - virtual bool read() /*override*/ { - if (headerPending) { - headerPending = false; - parseHeader(); - } - if (terminatorPending) { - terminatorPending = false; - if (elementStack.empty()) { - return false; - } - else { - nodeName = elementStack.top(); - elementStack.pop(); - currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END; - return true; - } - } - if (dataP >= dataEnd) { - return false; - } - uint8_t b = *dataP; - if (b < 0x80) { // Element (C.2.11.2, C.3.7.2) - // C.3 - parseElement(); - return true; - } - else if (b < 0xc0) { // Characters (C.3.7.5) - // C.7 - auto chars = parseNonIdentifyingStringOrIndex3(vocabulary.charactersTable); - nodeName = chars->toString(); - currentNodeType = irr::io::EXN_TEXT; - return true; - } - else if (b < 0xe0) { - if ((b & 0xfc) == 0xc4) { // DTD (C.2.11.5) - // C.9 - ++dataP; - if (b & 0x02) { - /*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); - } - if (b & 0x01) { - /*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); - } - elementStack.push(EmptyString); - currentNodeType = irr::io::EXN_UNKNOWN; - return true; - } - else if ((b & 0xfc) == 0xc8) { // Unexpanded entity reference (C.3.7.4) - // C.6 - ++dataP; - /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); - if (b & 0x02) { - /*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); - } - if (b & 0x01) { - /*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); - } - currentNodeType = irr::io::EXN_UNKNOWN; - return true; - } - } - else if (b < 0xf0) { - if (b == 0xe1) { // Processing instruction (C.2.11.3, C.3.7.3) - // C.5 - ++dataP; - /*const std::string &target =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - /*std::shared_ptr data =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable); - currentNodeType = irr::io::EXN_UNKNOWN; - return true; - } - else if (b == 0xe2) { // Comment (C.2.11.4, C.3.7.6) - // C.8 - ++dataP; - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - std::shared_ptr comment = parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable); - nodeName = comment->toString(); - currentNodeType = irr::io::EXN_COMMENT; - return true; - } - } - else { // Terminator (C.2.12, C.3.8) - ++dataP; - if (b == 0xff) { - terminatorPending = true; - } - if (elementStack.empty()) { - return false; - } - else { - nodeName = elementStack.top(); - elementStack.pop(); - currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END; - return true; - } - } - throw DeadlyImportError(parseErrorMessage); - } - - virtual irr::io::EXML_NODE getNodeType() const /*override*/ { - return currentNodeType; - } - - virtual int getAttributeCount() const /*override*/ { - return static_cast(attributes.size()); - } - - virtual const char* getAttributeName(int idx) const /*override*/ { - if (idx < 0 || idx >= (int)attributes.size()) { - return nullptr; - } - return attributes[idx].name.c_str(); - } - - virtual const char* getAttributeValue(int idx) const /*override*/ { - if (idx < 0 || idx >= (int)attributes.size()) { - return nullptr; - } - return attributes[idx].value->toString().c_str(); - } - - virtual const char* getAttributeValue(const char* name) const /*override*/ { - const Attribute* attr = getAttributeByName(name); - if (!attr) { - return nullptr; - } - return attr->value->toString().c_str(); - } - - virtual const char* getAttributeValueSafe(const char* name) const /*override*/ { - const Attribute* attr = getAttributeByName(name); - if (!attr) { - return EmptyString.c_str(); - } - return attr->value->toString().c_str(); - } - - virtual int getAttributeValueAsInt(const char* name) const /*override*/ { - const Attribute* attr = getAttributeByName(name); - if (!attr) { - return 0; - } - std::shared_ptr intValue = std::dynamic_pointer_cast(attr->value); - if (intValue) { - return intValue->value.size() == 1 ? intValue->value.front() : 0; - } - return atoi(attr->value->toString().c_str()); - } - - virtual int getAttributeValueAsInt(int idx) const /*override*/ { - if (idx < 0 || idx >= (int)attributes.size()) { - return 0; - } - std::shared_ptr intValue = std::dynamic_pointer_cast(attributes[idx].value); - if (intValue) { - return intValue->value.size() == 1 ? intValue->value.front() : 0; - } - return atoi(attributes[idx].value->toString().c_str()); - } - - virtual float getAttributeValueAsFloat(const char* name) const /*override*/ { - const Attribute* attr = getAttributeByName(name); - if (!attr) { - return 0; - } - std::shared_ptr floatValue = std::dynamic_pointer_cast(attr->value); - if (floatValue) { - return floatValue->value.size() == 1 ? floatValue->value.front() : 0; - } - - return fast_atof(attr->value->toString().c_str()); - } - - virtual float getAttributeValueAsFloat(int idx) const /*override*/ { - if (idx < 0 || idx >= (int)attributes.size()) { - return 0; - } - std::shared_ptr floatValue = std::dynamic_pointer_cast(attributes[idx].value); - if (floatValue) { - return floatValue->value.size() == 1 ? floatValue->value.front() : 0; - } - return fast_atof(attributes[idx].value->toString().c_str()); - } - - virtual const char* getNodeName() const /*override*/ { - return nodeName.c_str(); - } - - virtual const char* getNodeData() const /*override*/ { - return nodeName.c_str(); - } - - virtual bool isEmptyElement() const /*override*/ { - return emptyElement; - } - - virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ { - return irr::io::ETF_UTF8; - } - - virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ { - return irr::io::ETF_UTF8; - } - - virtual std::shared_ptr getAttributeEncodedValue(int idx) const /*override*/ { - if (idx < 0 || idx >= (int)attributes.size()) { - return nullptr; - } - return attributes[idx].value; - } - - virtual std::shared_ptr getAttributeEncodedValue(const char* name) const /*override*/ { - const Attribute* attr = getAttributeByName(name); - if (!attr) { - return nullptr; - } - return attr->value; - } - - virtual void registerDecoder(const std::string &algorithmUri, std::unique_ptr decoder) /*override*/ { - decoderMap[algorithmUri] = std::move(decoder); - } - - virtual void registerVocabulary(const std::string &vocabularyUri, const FIVocabulary *vocabulary) /*override*/ { - vocabularyMap[vocabularyUri] = vocabulary; - } - -private: - - struct QName { - std::string prefix; - std::string uri; - std::string name; - inline QName() {} - inline QName(const FIQName &qname): prefix(qname.prefix ? qname.prefix : ""), uri(qname.uri ? qname.uri : ""), name(qname.name) {} - }; - - struct Attribute { - QName qname; - std::string name; - std::shared_ptr value; - }; - - struct Vocabulary { - std::vector restrictedAlphabetTable; - std::vector encodingAlgorithmTable; - std::vector prefixTable; - std::vector namespaceNameTable; - std::vector localNameTable; - std::vector otherNCNameTable; - std::vector otherURITable; - std::vector> attributeValueTable; - std::vector> charactersTable; - std::vector> otherStringTable; - std::vector elementNameTable; - std::vector attributeNameTable; - Vocabulary() { - prefixTable.push_back("xml"); - namespaceNameTable.push_back("http://www.w3.org/XML/1998/namespace"); - } - }; - - const Attribute* getAttributeByName(const char* name) const { - if (!name) { - return 0; - } - std::string n = name; - for (int i=0; i<(int)attributes.size(); ++i) { - if (attributes[i].name == n) { - return &attributes[i]; - } - } - return 0; - } - - size_t parseInt2() { // C.25 - uint8_t b = *dataP++; - if (!(b & 0x40)) { // x0...... (C.25.2) - return b & 0x3f; - } - else if ((b & 0x60) == 0x40) { // x10..... ........ (C.25.3) - if (dataEnd - dataP > 0) { - return (((b & 0x1f) << 8) | *dataP++) + 0x40; - } - } - else if ((b & 0x70) == 0x60) { // x110.... ........ ........ (C.25.4) - if (dataEnd - dataP > 1) { - size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x2040; - dataP += 2; - return result; - } - } - throw DeadlyImportError(parseErrorMessage); - } - - size_t parseInt3() { // C.27 - uint8_t b = *dataP++; - if (!(b & 0x20)) { // xx0..... (C.27.2) - return b & 0x1f; - } - else if ((b & 0x38) == 0x20) { // xx100... ........ (C.27.3) - if (dataEnd - dataP > 0) { - return (((b & 0x07) << 8) | *dataP++) + 0x20; - } - } - else if ((b & 0x38) == 0x28) { // xx101... ........ ........ (C.27.4) - if (dataEnd - dataP > 1) { - size_t result = (((b & 0x07) << 16) | (dataP[0] << 8) | dataP[1]) + 0x820; - dataP += 2; - return result; - } - } - else if ((b & 0x3f) == 0x30) { // xx110000 0000.... ........ ........ (C.27.5) - if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) { - size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x80820; - dataP += 3; - return result; - } - } - throw DeadlyImportError(parseErrorMessage); - } - - size_t parseInt4() { // C.28 - uint8_t b = *dataP++; - if (!(b & 0x10)) { // xxx0.... (C.28.2) - return b & 0x0f; - } - else if ((b & 0x1c) == 0x10) { // xxx100.. ........ (C.28.3) - if (dataEnd - dataP > 0) { - return (((b & 0x03) << 8) | *dataP++) + 0x10; - } - } - else if ((b & 0x1c) == 0x14) { // xxx101.. ........ ........ (C.28.4) - if (dataEnd - dataP > 1) { - size_t result = (((b & 0x03) << 16) | (dataP[0] << 8) | dataP[1]) + 0x410; - dataP += 2; - return result; - } - } - else if ((b & 0x1f) == 0x18) { // xxx11000 0000.... ........ ........ (C.28.5) - if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) { - size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x40410; - dataP += 3; - return result; - } - } - throw DeadlyImportError(parseErrorMessage); - } - - size_t parseSequenceLen() { // C.21 - if (dataEnd - dataP > 0) { - uint8_t b = *dataP++; - if (b < 0x80) { // 0....... (C.21.2) - return b; - } - else if ((b & 0xf0) == 0x80) { // 1000.... ........ ........ (C.21.3) - if (dataEnd - dataP > 1) { - size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x80; - dataP += 2; - return result; - } - } - } - throw DeadlyImportError(parseErrorMessage); - } - - std::string parseNonEmptyOctetString2() { // C.22 - // Parse the length of the string - uint8_t b = *dataP++ & 0x7f; - size_t len; - if (!(b & 0x40)) { // x0...... (C.22.3.1) - len = b + 1; - } - else if (b == 0x40) { // x1000000 ........ (C.22.3.2) - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - len = *dataP++ + 0x41; - } - else if (b == 0x60) { // x1100000 ........ ........ ........ ........ (C.22.3.3) - if (dataEnd - dataP < 4) { - throw DeadlyImportError(parseErrorMessage); - } - len = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x141; - dataP += 4; - } - else { - throw DeadlyImportError(parseErrorMessage); - } - - // Parse the string (C.22.4) - if (dataEnd - dataP < static_cast(len)) { - throw DeadlyImportError(parseErrorMessage); - } - std::string s = parseUTF8String(dataP, len); - dataP += len; - - return s; - } - - size_t parseNonEmptyOctetString5Length() { // C.23 - // Parse the length of the string - size_t b = *dataP++ & 0x0f; - if (!(b & 0x08)) { // xxxx0... (C.23.3.1) - return b + 1; - } - else if (b == 0x08) { // xxxx1000 ........ (C.23.3.2) - if (dataEnd - dataP > 0) { - return *dataP++ + 0x09; - } - } - else if (b == 0x0c) { // xxxx1100 ........ ........ ........ ........ (C.23.3.3) - if (dataEnd - dataP > 3) { - size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x109; - dataP += 4; - return result; - } - } - throw DeadlyImportError(parseErrorMessage); - } - - size_t parseNonEmptyOctetString7Length() { // C.24 - // Parse the length of the string - size_t b = *dataP++ & 0x03; - if (!(b & 0x02)) { // xxxxxx0. (C.24.3.1) - return b + 1; - } - else if (b == 0x02) { // xxxxxx10 ........ (C.24.3.2) - if (dataEnd - dataP > 0) { - return *dataP++ + 0x3; - } - } - else if (b == 0x03) { // xxxxxx11 ........ ........ ........ ........ (C.24.3.3) - if (dataEnd - dataP > 3) { - size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x103; - dataP += 4; - return result; - } - } - throw DeadlyImportError(parseErrorMessage); - } - - std::shared_ptr parseEncodedData(size_t index, size_t len) { - if (index < 32) { - FIDecoder *decoder = defaultDecoder[index]; - if (!decoder) { - throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index)); - } - return decoder->decode(dataP, len); - } - else { - if (index - 32 >= vocabulary.encodingAlgorithmTable.size()) { - throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index)); - } - std::string uri = vocabulary.encodingAlgorithmTable[index - 32]; - auto it = decoderMap.find(uri); - if (it == decoderMap.end()) { - throw DeadlyImportError("Unsupported encoding algorithm " + uri); - } - else { - return it->second->decode(dataP, len); - } - } - } - - std::shared_ptr parseRestrictedAlphabet(size_t index, size_t len) { - std::string alphabet; - if (index < 16) { - switch (index) { - case 0: // numeric - alphabet = "0123456789-+.e "; - break; - case 1: // date and time - alphabet = "0123456789-:TZ "; - break; - default: - throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index)); - } - } - else { - if (index - 16 >= vocabulary.restrictedAlphabetTable.size()) { - throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index)); - } - alphabet = vocabulary.restrictedAlphabetTable[index - 16]; - } - std::vector alphabetUTF32; - utf8::utf8to32(alphabet.begin(), alphabet.end(), back_inserter(alphabetUTF32)); - std::string::size_type alphabetLength = alphabetUTF32.size(); - if (alphabetLength < 2) { - throw DeadlyImportError("Invalid restricted alphabet length " + to_string(alphabetLength)); - } - std::string::size_type bitsPerCharacter = 1; - while ((1ull << bitsPerCharacter) <= alphabetLength) { - ++bitsPerCharacter; - } - size_t bitsAvail = 0; - uint8_t mask = (1 << bitsPerCharacter) - 1; - uint32_t bits = 0; - std::string s; - for (size_t i = 0; i < len; ++i) { - bits = (bits << 8) | dataP[i]; - bitsAvail += 8; - while (bitsAvail >= bitsPerCharacter) { - bitsAvail -= bitsPerCharacter; - size_t charIndex = (bits >> bitsAvail) & mask; - if (charIndex < alphabetLength) { - s.push_back(alphabetUTF32[charIndex]); - } - else if (charIndex != mask) { - throw DeadlyImportError(parseErrorMessage); - } - } - } - return FIStringValue::create(std::move(s)); - } - - std::shared_ptr parseEncodedCharacterString3() { // C.19 - std::shared_ptr result; - size_t len; - uint8_t b = *dataP; - if (b & 0x20) { - ++dataP; - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - size_t index = ((b & 0x0f) << 4) | ((*dataP & 0xf0) >> 4); // C.29 - len = parseNonEmptyOctetString5Length(); - if (dataEnd - dataP < static_cast(len)) { - throw DeadlyImportError(parseErrorMessage); - } - if (b & 0x10) { - // encoding algorithm (C.19.3.4) - result = parseEncodedData(index, len); - } - else { - // Restricted alphabet (C.19.3.3) - result = parseRestrictedAlphabet(index, len); - } - } - else { - len = parseNonEmptyOctetString5Length(); - if (dataEnd - dataP < static_cast(len)) { - throw DeadlyImportError(parseErrorMessage); - } - if (b & 0x10) { - // UTF-16 (C.19.3.2) - if (len & 1) { - throw DeadlyImportError(parseErrorMessage); - } - result = FIStringValue::create(parseUTF16String(dataP, len)); - } - else { - // UTF-8 (C.19.3.1) - result = FIStringValue::create(parseUTF8String(dataP, len)); - } - } - dataP += len; - return result; - } - - std::shared_ptr parseEncodedCharacterString5() { // C.20 - std::shared_ptr result; - size_t len; - uint8_t b = *dataP; - if (b & 0x08) { - ++dataP; - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - size_t index = ((b & 0x03) << 6) | ((*dataP & 0xfc) >> 2); /* C.29 */ - len = parseNonEmptyOctetString7Length(); - if (dataEnd - dataP < static_cast(len)) { - throw DeadlyImportError(parseErrorMessage); - } - if (b & 0x04) { - // encoding algorithm (C.20.3.4) - result = parseEncodedData(index, len); - } - else { - // Restricted alphabet (C.20.3.3) - result = parseRestrictedAlphabet(index, len); - } - } - else { - len = parseNonEmptyOctetString7Length(); - if (dataEnd - dataP < static_cast(len)) { - throw DeadlyImportError(parseErrorMessage); - } - if (b & 0x04) { - // UTF-16 (C.20.3.2) - if (len & 1) { - throw DeadlyImportError(parseErrorMessage); - } - result = FIStringValue::create(parseUTF16String(dataP, len)); - } - else { - // UTF-8 (C.20.3.1) - result = FIStringValue::create(parseUTF8String(dataP, len)); - } - } - dataP += len; - return result; - } - - const std::string &parseIdentifyingStringOrIndex(std::vector &stringTable) { // C.13 - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - uint8_t b = *dataP; - if (b & 0x80) { - // We have an index (C.13.4) - size_t index = parseInt2(); - if (index >= stringTable.size()) { - throw DeadlyImportError(parseErrorMessage); - } - return stringTable[index]; - } - else { - // We have a string (C.13.3) - stringTable.push_back(parseNonEmptyOctetString2()); - return stringTable.back(); - } - } - - QName parseNameSurrogate() { // C.16 - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - uint8_t b = *dataP++; - if (b & 0xfc) { // Padding '000000' C.2.5.5 - throw DeadlyImportError(parseErrorMessage); - } - QName result; - size_t index; - if (b & 0x02) { // prefix (C.16.3) - if ((dataEnd - dataP < 1) || (*dataP & 0x80)) { - throw DeadlyImportError(parseErrorMessage); - } - index = parseInt2(); - if (index >= vocabulary.prefixTable.size()) { - throw DeadlyImportError(parseErrorMessage); - } - result.prefix = vocabulary.prefixTable[index]; - } - if (b & 0x01) { // namespace-name (C.16.4) - if ((dataEnd - dataP < 1) || (*dataP & 0x80)) { - throw DeadlyImportError(parseErrorMessage); - } - index = parseInt2(); - if (index >= vocabulary.namespaceNameTable.size()) { - throw DeadlyImportError(parseErrorMessage); - } - result.uri = vocabulary.namespaceNameTable[index]; - } - // local-name - if ((dataEnd - dataP < 1) || (*dataP & 0x80)) { - throw DeadlyImportError(parseErrorMessage); - } - index = parseInt2(); - if (index >= vocabulary.localNameTable.size()) { - throw DeadlyImportError(parseErrorMessage); - } - result.name = vocabulary.localNameTable[index]; - return result; - } - - const QName &parseQualifiedNameOrIndex2(std::vector &qNameTable) { // C.17 - uint8_t b = *dataP; - if ((b & 0x7c) == 0x78) { // x11110.. - // We have a literal (C.17.3) - ++dataP; - QName result; - // prefix (C.17.3.1) - result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string(); - // namespace-name (C.17.3.1) - result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string(); - // local-name - result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable); - qNameTable.push_back(result); - return qNameTable.back(); - } - else { - // We have an index (C.17.4) - size_t index = parseInt2(); - if (index >= qNameTable.size()) { - throw DeadlyImportError(parseErrorMessage); - } - return qNameTable[index]; - } - } - - const QName &parseQualifiedNameOrIndex3(std::vector &qNameTable) { // C.18 - uint8_t b = *dataP; - if ((b & 0x3c) == 0x3c) { // xx1111.. - // We have a literal (C.18.3) - ++dataP; - QName result; - // prefix (C.18.3.1) - result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string(); - // namespace-name (C.18.3.1) - result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string(); - // local-name - result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable); - qNameTable.push_back(result); - return qNameTable.back(); - } - else { - // We have an index (C.18.4) - size_t index = parseInt3(); - if (index >= qNameTable.size()) { - throw DeadlyImportError(parseErrorMessage); - } - return qNameTable[index]; - } - } - - std::shared_ptr parseNonIdentifyingStringOrIndex1(std::vector> &valueTable) { // C.14 - uint8_t b = *dataP; - if (b == 0xff) { // C.26.2 - // empty string - ++dataP; - return EmptyFIString; - } - else if (b & 0x80) { // C.14.4 - // We have an index - size_t index = parseInt2(); - if (index >= valueTable.size()) { - throw DeadlyImportError(parseErrorMessage); - } - return valueTable[index]; - } - else { // C.14.3 - // We have a literal - std::shared_ptr result = parseEncodedCharacterString3(); - if (b & 0x40) { // C.14.3.1 - valueTable.push_back(result); - } - return result; - } - } - - std::shared_ptr parseNonIdentifyingStringOrIndex3(std::vector> &valueTable) { // C.15 - uint8_t b = *dataP; - if (b & 0x20) { // C.15.4 - // We have an index - size_t index = parseInt4(); - if (index >= valueTable.size()) { - throw DeadlyImportError(parseErrorMessage); - } - return valueTable[index]; - } - else { // C.15.3 - // We have a literal - std::shared_ptr result = parseEncodedCharacterString5(); - if (b & 0x10) { // C.15.3.1 - valueTable.push_back(result); - } - return result; - } - } - - void parseElement() { - // C.3 - - attributes.clear(); - - uint8_t b = *dataP; - bool hasAttributes = (b & 0x40) != 0; // C.3.3 - if ((b & 0x3f) == 0x38) { // C.3.4.1 - // Parse namespaces - ++dataP; - for (;;) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - b = *dataP++; - if (b == 0xf0) { // C.3.4.3 - break; - } - if ((b & 0xfc) != 0xcc) { // C.3.4.2 - throw DeadlyImportError(parseErrorMessage); - } - // C.12 - Attribute attr; - attr.qname.prefix = "xmlns"; - attr.qname.name = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string(); - attr.qname.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string(); - attr.name = attr.qname.name.empty() ? "xmlns" : "xmlns:" + attr.qname.name; - attr.value = FIStringValue::create(std::string(attr.qname.uri)); - attributes.push_back(attr); - } - if ((dataEnd - dataP < 1) || (*dataP & 0xc0)) { - throw DeadlyImportError(parseErrorMessage); - } - } - - // Parse Element name (C.3.5) - const QName &elemName = parseQualifiedNameOrIndex3(vocabulary.elementNameTable); - nodeName = elemName.prefix.empty() ? elemName.name : elemName.prefix + ':' + elemName.name; - - if (hasAttributes) { - for (;;) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - b = *dataP; - if (b < 0x80) { // C.3.6.1 - // C.4 - Attribute attr; - attr.qname = parseQualifiedNameOrIndex2(vocabulary.attributeNameTable); - attr.name = attr.qname.prefix.empty() ? attr.qname.name : attr.qname.prefix + ':' + attr.qname.name; - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - attr.value = parseNonIdentifyingStringOrIndex1(vocabulary.attributeValueTable); - attributes.push_back(attr); - } - else { - if ((b & 0xf0) != 0xf0) { // C.3.6.2 - throw DeadlyImportError(parseErrorMessage); - } - emptyElement = b == 0xff; // C.3.6.2, C.3.8 - ++dataP; - break; - } - } - } - else { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - b = *dataP; - switch (b) { - case 0xff: - terminatorPending = true; - // Intentionally fall through - case 0xf0: - emptyElement = true; - ++dataP; - break; - default: - emptyElement = false; - } - } - if (!emptyElement) { - elementStack.push(nodeName); - } - - currentNodeType = irr::io::EXN_ELEMENT; - } - - void parseHeader() { - // Parse header (C.1.3) - size_t magicSize = parseMagic(dataP, dataEnd); - if (!magicSize) { - throw DeadlyImportError(parseErrorMessage); - } - dataP += magicSize; - // C.2.3 - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - uint8_t b = *dataP++; - if (b & 0x40) { - // Parse additional data (C.2.4) - size_t len = parseSequenceLen(); - for (size_t i = 0; i < len; ++i) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - /*std::string id =*/ parseNonEmptyOctetString2(); - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - /*std::string data =*/ parseNonEmptyOctetString2(); - } - } - if (b & 0x20) { - // Parse initial vocabulary (C.2.5) - if (dataEnd - dataP < 2) { - throw DeadlyImportError(parseErrorMessage); - } - uint16_t b1 = (dataP[0] << 8) | dataP[1]; - dataP += 2; - if (b1 & 0x1000) { - // External vocabulary (C.2.5.2) - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - std::string uri = parseNonEmptyOctetString2(); - auto it = vocabularyMap.find(uri); - if (it == vocabularyMap.end()) { - throw DeadlyImportError("Unknown vocabulary " + uri); - } - const FIVocabulary *externalVocabulary = it->second; - if (externalVocabulary->restrictedAlphabetTable) { - std::copy(externalVocabulary->restrictedAlphabetTable, externalVocabulary->restrictedAlphabetTable + externalVocabulary->restrictedAlphabetTableSize, std::back_inserter(vocabulary.restrictedAlphabetTable)); - } - if (externalVocabulary->encodingAlgorithmTable) { - std::copy(externalVocabulary->encodingAlgorithmTable, externalVocabulary->encodingAlgorithmTable + externalVocabulary->encodingAlgorithmTableSize, std::back_inserter(vocabulary.encodingAlgorithmTable)); - } - if (externalVocabulary->prefixTable) { - std::copy(externalVocabulary->prefixTable, externalVocabulary->prefixTable + externalVocabulary->prefixTableSize, std::back_inserter(vocabulary.prefixTable)); - } - if (externalVocabulary->namespaceNameTable) { - std::copy(externalVocabulary->namespaceNameTable, externalVocabulary->namespaceNameTable + externalVocabulary->namespaceNameTableSize, std::back_inserter(vocabulary.namespaceNameTable)); - } - if (externalVocabulary->localNameTable) { - std::copy(externalVocabulary->localNameTable, externalVocabulary->localNameTable + externalVocabulary->localNameTableSize, std::back_inserter(vocabulary.localNameTable)); - } - if (externalVocabulary->otherNCNameTable) { - std::copy(externalVocabulary->otherNCNameTable, externalVocabulary->otherNCNameTable + externalVocabulary->otherNCNameTableSize, std::back_inserter(vocabulary.otherNCNameTable)); - } - if (externalVocabulary->otherURITable) { - std::copy(externalVocabulary->otherURITable, externalVocabulary->otherURITable + externalVocabulary->otherURITableSize, std::back_inserter(vocabulary.otherURITable)); - } - if (externalVocabulary->attributeValueTable) { - std::copy(externalVocabulary->attributeValueTable, externalVocabulary->attributeValueTable + externalVocabulary->attributeValueTableSize, std::back_inserter(vocabulary.attributeValueTable)); - } - if (externalVocabulary->charactersTable) { - std::copy(externalVocabulary->charactersTable, externalVocabulary->charactersTable + externalVocabulary->charactersTableSize, std::back_inserter(vocabulary.charactersTable)); - } - if (externalVocabulary->otherStringTable) { - std::copy(externalVocabulary->otherStringTable, externalVocabulary->otherStringTable + externalVocabulary->otherStringTableSize, std::back_inserter(vocabulary.otherStringTable)); - } - if (externalVocabulary->elementNameTable) { - std::copy(externalVocabulary->elementNameTable, externalVocabulary->elementNameTable + externalVocabulary->elementNameTableSize, std::back_inserter(vocabulary.elementNameTable)); - } - if (externalVocabulary->attributeNameTable) { - std::copy(externalVocabulary->attributeNameTable, externalVocabulary->attributeNameTable + externalVocabulary->attributeNameTableSize, std::back_inserter(vocabulary.attributeNameTable)); - } - } - if (b1 & 0x0800) { - // Parse restricted alphabets (C.2.5.3) - for (size_t len = parseSequenceLen(); len > 0; --len) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - vocabulary.restrictedAlphabetTable.push_back(parseNonEmptyOctetString2()); - } - } - if (b1 & 0x0400) { - // Parse encoding algorithms (C.2.5.3) - for (size_t len = parseSequenceLen(); len > 0; --len) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - vocabulary.encodingAlgorithmTable.push_back(parseNonEmptyOctetString2()); - } - } - if (b1 & 0x0200) { - // Parse prefixes (C.2.5.3) - for (size_t len = parseSequenceLen(); len > 0; --len) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - vocabulary.prefixTable.push_back(parseNonEmptyOctetString2()); - } - } - if (b1 & 0x0100) { - // Parse namespace names (C.2.5.3) - for (size_t len = parseSequenceLen(); len > 0; --len) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - vocabulary.namespaceNameTable.push_back(parseNonEmptyOctetString2()); - } - } - if (b1 & 0x0080) { - // Parse local names (C.2.5.3) - for (size_t len = parseSequenceLen(); len > 0; --len) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - vocabulary.localNameTable.push_back(parseNonEmptyOctetString2()); - } - } - if (b1 & 0x0040) { - // Parse other ncnames (C.2.5.3) - for (size_t len = parseSequenceLen(); len > 0; --len) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - vocabulary.otherNCNameTable.push_back(parseNonEmptyOctetString2()); - } - } - if (b1 & 0x0020) { - // Parse other uris (C.2.5.3) - for (size_t len = parseSequenceLen(); len > 0; --len) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - vocabulary.otherURITable.push_back(parseNonEmptyOctetString2()); - } - } - if (b1 & 0x0010) { - // Parse attribute values (C.2.5.4) - for (size_t len = parseSequenceLen(); len > 0; --len) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - vocabulary.attributeValueTable.push_back(parseEncodedCharacterString3()); - } - } - if (b1 & 0x0008) { - // Parse content character chunks (C.2.5.4) - for (size_t len = parseSequenceLen(); len > 0; --len) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - vocabulary.charactersTable.push_back(parseEncodedCharacterString3()); - } - } - if (b1 & 0x0004) { - // Parse other strings (C.2.5.4) - for (size_t len = parseSequenceLen(); len > 0; --len) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - vocabulary.otherStringTable.push_back(parseEncodedCharacterString3()); - } - } - if (b1 & 0x0002) { - // Parse element name surrogates (C.2.5.5) - for (size_t len = parseSequenceLen(); len > 0; --len) { - vocabulary.elementNameTable.push_back(parseNameSurrogate()); - } - } - if (b1 & 0x0001) { - // Parse attribute name surrogates (C.2.5.5) - for (size_t len = parseSequenceLen(); len > 0; --len) { - vocabulary.attributeNameTable.push_back(parseNameSurrogate()); - } - } - } - if (b & 0x10) { - // Parse notations (C.2.6) - for (;;) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - uint8_t b1 = *dataP++; - if (b1 == 0xf0) { - break; - } - if ((b1 & 0xfc) != 0xc0) { - throw DeadlyImportError(parseErrorMessage); - } - /* C.11 */ - /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); - if (b1 & 0x02) { - /*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); - } - if (b1 & 0x01) { - /*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); - } - } - } - if (b & 0x08) { - // Parse unparsed entities (C.2.7) - for (;;) { - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - uint8_t b1 = *dataP++; - if (b1 == 0xf0) { - break; - } - if ((b1 & 0xfe) != 0xd0) { - throw DeadlyImportError(parseErrorMessage); - } - /* C.10 */ - /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); - /*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); - if (b1 & 0x01) { - /*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); - } - /*const std::string ¬ationName =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); - } - } - if (b & 0x04) { - // Parse character encoding scheme (C.2.8) - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - /*std::string characterEncodingScheme =*/ parseNonEmptyOctetString2(); - } - if (b & 0x02) { - // Parse standalone flag (C.2.9) - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - uint8_t b1 = *dataP++; - if (b1 & 0xfe) { - throw DeadlyImportError(parseErrorMessage); - } - //bool standalone = b1 & 0x01; - } - if (b & 0x01) { - // Parse version (C.2.10) - if (dataEnd - dataP < 1) { - throw DeadlyImportError(parseErrorMessage); - } - /*std::shared_ptr version =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable); - } - } - - std::unique_ptr data; - uint8_t *dataP, *dataEnd; - irr::io::EXML_NODE currentNodeType; - bool emptyElement; - bool headerPending; - bool terminatorPending; - Vocabulary vocabulary; - std::vector attributes; - std::stack elementStack; - std::string nodeName; - std::map> decoderMap; - std::map vocabularyMap; - - static const std::string EmptyString; - static std::shared_ptr EmptyFIString; - - static FIHexDecoder hexDecoder; - static FIBase64Decoder base64Decoder; - static FIShortDecoder shortDecoder; - static FIIntDecoder intDecoder; - static FILongDecoder longDecoder; - static FIBoolDecoder boolDecoder; - static FIFloatDecoder floatDecoder; - static FIDoubleDecoder doubleDecoder; - static FIUUIDDecoder uuidDecoder; - static FICDATADecoder cdataDecoder; - static FIDecoder *defaultDecoder[32]; -}; - -const std::string CFIReaderImpl::EmptyString; -std::shared_ptr CFIReaderImpl::EmptyFIString = FIStringValue::create(std::string()); - -FIHexDecoder CFIReaderImpl::hexDecoder; -FIBase64Decoder CFIReaderImpl::base64Decoder; -FIShortDecoder CFIReaderImpl::shortDecoder; -FIIntDecoder CFIReaderImpl::intDecoder; -FILongDecoder CFIReaderImpl::longDecoder; -FIBoolDecoder CFIReaderImpl::boolDecoder; -FIFloatDecoder CFIReaderImpl::floatDecoder; -FIDoubleDecoder CFIReaderImpl::doubleDecoder; -FIUUIDDecoder CFIReaderImpl::uuidDecoder; -FICDATADecoder CFIReaderImpl::cdataDecoder; - -FIDecoder *CFIReaderImpl::defaultDecoder[32] = { - &hexDecoder, - &base64Decoder, - &shortDecoder, - &intDecoder, - &longDecoder, - &boolDecoder, - &floatDecoder, - &doubleDecoder, - &uuidDecoder, - &cdataDecoder -}; - -class CXMLReaderImpl : public FIReader -{ -public: - - //! Constructor - CXMLReaderImpl(std::unique_ptr> reader_) - : reader(std::move(reader_)) - {} - - virtual ~CXMLReaderImpl() {} - - virtual bool read() /*override*/ { - return reader->read(); - } - - virtual irr::io::EXML_NODE getNodeType() const /*override*/ { - return reader->getNodeType(); - } - - virtual int getAttributeCount() const /*override*/ { - return reader->getAttributeCount(); - } - - virtual const char* getAttributeName(int idx) const /*override*/ { - return reader->getAttributeName(idx); - } - - virtual const char* getAttributeValue(int idx) const /*override*/ { - return reader->getAttributeValue(idx); - } - - virtual const char* getAttributeValue(const char* name) const /*override*/ { - return reader->getAttributeValue(name); - } - - virtual const char* getAttributeValueSafe(const char* name) const /*override*/ { - return reader->getAttributeValueSafe(name); - } - - virtual int getAttributeValueAsInt(const char* name) const /*override*/ { - return reader->getAttributeValueAsInt(name); - } - - virtual int getAttributeValueAsInt(int idx) const /*override*/ { - return reader->getAttributeValueAsInt(idx); - } - - virtual float getAttributeValueAsFloat(const char* name) const /*override*/ { - return reader->getAttributeValueAsFloat(name); - } - - virtual float getAttributeValueAsFloat(int idx) const /*override*/ { - return reader->getAttributeValueAsFloat(idx); - } - - virtual const char* getNodeName() const /*override*/ { - return reader->getNodeName(); - } - - virtual const char* getNodeData() const /*override*/ { - return reader->getNodeData(); - } - - virtual bool isEmptyElement() const /*override*/ { - return reader->isEmptyElement(); - } - - virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ { - return reader->getSourceFormat(); - } - - virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ { - return reader->getParserFormat(); - } - - virtual std::shared_ptr getAttributeEncodedValue(int /*idx*/) const /*override*/ { - return nullptr; - } - - virtual std::shared_ptr getAttributeEncodedValue(const char* /*name*/) const /*override*/ { - return nullptr; - } - - virtual void registerDecoder(const std::string & /*algorithmUri*/, std::unique_ptr /*decoder*/) /*override*/ {} - - - virtual void registerVocabulary(const std::string &/*vocabularyUri*/, const FIVocabulary * /*vocabulary*/) /*override*/ {} - -private: - - std::unique_ptr> reader; -}; - -static std::unique_ptr readFile(IOStream *stream, size_t &size, bool &isFI) { - size = stream->FileSize(); - std::unique_ptr data = std::unique_ptr(new uint8_t[size]); - if (stream->Read(data.get(), size, 1) != 1) { - size = 0; - data.reset(); - } - isFI = parseMagic(data.get(), data.get() + size) > 0; - return data; -} - -std::unique_ptr FIReader::create(IOStream *stream) -{ - size_t size; - bool isFI; - auto data = readFile(stream, size, isFI); - if (isFI) { - return std::unique_ptr(new CFIReaderImpl(std::move(data), size)); - } - else { - auto memios = std::unique_ptr(new MemoryIOStream(data.release(), size, true)); - auto callback = std::unique_ptr(new CIrrXML_IOStreamReader(memios.get())); - return std::unique_ptr(new CXMLReaderImpl(std::unique_ptr>(createIrrXMLReader(callback.get())))); - } -} - -}// namespace Assimp - -#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/thirdparty/assimp/code/FileLogStream.h b/thirdparty/assimp/code/FileLogStream.h deleted file mode 100644 index 740c503192..0000000000 --- a/thirdparty/assimp/code/FileLogStream.h +++ /dev/null @@ -1,107 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -/** @file FileLofStream.h -*/ -#ifndef ASSIMP_FILELOGSTREAM_H_INC -#define ASSIMP_FILELOGSTREAM_H_INC - -#include -#include -#include - -namespace Assimp { - -// ---------------------------------------------------------------------------------- -/** @class FileLogStream - * @brief Logstream to write into a file. - */ -class FileLogStream : - public LogStream -{ -public: - FileLogStream( const char* file, IOSystem* io = NULL ); - ~FileLogStream(); - void write( const char* message ); - -private: - IOStream *m_pStream; -}; - -// ---------------------------------------------------------------------------------- -// Constructor -inline FileLogStream::FileLogStream( const char* file, IOSystem* io ) : - m_pStream(NULL) -{ - if ( !file || 0 == *file ) - return; - - // If no IOSystem is specified: take a default one - if (!io) - { - DefaultIOSystem FileSystem; - m_pStream = FileSystem.Open( file, "wt"); - } - else m_pStream = io->Open( file, "wt" ); -} - -// ---------------------------------------------------------------------------------- -// Destructor -inline FileLogStream::~FileLogStream() -{ - // The virtual d'tor should destroy the underlying file - delete m_pStream; -} - -// ---------------------------------------------------------------------------------- -// Write method -inline void FileLogStream::write( const char* message ) -{ - if (m_pStream != NULL) - { - m_pStream->Write(message, sizeof(char), ::strlen(message)); - m_pStream->Flush(); - } -} - -// ---------------------------------------------------------------------------------- -} // !Namespace Assimp - -#endif // !! ASSIMP_FILELOGSTREAM_H_INC diff --git a/thirdparty/assimp/code/FileSystemFilter.h b/thirdparty/assimp/code/FileSystemFilter.h deleted file mode 100644 index 9923cdbdd3..0000000000 --- a/thirdparty/assimp/code/FileSystemFilter.h +++ /dev/null @@ -1,345 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2008, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FileSystemFilter.h - * Implements a filter system to filter calls to Exists() and Open() - * in order to improve the success rate of file opening ... - */ -#pragma once -#ifndef AI_FILESYSTEMFILTER_H_INC -#define AI_FILESYSTEMFILTER_H_INC - -#include -#include -#include -#include - -namespace Assimp { - -inline bool IsHex(char s) { - return (s>='0' && s<='9') || (s>='a' && s<='f') || (s>='A' && s<='F'); -} - -// --------------------------------------------------------------------------- -/** File system filter - */ -class FileSystemFilter : public IOSystem -{ -public: - /** Constructor. */ - FileSystemFilter(const std::string& file, IOSystem* old) - : mWrapped (old) - , mSrc_file(file) - , mSep(mWrapped->getOsSeparator()) { - ai_assert(nullptr != mWrapped); - - // Determine base directory - mBase = mSrc_file; - std::string::size_type ss2; - if (std::string::npos != (ss2 = mBase.find_last_of("\\/"))) { - mBase.erase(ss2,mBase.length()-ss2); - } else { - mBase = ""; - } - - // make sure the directory is terminated properly - char s; - - if ( mBase.empty() ) { - mBase = "."; - mBase += getOsSeparator(); - } else if ((s = *(mBase.end()-1)) != '\\' && s != '/') { - mBase += getOsSeparator(); - } - - DefaultLogger::get()->info("Import root directory is \'" + mBase + "\'"); - } - - /** Destructor. */ - ~FileSystemFilter() { - // empty - } - - // ------------------------------------------------------------------- - /** Tests for the existence of a file at the given path. */ - bool Exists( const char* pFile) const { - ai_assert( nullptr != mWrapped ); - - std::string tmp = pFile; - - // Currently this IOSystem is also used to open THE ONE FILE. - if (tmp != mSrc_file) { - BuildPath(tmp); - Cleanup(tmp); - } - - return mWrapped->Exists(tmp); - } - - // ------------------------------------------------------------------- - /** Returns the directory separator. */ - char getOsSeparator() const { - return mSep; - } - - // ------------------------------------------------------------------- - /** Open a new file with a given path. */ - IOStream* Open( const char* pFile, const char* pMode = "rb") { - ai_assert( nullptr != mWrapped ); - if ( nullptr == pFile || nullptr == pMode ) { - return nullptr; - } - - ai_assert( nullptr != pFile ); - ai_assert( nullptr != pMode ); - - // First try the unchanged path - IOStream* s = mWrapped->Open(pFile,pMode); - - if (nullptr == s) { - std::string tmp = pFile; - - // Try to convert between absolute and relative paths - BuildPath(tmp); - s = mWrapped->Open(tmp,pMode); - - if (nullptr == s) { - // Finally, look for typical issues with paths - // and try to correct them. This is our last - // resort. - tmp = pFile; - Cleanup(tmp); - BuildPath(tmp); - s = mWrapped->Open(tmp,pMode); - } - } - - return s; - } - - // ------------------------------------------------------------------- - /** Closes the given file and releases all resources associated with it. */ - void Close( IOStream* pFile) { - ai_assert( nullptr != mWrapped ); - return mWrapped->Close(pFile); - } - - // ------------------------------------------------------------------- - /** Compare two paths */ - bool ComparePaths (const char* one, const char* second) const { - ai_assert( nullptr != mWrapped ); - return mWrapped->ComparePaths (one,second); - } - - // ------------------------------------------------------------------- - /** Pushes a new directory onto the directory stack. */ - bool PushDirectory(const std::string &path ) { - ai_assert( nullptr != mWrapped ); - return mWrapped->PushDirectory(path); - } - - // ------------------------------------------------------------------- - /** Returns the top directory from the stack. */ - const std::string &CurrentDirectory() const { - ai_assert( nullptr != mWrapped ); - return mWrapped->CurrentDirectory(); - } - - // ------------------------------------------------------------------- - /** Returns the number of directories stored on the stack. */ - size_t StackSize() const { - ai_assert( nullptr != mWrapped ); - return mWrapped->StackSize(); - } - - // ------------------------------------------------------------------- - /** Pops the top directory from the stack. */ - bool PopDirectory() { - ai_assert( nullptr != mWrapped ); - return mWrapped->PopDirectory(); - } - - // ------------------------------------------------------------------- - /** Creates an new directory at the given path. */ - bool CreateDirectory(const std::string &path) { - ai_assert( nullptr != mWrapped ); - return mWrapped->CreateDirectory(path); - } - - // ------------------------------------------------------------------- - /** Will change the current directory to the given path. */ - bool ChangeDirectory(const std::string &path) { - ai_assert( nullptr != mWrapped ); - return mWrapped->ChangeDirectory(path); - } - - // ------------------------------------------------------------------- - /** Delete file. */ - bool DeleteFile(const std::string &file) { - ai_assert( nullptr != mWrapped ); - return mWrapped->DeleteFile(file); - } - -private: - // ------------------------------------------------------------------- - /** Build a valid path from a given relative or absolute path. - */ - void BuildPath (std::string& in) const { - ai_assert( nullptr != mWrapped ); - // if we can already access the file, great. - if (in.length() < 3 || mWrapped->Exists(in)) { - return; - } - - // Determine whether this is a relative path (Windows-specific - most assets are packaged on Windows). - if (in[1] != ':') { - - // append base path and try - const std::string tmp = mBase + in; - if (mWrapped->Exists(tmp)) { - in = tmp; - return; - } - } - - // Chop of the file name and look in the model directory, if - // this fails try all sub paths of the given path, i.e. - // if the given path is foo/bar/something.lwo, try - // /something.lwo - // /bar/something.lwo - // /foo/bar/something.lwo - std::string::size_type pos = in.rfind('/'); - if (std::string::npos == pos) { - pos = in.rfind('\\'); - } - - if (std::string::npos != pos) { - std::string tmp; - std::string::size_type last_dirsep = std::string::npos; - - while(true) { - tmp = mBase; - tmp += mSep; - - std::string::size_type dirsep = in.rfind('/', last_dirsep); - if (std::string::npos == dirsep) { - dirsep = in.rfind('\\', last_dirsep); - } - - if (std::string::npos == dirsep || dirsep == 0) { - // we did try this already. - break; - } - - last_dirsep = dirsep-1; - - tmp += in.substr(dirsep+1, in.length()-pos); - if (mWrapped->Exists(tmp)) { - in = tmp; - return; - } - } - } - - // hopefully the underlying file system has another few tricks to access this file ... - } - - // ------------------------------------------------------------------- - /** Cleanup the given path - */ - void Cleanup (std::string& in) const { - if(in.empty()) { - return; - } - - // Remove a very common issue when we're parsing file names: spaces at the - // beginning of the path. - char last = 0; - std::string::iterator it = in.begin(); - while (IsSpaceOrNewLine( *it ))++it; - if (it != in.begin()) { - in.erase(in.begin(),it+1); - } - - const char separator = getOsSeparator(); - for (it = in.begin(); it != in.end(); ++it) { - // Exclude :// and \\, which remain untouched. - // https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632 - if ( !strncmp(&*it, "://", 3 )) { - it += 3; - continue; - } - if (it == in.begin() && !strncmp(&*it, "\\\\", 2)) { - it += 2; - continue; - } - - // Cleanup path delimiters - if (*it == '/' || (*it) == '\\') { - *it = separator; - - // And we're removing double delimiters, frequent issue with - // incorrectly composited paths ... - if (last == *it) { - it = in.erase(it); - --it; - } - } else if (*it == '%' && in.end() - it > 2) { - // Hex sequence in URIs - if( IsHex((&*it)[0]) && IsHex((&*it)[1]) ) { - *it = HexOctetToDecimal(&*it); - it = in.erase(it+1,it+2); - --it; - } - } - - last = *it; - } - } - -private: - IOSystem *mWrapped; - std::string mSrc_file, mBase; - char mSep; -}; - -} //!ns Assimp - -#endif //AI_DEFAULTIOSYSTEM_H_INC diff --git a/thirdparty/assimp/code/FindDegenerates.cpp b/thirdparty/assimp/code/FindDegenerates.cpp deleted file mode 100644 index 365f5d7447..0000000000 --- a/thirdparty/assimp/code/FindDegenerates.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file FindDegenerates.cpp - * @brief Implementation of the FindDegenerates post-process step. -*/ - - - -// internal headers -#include "ProcessHelper.h" -#include "FindDegenerates.h" -#include - -using namespace Assimp; - -//remove mesh at position 'index' from the scene -static void removeMesh(aiScene* pScene, unsigned const index); -//correct node indices to meshes and remove references to deleted mesh -static void updateSceneGraph(aiNode* pNode, unsigned const index); - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -FindDegeneratesProcess::FindDegeneratesProcess() -: mConfigRemoveDegenerates( false ) -, mConfigCheckAreaOfTriangle( false ){ - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -FindDegeneratesProcess::~FindDegeneratesProcess() { - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const { - return 0 != (pFlags & aiProcess_FindDegenerates); -} - -// ------------------------------------------------------------------------------------------------ -// Setup import configuration -void FindDegeneratesProcess::SetupProperties(const Importer* pImp) { - // Get the current value of AI_CONFIG_PP_FD_REMOVE - mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0)); - mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) ); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void FindDegeneratesProcess::Execute( aiScene* pScene) { - ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin"); - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - { - //Do not process point cloud, ExecuteOnMesh works only with faces data - if ((pScene->mMeshes[i]->mPrimitiveTypes != aiPrimitiveType::aiPrimitiveType_POINT) && ExecuteOnMesh(pScene->mMeshes[i])) { - removeMesh(pScene, i); - --i; //the current i is removed, do not skip the next one - } - } - ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished"); -} - -static void removeMesh(aiScene* pScene, unsigned const index) { - //we start at index and copy the pointers one position forward - //save the mesh pointer to delete it later - auto delete_me = pScene->mMeshes[index]; - for (unsigned i = index; i < pScene->mNumMeshes - 1; ++i) { - pScene->mMeshes[i] = pScene->mMeshes[i+1]; - } - pScene->mMeshes[pScene->mNumMeshes - 1] = nullptr; - --(pScene->mNumMeshes); - delete delete_me; - - //removing a mesh also requires updating all references to it in the scene graph - updateSceneGraph(pScene->mRootNode, index); -} - -static void updateSceneGraph(aiNode* pNode, unsigned const index) { - for (unsigned i = 0; i < pNode->mNumMeshes; ++i) { - if (pNode->mMeshes[i] > index) { - --(pNode->mMeshes[i]); - continue; - } - if (pNode->mMeshes[i] == index) { - for (unsigned j = i; j < pNode->mNumMeshes -1; ++j) { - pNode->mMeshes[j] = pNode->mMeshes[j+1]; - } - --(pNode->mNumMeshes); - --i; - continue; - } - } - //recurse to all children - for (unsigned i = 0; i < pNode->mNumChildren; ++i) { - updateSceneGraph(pNode->mChildren[i], index); - } -} - -static ai_real heron( ai_real a, ai_real b, ai_real c ) { - ai_real s = (a + b + c) / 2; - ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 ); - return area; -} - -static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) { - const ai_real lx = ( vB.x - vA.x ); - const ai_real ly = ( vB.y - vA.y ); - const ai_real lz = ( vB.z - vA.z ); - ai_real a = lx*lx + ly*ly + lz*lz; - ai_real d = pow( a, (ai_real)0.5 ); - - return d; -} - -static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) { - ai_real area = 0; - - aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] ); - aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] ); - aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] ); - - ai_real a( distance3D( vA, vB ) ); - ai_real b( distance3D( vB, vC ) ); - ai_real c( distance3D( vC, vA ) ); - area = heron( a, b, c ); - - return area; -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported mesh -bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) { - mesh->mPrimitiveTypes = 0; - - std::vector remove_me; - if (mConfigRemoveDegenerates) { - remove_me.resize( mesh->mNumFaces, false ); - } - - unsigned int deg = 0, limit; - for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) { - aiFace& face = mesh->mFaces[a]; - bool first = true; - - // check whether the face contains degenerated entries - for (unsigned int i = 0; i < face.mNumIndices; ++i) { - // Polygons with more than 4 points are allowed to have double points, that is - // simulating polygons with holes just with concave polygons. However, - // double points may not come directly after another. - limit = face.mNumIndices; - if (face.mNumIndices > 4) { - limit = std::min( limit, i+2 ); - } - - for (unsigned int t = i+1; t < limit; ++t) { - if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) { - // we have found a matching vertex position - // remove the corresponding index from the array - --face.mNumIndices; - --limit; - for (unsigned int m = t; m < face.mNumIndices; ++m) { - face.mIndices[ m ] = face.mIndices[ m+1 ]; - } - --t; - - // NOTE: we set the removed vertex index to an unique value - // to make sure the developer gets notified when his - // application attempts to access this data. - face.mIndices[ face.mNumIndices ] = 0xdeadbeef; - - if(first) { - ++deg; - first = false; - } - - if ( mConfigRemoveDegenerates ) { - remove_me[ a ] = true; - goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby! - } - } - } - - if ( mConfigCheckAreaOfTriangle ) { - if ( face.mNumIndices == 3 ) { - ai_real area = calculateAreaOfTriangle( face, mesh ); - if ( area < 1e-6 ) { - if ( mConfigRemoveDegenerates ) { - remove_me[ a ] = true; - goto evil_jump_outside; - } - - // todo: check for index which is corrupt. - } - } - } - } - - // We need to update the primitive flags array of the mesh. - switch (face.mNumIndices) - { - case 1u: - mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; - break; - case 2u: - mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; - break; - case 3u: - mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; - break; - default: - mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; - break; - }; -evil_jump_outside: - continue; - } - - // If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import - if (mConfigRemoveDegenerates && deg) { - unsigned int n = 0; - for (unsigned int a = 0; a < mesh->mNumFaces; ++a) - { - aiFace& face_src = mesh->mFaces[a]; - if (!remove_me[a]) { - aiFace& face_dest = mesh->mFaces[n++]; - - // Do a manual copy, keep the index array - face_dest.mNumIndices = face_src.mNumIndices; - face_dest.mIndices = face_src.mIndices; - - if (&face_src != &face_dest) { - // clear source - face_src.mNumIndices = 0; - face_src.mIndices = nullptr; - } - } - else { - // Otherwise delete it if we don't need this face - delete[] face_src.mIndices; - face_src.mIndices = nullptr; - face_src.mNumIndices = 0; - } - } - // Just leave the rest of the array unreferenced, we don't care for now - mesh->mNumFaces = n; - if (!mesh->mNumFaces) { - //The whole mesh consists of degenerated faces - //signal upward, that this mesh should be deleted. - ASSIMP_LOG_DEBUG("FindDegeneratesProcess removed a mesh full of degenerated primitives"); - return true; - } - } - - if (deg && !DefaultLogger::isNullLogger()) { - ASSIMP_LOG_WARN_F( "Found ", deg, " degenerated primitives"); - } - return false; -} diff --git a/thirdparty/assimp/code/FindDegenerates.h b/thirdparty/assimp/code/FindDegenerates.h deleted file mode 100644 index 880f5f16a2..0000000000 --- a/thirdparty/assimp/code/FindDegenerates.h +++ /dev/null @@ -1,129 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a post processing step to search all meshes for - degenerated faces */ -#ifndef AI_FINDDEGENERATESPROCESS_H_INC -#define AI_FINDDEGENERATESPROCESS_H_INC - -#include "BaseProcess.h" -#include - -class FindDegeneratesProcessTest; -namespace Assimp { - - -// --------------------------------------------------------------------------- -/** FindDegeneratesProcess: Searches a mesh for degenerated triangles. -*/ -class ASSIMP_API FindDegeneratesProcess : public BaseProcess { -public: - FindDegeneratesProcess(); - ~FindDegeneratesProcess(); - - // ------------------------------------------------------------------- - // Check whether step is active - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - // Execute step on a given scene - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - // Setup import settings - void SetupProperties(const Importer* pImp); - - // ------------------------------------------------------------------- - // Execute step on a given mesh - ///@returns true if the current mesh should be deleted, false otherwise - bool ExecuteOnMesh( aiMesh* mesh); - - // ------------------------------------------------------------------- - /// @brief Enable the instant removal of degenerated primitives - /// @param enabled true for enabled. - void EnableInstantRemoval(bool enabled); - - // ------------------------------------------------------------------- - /// @brief Check whether instant removal is currently enabled - /// @return The instant removal state. - bool IsInstantRemoval() const; - - // ------------------------------------------------------------------- - /// @brief Enable the area check for triangles. - /// @param enabled true for enabled. - void EnableAreaCheck( bool enabled ); - - // ------------------------------------------------------------------- - /// @brief Check whether the area check is enabled. - /// @return The area check state. - bool isAreaCheckEnabled() const; - -private: - //! Configuration option: remove degenerates faces immediately - bool mConfigRemoveDegenerates; - //! Configuration option: check for area - bool mConfigCheckAreaOfTriangle; -}; - -inline -void FindDegeneratesProcess::EnableInstantRemoval(bool enabled) { - mConfigRemoveDegenerates = enabled; -} - -inline -bool FindDegeneratesProcess::IsInstantRemoval() const { - return mConfigRemoveDegenerates; -} - -inline -void FindDegeneratesProcess::EnableAreaCheck( bool enabled ) { - mConfigCheckAreaOfTriangle = enabled; -} - -inline -bool FindDegeneratesProcess::isAreaCheckEnabled() const { - return mConfigCheckAreaOfTriangle; -} - -} // Namespace Assimp - -#endif // !! AI_FINDDEGENERATESPROCESS_H_INC diff --git a/thirdparty/assimp/code/FindInstancesProcess.cpp b/thirdparty/assimp/code/FindInstancesProcess.cpp deleted file mode 100644 index be1138116e..0000000000 --- a/thirdparty/assimp/code/FindInstancesProcess.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file FindInstancesProcess.cpp - * @brief Implementation of the aiProcess_FindInstances postprocessing step -*/ - - -#include "FindInstancesProcess.h" -#include -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -FindInstancesProcess::FindInstancesProcess() -: configSpeedFlag (false) -{} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -FindInstancesProcess::~FindInstancesProcess() -{} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool FindInstancesProcess::IsActive( unsigned int pFlags) const -{ - // FindInstances makes absolutely no sense together with PreTransformVertices - // fixme: spawn error message somewhere else? - return 0 != (pFlags & aiProcess_FindInstances) && 0 == (pFlags & aiProcess_PreTransformVertices); -} - -// ------------------------------------------------------------------------------------------------ -// Setup properties for the step -void FindInstancesProcess::SetupProperties(const Importer* pImp) -{ - // AI_CONFIG_FAVOUR_SPEED - configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0)); -} - -// ------------------------------------------------------------------------------------------------ -// Compare the bones of two meshes -bool CompareBones(const aiMesh* orig, const aiMesh* inst) -{ - for (unsigned int i = 0; i < orig->mNumBones;++i) { - aiBone* aha = orig->mBones[i]; - aiBone* oha = inst->mBones[i]; - - if (aha->mNumWeights != oha->mNumWeights || - aha->mOffsetMatrix != oha->mOffsetMatrix) { - return false; - } - - // compare weight per weight --- - for (unsigned int n = 0; n < aha->mNumWeights;++n) { - if (aha->mWeights[n].mVertexId != oha->mWeights[n].mVertexId || - (aha->mWeights[n].mWeight - oha->mWeights[n].mWeight) < 10e-3f) { - return false; - } - } - } - return true; -} - -// ------------------------------------------------------------------------------------------------ -// Update mesh indices in the node graph -void UpdateMeshIndices(aiNode* node, unsigned int* lookup) -{ - for (unsigned int n = 0; n < node->mNumMeshes;++n) - node->mMeshes[n] = lookup[node->mMeshes[n]]; - - for (unsigned int n = 0; n < node->mNumChildren;++n) - UpdateMeshIndices(node->mChildren[n],lookup); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void FindInstancesProcess::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("FindInstancesProcess begin"); - if (pScene->mNumMeshes) { - - // use a pseudo hash for all meshes in the scene to quickly find - // the ones which are possibly equal. This step is executed early - // in the pipeline, so we could, depending on the file format, - // have several thousand small meshes. That's too much for a brute - // everyone-against-everyone check involving up to 10 comparisons - // each. - std::unique_ptr hashes (new uint64_t[pScene->mNumMeshes]); - std::unique_ptr remapping (new unsigned int[pScene->mNumMeshes]); - - unsigned int numMeshesOut = 0; - for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { - - aiMesh* inst = pScene->mMeshes[i]; - hashes[i] = GetMeshHash(inst); - - for (int a = i-1; a >= 0; --a) { - if (hashes[i] == hashes[a]) - { - aiMesh* orig = pScene->mMeshes[a]; - if (!orig) - continue; - - // check for hash collision .. we needn't check - // the vertex format, it *must* match due to the - // (brilliant) construction of the hash - if (orig->mNumBones != inst->mNumBones || - orig->mNumFaces != inst->mNumFaces || - orig->mNumVertices != inst->mNumVertices || - orig->mMaterialIndex != inst->mMaterialIndex || - orig->mPrimitiveTypes != inst->mPrimitiveTypes) - continue; - - // up to now the meshes are equal. find an appropriate - // epsilon to compare position differences against - float epsilon = ComputePositionEpsilon(inst); - epsilon *= epsilon; - - // now compare vertex positions, normals, - // tangents and bitangents using this epsilon. - if (orig->HasPositions()) { - if(!CompareArrays(orig->mVertices,inst->mVertices,orig->mNumVertices,epsilon)) - continue; - } - if (orig->HasNormals()) { - if(!CompareArrays(orig->mNormals,inst->mNormals,orig->mNumVertices,epsilon)) - continue; - } - if (orig->HasTangentsAndBitangents()) { - if (!CompareArrays(orig->mTangents,inst->mTangents,orig->mNumVertices,epsilon) || - !CompareArrays(orig->mBitangents,inst->mBitangents,orig->mNumVertices,epsilon)) - continue; - } - - // use a constant epsilon for colors and UV coordinates - static const float uvEpsilon = 10e-4f; - { - unsigned int j, end = orig->GetNumUVChannels(); - for(j = 0; j < end; ++j) { - if (!orig->mTextureCoords[j]) { - continue; - } - if(!CompareArrays(orig->mTextureCoords[j],inst->mTextureCoords[j],orig->mNumVertices,uvEpsilon)) { - break; - } - } - if (j != end) { - continue; - } - } - { - unsigned int j, end = orig->GetNumColorChannels(); - for(j = 0; j < end; ++j) { - if (!orig->mColors[j]) { - continue; - } - if(!CompareArrays(orig->mColors[j],inst->mColors[j],orig->mNumVertices,uvEpsilon)) { - break; - } - } - if (j != end) { - continue; - } - } - - // These two checks are actually quite expensive and almost *never* required. - // Almost. That's why they're still here. But there's no reason to do them - // in speed-targeted imports. - if (!configSpeedFlag) { - - // It seems to be strange, but we really need to check whether the - // bones are identical too. Although it's extremely unprobable - // that they're not if control reaches here, we need to deal - // with unprobable cases, too. It could still be that there are - // equal shapes which are deformed differently. - if (!CompareBones(orig,inst)) - continue; - - // For completeness ... compare even the index buffers for equality - // face order & winding order doesn't care. Input data is in verbose format. - std::unique_ptr ftbl_orig(new unsigned int[orig->mNumVertices]); - std::unique_ptr ftbl_inst(new unsigned int[orig->mNumVertices]); - - for (unsigned int tt = 0; tt < orig->mNumFaces;++tt) { - aiFace& f = orig->mFaces[tt]; - for (unsigned int nn = 0; nn < f.mNumIndices;++nn) - ftbl_orig[f.mIndices[nn]] = tt; - - aiFace& f2 = inst->mFaces[tt]; - for (unsigned int nn = 0; nn < f2.mNumIndices;++nn) - ftbl_inst[f2.mIndices[nn]] = tt; - } - if (0 != ::memcmp(ftbl_inst.get(),ftbl_orig.get(),orig->mNumVertices*sizeof(unsigned int))) - continue; - } - - // We're still here. Or in other words: 'inst' is an instance of 'orig'. - // Place a marker in our list that we can easily update mesh indices. - remapping[i] = remapping[a]; - - // Delete the instanced mesh, we don't need it anymore - delete inst; - pScene->mMeshes[i] = NULL; - break; - } - } - - // If we didn't find a match for the current mesh: keep it - if (pScene->mMeshes[i]) { - remapping[i] = numMeshesOut++; - } - } - ai_assert(0 != numMeshesOut); - if (numMeshesOut != pScene->mNumMeshes) { - - // Collapse the meshes array by removing all NULL entries - for (unsigned int real = 0, i = 0; real < numMeshesOut; ++i) { - if (pScene->mMeshes[i]) - pScene->mMeshes[real++] = pScene->mMeshes[i]; - } - - // And update the node graph with our nice lookup table - UpdateMeshIndices(pScene->mRootNode,remapping.get()); - - // write to log - if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_INFO_F( "FindInstancesProcess finished. Found ", (pScene->mNumMeshes - numMeshesOut), " instances" ); - } - pScene->mNumMeshes = numMeshesOut; - } else { - ASSIMP_LOG_DEBUG("FindInstancesProcess finished. No instanced meshes found"); - } - } -} diff --git a/thirdparty/assimp/code/FindInstancesProcess.h b/thirdparty/assimp/code/FindInstancesProcess.h deleted file mode 100644 index ab4a371c71..0000000000 --- a/thirdparty/assimp/code/FindInstancesProcess.h +++ /dev/null @@ -1,137 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FindInstancesProcess.h - * @brief Declares the aiProcess_FindInstances post-process step - */ -#ifndef AI_FINDINSTANCES_H_INC -#define AI_FINDINSTANCES_H_INC - -#include "BaseProcess.h" -#include "ProcessHelper.h" - -class FindInstancesProcessTest; -namespace Assimp { - -// ------------------------------------------------------------------------------- -/** @brief Get a pseudo(!)-hash representing a mesh. - * - * The hash is built from number of vertices, faces, primitive types, - * .... but *not* from the real mesh data. The funcction is not a perfect hash. - * @param in Input mesh - * @return Hash. - */ -inline -uint64_t GetMeshHash(aiMesh* in) { - ai_assert(nullptr != in); - - // ... get an unique value representing the vertex format of the mesh - const unsigned int fhash = GetMeshVFormatUnique(in); - - // and bake it with number of vertices/faces/bones/matidx/ptypes - return ((uint64_t)fhash << 32u) | (( - (in->mNumBones << 16u) ^ (in->mNumVertices) ^ - (in->mNumFaces<<4u) ^ (in->mMaterialIndex<<15) ^ - (in->mPrimitiveTypes<<28)) & 0xffffffff ); -} - -// ------------------------------------------------------------------------------- -/** @brief Perform a component-wise comparison of two arrays - * - * @param first First array - * @param second Second array - * @param size Size of both arrays - * @param e Epsilon - * @return true if the arrays are identical - */ -inline -bool CompareArrays(const aiVector3D* first, const aiVector3D* second, - unsigned int size, float e) { - for (const aiVector3D* end = first+size; first != end; ++first,++second) { - if ( (*first - *second).SquareLength() >= e) - return false; - } - return true; -} - -// and the same for colors ... -inline bool CompareArrays(const aiColor4D* first, const aiColor4D* second, - unsigned int size, float e) -{ - for (const aiColor4D* end = first+size; first != end; ++first,++second) { - if ( GetColorDifference(*first,*second) >= e) - return false; - } - return true; -} - -// --------------------------------------------------------------------------- -/** @brief A post-processing steps to search for instanced meshes -*/ -class FindInstancesProcess : public BaseProcess -{ -public: - - FindInstancesProcess(); - ~FindInstancesProcess(); - -public: - // ------------------------------------------------------------------- - // Check whether step is active in given flags combination - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - // Execute step on a given scene - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - // Setup properties prior to executing the process - void SetupProperties(const Importer* pImp); - -private: - - bool configSpeedFlag; - -}; // ! end class FindInstancesProcess -} // ! end namespace Assimp - -#endif // !! AI_FINDINSTANCES_H_INC diff --git a/thirdparty/assimp/code/FindInvalidDataProcess.cpp b/thirdparty/assimp/code/FindInvalidDataProcess.cpp deleted file mode 100644 index 433f042448..0000000000 --- a/thirdparty/assimp/code/FindInvalidDataProcess.cpp +++ /dev/null @@ -1,424 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Defines a post processing step to search an importer's output - for data that is obviously invalid */ - - - -#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS - -// internal headers -#include "FindInvalidDataProcess.h" -#include "ProcessHelper.h" - -#include -#include -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -FindInvalidDataProcess::FindInvalidDataProcess() -: configEpsilon(0.0) -, mIgnoreTexCoods( false ){ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -FindInvalidDataProcess::~FindInvalidDataProcess() { - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool FindInvalidDataProcess::IsActive( unsigned int pFlags) const { - return 0 != (pFlags & aiProcess_FindInvalidData); -} - -// ------------------------------------------------------------------------------------------------ -// Setup import configuration -void FindInvalidDataProcess::SetupProperties(const Importer* pImp) { - // Get the current value of AI_CONFIG_PP_FID_ANIM_ACCURACY - configEpsilon = (0 != pImp->GetPropertyFloat(AI_CONFIG_PP_FID_ANIM_ACCURACY,0.f)); - mIgnoreTexCoods = pImp->GetPropertyBool(AI_CONFIG_PP_FID_IGNORE_TEXTURECOORDS, false); -} - -// ------------------------------------------------------------------------------------------------ -// Update mesh references in the node graph -void UpdateMeshReferences(aiNode* node, const std::vector& meshMapping) { - if (node->mNumMeshes) { - unsigned int out = 0; - for (unsigned int a = 0; a < node->mNumMeshes;++a) { - - unsigned int ref = node->mMeshes[a]; - if (UINT_MAX != (ref = meshMapping[ref])) { - node->mMeshes[out++] = ref; - } - } - // just let the members that are unused, that's much cheaper - // than a full array realloc'n'copy party ... - if(!(node->mNumMeshes = out)) { - - delete[] node->mMeshes; - node->mMeshes = NULL; - } - } - // recursively update all children - for (unsigned int i = 0; i < node->mNumChildren;++i) { - UpdateMeshReferences(node->mChildren[i],meshMapping); - } -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void FindInvalidDataProcess::Execute( aiScene* pScene) { - ASSIMP_LOG_DEBUG("FindInvalidDataProcess begin"); - - bool out = false; - std::vector meshMapping(pScene->mNumMeshes); - unsigned int real = 0; - - // Process meshes - for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { - - int result; - if ((result = ProcessMesh( pScene->mMeshes[a]))) { - out = true; - - if (2 == result) { - // remove this mesh - delete pScene->mMeshes[a]; - AI_DEBUG_INVALIDATE_PTR(pScene->mMeshes[a]); - - meshMapping[a] = UINT_MAX; - continue; - } - } - pScene->mMeshes[real] = pScene->mMeshes[a]; - meshMapping[a] = real++; - } - - // Process animations - for (unsigned int a = 0; a < pScene->mNumAnimations;++a) { - ProcessAnimation( pScene->mAnimations[a]); - } - - - if (out) { - if ( real != pScene->mNumMeshes) { - if (!real) { - throw DeadlyImportError("No meshes remaining"); - } - - // we need to remove some meshes. - // therefore we'll also need to remove all references - // to them from the scenegraph - UpdateMeshReferences(pScene->mRootNode,meshMapping); - pScene->mNumMeshes = real; - } - - ASSIMP_LOG_INFO("FindInvalidDataProcess finished. Found issues ..."); - } else { - ASSIMP_LOG_DEBUG("FindInvalidDataProcess finished. Everything seems to be OK."); - } -} - -// ------------------------------------------------------------------------------------------------ -template -inline -const char* ValidateArrayContents(const T* /*arr*/, unsigned int /*size*/, - const std::vector& /*dirtyMask*/, bool /*mayBeIdentical = false*/, bool /*mayBeZero = true*/) -{ - return nullptr; -} - -// ------------------------------------------------------------------------------------------------ -template <> -inline -const char* ValidateArrayContents(const aiVector3D* arr, unsigned int size, - const std::vector& dirtyMask, bool mayBeIdentical , bool mayBeZero ) { - bool b = false; - unsigned int cnt = 0; - for (unsigned int i = 0; i < size;++i) { - - if (dirtyMask.size() && dirtyMask[i]) { - continue; - } - ++cnt; - - const aiVector3D& v = arr[i]; - if (is_special_float(v.x) || is_special_float(v.y) || is_special_float(v.z)) { - return "INF/NAN was found in a vector component"; - } - if (!mayBeZero && !v.x && !v.y && !v.z ) { - return "Found zero-length vector"; - } - if (i && v != arr[i-1])b = true; - } - if (cnt > 1 && !b && !mayBeIdentical) { - return "All vectors are identical"; - } - return nullptr; -} - -// ------------------------------------------------------------------------------------------------ -template -inline -bool ProcessArray(T*& in, unsigned int num,const char* name, - const std::vector& dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true) { - const char* err = ValidateArrayContents(in,num,dirtyMask,mayBeIdentical,mayBeZero); - if (err) { - ASSIMP_LOG_ERROR_F( "FindInvalidDataProcess fails on mesh ", name, ": ", err); - delete[] in; - in = NULL; - return true; - } - return false; -} - -// ------------------------------------------------------------------------------------------------ -template -AI_FORCE_INLINE bool EpsilonCompare(const T& n, const T& s, ai_real epsilon); - -// ------------------------------------------------------------------------------------------------ -AI_FORCE_INLINE bool EpsilonCompare(ai_real n, ai_real s, ai_real epsilon) { - return std::fabs(n-s)>epsilon; -} - -// ------------------------------------------------------------------------------------------------ -template <> -bool EpsilonCompare(const aiVectorKey& n, const aiVectorKey& s, ai_real epsilon) { - return - EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) && - EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) && - EpsilonCompare(n.mValue.z,s.mValue.z,epsilon); -} - -// ------------------------------------------------------------------------------------------------ -template <> -bool EpsilonCompare(const aiQuatKey& n, const aiQuatKey& s, ai_real epsilon) { - return - EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) && - EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) && - EpsilonCompare(n.mValue.z,s.mValue.z,epsilon) && - EpsilonCompare(n.mValue.w,s.mValue.w,epsilon); -} - -// ------------------------------------------------------------------------------------------------ -template -inline -bool AllIdentical(T* in, unsigned int num, ai_real epsilon) { - if (num <= 1) { - return true; - } - - if (fabs(epsilon) > 0.f) { - for (unsigned int i = 0; i < num-1;++i) { - if (!EpsilonCompare(in[i],in[i+1],epsilon)) { - return false; - } - } - } else { - for (unsigned int i = 0; i < num-1;++i) { - if (in[i] != in[i+1]) { - return false; - } - } - } - return true; -} - -// ------------------------------------------------------------------------------------------------ -// Search an animation for invalid content -void FindInvalidDataProcess::ProcessAnimation (aiAnimation* anim) { - // Process all animation channels - for ( unsigned int a = 0; a < anim->mNumChannels; ++a ) { - ProcessAnimationChannel( anim->mChannels[a]); - } -} - -// ------------------------------------------------------------------------------------------------ -void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) { - ai_assert( nullptr != anim ); - if (anim->mNumPositionKeys == 0 && anim->mNumRotationKeys == 0 && anim->mNumScalingKeys == 0) { - ai_assert_entry(); - return; - } - - // Check whether all values in a tracks are identical - in this case - // we can remove al keys except one. - // POSITIONS - int i = 0; - if (anim->mNumPositionKeys > 1 && AllIdentical(anim->mPositionKeys,anim->mNumPositionKeys,configEpsilon)) { - aiVectorKey v = anim->mPositionKeys[0]; - - // Reallocate ... we need just ONE element, it makes no sense to reuse the array - delete[] anim->mPositionKeys; - anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys = 1]; - anim->mPositionKeys[0] = v; - i = 1; - } - - // ROTATIONS - if (anim->mNumRotationKeys > 1 && AllIdentical(anim->mRotationKeys,anim->mNumRotationKeys,configEpsilon)) { - aiQuatKey v = anim->mRotationKeys[0]; - - // Reallocate ... we need just ONE element, it makes no sense to reuse the array - delete[] anim->mRotationKeys; - anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys = 1]; - anim->mRotationKeys[0] = v; - i = 1; - } - - // SCALINGS - if (anim->mNumScalingKeys > 1 && AllIdentical(anim->mScalingKeys,anim->mNumScalingKeys,configEpsilon)) { - aiVectorKey v = anim->mScalingKeys[0]; - - // Reallocate ... we need just ONE element, it makes no sense to reuse the array - delete[] anim->mScalingKeys; - anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys = 1]; - anim->mScalingKeys[0] = v; - i = 1; - } - if ( 1 == i ) { - ASSIMP_LOG_WARN("Simplified dummy tracks with just one key"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Search a mesh for invalid contents -int FindInvalidDataProcess::ProcessMesh(aiMesh* pMesh) -{ - bool ret = false; - std::vector dirtyMask(pMesh->mNumVertices, pMesh->mNumFaces != 0); - - // Ignore elements that are not referenced by vertices. - // (they are, for example, caused by the FindDegenerates step) - for (unsigned int m = 0; m < pMesh->mNumFaces; ++m) { - const aiFace& f = pMesh->mFaces[m]; - - for (unsigned int i = 0; i < f.mNumIndices; ++i) { - dirtyMask[f.mIndices[i]] = false; - } - } - - // Process vertex positions - if (pMesh->mVertices && ProcessArray(pMesh->mVertices, pMesh->mNumVertices, "positions", dirtyMask)) { - ASSIMP_LOG_ERROR("Deleting mesh: Unable to continue without vertex positions"); - - return 2; - } - - // process texture coordinates - if (!mIgnoreTexCoods) { - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS && pMesh->mTextureCoords[i]; ++i) { - if (ProcessArray(pMesh->mTextureCoords[i], pMesh->mNumVertices, "uvcoords", dirtyMask)) { - pMesh->mNumUVComponents[i] = 0; - - // delete all subsequent texture coordinate sets. - for (unsigned int a = i + 1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { - delete[] pMesh->mTextureCoords[a]; - pMesh->mTextureCoords[a] = NULL; - pMesh->mNumUVComponents[a] = 0; - } - - ret = true; - } - } - } - - // -- we don't validate vertex colors, it's difficult to say whether - // they are invalid or not. - - // Normals and tangents are undefined for point and line faces. - if (pMesh->mNormals || pMesh->mTangents) { - - if (aiPrimitiveType_POINT & pMesh->mPrimitiveTypes || - aiPrimitiveType_LINE & pMesh->mPrimitiveTypes) - { - if (aiPrimitiveType_TRIANGLE & pMesh->mPrimitiveTypes || - aiPrimitiveType_POLYGON & pMesh->mPrimitiveTypes) - { - // We need to update the lookup-table - for (unsigned int m = 0; m < pMesh->mNumFaces;++m) { - const aiFace& f = pMesh->mFaces[ m ]; - - if (f.mNumIndices < 3) { - dirtyMask[f.mIndices[0]] = true; - if (f.mNumIndices == 2) { - dirtyMask[f.mIndices[1]] = true; - } - } - } - } - // Normals, tangents and bitangents are undefined for - // the whole mesh (and should not even be there) - else { - return ret; - } - } - - // Process mesh normals - if (pMesh->mNormals && ProcessArray(pMesh->mNormals,pMesh->mNumVertices, - "normals",dirtyMask,true,false)) - ret = true; - - // Process mesh tangents - if (pMesh->mTangents && ProcessArray(pMesh->mTangents,pMesh->mNumVertices,"tangents",dirtyMask)) { - delete[] pMesh->mBitangents; pMesh->mBitangents = NULL; - ret = true; - } - - // Process mesh bitangents - if (pMesh->mBitangents && ProcessArray(pMesh->mBitangents,pMesh->mNumVertices,"bitangents",dirtyMask)) { - delete[] pMesh->mTangents; pMesh->mTangents = NULL; - ret = true; - } - } - return ret ? 1 : 0; -} - -#endif // !! ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS diff --git a/thirdparty/assimp/code/FindInvalidDataProcess.h b/thirdparty/assimp/code/FindInvalidDataProcess.h deleted file mode 100644 index 8504fb7b1f..0000000000 --- a/thirdparty/assimp/code/FindInvalidDataProcess.h +++ /dev/null @@ -1,105 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file 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 - -struct aiMesh; - -class FindInvalidDataProcessTest; - -namespace Assimp { - -// --------------------------------------------------------------------------- -/** The FindInvalidData post-processing step. It searches the mesh data - * for parts that are obviously invalid and removes them. - * - * Originally this was a workaround for some models written by Blender - * which have zero normal vectors. */ -class ASSIMP_API FindInvalidDataProcess : public BaseProcess { -public: - FindInvalidDataProcess(); - ~FindInvalidDataProcess(); - - // ------------------------------------------------------------------- - // - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - // Setup import settings - void SetupProperties(const Importer* pImp); - - // ------------------------------------------------------------------- - // Run the step - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - /** Executes the post-processing step on the given mesh - * @param pMesh The mesh to process. - * @return 0 - nothing, 1 - removed sth, 2 - please delete me */ - int ProcessMesh( aiMesh* pMesh); - - // ------------------------------------------------------------------- - /** Executes the post-processing step on the given animation - * @param anim The animation to process. */ - void ProcessAnimation (aiAnimation* anim); - - // ------------------------------------------------------------------- - /** Executes the post-processing step on the given anim channel - * @param anim The animation channel to process.*/ - void ProcessAnimationChannel (aiNodeAnim* anim); - -private: - ai_real configEpsilon; - bool mIgnoreTexCoods; -}; - -} // end of namespace Assimp - -#endif // AI_AI_FINDINVALIDDATA_H_INC diff --git a/thirdparty/assimp/code/FixNormalsStep.cpp b/thirdparty/assimp/code/FixNormalsStep.cpp deleted file mode 100644 index bbbe6899b4..0000000000 --- a/thirdparty/assimp/code/FixNormalsStep.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of the post processing step to invert - * all normals in meshes with infacing normals. - */ - -// internal headers -#include "FixNormalsStep.h" -#include -#include -#include -#include -#include - - -using namespace Assimp; - - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -FixInfacingNormalsProcess::FixInfacingNormalsProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -FixInfacingNormalsProcess::~FixInfacingNormalsProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool FixInfacingNormalsProcess::IsActive( unsigned int pFlags) const -{ - return (pFlags & aiProcess_FixInfacingNormals) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void FixInfacingNormalsProcess::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess begin"); - - bool bHas( false ); - for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { - if (ProcessMesh(pScene->mMeshes[a], a)) { - bHas = true; - } - } - - if (bHas) { - ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess finished. Found issues."); - } else { - ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess finished. No changes to the scene."); - } -} - -// ------------------------------------------------------------------------------------------------ -// Apply the step to the mesh -bool FixInfacingNormalsProcess::ProcessMesh( aiMesh* pcMesh, unsigned int index) -{ - ai_assert(nullptr != pcMesh); - - // Nothing to do if there are no model normals - if (!pcMesh->HasNormals()) { - return false; - } - - // Compute the bounding box of both the model vertices + normals and - // the unmodified model vertices. Then check whether the first BB - // is smaller than the second. In this case we can assume that the - // normals need to be flipped, although there are a few special cases .. - // convex, concave, planar models ... - - aiVector3D vMin0 (1e10f,1e10f,1e10f); - aiVector3D vMin1 (1e10f,1e10f,1e10f); - aiVector3D vMax0 (-1e10f,-1e10f,-1e10f); - aiVector3D vMax1 (-1e10f,-1e10f,-1e10f); - - for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) - { - vMin1.x = std::min(vMin1.x,pcMesh->mVertices[i].x); - vMin1.y = std::min(vMin1.y,pcMesh->mVertices[i].y); - vMin1.z = std::min(vMin1.z,pcMesh->mVertices[i].z); - - vMax1.x = std::max(vMax1.x,pcMesh->mVertices[i].x); - vMax1.y = std::max(vMax1.y,pcMesh->mVertices[i].y); - vMax1.z = std::max(vMax1.z,pcMesh->mVertices[i].z); - - const aiVector3D vWithNormal = pcMesh->mVertices[i] + pcMesh->mNormals[i]; - - vMin0.x = std::min(vMin0.x,vWithNormal.x); - vMin0.y = std::min(vMin0.y,vWithNormal.y); - vMin0.z = std::min(vMin0.z,vWithNormal.z); - - vMax0.x = std::max(vMax0.x,vWithNormal.x); - vMax0.y = std::max(vMax0.y,vWithNormal.y); - vMax0.z = std::max(vMax0.z,vWithNormal.z); - } - - const float fDelta0_x = (vMax0.x - vMin0.x); - const float fDelta0_y = (vMax0.y - vMin0.y); - const float fDelta0_z = (vMax0.z - vMin0.z); - - const float fDelta1_x = (vMax1.x - vMin1.x); - const float fDelta1_y = (vMax1.y - vMin1.y); - const float fDelta1_z = (vMax1.z - vMin1.z); - - // Check whether the boxes are overlapping - if ((fDelta0_x > 0.0f) != (fDelta1_x > 0.0f))return false; - if ((fDelta0_y > 0.0f) != (fDelta1_y > 0.0f))return false; - if ((fDelta0_z > 0.0f) != (fDelta1_z > 0.0f))return false; - - // Check whether this is a planar surface - const float fDelta1_yz = fDelta1_y * fDelta1_z; - - if (fDelta1_x < 0.05f * std::sqrt( fDelta1_yz ))return false; - if (fDelta1_y < 0.05f * std::sqrt( fDelta1_z * fDelta1_x ))return false; - if (fDelta1_z < 0.05f * std::sqrt( fDelta1_y * fDelta1_x ))return false; - - // now compare the volumes of the bounding boxes - if (std::fabs(fDelta0_x * fDelta0_y * fDelta0_z) < std::fabs(fDelta1_x * fDelta1_yz)) { - if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_INFO_F("Mesh ", index, ": Normals are facing inwards (or the mesh is planar)", index); - } - - // Invert normals - for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) - pcMesh->mNormals[i] *= -1.0f; - - // ... and flip faces - for (unsigned int i = 0; i < pcMesh->mNumFaces;++i) - { - aiFace& face = pcMesh->mFaces[i]; - for( unsigned int b = 0; b < face.mNumIndices / 2; b++) - std::swap( face.mIndices[b], face.mIndices[ face.mNumIndices - 1 - b]); - } - return true; - } - return false; -} diff --git a/thirdparty/assimp/code/FixNormalsStep.h b/thirdparty/assimp/code/FixNormalsStep.h deleted file mode 100644 index 6be27faef6..0000000000 --- a/thirdparty/assimp/code/FixNormalsStep.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - - -/** @file Defines a post processing step to fix infacing normals */ -#ifndef AI_FIXNORMALSPROCESS_H_INC -#define AI_FIXNORMALSPROCESS_H_INC - -#include "BaseProcess.h" - -struct aiMesh; - -namespace Assimp -{ - -// --------------------------------------------------------------------------- -/** The FixInfacingNormalsProcess tries to determine whether the normal - * vectors of an object are facing inwards. In this case they will be - * flipped. - */ -class FixInfacingNormalsProcess : public BaseProcess { -public: - FixInfacingNormalsProcess(); - ~FixInfacingNormalsProcess(); - - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag field. - * @param pFlags The processing flags the importer was called with. A bitwise - * combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, false if not. - */ - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - -protected: - - // ------------------------------------------------------------------- - /** Executes the step on the given mesh - * @param pMesh The mesh to process. - */ - bool ProcessMesh( aiMesh* pMesh, unsigned int index); -}; - -} // end of namespace Assimp - -#endif // AI_FIXNORMALSPROCESS_H_INC diff --git a/thirdparty/assimp/code/GenFaceNormalsProcess.cpp b/thirdparty/assimp/code/GenFaceNormalsProcess.cpp deleted file mode 100644 index 028334dec7..0000000000 --- a/thirdparty/assimp/code/GenFaceNormalsProcess.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of the post processing step to generate face -* normals for all imported faces. -*/ - - -#include "GenFaceNormalsProcess.h" -#include -#include -#include -#include -#include - - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -GenFaceNormalsProcess::GenFaceNormalsProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -GenFaceNormalsProcess::~GenFaceNormalsProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool GenFaceNormalsProcess::IsActive( unsigned int pFlags) const { - force_ = (pFlags & aiProcess_ForceGenNormals) != 0; - return (pFlags & aiProcess_GenNormals) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void GenFaceNormalsProcess::Execute( aiScene* pScene) { - ASSIMP_LOG_DEBUG("GenFaceNormalsProcess begin"); - - if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { - throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); - } - - bool bHas = false; - for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { - if(this->GenMeshFaceNormals( pScene->mMeshes[a])) { - bHas = true; - } - } - if (bHas) { - ASSIMP_LOG_INFO("GenFaceNormalsProcess finished. " - "Face normals have been calculated"); - } else { - ASSIMP_LOG_DEBUG("GenFaceNormalsProcess finished. " - "Normals are already there"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh) -{ - if (NULL != pMesh->mNormals) { - if (force_) delete[] pMesh->mNormals; - else return false; - } - - // If the mesh consists of lines and/or points but not of - // triangles or higher-order polygons the normal vectors - // are undefined. - if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) { - ASSIMP_LOG_INFO("Normal vectors are undefined for line and point meshes"); - return false; - } - - // allocate an array to hold the output normals - pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; - const float qnan = get_qnan(); - - // iterate through all faces and compute per-face normals but store them per-vertex. - for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { - const aiFace& face = pMesh->mFaces[a]; - if (face.mNumIndices < 3) { - // either a point or a line -> no well-defined normal vector - for (unsigned int i = 0;i < face.mNumIndices;++i) { - pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan); - } - continue; - } - - const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]]; - const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]]; - const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]]; - const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe(); - - for (unsigned int i = 0;i < face.mNumIndices;++i) { - pMesh->mNormals[face.mIndices[i]] = vNor; - } - } - return true; -} diff --git a/thirdparty/assimp/code/GenFaceNormalsProcess.h b/thirdparty/assimp/code/GenFaceNormalsProcess.h deleted file mode 100644 index c80ec9fddc..0000000000 --- a/thirdparty/assimp/code/GenFaceNormalsProcess.h +++ /dev/null @@ -1,87 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a post processing step to compute face normals for all loaded faces*/ -#ifndef AI_GENFACENORMALPROCESS_H_INC -#define AI_GENFACENORMALPROCESS_H_INC - -#include "BaseProcess.h" -#include - -namespace Assimp -{ - -// --------------------------------------------------------------------------- -/** The GenFaceNormalsProcess computes face normals for all faces of all meshes -*/ -class ASSIMP_API_WINONLY GenFaceNormalsProcess : public BaseProcess -{ -public: - - GenFaceNormalsProcess(); - ~GenFaceNormalsProcess(); - -public: - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag field. - * @param pFlags The processing flags the importer was called with. A bitwise - * combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, false if not. - */ - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - - -private: - bool GenMeshFaceNormals(aiMesh* pcMesh); - mutable bool force_ = false; -}; - -} // end of namespace Assimp - -#endif // !!AI_GENFACENORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/GenVertexNormalsProcess.cpp b/thirdparty/assimp/code/GenVertexNormalsProcess.cpp deleted file mode 100644 index 3f6c2f86bd..0000000000 --- a/thirdparty/assimp/code/GenVertexNormalsProcess.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of the post processing step to generate face -* normals for all imported faces. -*/ - - - -// internal headers -#include "GenVertexNormalsProcess.h" -#include "ProcessHelper.h" -#include -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -GenVertexNormalsProcess::GenVertexNormalsProcess() -: configMaxAngle( AI_DEG_TO_RAD( 175.f ) ) { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -GenVertexNormalsProcess::~GenVertexNormalsProcess() { - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const -{ - force_ = (pFlags & aiProcess_ForceGenNormals) != 0; - return (pFlags & aiProcess_GenSmoothNormals) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void GenVertexNormalsProcess::SetupProperties(const Importer* pImp) -{ - // Get the current value of the AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE property - configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,(ai_real)175.0); - configMaxAngle = AI_DEG_TO_RAD(std::max(std::min(configMaxAngle,(ai_real)175.0),(ai_real)0.0)); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void GenVertexNormalsProcess::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("GenVertexNormalsProcess begin"); - - if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { - throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); - } - - bool bHas = false; - for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) { - if(GenMeshVertexNormals( pScene->mMeshes[a],a)) - bHas = true; - } - - if (bHas) { - ASSIMP_LOG_INFO("GenVertexNormalsProcess finished. " - "Vertex normals have been calculated"); - } else { - ASSIMP_LOG_DEBUG("GenVertexNormalsProcess finished. " - "Normals are already there"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int meshIndex) -{ - if (NULL != pMesh->mNormals) { - if (force_) delete[] pMesh->mNormals; - else return false; - } - - // If the mesh consists of lines and/or points but not of - // triangles or higher-order polygons the normal vectors - // are undefined. - if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) - { - ASSIMP_LOG_INFO("Normal vectors are undefined for line and point meshes"); - return false; - } - - // Allocate the array to hold the output normals - const float qnan = std::numeric_limits::quiet_NaN(); - pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; - - // Compute per-face normals but store them per-vertex - for( unsigned int a = 0; a < pMesh->mNumFaces; a++) - { - const aiFace& face = pMesh->mFaces[a]; - if (face.mNumIndices < 3) - { - // either a point or a line -> no normal vector - for (unsigned int i = 0;i < face.mNumIndices;++i) { - pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan); - } - - continue; - } - - const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]]; - const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]]; - const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]]; - const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe(); - - for (unsigned int i = 0;i < face.mNumIndices;++i) { - pMesh->mNormals[face.mIndices[i]] = vNor; - } - } - - // Set up a SpatialSort to quickly find all vertices close to a given position - // check whether we can reuse the SpatialSort of a previous step. - SpatialSort* vertexFinder = NULL; - SpatialSort _vertexFinder; - ai_real posEpsilon = ai_real( 1e-5 ); - if (shared) { - std::vector >* avf; - shared->GetProperty(AI_SPP_SPATIAL_SORT,avf); - if (avf) - { - std::pair& 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 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 abHad(pMesh->mNumVertices,false); - for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { - if (abHad[i]) { - continue; - } - - // Get all vertices that share this one ... - vertexFinder->FindPositions( pMesh->mVertices[i], posEpsilon, verticesFound); - - aiVector3D pcNor; - for (unsigned int a = 0; a < verticesFound.size(); ++a) { - const aiVector3D& v = pMesh->mNormals[verticesFound[a]]; - if (is_not_qnan(v.x))pcNor += v; - } - pcNor.NormalizeSafe(); - - // Write the smoothed normal back to all affected normals - for (unsigned int a = 0; a < verticesFound.size(); ++a) - { - unsigned int vidx = verticesFound[a]; - pcNew[vidx] = pcNor; - abHad[vidx] = true; - } - } - } - // Slower code path if a smooth angle is set. There are many ways to achieve - // the effect, this one is the most straightforward one. - else { - const ai_real fLimit = std::cos(configMaxAngle); - for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { - // Get all vertices that share this one ... - vertexFinder->FindPositions( pMesh->mVertices[i] , posEpsilon, verticesFound); - - aiVector3D vr = pMesh->mNormals[i]; - - aiVector3D pcNor; - for (unsigned int a = 0; a < verticesFound.size(); ++a) { - aiVector3D v = pMesh->mNormals[verticesFound[a]]; - - // Check whether the angle between the two normals is not too large. - // Skip the angle check on our own normal to avoid false negatives - // (v*v is not guaranteed to be 1.0 for all unit vectors v) - if (is_not_qnan(v.x) && (verticesFound[a] == i || (v * vr >= fLimit))) - pcNor += v; - } - pcNew[i] = pcNor.NormalizeSafe(); - } - } - - delete[] pMesh->mNormals; - pMesh->mNormals = pcNew; - - return true; -} diff --git a/thirdparty/assimp/code/GenVertexNormalsProcess.h b/thirdparty/assimp/code/GenVertexNormalsProcess.h deleted file mode 100644 index 9142ad26f5..0000000000 --- a/thirdparty/assimp/code/GenVertexNormalsProcess.h +++ /dev/null @@ -1,115 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a post processing step to compute vertex normals - for all loaded vertizes */ -#ifndef AI_GENVERTEXNORMALPROCESS_H_INC -#define AI_GENVERTEXNORMALPROCESS_H_INC - -#include "BaseProcess.h" -#include - -class GenNormalsTest; - -namespace Assimp { - -// --------------------------------------------------------------------------- -/** The GenFaceNormalsProcess computes vertex normals for all vertizes -*/ -class ASSIMP_API GenVertexNormalsProcess : public BaseProcess -{ -public: - - GenVertexNormalsProcess(); - ~GenVertexNormalsProcess(); - -public: - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag. - * @param pFlags The processing flags the importer was called with. - * A bitwise combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, - * false if not. - */ - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - /** Called prior to ExecuteOnScene(). - * The function is a request to the process to update its configuration - * basing on the Importer's configuration property list. - */ - void SetupProperties(const Importer* pImp); - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - - - // setter for configMaxAngle - inline void SetMaxSmoothAngle(ai_real f) - { - configMaxAngle =f; - } - -public: - - // ------------------------------------------------------------------- - /** Computes normals for a specific mesh - * @param pcMesh Mesh - * @param meshIndex Index of the mesh - * @return true if vertex normals have been computed - */ - bool GenMeshVertexNormals (aiMesh* pcMesh, unsigned int meshIndex); - -private: - - /** Configuration option: maximum smoothing angle, in radians*/ - ai_real configMaxAngle; - mutable bool force_ = false; -}; - -} // end of namespace Assimp - -#endif // !!AI_GENVERTEXNORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/Importer.cpp b/thirdparty/assimp/code/Importer.cpp deleted file mode 100644 index 65b16471cc..0000000000 --- a/thirdparty/assimp/code/Importer.cpp +++ /dev/null @@ -1,1171 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Importer.cpp - * @brief Implementation of the CPP-API class #Importer - */ - -#include -#include -#include - -// ------------------------------------------------------------------------------------------------ -/* Uncomment this line to prevent Assimp from catching unknown exceptions. - * - * Note that any Exception except DeadlyImportError may lead to - * undefined behaviour -> loaders could remain in an unusable state and - * further imports with the same Importer instance could fail/crash/burn ... - */ -// ------------------------------------------------------------------------------------------------ -#ifndef ASSIMP_BUILD_DEBUG -# define ASSIMP_CATCH_GLOBAL_EXCEPTIONS -#endif - -// ------------------------------------------------------------------------------------------------ -// Internal headers -// ------------------------------------------------------------------------------------------------ -#include "Importer.h" -#include -#include "BaseProcess.h" - -#include "DefaultProgressHandler.h" -#include -#include "ProcessHelper.h" -#include "ScenePreprocessor.h" -#include "ScenePrivate.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS -# include "ValidateDataStructure.h" -#endif - -using namespace Assimp::Profiling; -using namespace Assimp::Formatter; - -namespace Assimp { - // ImporterRegistry.cpp - void GetImporterInstanceList(std::vector< BaseImporter* >& out); - void DeleteImporterInstanceList(std::vector< BaseImporter* >& out); - - // PostStepRegistry.cpp - void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); -} - -using namespace Assimp; -using namespace Assimp::Intern; - -// ------------------------------------------------------------------------------------------------ -// Intern::AllocateFromAssimpHeap serves as abstract base class. It overrides -// new and delete (and their array counterparts) of public API classes (e.g. Logger) to -// utilize our DLL heap. -// See http://www.gotw.ca/publications/mill15.htm -// ------------------------------------------------------------------------------------------------ -void* AllocateFromAssimpHeap::operator new ( size_t num_bytes) { - return ::operator new(num_bytes); -} - -void* AllocateFromAssimpHeap::operator new ( size_t num_bytes, const std::nothrow_t& ) throw() { - try { - return AllocateFromAssimpHeap::operator new( num_bytes ); - } - catch( ... ) { - return NULL; - } -} - -void AllocateFromAssimpHeap::operator delete ( void* data) { - return ::operator delete(data); -} - -void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes) { - return ::operator new[](num_bytes); -} - -void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw() { - try { - return AllocateFromAssimpHeap::operator new[]( num_bytes ); - } - catch( ... ) { - return NULL; - } -} - -void AllocateFromAssimpHeap::operator delete[] ( void* data) { - return ::operator delete[](data); -} - -// ------------------------------------------------------------------------------------------------ -// Importer constructor. -Importer::Importer() - : pimpl( new ImporterPimpl ) { - pimpl->mScene = NULL; - pimpl->mErrorString = ""; - - // Allocate a default IO handler - pimpl->mIOHandler = new DefaultIOSystem; - pimpl->mIsDefaultHandler = true; - pimpl->bExtraVerbose = false; // disable extra verbose mode by default - - pimpl->mProgressHandler = new DefaultProgressHandler(); - pimpl->mIsDefaultProgressHandler = true; - - GetImporterInstanceList(pimpl->mImporter); - GetPostProcessingStepInstanceList(pimpl->mPostProcessingSteps); - - // Allocate a SharedPostProcessInfo object and store pointers to it in all post-process steps in the list. - pimpl->mPPShared = new SharedPostProcessInfo(); - for (std::vector::iterator it = pimpl->mPostProcessingSteps.begin(); - it != pimpl->mPostProcessingSteps.end(); - ++it) { - - (*it)->SetSharedData(pimpl->mPPShared); - } -} - -// ------------------------------------------------------------------------------------------------ -// Destructor of Importer -Importer::~Importer() -{ - // Delete all import plugins - DeleteImporterInstanceList(pimpl->mImporter); - - // Delete all post-processing plug-ins - for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) - delete pimpl->mPostProcessingSteps[a]; - - // Delete the assigned IO and progress handler - delete pimpl->mIOHandler; - delete pimpl->mProgressHandler; - - // Kill imported scene. Destructor's should do that recursively - delete pimpl->mScene; - - // Delete shared post-processing data - delete pimpl->mPPShared; - - // and finally the pimpl itself - delete pimpl; -} - -// ------------------------------------------------------------------------------------------------ -// Register a custom post-processing step -aiReturn Importer::RegisterPPStep(BaseProcess* pImp) -{ - ai_assert(NULL != pImp); - ASSIMP_BEGIN_EXCEPTION_REGION(); - - pimpl->mPostProcessingSteps.push_back(pImp); - ASSIMP_LOG_INFO("Registering custom post-processing step"); - - ASSIMP_END_EXCEPTION_REGION(aiReturn); - return AI_SUCCESS; -} - -// ------------------------------------------------------------------------------------------------ -// Register a custom loader plugin -aiReturn Importer::RegisterLoader(BaseImporter* pImp) -{ - ai_assert(NULL != pImp); - ASSIMP_BEGIN_EXCEPTION_REGION(); - - // -------------------------------------------------------------------- - // Check whether we would have two loaders for the same file extension - // This is absolutely OK, but we should warn the developer of the new - // loader that his code will probably never be called if the first - // loader is a bit too lazy in his file checking. - // -------------------------------------------------------------------- - std::set st; - std::string baked; - pImp->GetExtensionList(st); - - for(std::set::const_iterator it = st.begin(); it != st.end(); ++it) { - -#ifdef ASSIMP_BUILD_DEBUG - if (IsExtensionSupported(*it)) { - ASSIMP_LOG_WARN_F("The file extension ", *it, " is already in use"); - } -#endif - baked += *it; - } - - // add the loader - pimpl->mImporter.push_back(pImp); - ASSIMP_LOG_INFO_F("Registering custom importer for these file extensions: ", baked); - ASSIMP_END_EXCEPTION_REGION(aiReturn); - return AI_SUCCESS; -} - -// ------------------------------------------------------------------------------------------------ -// Unregister a custom loader plugin -aiReturn Importer::UnregisterLoader(BaseImporter* pImp) -{ - if(!pImp) { - // unregistering a NULL importer is no problem for us ... really! - return AI_SUCCESS; - } - - ASSIMP_BEGIN_EXCEPTION_REGION(); - std::vector::iterator it = std::find(pimpl->mImporter.begin(), - pimpl->mImporter.end(),pImp); - - if (it != pimpl->mImporter.end()) { - pimpl->mImporter.erase(it); - ASSIMP_LOG_INFO("Unregistering custom importer: "); - return AI_SUCCESS; - } - ASSIMP_LOG_WARN("Unable to remove custom importer: I can't find you ..."); - ASSIMP_END_EXCEPTION_REGION(aiReturn); - return AI_FAILURE; -} - -// ------------------------------------------------------------------------------------------------ -// Unregister a custom loader plugin -aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) -{ - if(!pImp) { - // unregistering a NULL ppstep is no problem for us ... really! - return AI_SUCCESS; - } - - ASSIMP_BEGIN_EXCEPTION_REGION(); - std::vector::iterator it = std::find(pimpl->mPostProcessingSteps.begin(), - pimpl->mPostProcessingSteps.end(),pImp); - - if (it != pimpl->mPostProcessingSteps.end()) { - pimpl->mPostProcessingSteps.erase(it); - ASSIMP_LOG_INFO("Unregistering custom post-processing step"); - return AI_SUCCESS; - } - ASSIMP_LOG_WARN("Unable to remove custom post-processing step: I can't find you .."); - ASSIMP_END_EXCEPTION_REGION(aiReturn); - return AI_FAILURE; -} - -// ------------------------------------------------------------------------------------------------ -// Supplies a custom IO handler to the importer to open and access files. -void Importer::SetIOHandler( IOSystem* pIOHandler) -{ - ASSIMP_BEGIN_EXCEPTION_REGION(); - // If the new handler is zero, allocate a default IO implementation. - if (!pIOHandler) - { - // Release pointer in the possession of the caller - pimpl->mIOHandler = new DefaultIOSystem(); - pimpl->mIsDefaultHandler = true; - } - // Otherwise register the custom handler - else if (pimpl->mIOHandler != pIOHandler) - { - delete pimpl->mIOHandler; - pimpl->mIOHandler = pIOHandler; - pimpl->mIsDefaultHandler = false; - } - ASSIMP_END_EXCEPTION_REGION(void); -} - -// ------------------------------------------------------------------------------------------------ -// Get the currently set IO handler -IOSystem* Importer::GetIOHandler() const { - return pimpl->mIOHandler; -} - -// ------------------------------------------------------------------------------------------------ -// Check whether a custom IO handler is currently set -bool Importer::IsDefaultIOHandler() const { - return pimpl->mIsDefaultHandler; -} - -// ------------------------------------------------------------------------------------------------ -// Supplies a custom progress handler to get regular callbacks during importing -void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { - ASSIMP_BEGIN_EXCEPTION_REGION(); - // If the new handler is zero, allocate a default implementation. - if (!pHandler) - { - // Release pointer in the possession of the caller - pimpl->mProgressHandler = new DefaultProgressHandler(); - pimpl->mIsDefaultProgressHandler = true; - } - // Otherwise register the custom handler - else if (pimpl->mProgressHandler != pHandler) - { - delete pimpl->mProgressHandler; - pimpl->mProgressHandler = pHandler; - pimpl->mIsDefaultProgressHandler = false; - } - ASSIMP_END_EXCEPTION_REGION(void); -} - -// ------------------------------------------------------------------------------------------------ -// Get the currently set progress handler -ProgressHandler* Importer::GetProgressHandler() const { - return pimpl->mProgressHandler; -} - -// ------------------------------------------------------------------------------------------------ -// Check whether a custom progress handler is currently set -bool Importer::IsDefaultProgressHandler() const { - return pimpl->mIsDefaultProgressHandler; -} - -// ------------------------------------------------------------------------------------------------ -// Validate post process step flags -bool _ValidateFlags(unsigned int pFlags) -{ - if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals) { - ASSIMP_LOG_ERROR("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible"); - return false; - } - if (pFlags & aiProcess_OptimizeGraph && pFlags & aiProcess_PreTransformVertices) { - ASSIMP_LOG_ERROR("#aiProcess_OptimizeGraph and #aiProcess_PreTransformVertices are incompatible"); - return false; - } - return true; -} - -// ------------------------------------------------------------------------------------------------ -// Free the current scene -void Importer::FreeScene( ) -{ - ASSIMP_BEGIN_EXCEPTION_REGION(); - - delete pimpl->mScene; - pimpl->mScene = NULL; - - pimpl->mErrorString = ""; - ASSIMP_END_EXCEPTION_REGION(void); -} - -// ------------------------------------------------------------------------------------------------ -// Get the current error string, if any -const char* Importer::GetErrorString() const -{ - /* Must remain valid as long as ReadFile() or FreeFile() are not called */ - return pimpl->mErrorString.c_str(); -} - -// ------------------------------------------------------------------------------------------------ -// Enable extra-verbose mode -void Importer::SetExtraVerbose(bool bDo) -{ - pimpl->bExtraVerbose = bDo; -} - -// ------------------------------------------------------------------------------------------------ -// Get the current scene -const aiScene* Importer::GetScene() const -{ - return pimpl->mScene; -} - -// ------------------------------------------------------------------------------------------------ -// Orphan the current scene and return it. -aiScene* Importer::GetOrphanedScene() -{ - aiScene* s = pimpl->mScene; - - ASSIMP_BEGIN_EXCEPTION_REGION(); - pimpl->mScene = NULL; - - pimpl->mErrorString = ""; /* reset error string */ - ASSIMP_END_EXCEPTION_REGION(aiScene*); - return s; -} - -// ------------------------------------------------------------------------------------------------ -// Validate post-processing flags -bool Importer::ValidateFlags(unsigned int pFlags) const -{ - ASSIMP_BEGIN_EXCEPTION_REGION(); - // run basic checks for mutually exclusive flags - if(!_ValidateFlags(pFlags)) { - return false; - } - - // ValidateDS does not anymore occur in the pp list, it plays an awesome extra role ... -#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS - if (pFlags & aiProcess_ValidateDataStructure) { - return false; - } -#endif - pFlags &= ~aiProcess_ValidateDataStructure; - - // Now iterate through all bits which are set in the flags and check whether we find at least - // one pp plugin which handles it. - for (unsigned int mask = 1; mask < (1u << (sizeof(unsigned int)*8-1));mask <<= 1) { - - if (pFlags & mask) { - - bool have = false; - for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { - if (pimpl->mPostProcessingSteps[a]-> IsActive(mask) ) { - - have = true; - break; - } - } - if (!have) { - return false; - } - } - } - ASSIMP_END_EXCEPTION_REGION(bool); - return true; -} - -// ------------------------------------------------------------------------------------------------ -const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, - size_t pLength, - unsigned int pFlags, - const char* pHint /*= ""*/) -{ - ASSIMP_BEGIN_EXCEPTION_REGION(); - if (!pHint) { - pHint = ""; - } - - if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) { - pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()"; - return NULL; - } - - // prevent deletion of the previous IOHandler - IOSystem* io = pimpl->mIOHandler; - pimpl->mIOHandler = NULL; - - SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength,io)); - - // read the file and recover the previous IOSystem - static const size_t BufSize(Importer::MaxLenHint + 28); - char fbuff[BufSize]; - ai_snprintf(fbuff, BufSize, "%s.%s",AI_MEMORYIO_MAGIC_FILENAME,pHint); - - ReadFile(fbuff,pFlags); - SetIOHandler(io); - - ASSIMP_END_EXCEPTION_REGION(const aiScene*); - return pimpl->mScene; -} - -// ------------------------------------------------------------------------------------------------ -void WriteLogOpening(const std::string& file) -{ - ASSIMP_LOG_INFO_F("Load ", file); - - // print a full version dump. This is nice because we don't - // need to ask the authors of incoming bug reports for - // the library version they're using - a log dump is - // sufficient. - const unsigned int flags( aiGetCompileFlags() ); - std::stringstream stream; - stream << "Assimp " << aiGetVersionMajor() << "." << aiGetVersionMinor() << "." << aiGetVersionRevision() << " " -#if defined(ASSIMP_BUILD_ARCHITECTURE) - << ASSIMP_BUILD_ARCHITECTURE -#elif defined(_M_IX86) || defined(__x86_32__) || defined(__i386__) - << "x86" -#elif defined(_M_X64) || defined(__x86_64__) - << "amd64" -#elif defined(_M_IA64) || defined(__ia64__) - << "itanium" -#elif defined(__ppc__) || defined(__powerpc__) - << "ppc32" -#elif defined(__powerpc64__) - << "ppc64" -#elif defined(__arm__) - << "arm" -#else - << "" -#endif - << " " -#if defined(ASSIMP_BUILD_COMPILER) - << ( ASSIMP_BUILD_COMPILER ) -#elif defined(_MSC_VER) - << "msvc" -#elif defined(__GNUC__) - << "gcc" -#else - << "" -#endif - -#ifdef ASSIMP_BUILD_DEBUG - << " debug" -#endif - - << (flags & ASSIMP_CFLAGS_NOBOOST ? " noboost" : "") - << (flags & ASSIMP_CFLAGS_SHARED ? " shared" : "") - << (flags & ASSIMP_CFLAGS_SINGLETHREADED ? " singlethreaded" : ""); - - ASSIMP_LOG_DEBUG(stream.str()); -} - -// ------------------------------------------------------------------------------------------------ -// Reads the given file and returns its contents if successful. -const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) -{ - ASSIMP_BEGIN_EXCEPTION_REGION(); - const std::string pFile(_pFile); - - // ---------------------------------------------------------------------- - // Put a large try block around everything to catch all std::exception's - // that might be thrown by STL containers or by new(). - // ImportErrorException's are throw by ourselves and caught elsewhere. - //----------------------------------------------------------------------- - - WriteLogOpening(pFile); - -#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS - try -#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS - { - // Check whether this Importer instance has already loaded - // a scene. In this case we need to delete the old one - if (pimpl->mScene) { - - ASSIMP_LOG_DEBUG("(Deleting previous scene)"); - FreeScene(); - } - - // First check if the file is accessible at all - if( !pimpl->mIOHandler->Exists( pFile)) { - - pimpl->mErrorString = "Unable to open file \"" + pFile + "\"."; - ASSIMP_LOG_ERROR(pimpl->mErrorString); - return NULL; - } - - std::unique_ptr profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL); - if (profiler) { - profiler->BeginRegion("total"); - } - - // Find an worker class which can handle the file - BaseImporter* imp = NULL; - for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { - - if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) { - imp = pimpl->mImporter[a]; - break; - } - } - - if (!imp) { - // not so bad yet ... try format auto detection. - const std::string::size_type s = pFile.find_last_of('.'); - if (s != std::string::npos) { - ASSIMP_LOG_INFO("File extension not known, trying signature-based detection"); - for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { - if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) { - imp = pimpl->mImporter[a]; - break; - } - } - } - // Put a proper error message if no suitable importer was found - if( !imp) { - pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\"."; - ASSIMP_LOG_ERROR(pimpl->mErrorString); - return NULL; - } - } - - // Get file size for progress handler - IOStream * fileIO = pimpl->mIOHandler->Open( pFile ); - uint32_t fileSize = 0; - if (fileIO) - { - fileSize = static_cast(fileIO->FileSize()); - pimpl->mIOHandler->Close( fileIO ); - } - - // Dispatch the reading to the worker class for this format - const aiImporterDesc *desc( imp->GetInfo() ); - std::string ext( "unknown" ); - if ( NULL != desc ) { - ext = desc->mName; - } - ASSIMP_LOG_INFO("Found a matching importer for this file format: " + ext + "." ); - pimpl->mProgressHandler->UpdateFileRead( 0, fileSize ); - - if (profiler) { - profiler->BeginRegion("import"); - } - - pimpl->mScene = imp->ReadFile( this, pFile, pimpl->mIOHandler); - pimpl->mProgressHandler->UpdateFileRead( fileSize, fileSize ); - - if (profiler) { - profiler->EndRegion("import"); - } - - SetPropertyString("sourceFilePath", pFile); - - // If successful, apply all active post processing steps to the imported data - if( pimpl->mScene) { - -#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS - // The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called. - if (pFlags & aiProcess_ValidateDataStructure) - { - ValidateDSProcess ds; - ds.ExecuteOnScene (this); - if (!pimpl->mScene) { - return NULL; - } - } -#endif // no validation - - // Preprocess the scene and prepare it for post-processing - if (profiler) { - profiler->BeginRegion("preprocess"); - } - - ScenePreprocessor pre(pimpl->mScene); - pre.ProcessScene(); - - if (profiler) { - profiler->EndRegion("preprocess"); - } - - // Ensure that the validation process won't be called twice - ApplyPostProcessing(pFlags & (~aiProcess_ValidateDataStructure)); - } - // if failed, extract the error string - else if( !pimpl->mScene) { - pimpl->mErrorString = imp->GetErrorText(); - } - - // clear any data allocated by post-process steps - pimpl->mPPShared->Clean(); - - if (profiler) { - profiler->EndRegion("total"); - } - } -#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS - catch (std::exception &e) - { -#if (defined _MSC_VER) && (defined _CPPRTTI) - // if we have RTTI get the full name of the exception that occurred - pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what(); -#else - pimpl->mErrorString = std::string("std::exception: ") + e.what(); -#endif - - ASSIMP_LOG_ERROR(pimpl->mErrorString); - delete pimpl->mScene; pimpl->mScene = NULL; - } -#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS - - // either successful or failure - the pointer expresses it anyways - ASSIMP_END_EXCEPTION_REGION(const aiScene*); - return pimpl->mScene; -} - - -// ------------------------------------------------------------------------------------------------ -// Apply post-processing to the currently bound scene -const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) -{ - ASSIMP_BEGIN_EXCEPTION_REGION(); - // Return immediately if no scene is active - if (!pimpl->mScene) { - return NULL; - } - - // If no flags are given, return the current scene with no further action - if (!pFlags) { - return pimpl->mScene; - } - - // In debug builds: run basic flag validation - ai_assert(_ValidateFlags(pFlags)); - ASSIMP_LOG_INFO("Entering post processing pipeline"); - -#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS - // The ValidateDS process plays an exceptional role. It isn't contained in the global - // list of post-processing steps, so we need to call it manually. - if (pFlags & aiProcess_ValidateDataStructure) - { - ValidateDSProcess ds; - ds.ExecuteOnScene (this); - if (!pimpl->mScene) { - return NULL; - } - } -#endif // no validation -#ifdef ASSIMP_BUILD_DEBUG - if (pimpl->bExtraVerbose) - { -#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS - ASSIMP_LOG_ERROR("Verbose Import is not available due to build settings"); -#endif // no validation - pFlags |= aiProcess_ValidateDataStructure; - } -#else - if (pimpl->bExtraVerbose) { - ASSIMP_LOG_WARN("Not a debug build, ignoring extra verbose setting"); - } -#endif // ! DEBUG - - std::unique_ptr profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL); - for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { - - BaseProcess* process = pimpl->mPostProcessingSteps[a]; - pimpl->mProgressHandler->UpdatePostProcess(static_cast(a), static_cast(pimpl->mPostProcessingSteps.size()) ); - if( process->IsActive( pFlags)) { - - if (profiler) { - profiler->BeginRegion("postprocess"); - } - - process->ExecuteOnScene ( this ); - - if (profiler) { - profiler->EndRegion("postprocess"); - } - } - if( !pimpl->mScene) { - break; - } -#ifdef ASSIMP_BUILD_DEBUG - -#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS - continue; -#endif // no validation - - // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step - if (pimpl->bExtraVerbose) { - ASSIMP_LOG_DEBUG("Verbose Import: re-validating data structures"); - - ValidateDSProcess ds; - ds.ExecuteOnScene (this); - if( !pimpl->mScene) { - ASSIMP_LOG_ERROR("Verbose Import: failed to re-validate data structures"); - break; - } - } -#endif // ! DEBUG - } - pimpl->mProgressHandler->UpdatePostProcess( static_cast(pimpl->mPostProcessingSteps.size()), - static_cast(pimpl->mPostProcessingSteps.size()) ); - - // update private scene flags - if( pimpl->mScene ) - ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags; - - // clear any data allocated by post-process steps - pimpl->mPPShared->Clean(); - ASSIMP_LOG_INFO("Leaving post processing pipeline"); - - ASSIMP_END_EXCEPTION_REGION(const aiScene*); - return pimpl->mScene; -} - -// ------------------------------------------------------------------------------------------------ -const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) { - ASSIMP_BEGIN_EXCEPTION_REGION(); - - // Return immediately if no scene is active - if ( NULL == pimpl->mScene ) { - return NULL; - } - - // If no flags are given, return the current scene with no further action - if ( NULL == rootProcess ) { - return pimpl->mScene; - } - - // In debug builds: run basic flag validation - ASSIMP_LOG_INFO( "Entering customized post processing pipeline" ); - -#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS - // The ValidateDS process plays an exceptional role. It isn't contained in the global - // list of post-processing steps, so we need to call it manually. - if ( requestValidation ) - { - ValidateDSProcess ds; - ds.ExecuteOnScene( this ); - if ( !pimpl->mScene ) { - return NULL; - } - } -#endif // no validation -#ifdef ASSIMP_BUILD_DEBUG - if ( pimpl->bExtraVerbose ) - { -#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS - ASSIMP_LOG_ERROR( "Verbose Import is not available due to build settings" ); -#endif // no validation - } -#else - if ( pimpl->bExtraVerbose ) { - ASSIMP_LOG_WARN( "Not a debug build, ignoring extra verbose setting" ); - } -#endif // ! DEBUG - - std::unique_ptr profiler( GetPropertyInteger( AI_CONFIG_GLOB_MEASURE_TIME, 0 ) ? new Profiler() : NULL ); - - if ( profiler ) { - profiler->BeginRegion( "postprocess" ); - } - - rootProcess->ExecuteOnScene( this ); - - if ( profiler ) { - profiler->EndRegion( "postprocess" ); - } - - // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step - if ( pimpl->bExtraVerbose || requestValidation ) { - ASSIMP_LOG_DEBUG( "Verbose Import: revalidating data structures" ); - - ValidateDSProcess ds; - ds.ExecuteOnScene( this ); - if ( !pimpl->mScene ) { - ASSIMP_LOG_ERROR( "Verbose Import: failed to revalidate data structures" ); - } - } - - // clear any data allocated by post-process steps - pimpl->mPPShared->Clean(); - ASSIMP_LOG_INFO( "Leaving customized post processing pipeline" ); - - ASSIMP_END_EXCEPTION_REGION( const aiScene* ); - - return pimpl->mScene; -} - -// ------------------------------------------------------------------------------------------------ -// Helper function to check whether an extension is supported by ASSIMP -bool Importer::IsExtensionSupported(const char* szExtension) const -{ - return nullptr != GetImporter(szExtension); -} - -// ------------------------------------------------------------------------------------------------ -size_t Importer::GetImporterCount() const -{ - return pimpl->mImporter.size(); -} - -// ------------------------------------------------------------------------------------------------ -const aiImporterDesc* Importer::GetImporterInfo(size_t index) const -{ - if (index >= pimpl->mImporter.size()) { - return NULL; - } - return pimpl->mImporter[index]->GetInfo(); -} - - -// ------------------------------------------------------------------------------------------------ -BaseImporter* Importer::GetImporter (size_t index) const -{ - if (index >= pimpl->mImporter.size()) { - return NULL; - } - return pimpl->mImporter[index]; -} - -// ------------------------------------------------------------------------------------------------ -// Find a loader plugin for a given file extension -BaseImporter* Importer::GetImporter (const char* szExtension) const -{ - return GetImporter(GetImporterIndex(szExtension)); -} - -// ------------------------------------------------------------------------------------------------ -// Find a loader plugin for a given file extension -size_t Importer::GetImporterIndex (const char* szExtension) const { - ai_assert(nullptr != szExtension); - - ASSIMP_BEGIN_EXCEPTION_REGION(); - - // skip over wildcard and dot characters at string head -- - for ( ; *szExtension == '*' || *szExtension == '.'; ++szExtension ); - - std::string ext(szExtension); - if (ext.empty()) { - return static_cast(-1); - } - std::transform( ext.begin(), ext.end(), ext.begin(), ToLower ); - - std::set str; - for (std::vector::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { - str.clear(); - - (*i)->GetExtensionList(str); - for (std::set::const_iterator it = str.begin(); it != str.end(); ++it) { - if (ext == *it) { - return std::distance(static_cast< std::vector::const_iterator >(pimpl->mImporter.begin()), i); - } - } - } - ASSIMP_END_EXCEPTION_REGION(size_t); - return static_cast(-1); -} - -// ------------------------------------------------------------------------------------------------ -// Helper function to build a list of all file extensions supported by ASSIMP -void Importer::GetExtensionList(aiString& szOut) const -{ - ASSIMP_BEGIN_EXCEPTION_REGION(); - std::set str; - for (std::vector::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { - (*i)->GetExtensionList(str); - } - - // List can be empty - if( !str.empty() ) { - for (std::set::const_iterator it = str.begin();; ) { - szOut.Append("*."); - szOut.Append((*it).c_str()); - - if (++it == str.end()) { - break; - } - szOut.Append(";"); - } - } - ASSIMP_END_EXCEPTION_REGION(void); -} - -// ------------------------------------------------------------------------------------------------ -// Set a configuration property -bool Importer::SetPropertyInteger(const char* szName, int iValue) -{ - bool existing; - ASSIMP_BEGIN_EXCEPTION_REGION(); - existing = SetGenericProperty(pimpl->mIntProperties, szName,iValue); - ASSIMP_END_EXCEPTION_REGION(bool); - return existing; -} - -// ------------------------------------------------------------------------------------------------ -// Set a configuration property -bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) -{ - bool existing; - ASSIMP_BEGIN_EXCEPTION_REGION(); - existing = SetGenericProperty(pimpl->mFloatProperties, szName,iValue); - ASSIMP_END_EXCEPTION_REGION(bool); - return existing; -} - -// ------------------------------------------------------------------------------------------------ -// Set a configuration property -bool Importer::SetPropertyString(const char* szName, const std::string& value) -{ - bool existing; - ASSIMP_BEGIN_EXCEPTION_REGION(); - existing = SetGenericProperty(pimpl->mStringProperties, szName,value); - ASSIMP_END_EXCEPTION_REGION(bool); - return existing; -} - -// ------------------------------------------------------------------------------------------------ -// Set a configuration property -bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) -{ - bool existing; - ASSIMP_BEGIN_EXCEPTION_REGION(); - existing = SetGenericProperty(pimpl->mMatrixProperties, szName,value); - ASSIMP_END_EXCEPTION_REGION(bool); - return existing; -} - -// ------------------------------------------------------------------------------------------------ -// Get a configuration property -int Importer::GetPropertyInteger(const char* szName, - int iErrorReturn /*= 0xffffffff*/) const -{ - return GetGenericProperty(pimpl->mIntProperties,szName,iErrorReturn); -} - -// ------------------------------------------------------------------------------------------------ -// Get a configuration property -ai_real Importer::GetPropertyFloat(const char* szName, - ai_real iErrorReturn /*= 10e10*/) const -{ - return GetGenericProperty(pimpl->mFloatProperties,szName,iErrorReturn); -} - -// ------------------------------------------------------------------------------------------------ -// Get a configuration property -const std::string Importer::GetPropertyString(const char* szName, - const std::string& iErrorReturn /*= ""*/) const -{ - return GetGenericProperty(pimpl->mStringProperties,szName,iErrorReturn); -} - -// ------------------------------------------------------------------------------------------------ -// Get a configuration property -const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, - const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const -{ - return GetGenericProperty(pimpl->mMatrixProperties,szName,iErrorReturn); -} - -// ------------------------------------------------------------------------------------------------ -// Get the memory requirements of a single node -inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) -{ - iScene += sizeof(aiNode); - iScene += sizeof(unsigned int) * pcNode->mNumMeshes; - iScene += sizeof(void*) * pcNode->mNumChildren; - - for (unsigned int i = 0; i < pcNode->mNumChildren;++i) { - AddNodeWeight(iScene,pcNode->mChildren[i]); - } -} - -// ------------------------------------------------------------------------------------------------ -// Get the memory requirements of the scene -void Importer::GetMemoryRequirements(aiMemoryInfo& in) const -{ - in = aiMemoryInfo(); - aiScene* mScene = pimpl->mScene; - - // return if we have no scene loaded - if (!pimpl->mScene) - return; - - - in.total = sizeof(aiScene); - - // add all meshes - for (unsigned int i = 0; i < mScene->mNumMeshes;++i) - { - in.meshes += sizeof(aiMesh); - if (mScene->mMeshes[i]->HasPositions()) { - in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; - } - - if (mScene->mMeshes[i]->HasNormals()) { - in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; - } - - if (mScene->mMeshes[i]->HasTangentsAndBitangents()) { - in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices * 2; - } - - for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) { - if (mScene->mMeshes[i]->HasVertexColors(a)) { - in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices; - } - else break; - } - for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) { - if (mScene->mMeshes[i]->HasTextureCoords(a)) { - in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; - } - else break; - } - if (mScene->mMeshes[i]->HasBones()) { - in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones; - for (unsigned int p = 0; p < mScene->mMeshes[i]->mNumBones;++p) { - in.meshes += sizeof(aiBone); - in.meshes += mScene->mMeshes[i]->mBones[p]->mNumWeights * sizeof(aiVertexWeight); - } - } - in.meshes += (sizeof(aiFace) + 3 * sizeof(unsigned int))*mScene->mMeshes[i]->mNumFaces; - } - in.total += in.meshes; - - // add all embedded textures - for (unsigned int i = 0; i < mScene->mNumTextures;++i) { - const aiTexture* pc = mScene->mTextures[i]; - in.textures += sizeof(aiTexture); - if (pc->mHeight) { - in.textures += 4 * pc->mHeight * pc->mWidth; - } - else in.textures += pc->mWidth; - } - in.total += in.textures; - - // add all animations - for (unsigned int i = 0; i < mScene->mNumAnimations;++i) { - const aiAnimation* pc = mScene->mAnimations[i]; - in.animations += sizeof(aiAnimation); - - // add all bone anims - for (unsigned int a = 0; a < pc->mNumChannels; ++a) { - const aiNodeAnim* pc2 = pc->mChannels[i]; - in.animations += sizeof(aiNodeAnim); - in.animations += pc2->mNumPositionKeys * sizeof(aiVectorKey); - in.animations += pc2->mNumScalingKeys * sizeof(aiVectorKey); - in.animations += pc2->mNumRotationKeys * sizeof(aiQuatKey); - } - } - in.total += in.animations; - - // add all cameras and all lights - in.total += in.cameras = sizeof(aiCamera) * mScene->mNumCameras; - in.total += in.lights = sizeof(aiLight) * mScene->mNumLights; - - // add all nodes - AddNodeWeight(in.nodes,mScene->mRootNode); - in.total += in.nodes; - - // add all materials - for (unsigned int i = 0; i < mScene->mNumMaterials;++i) { - const aiMaterial* pc = mScene->mMaterials[i]; - in.materials += sizeof(aiMaterial); - in.materials += pc->mNumAllocated * sizeof(void*); - - for (unsigned int a = 0; a < pc->mNumProperties;++a) { - in.materials += pc->mProperties[a]->mDataLength; - } - } - in.total += in.materials; -} diff --git a/thirdparty/assimp/code/Importer.h b/thirdparty/assimp/code/Importer.h deleted file mode 100644 index a439d99c2f..0000000000 --- a/thirdparty/assimp/code/Importer.h +++ /dev/null @@ -1,247 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Importer.h mostly internal stuff for use by #Assimp::Importer */ -#pragma once -#ifndef INCLUDED_AI_IMPORTER_H -#define INCLUDED_AI_IMPORTER_H - -#include -#include -#include -#include - -struct aiScene; - -namespace Assimp { - class ProgressHandler; - class IOSystem; - class BaseImporter; - class BaseProcess; - class SharedPostProcessInfo; - - -//! @cond never -// --------------------------------------------------------------------------- -/** @brief Internal PIMPL implementation for Assimp::Importer - * - * Using this idiom here allows us to drop the dependency from - * std::vector and std::map in the public headers. Furthermore we are dropping - * any STL interface problems caused by mismatching STL settings. All - * size calculation are now done by us, not the app heap. */ -class ImporterPimpl { -public: - // Data type to store the key hash - typedef unsigned int KeyType; - - // typedefs for our four configuration maps. - // We don't need more, so there is no need for a generic solution - typedef std::map IntPropertyMap; - typedef std::map FloatPropertyMap; - typedef std::map StringPropertyMap; - typedef std::map MatrixPropertyMap; - - /** IO handler to use for all file accesses. */ - IOSystem* mIOHandler; - bool mIsDefaultHandler; - - /** Progress handler for feedback. */ - ProgressHandler* mProgressHandler; - bool mIsDefaultProgressHandler; - - /** Format-specific importer worker objects - one for each format we can read.*/ - std::vector< BaseImporter* > mImporter; - - /** Post processing steps we can apply at the imported data. */ - std::vector< BaseProcess* > mPostProcessingSteps; - - /** The imported data, if ReadFile() was successful, NULL otherwise. */ - aiScene* mScene; - - /** The error description, if there was one. */ - std::string mErrorString; - - /** List of integer properties */ - IntPropertyMap mIntProperties; - - /** List of floating-point properties */ - FloatPropertyMap mFloatProperties; - - /** List of string properties */ - StringPropertyMap mStringProperties; - - /** List of Matrix properties */ - MatrixPropertyMap mMatrixProperties; - - /** Used for testing - extra verbose mode causes the ValidateDataStructure-Step - * to be executed before and after every single post-process step */ - bool bExtraVerbose; - - /** Used by post-process steps to share data */ - SharedPostProcessInfo* mPPShared; - - /// The default class constructor. - ImporterPimpl() AI_NO_EXCEPT; -}; - -inline -ImporterPimpl::ImporterPimpl() AI_NO_EXCEPT -: mIOHandler( nullptr ) -, mIsDefaultHandler( false ) -, mProgressHandler( nullptr ) -, mIsDefaultProgressHandler( false ) -, mImporter() -, mPostProcessingSteps() -, mScene( nullptr ) -, mErrorString() -, mIntProperties() -, mFloatProperties() -, mStringProperties() -, mMatrixProperties() -, bExtraVerbose( false ) -, mPPShared( nullptr ) { - // empty -} -//! @endcond - - -struct BatchData; - -// --------------------------------------------------------------------------- -/** FOR IMPORTER PLUGINS ONLY: A helper class to the pleasure of importers - * that need to load many external meshes recursively. - * - * The class uses several threads to load these meshes (or at least it - * could, this has not yet been implemented at the moment). - * - * @note The class may not be used by more than one thread*/ -class ASSIMP_API BatchLoader -{ - // friend of Importer - -public: - //! @cond never - // ------------------------------------------------------------------- - /** Wraps a full list of configuration properties for an importer. - * Properties can be set using SetGenericProperty */ - struct PropertyMap - { - ImporterPimpl::IntPropertyMap ints; - ImporterPimpl::FloatPropertyMap floats; - ImporterPimpl::StringPropertyMap strings; - ImporterPimpl::MatrixPropertyMap matrices; - - bool operator == (const PropertyMap& prop) const { - // fixme: really isocpp? gcc complains - return ints == prop.ints && floats == prop.floats && strings == prop.strings && matrices == prop.matrices; - } - - bool empty () const { - return ints.empty() && floats.empty() && strings.empty() && matrices.empty(); - } - }; - //! @endcond - -public: - // ------------------------------------------------------------------- - /** Construct a batch loader from a given IO system to be used - * to access external files - */ - explicit BatchLoader(IOSystem* pIO, bool validate = false ); - - // ------------------------------------------------------------------- - /** The class destructor. - */ - ~BatchLoader(); - - // ------------------------------------------------------------------- - /** Sets the validation step. True for enable validation during postprocess. - * @param enable True for validation. - */ - void setValidation( bool enabled ); - - // ------------------------------------------------------------------- - /** Returns the current validation step. - * @return The current validation step. - */ - bool getValidation() const; - - // ------------------------------------------------------------------- - /** Add a new file to the list of files to be loaded. - * @param file File to be loaded - * @param steps Post-processing steps to be executed on the file - * @param map Optional configuration properties - * @return 'Load request channel' - an unique ID that can later - * be used to access the imported file data. - * @see GetImport */ - unsigned int AddLoadRequest ( - const std::string& file, - unsigned int steps = 0, - const PropertyMap* map = NULL - ); - - // ------------------------------------------------------------------- - /** Get an imported scene. - * This polls the import from the internal request list. - * If an import is requested several times, this function - * can be called several times, too. - * - * @param which LRWC returned by AddLoadRequest(). - * @return NULL if there is no scene with this file name - * in the queue of the scene hasn't been loaded yet. */ - aiScene* GetImport( - unsigned int which - ); - - // ------------------------------------------------------------------- - /** Waits until all scenes have been loaded. This returns - * immediately if no scenes are queued.*/ - void LoadAll(); - -private: - // No need to have that in the public API ... - BatchData *m_data; -}; - -} // Namespace Assimp - -#endif // INCLUDED_AI_IMPORTER_H diff --git a/thirdparty/assimp/code/ImporterRegistry.cpp b/thirdparty/assimp/code/ImporterRegistry.cpp deleted file mode 100644 index 747815fa6f..0000000000 --- a/thirdparty/assimp/code/ImporterRegistry.cpp +++ /dev/null @@ -1,371 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file ImporterRegistry.cpp - -Central registry for all importers available. Do not edit this file -directly (unless you are adding new loaders), instead use the -corresponding preprocessor flag to selectively disable formats. -*/ - -#include -#include - -// ------------------------------------------------------------------------------------------------ -// Importers -// (include_new_importers_here) -// ------------------------------------------------------------------------------------------------ -#ifndef ASSIMP_BUILD_NO_X_IMPORTER -# include "XFileImporter.h" -#endif -#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER -# include "AMFImporter.hpp" -#endif -#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER -# include "3DSLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER -# include "MD3Loader.h" -#endif -#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER -# include "MDLLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER -# include "MD2Loader.h" -#endif -#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER -# include "PlyLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER -# include "ASELoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER -# include "ObjFileImporter.h" -#endif -#ifndef ASSIMP_BUILD_NO_HMP_IMPORTER -# include "HMPLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER -# include "SMDLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER -# include "MDCLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER -# include "MD5Loader.h" -#endif -#ifndef ASSIMP_BUILD_NO_STL_IMPORTER -# include "STLLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER -# include "LWOLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER -# include "DXFLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER -# include "NFFLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER -# include "RawLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_SIB_IMPORTER -# include "SIBImporter.h" -#endif -#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER -# include "OFFLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_AC_IMPORTER -# include "ACLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER -# include "BVHLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER -# include "IRRMeshLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER -# include "IRRLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER -# include "Q3DLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER -# include "B3DImporter.h" -#endif -#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER -# include "ColladaLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER -# include "TerragenLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_CSM_IMPORTER -# include "CSMLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_3D_IMPORTER -# include "UnrealLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_LWS_IMPORTER -# include "LWSLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER -# include "OgreImporter.h" -#endif -#ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER -# include "OpenGEXImporter.h" -#endif -#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER -# include "MS3DLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_COB_IMPORTER -# include "COBLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER -# include "BlenderLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER -# include "Q3BSPFileImporter.h" -#endif -#ifndef ASSIMP_BUILD_NO_NDO_IMPORTER -# include "NDOLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER -# include "Importer/IFC/IFCLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_XGL_IMPORTER -# include "XGLLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER -# include "FBXImporter.h" -#endif -#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER -# include "AssbinLoader.h" -#endif -#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER -# include "glTFImporter.h" -# include "glTF2Importer.h" -#endif -#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER -# include "C4DImporter.h" -#endif -#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER -# include "D3MFImporter.h" -#endif -#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER -# include "X3DImporter.hpp" -#endif -#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER -# include "MMDImporter.h" -#endif -#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER -# include "Importer/StepFile/StepFileImporter.h" -#endif - -namespace Assimp { - -// ------------------------------------------------------------------------------------------------ -void GetImporterInstanceList(std::vector< BaseImporter* >& out) -{ - // ---------------------------------------------------------------------------- - // Add an instance of each worker class here - // (register_new_importers_here) - // ---------------------------------------------------------------------------- - out.reserve(64); -#if (!defined ASSIMP_BUILD_NO_X_IMPORTER) - out.push_back( new XFileImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_OBJ_IMPORTER) - out.push_back( new ObjFileImporter()); -#endif -#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER - out.push_back( new AMFImporter() ); -#endif -#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER) - out.push_back( new Discreet3DSImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_MD3_IMPORTER) - out.push_back( new MD3Importer()); -#endif -#if (!defined ASSIMP_BUILD_NO_MD2_IMPORTER) - out.push_back( new MD2Importer()); -#endif -#if (!defined ASSIMP_BUILD_NO_PLY_IMPORTER) - out.push_back( new PLYImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_MDL_IMPORTER) - out.push_back( new MDLImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_ASE_IMPORTER) - #if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER) - out.push_back( new ASEImporter()); -# endif -#endif -#if (!defined ASSIMP_BUILD_NO_HMP_IMPORTER) - out.push_back( new HMPImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_SMD_IMPORTER) - out.push_back( new SMDImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_MDC_IMPORTER) - out.push_back( new MDCImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_MD5_IMPORTER) - out.push_back( new MD5Importer()); -#endif -#if (!defined ASSIMP_BUILD_NO_STL_IMPORTER) - out.push_back( new STLImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER) - out.push_back( new LWOImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_DXF_IMPORTER) - out.push_back( new DXFImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_NFF_IMPORTER) - out.push_back( new NFFImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_RAW_IMPORTER) - out.push_back( new RAWImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_SIB_IMPORTER) - out.push_back( new SIBImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_OFF_IMPORTER) - out.push_back( new OFFImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_AC_IMPORTER) - out.push_back( new AC3DImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_BVH_IMPORTER) - out.push_back( new BVHLoader()); -#endif -#if (!defined ASSIMP_BUILD_NO_IRRMESH_IMPORTER) - out.push_back( new IRRMeshImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_IRR_IMPORTER) - out.push_back( new IRRImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_Q3D_IMPORTER) - out.push_back( new Q3DImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_B3D_IMPORTER) - out.push_back( new B3DImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_COLLADA_IMPORTER) - out.push_back( new ColladaLoader()); -#endif -#if (!defined ASSIMP_BUILD_NO_TERRAGEN_IMPORTER) - out.push_back( new TerragenImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_CSM_IMPORTER) - out.push_back( new CSMImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_3D_IMPORTER) - out.push_back( new UnrealImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_LWS_IMPORTER) - out.push_back( new LWSImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_OGRE_IMPORTER) - out.push_back( new Ogre::OgreImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_OPENGEX_IMPORTER ) - out.push_back( new OpenGEX::OpenGEXImporter() ); -#endif -#if (!defined ASSIMP_BUILD_NO_MS3D_IMPORTER) - out.push_back( new MS3DImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_COB_IMPORTER) - out.push_back( new COBImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_BLEND_IMPORTER) - out.push_back( new BlenderImporter()); -#endif -#if (!defined ASSIMP_BUILD_NO_Q3BSP_IMPORTER) - out.push_back( new Q3BSPFileImporter() ); -#endif -#if (!defined ASSIMP_BUILD_NO_NDO_IMPORTER) - out.push_back( new NDOImporter() ); -#endif -#if (!defined ASSIMP_BUILD_NO_IFC_IMPORTER) - out.push_back( new IFCImporter() ); -#endif -#if ( !defined ASSIMP_BUILD_NO_XGL_IMPORTER ) - out.push_back( new XGLImporter() ); -#endif -#if ( !defined ASSIMP_BUILD_NO_FBX_IMPORTER ) - out.push_back( new FBXImporter() ); -#endif -#if ( !defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER ) - out.push_back( new AssbinImporter() ); -#endif -#if ( !defined ASSIMP_BUILD_NO_GLTF_IMPORTER ) - out.push_back( new glTFImporter() ); - out.push_back( new glTF2Importer() ); -#endif -#if ( !defined ASSIMP_BUILD_NO_C4D_IMPORTER ) - out.push_back( new C4DImporter() ); -#endif -#if ( !defined ASSIMP_BUILD_NO_3MF_IMPORTER ) - out.push_back( new D3MFImporter() ); -#endif -#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER - out.push_back( new X3DImporter() ); -#endif -#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER - out.push_back( new MMDImporter() ); -#endif -#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER - out.push_back(new StepFile::StepFileImporter()); -#endif -} - -/** will delete all registered importers. */ -void DeleteImporterInstanceList(std::vector< BaseImporter* >& deleteList){ - for(size_t i= 0; i - * The algorithm is roughly basing on this paper: - * http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf - * .. although overdraw rduction isn't implemented yet ... - */ - - - -// internal headers -#include "ImproveCacheLocality.h" -#include "VertexTriangleAdjacency.h" -#include -#include -#include -#include -#include -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() { - configCacheDepth = PP_ICL_PTCACHE_SIZE; -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -ImproveCacheLocalityProcess::~ImproveCacheLocalityProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool ImproveCacheLocalityProcess::IsActive( unsigned int pFlags) const -{ - return (pFlags & aiProcess_ImproveCacheLocality) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Setup configuration -void ImproveCacheLocalityProcess::SetupProperties(const Importer* pImp) -{ - // AI_CONFIG_PP_ICL_PTCACHE_SIZE controls the target cache size for the optimizer - configCacheDepth = pImp->GetPropertyInteger(AI_CONFIG_PP_ICL_PTCACHE_SIZE,PP_ICL_PTCACHE_SIZE); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void ImproveCacheLocalityProcess::Execute( aiScene* pScene) -{ - if (!pScene->mNumMeshes) { - ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess skipped; there are no meshes"); - return; - } - - ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess begin"); - - float out = 0.f; - unsigned int numf = 0, numm = 0; - for( unsigned int a = 0; a < pScene->mNumMeshes; a++){ - const float res = ProcessMesh( pScene->mMeshes[a],a); - if (res) { - numf += pScene->mMeshes[a]->mNumFaces; - out += res; - ++numm; - } - } - if (!DefaultLogger::isNullLogger()) { - if (numf > 0) { - ASSIMP_LOG_INFO_F("Cache relevant are ", numm, " meshes (", numf, " faces). Average output ACMR is ", out / numf); - } - ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess finished. "); - } -} - -// ------------------------------------------------------------------------------------------------ -// Improves the cache coherency of a specific mesh -float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshNum) -{ - // TODO: rewrite this to use std::vector or boost::shared_array - ai_assert(NULL != pMesh); - - // Check whether the input data is valid - // - there must be vertices and faces - // - all faces must be triangulated or we can't operate on them - if (!pMesh->HasFaces() || !pMesh->HasPositions()) - return 0.f; - - if (pMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) { - ASSIMP_LOG_ERROR("This algorithm works on triangle meshes only"); - return 0.f; - } - - if(pMesh->mNumVertices <= configCacheDepth) { - return 0.f; - } - - float fACMR = 3.f; - const aiFace* const pcEnd = pMesh->mFaces+pMesh->mNumFaces; - - // Input ACMR is for logging purposes only - if (!DefaultLogger::isNullLogger()) { - - unsigned int* piFIFOStack = new unsigned int[configCacheDepth]; - memset(piFIFOStack,0xff,configCacheDepth*sizeof(unsigned int)); - unsigned int* piCur = piFIFOStack; - const unsigned int* const piCurEnd = piFIFOStack + configCacheDepth; - - // count the number of cache misses - unsigned int iCacheMisses = 0; - for (const aiFace* pcFace = pMesh->mFaces;pcFace != pcEnd;++pcFace) { - - for (unsigned int qq = 0; qq < 3;++qq) { - bool bInCache = false; - - for (unsigned int* pp = piFIFOStack;pp < piCurEnd;++pp) { - if (*pp == pcFace->mIndices[qq]) { - // the vertex is in cache - bInCache = true; - break; - } - } - if (!bInCache) { - ++iCacheMisses; - if (piCurEnd == piCur) { - piCur = piFIFOStack; - } - *piCur++ = pcFace->mIndices[qq]; - } - } - } - delete[] piFIFOStack; - fACMR = (float)iCacheMisses / pMesh->mNumFaces; - if (3.0 == fACMR) { - char szBuff[128]; // should be sufficiently large in every case - - // the JoinIdenticalVertices process has not been executed on this - // mesh, otherwise this value would normally be at least minimally - // smaller than 3.0 ... - ai_snprintf(szBuff,128,"Mesh %u: Not suitable for vcache optimization",meshNum); - ASSIMP_LOG_WARN(szBuff); - return 0.f; - } - } - - // first we need to build a vertex-triangle adjacency list - VertexTriangleAdjacency adj(pMesh->mFaces,pMesh->mNumFaces, pMesh->mNumVertices,true); - - // build a list to store per-vertex caching time stamps - unsigned int* const piCachingStamps = new unsigned int[pMesh->mNumVertices]; - memset(piCachingStamps,0x0,pMesh->mNumVertices*sizeof(unsigned int)); - - // allocate an empty output index buffer. We store the output indices in one large array. - // Since the number of triangles won't change the input faces can be reused. This is how - // we save thousands of redundant mini allocations for aiFace::mIndices - const unsigned int iIdxCnt = pMesh->mNumFaces*3; - unsigned int* const piIBOutput = new unsigned int[iIdxCnt]; - unsigned int* piCSIter = piIBOutput; - - // allocate the flag array to hold the information - // whether a face has already been emitted or not - std::vector abEmitted(pMesh->mNumFaces,false); - - // dead-end vertex index stack - std::stack > sDeadEndVStack; - - // create a copy of the piNumTriPtr buffer - unsigned int* const piNumTriPtr = adj.mLiveTriangles; - const std::vector piNumTriPtrNoModify(piNumTriPtr, piNumTriPtr + pMesh->mNumVertices); - - // get the largest number of referenced triangles and allocate the "candidate buffer" - unsigned int iMaxRefTris = 0; { - const unsigned int* piCur = adj.mLiveTriangles; - const unsigned int* const piCurEnd = adj.mLiveTriangles+pMesh->mNumVertices; - for (;piCur != piCurEnd;++piCur) { - iMaxRefTris = std::max(iMaxRefTris,*piCur); - } - } - ai_assert(iMaxRefTris > 0); - unsigned int* piCandidates = new unsigned int[iMaxRefTris*3]; - unsigned int iCacheMisses = 0; - - // ................................................................................... - /** PSEUDOCODE for the algorithm - - A = Build-Adjacency(I) Vertex-triangle adjacency - L = Get-Triangle-Counts(A) Per-vertex live triangle counts - C = Zero(Vertex-Count(I)) Per-vertex caching time stamps - D = Empty-Stack() Dead-end vertex stack - E = False(Triangle-Count(I)) Per triangle emitted flag - O = Empty-Index-Buffer() Empty output buffer - f = 0 Arbitrary starting vertex - s = k+1, i = 1 Time stamp and cursor - while f >= 0 For all valid fanning vertices - N = Empty-Set() 1-ring of next candidates - for each Triangle t in Neighbors(A, f) - if !Emitted(E,t) - for each Vertex v in t - Append(O,v) Output vertex - Push(D,v) Add to dead-end stack - Insert(N,v) Register as candidate - L[v] = L[v]-1 Decrease live triangle count - if s-C[v] > k If not in cache - C[v] = s Set time stamp - s = s+1 Increment time stamp - E[t] = true Flag triangle as emitted - Select next fanning vertex - f = Get-Next-Vertex(I,i,k,N,C,s,L,D) - return O - */ - // ................................................................................... - - int ivdx = 0; - int ics = 1; - int iStampCnt = configCacheDepth+1; - while (ivdx >= 0) { - - unsigned int icnt = piNumTriPtrNoModify[ivdx]; - unsigned int* piList = adj.GetAdjacentTriangles(ivdx); - unsigned int* piCurCandidate = piCandidates; - - // get all triangles in the neighborhood - for (unsigned int tri = 0; tri < icnt;++tri) { - - // if they have not yet been emitted, add them to the output IB - const unsigned int fidx = *piList++; - if (!abEmitted[fidx]) { - - // so iterate through all vertices of the current triangle - const aiFace* pcFace = &pMesh->mFaces[ fidx ]; - unsigned nind = pcFace->mNumIndices; - for (unsigned ind = 0; ind < nind; ind++) { - unsigned dp = pcFace->mIndices[ind]; - - // the current vertex won't have any free triangles after this step - if (ivdx != (int)dp) { - // append the vertex to the dead-end stack - sDeadEndVStack.push(dp); - - // register as candidate for the next step - *piCurCandidate++ = dp; - - // decrease the per-vertex triangle counts - piNumTriPtr[dp]--; - } - - // append the vertex to the output index buffer - *piCSIter++ = dp; - - // if the vertex is not yet in cache, set its cache count - if (iStampCnt-piCachingStamps[dp] > configCacheDepth) { - piCachingStamps[dp] = iStampCnt++; - ++iCacheMisses; - } - } - // flag triangle as emitted - abEmitted[fidx] = true; - } - } - - // the vertex has now no living adjacent triangles anymore - piNumTriPtr[ivdx] = 0; - - // get next fanning vertex - ivdx = -1; - int max_priority = -1; - for (unsigned int* piCur = piCandidates;piCur != piCurCandidate;++piCur) { - const unsigned int dp = *piCur; - - // must have live triangles - if (piNumTriPtr[dp] > 0) { - int priority = 0; - - // will the vertex be in cache, even after fanning occurs? - unsigned int tmp; - if ((tmp = iStampCnt-piCachingStamps[dp]) + 2*piNumTriPtr[dp] <= configCacheDepth) { - priority = tmp; - } - - // keep best candidate - if (priority > max_priority) { - max_priority = priority; - ivdx = dp; - } - } - } - // did we reach a dead end? - if (-1 == ivdx) { - // need to get a non-local vertex for which we have a good chance that it is still - // in the cache ... - while (!sDeadEndVStack.empty()) { - unsigned int iCachedIdx = sDeadEndVStack.top(); - sDeadEndVStack.pop(); - if (piNumTriPtr[ iCachedIdx ] > 0) { - ivdx = iCachedIdx; - break; - } - } - - if (-1 == ivdx) { - // well, there isn't such a vertex. Simply get the next vertex in input order and - // hope it is not too bad ... - while (ics < (int)pMesh->mNumVertices) { - ++ics; - if (piNumTriPtr[ics] > 0) { - ivdx = ics; - break; - } - } - } - } - } - float fACMR2 = 0.0f; - if (!DefaultLogger::isNullLogger()) { - fACMR2 = (float)iCacheMisses / pMesh->mNumFaces; - - // very intense verbose logging ... prepare for much text if there are many meshes - if ( DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) { - ASSIMP_LOG_DEBUG_F("Mesh %u | ACMR in: ", meshNum, " out: ", fACMR, " | ~", fACMR2, ((fACMR - fACMR2) / fACMR) * 100.f); - } - - fACMR2 *= pMesh->mNumFaces; - } - // sort the output index buffer back to the input array - piCSIter = piIBOutput; - for (aiFace* pcFace = pMesh->mFaces; pcFace != pcEnd;++pcFace) { - unsigned nind = pcFace->mNumIndices; - unsigned * ind = pcFace->mIndices; - if (nind > 0) ind[0] = *piCSIter++; - if (nind > 1) ind[1] = *piCSIter++; - if (nind > 2) ind[2] = *piCSIter++; - } - - // delete temporary storage - delete[] piCachingStamps; - delete[] piIBOutput; - delete[] piCandidates; - - return fACMR2; -} diff --git a/thirdparty/assimp/code/ImproveCacheLocality.h b/thirdparty/assimp/code/ImproveCacheLocality.h deleted file mode 100644 index 1b29ee0d6e..0000000000 --- a/thirdparty/assimp/code/ImproveCacheLocality.h +++ /dev/null @@ -1,100 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a post processing step to reorder faces for - better cache locality*/ -#ifndef AI_IMPROVECACHELOCALITY_H_INC -#define AI_IMPROVECACHELOCALITY_H_INC - -#include "BaseProcess.h" -#include - -struct aiMesh; - -namespace Assimp -{ - -// --------------------------------------------------------------------------- -/** The ImproveCacheLocalityProcess reorders all faces for improved vertex - * cache locality. It tries to arrange all faces to fans and to render - * faces which share vertices directly one after the other. - * - * @note This step expects triagulated input data. - */ -class ImproveCacheLocalityProcess : public BaseProcess -{ -public: - - ImproveCacheLocalityProcess(); - ~ImproveCacheLocalityProcess(); - -public: - - // ------------------------------------------------------------------- - // Check whether the pp step is active - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - // Executes the pp step on a given scene - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - // Configures the pp step - void SetupProperties(const Importer* pImp); - -protected: - // ------------------------------------------------------------------- - /** Executes the postprocessing step on the given mesh - * @param pMesh The mesh to process. - * @param meshNum Index of the mesh to process - */ - float ProcessMesh( aiMesh* pMesh, unsigned int meshNum); - -private: - //! Configuration parameter: specifies the size of the cache to - //! optimize the vertex data for. - unsigned int configCacheDepth; -}; - -} // end of namespace Assimp - -#endif // AI_IMPROVECACHELOCALITY_H_INC diff --git a/thirdparty/assimp/code/JoinVerticesProcess.cpp b/thirdparty/assimp/code/JoinVerticesProcess.cpp deleted file mode 100644 index 914ec05b46..0000000000 --- a/thirdparty/assimp/code/JoinVerticesProcess.cpp +++ /dev/null @@ -1,463 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of the post processing step to join identical vertices - * for all imported meshes - */ - - -#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS - -#include "JoinVerticesProcess.h" -#include "ProcessHelper.h" -#include -#include -#include -#include - -using namespace Assimp; -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -JoinVerticesProcess::JoinVerticesProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -JoinVerticesProcess::~JoinVerticesProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool JoinVerticesProcess::IsActive( unsigned int pFlags) const -{ - return (pFlags & aiProcess_JoinIdenticalVertices) != 0; -} -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void JoinVerticesProcess::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("JoinVerticesProcess begin"); - - // get the total number of vertices BEFORE the step is executed - int iNumOldVertices = 0; - if (!DefaultLogger::isNullLogger()) { - for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { - iNumOldVertices += pScene->mMeshes[a]->mNumVertices; - } - } - - // execute the step - int iNumVertices = 0; - for( unsigned int a = 0; a < pScene->mNumMeshes; a++) - iNumVertices += ProcessMesh( pScene->mMeshes[a],a); - - // if logging is active, print detailed statistics - if (!DefaultLogger::isNullLogger()) { - if (iNumOldVertices == iNumVertices) { - ASSIMP_LOG_DEBUG("JoinVerticesProcess finished "); - } else { - ASSIMP_LOG_INFO_F("JoinVerticesProcess finished | Verts in: ", iNumOldVertices, - " out: ", iNumVertices, " | ~", - ((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f ); - } - } - - pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; -} - -namespace { - -bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex) -{ - // A little helper to find locally close vertices faster. - // Try to reuse the lookup table from the last step. - const static float epsilon = 1e-5f; - // Squared because we check against squared length of the vector difference - static const float squareEpsilon = epsilon * epsilon; - - // Square compare is useful for animeshes vertices compare - if ((lhs.position - rhs.position).SquareLength() > squareEpsilon) { - return false; - } - - // We just test the other attributes even if they're not present in the mesh. - // In this case they're initialized to 0 so the comparison succeeds. - // By this method the non-present attributes are effectively ignored in the comparison. - if ((lhs.normal - rhs.normal).SquareLength() > squareEpsilon) { - return false; - } - - if ((lhs.texcoords[0] - rhs.texcoords[0]).SquareLength() > squareEpsilon) { - return false; - } - - if ((lhs.tangent - rhs.tangent).SquareLength() > squareEpsilon) { - return false; - } - - if ((lhs.bitangent - rhs.bitangent).SquareLength() > squareEpsilon) { - return false; - } - - // Usually we won't have vertex colors or multiple UVs, so we can skip from here - // Actually this increases runtime performance slightly, at least if branch - // prediction is on our side. - if (complex) { - for (int i = 0; i < 8; i++) { - if (i > 0 && (lhs.texcoords[i] - rhs.texcoords[i]).SquareLength() > squareEpsilon) { - return false; - } - if (GetColorDifference(lhs.colors[i], rhs.colors[i]) > squareEpsilon) { - return false; - } - } - } - return true; -} - -template -void updateXMeshVertices(XMesh *pMesh, std::vector &uniqueVertices) { - // replace vertex data with the unique data sets - pMesh->mNumVertices = (unsigned int)uniqueVertices.size(); - - // ---------------------------------------------------------------------------- - // NOTE - we're *not* calling Vertex::SortBack() because it would check for - // presence of every single vertex component once PER VERTEX. And our CPU - // dislikes branches, even if they're easily predictable. - // ---------------------------------------------------------------------------- - - // Position, if present (check made for aiAnimMesh) - if (pMesh->mVertices) - { - delete [] pMesh->mVertices; - pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; - for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { - pMesh->mVertices[a] = uniqueVertices[a].position; - } - } - - // Normals, if present - if (pMesh->mNormals) - { - delete [] pMesh->mNormals; - pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; - for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { - pMesh->mNormals[a] = uniqueVertices[a].normal; - } - } - // Tangents, if present - if (pMesh->mTangents) - { - delete [] pMesh->mTangents; - pMesh->mTangents = new aiVector3D[pMesh->mNumVertices]; - for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { - pMesh->mTangents[a] = uniqueVertices[a].tangent; - } - } - // Bitangents as well - if (pMesh->mBitangents) - { - delete [] pMesh->mBitangents; - pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices]; - for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { - pMesh->mBitangents[a] = uniqueVertices[a].bitangent; - } - } - // Vertex colors - for (unsigned int a = 0; pMesh->HasVertexColors(a); a++) - { - delete [] pMesh->mColors[a]; - pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices]; - for( unsigned int b = 0; b < pMesh->mNumVertices; b++) { - pMesh->mColors[a][b] = uniqueVertices[b].colors[a]; - } - } - // Texture coords - for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++) - { - delete [] pMesh->mTextureCoords[a]; - pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices]; - for (unsigned int b = 0; b < pMesh->mNumVertices; b++) { - pMesh->mTextureCoords[a][b] = uniqueVertices[b].texcoords[a]; - } - } -} -} // namespace - -// ------------------------------------------------------------------------------------------------ -// Unites identical vertices in the given mesh -int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) -{ - static_assert( AI_MAX_NUMBER_OF_COLOR_SETS == 8, "AI_MAX_NUMBER_OF_COLOR_SETS == 8"); - static_assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 8, "AI_MAX_NUMBER_OF_TEXTURECOORDS == 8"); - - // Return early if we don't have any positions - if (!pMesh->HasPositions() || !pMesh->HasFaces()) { - return 0; - } - - // We should care only about used vertices, not all of them - // (this can happen due to original file vertices buffer being used by - // multiple meshes) - std::unordered_set usedVertexIndices; - usedVertexIndices.reserve(pMesh->mNumVertices); - for( unsigned int a = 0; a < pMesh->mNumFaces; a++) - { - aiFace& face = pMesh->mFaces[a]; - for( unsigned int b = 0; b < face.mNumIndices; b++) { - usedVertexIndices.insert(face.mIndices[b]); - } - } - - // We'll never have more vertices afterwards. - std::vector 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 and greatly enhances - // branching performance. - static_assert(AI_MAX_VERTICES == 0x7fffffff, "AI_MAX_VERTICES == 0x7fffffff"); - std::vector replaceIndex( pMesh->mNumVertices, 0xffffffff); - - // float posEpsilonSqr; - SpatialSort* vertexFinder = NULL; - SpatialSort _vertexFinder; - - typedef std::pair SpatPair; - if (shared) { - std::vector* avf; - shared->GetProperty(AI_SPP_SPATIAL_SORT,avf); - if (avf) { - SpatPair& blubb = (*avf)[meshIndex]; - vertexFinder = &blubb.first; - // posEpsilonSqr = blubb.second; - } - } - if (!vertexFinder) { - // bad, need to compute it. - _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D)); - vertexFinder = &_vertexFinder; - // posEpsilonSqr = ComputePositionEpsilon(pMesh); - } - - // Again, better waste some bytes than a realloc ... - std::vector verticesFound; - verticesFound.reserve(10); - - // Run an optimized code path if we don't have multiple UVs or vertex colors. - // This should yield false in more than 99% of all imports ... - const bool complex = ( pMesh->GetNumColorChannels() > 0 || pMesh->GetNumUVChannels() > 1); - const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0; - - // We'll never have more vertices afterwards. - std::vector> uniqueAnimatedVertices; - if (hasAnimMeshes) { - uniqueAnimatedVertices.resize(pMesh->mNumAnimMeshes); - for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { - uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices); - } - } - - // Now check each vertex if it brings something new to the table - for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { - if (usedVertexIndices.find(a) == usedVertexIndices.end()) { - continue; - } - - // collect the vertex data - Vertex v(pMesh,a); - - // collect all vertices that are close enough to the given position - vertexFinder->FindIdenticalPositions( v.position, verticesFound); - unsigned int matchIndex = 0xffffffff; - - // check all unique vertices close to the position if this vertex is already present among them - for( unsigned int b = 0; b < verticesFound.size(); b++) { - const unsigned int vidx = verticesFound[b]; - const unsigned int uidx = replaceIndex[ vidx]; - if( uidx & 0x80000000) - continue; - - const Vertex& uv = uniqueVertices[ uidx]; - - if (!areVerticesEqual(v, uv, complex)) { - continue; - } - - if (hasAnimMeshes) { - // If given vertex is animated, then it has to be preserver 1 to 1 (base mesh and animated mesh require same topology) - // NOTE: not doing this totaly breaks anim meshes as they don't have their own faces (they use pMesh->mFaces) - bool breaksAnimMesh = false; - for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { - const Vertex& animatedUV = uniqueAnimatedVertices[animMeshIndex][ uidx]; - Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a); - if (!areVerticesEqual(aniMeshVertex, animatedUV, complex)) { - breaksAnimMesh = true; - break; - } - } - if (breaksAnimMesh) { - continue; - } - } - - // we're still here -> this vertex perfectly matches our given vertex - matchIndex = uidx; - break; - } - - // found a replacement vertex among the uniques? - if( matchIndex != 0xffffffff) - { - // store where to found the matching unique vertex - replaceIndex[a] = matchIndex | 0x80000000; - } - else - { - // no unique vertex matches it up to now -> so add it - replaceIndex[a] = (unsigned int)uniqueVertices.size(); - uniqueVertices.push_back( v); - if (hasAnimMeshes) { - for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { - Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a); - uniqueAnimatedVertices[animMeshIndex].push_back(aniMeshVertex); - } - } - } - } - - if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) { - ASSIMP_LOG_DEBUG_F( - "Mesh ",meshIndex, - " (", - (pMesh->mName.length ? pMesh->mName.data : "unnamed"), - ") | Verts in: ",pMesh->mNumVertices, - " out: ", - uniqueVertices.size(), - " | ~", - ((pMesh->mNumVertices - uniqueVertices.size()) / (float)pMesh->mNumVertices) * 100.f, - "%" - ); - } - - updateXMeshVertices(pMesh, uniqueVertices); - if (hasAnimMeshes) { - for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { - updateXMeshVertices(pMesh->mAnimMeshes[animMeshIndex], uniqueAnimatedVertices[animMeshIndex]); - } - } - - // adjust the indices in all faces - for( unsigned int a = 0; a < pMesh->mNumFaces; a++) - { - aiFace& face = pMesh->mFaces[a]; - for( unsigned int b = 0; b < face.mNumIndices; b++) { - face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000; - } - } - - // adjust bone vertex weights. - for( int a = 0; a < (int)pMesh->mNumBones; a++) { - aiBone* bone = pMesh->mBones[a]; - std::vector newWeights; - newWeights.reserve( bone->mNumWeights); - - if ( NULL != bone->mWeights ) { - for ( unsigned int b = 0; b < bone->mNumWeights; b++ ) { - const aiVertexWeight& ow = bone->mWeights[ b ]; - // if the vertex is a unique one, translate it - if ( !( replaceIndex[ ow.mVertexId ] & 0x80000000 ) ) { - aiVertexWeight nw; - nw.mVertexId = replaceIndex[ ow.mVertexId ]; - nw.mWeight = ow.mWeight; - newWeights.push_back( nw ); - } - } - } else { - ASSIMP_LOG_ERROR( "X-Export: aiBone shall contain weights, but pointer to them is NULL." ); - } - - if (newWeights.size() > 0) { - // kill the old and replace them with the translated weights - delete [] bone->mWeights; - bone->mNumWeights = (unsigned int)newWeights.size(); - - bone->mWeights = new aiVertexWeight[bone->mNumWeights]; - memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight)); - } - else { - - /* NOTE: - * - * In the algorithm above we're assuming that there are no vertices - * with a different bone weight setup at the same position. That wouldn't - * make sense, but it is not absolutely impossible. SkeletonMeshBuilder - * for example generates such input data if two skeleton points - * share the same position. Again this doesn't make sense but is - * reality for some model formats (MD5 for example uses these special - * nodes as attachment tags for its weapons). - * - * Then it is possible that a bone has no weights anymore .... as a quick - * workaround, we're just removing these bones. If they're animated, - * model geometry might be modified but at least there's no risk of a crash. - */ - delete bone; - --pMesh->mNumBones; - for (unsigned int n = a; n < pMesh->mNumBones; ++n) { - pMesh->mBones[n] = pMesh->mBones[n+1]; - } - - --a; - ASSIMP_LOG_WARN("Removing bone -> no weights remaining"); - } - } - return pMesh->mNumVertices; -} - -#endif // !! ASSIMP_BUILD_NO_JOINVERTICES_PROCESS diff --git a/thirdparty/assimp/code/JoinVerticesProcess.h b/thirdparty/assimp/code/JoinVerticesProcess.h deleted file mode 100644 index 66fa362de2..0000000000 --- a/thirdparty/assimp/code/JoinVerticesProcess.h +++ /dev/null @@ -1,99 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file 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 - -struct aiMesh; - -namespace Assimp -{ - -// --------------------------------------------------------------------------- -/** The JoinVerticesProcess unites identical vertices in all imported meshes. - * By default the importer returns meshes where each face addressed its own - * set of vertices even if that means that identical vertices are stored multiple - * times. The JoinVerticesProcess finds these identical vertices and - * erases all but one of the copies. This usually reduces the number of vertices - * in a mesh by a serious amount and is the standard form to render a mesh. - */ -class ASSIMP_API JoinVerticesProcess : public BaseProcess -{ -public: - JoinVerticesProcess(); - ~JoinVerticesProcess(); - -public: - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag field. - * @param pFlags The processing flags the importer was called with. A bitwise - * combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, false if not. - */ - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - -public: - // ------------------------------------------------------------------- - /** Unites identical vertices in the given mesh. - * @param pMesh The mesh to process. - * @param meshIndex Index of the mesh to process - */ - int ProcessMesh( aiMesh* pMesh, unsigned int meshIndex); - -private: -}; - -} // end of namespace Assimp - -#endif // AI_CALCTANGENTSPROCESS_H_INC diff --git a/thirdparty/assimp/code/LimitBoneWeightsProcess.cpp b/thirdparty/assimp/code/LimitBoneWeightsProcess.cpp deleted file mode 100644 index d560f19287..0000000000 --- a/thirdparty/assimp/code/LimitBoneWeightsProcess.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** Implementation of the LimitBoneWeightsProcess post processing step */ - - -#include "LimitBoneWeightsProcess.h" -#include -#include -#include -#include -#include - -using namespace Assimp; - - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -LimitBoneWeightsProcess::LimitBoneWeightsProcess() -{ - mMaxWeights = AI_LMW_MAX_WEIGHTS; -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -LimitBoneWeightsProcess::~LimitBoneWeightsProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const -{ - return (pFlags & aiProcess_LimitBoneWeights) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void LimitBoneWeightsProcess::Execute( aiScene* pScene) { - ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess begin"); - for (unsigned int a = 0; a < pScene->mNumMeshes; ++a ) { - ProcessMesh(pScene->mMeshes[a]); - } - - ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess end"); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp) -{ - // get the current value of the property - this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS); -} - -// ------------------------------------------------------------------------------------------------ -// Unites identical vertices in the given mesh -void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh) -{ - if( !pMesh->HasBones()) - return; - - // collect all bone weights per vertex - typedef std::vector< std::vector< Weight > > WeightsPerVertex; - WeightsPerVertex vertexWeights( pMesh->mNumVertices); - - // collect all weights per vertex - for( unsigned int a = 0; a < pMesh->mNumBones; a++) - { - const aiBone* bone = pMesh->mBones[a]; - for( unsigned int b = 0; b < bone->mNumWeights; b++) - { - const aiVertexWeight& w = bone->mWeights[b]; - vertexWeights[w.mVertexId].push_back( Weight( a, w.mWeight)); - } - } - - unsigned int removed = 0, old_bones = pMesh->mNumBones; - - // now cut the weight count if it exceeds the maximum - bool bChanged = false; - for( WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit) - { - if( vit->size() <= mMaxWeights) - continue; - - bChanged = true; - - // more than the defined maximum -> first sort by weight in descending order. That's - // why we defined the < operator in such a weird way. - std::sort( vit->begin(), vit->end()); - - // now kill everything beyond the maximum count - unsigned int m = static_cast(vit->size()); - vit->erase( vit->begin() + mMaxWeights, vit->end()); - removed += static_cast(m-vit->size()); - - // and renormalize the weights - float sum = 0.0f; - for( std::vector::const_iterator it = vit->begin(); it != vit->end(); ++it ) { - sum += it->mWeight; - } - if( 0.0f != sum ) { - const float invSum = 1.0f / sum; - for( std::vector::iterator it = vit->begin(); it != vit->end(); ++it ) { - it->mWeight *= invSum; - } - } - } - - if (bChanged) { - // rebuild the vertex weight array for all bones - typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone; - WeightsPerBone boneWeights( pMesh->mNumBones); - for( unsigned int a = 0; a < vertexWeights.size(); a++) - { - const std::vector& vw = vertexWeights[a]; - for( std::vector::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 abNoNeed(pMesh->mNumBones,false); - bChanged = false; - - for( unsigned int a = 0; a < pMesh->mNumBones; a++) - { - const std::vector& bw = boneWeights[a]; - aiBone* bone = pMesh->mBones[a]; - - if ( bw.empty() ) - { - abNoNeed[a] = bChanged = true; - continue; - } - - // copy the weight list. should always be less weights than before, so we don't need a new allocation - ai_assert( bw.size() <= bone->mNumWeights); - bone->mNumWeights = static_cast( 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::const_iterator iter = abNoNeed.begin();iter != abNoNeed.end() ;++iter) { - if (*iter) { - delete *ppcSrc; - --pMesh->mNumBones; - } - else *ppcCur++ = *ppcSrc; - ++ppcSrc; - } - } - - if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_INFO_F("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones ); - } - } -} diff --git a/thirdparty/assimp/code/LimitBoneWeightsProcess.h b/thirdparty/assimp/code/LimitBoneWeightsProcess.h deleted file mode 100644 index 3602fd8edf..0000000000 --- a/thirdparty/assimp/code/LimitBoneWeightsProcess.h +++ /dev/null @@ -1,148 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** Defines a post processing step to limit the number of bones affecting a single vertex. */ -#ifndef AI_LIMITBONEWEIGHTSPROCESS_H_INC -#define AI_LIMITBONEWEIGHTSPROCESS_H_INC - -#include "BaseProcess.h" - -struct aiMesh; - -class LimitBoneWeightsTest; - -namespace Assimp -{ - -// NOTE: If you change these limits, don't forget to change the -// corresponding values in all Assimp ports - -// ********************************************************** -// Java: ConfigProperty.java, -// ConfigProperty.DEFAULT_BONE_WEIGHT_LIMIT -// ********************************************************** - -#if (!defined AI_LMW_MAX_WEIGHTS) -# define AI_LMW_MAX_WEIGHTS 0x4 -#endif // !! AI_LMW_MAX_WEIGHTS - -// --------------------------------------------------------------------------- -/** This post processing step limits the number of bones affecting a vertex -* to a certain maximum value. If a vertex is affected by more than that number -* of bones, the bone weight with the least influence on this vertex are removed. -* The other weights on this bone are then renormalized to assure the sum weight -* to be 1. -*/ -class ASSIMP_API LimitBoneWeightsProcess : public BaseProcess -{ -public: - - LimitBoneWeightsProcess(); - ~LimitBoneWeightsProcess(); - -public: - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag. - * @param pFlags The processing flags the importer was called with. - * A bitwise combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, - * false if not. - */ - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - /** Called prior to ExecuteOnScene(). - * The function is a request to the process to update its configuration - * basing on the Importer's configuration property list. - */ - void SetupProperties(const Importer* pImp); - -public: - - // ------------------------------------------------------------------- - /** Limits the bone weight count for all vertices in the given mesh. - * @param pMesh The mesh to process. - */ - void ProcessMesh( aiMesh* pMesh); - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - - -public: - - // ------------------------------------------------------------------- - /** Describes a bone weight on a vertex */ - struct Weight - { - unsigned int mBone; ///< Index of the bone - float mWeight; ///< Weight of that bone on this vertex - Weight() AI_NO_EXCEPT - : mBone(0) - , mWeight(0.0f) - { } - - Weight( unsigned int pBone, float pWeight) - { - mBone = pBone; - mWeight = pWeight; - } - - /** Comparison operator to sort bone weights by descending weight */ - bool operator < (const Weight& pWeight) const - { - return mWeight > pWeight.mWeight; - } - }; - -public: - /** Maximum number of bones influencing any single vertex. */ - unsigned int mMaxWeights; -}; - -} // end of namespace Assimp - -#endif // AI_LIMITBONEWEIGHTSPROCESS_H_INC diff --git a/thirdparty/assimp/code/MMD/MMDCpp14.h b/thirdparty/assimp/code/MMD/MMDCpp14.h new file mode 100644 index 0000000000..638b0bfd2f --- /dev/null +++ b/thirdparty/assimp/code/MMD/MMDCpp14.h @@ -0,0 +1,83 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#ifndef MMD_CPP14_H +#define MMD_CPP14_H + +#include +#include +#include +#include + +namespace mmd { + template struct _Unique_if { + typedef std::unique_ptr _Single_object; + }; + + template struct _Unique_if { + typedef std::unique_ptr _Unknown_bound; + }; + + template struct _Unique_if { + typedef void _Known_bound; + }; + + template + typename _Unique_if::_Single_object + make_unique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); + } + + template + typename _Unique_if::_Unknown_bound + make_unique(size_t n) { + typedef typename std::remove_extent::type U; + return std::unique_ptr(new U[n]()); + } + + template + typename _Unique_if::_Known_bound + make_unique(Args&&...) = delete; +} + +#endif diff --git a/thirdparty/assimp/code/MMD/MMDImporter.cpp b/thirdparty/assimp/code/MMD/MMDImporter.cpp new file mode 100644 index 0000000000..e7744e4cd0 --- /dev/null +++ b/thirdparty/assimp/code/MMD/MMDImporter.cpp @@ -0,0 +1,372 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2016, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER + +#include "MMD/MMDImporter.h" +#include "MMD/MMDPmdParser.h" +#include "MMD/MMDPmxParser.h" +#include "MMD/MMDVmdParser.h" +#include "PostProcessing/ConvertToLHProcess.h" + +#include +#include +#include +#include + +#include +#include +#include + +static const aiImporterDesc desc = {"MMD Importer", + "", + "", + "surfaces supported?", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "pmx"}; + +namespace Assimp { + +using namespace std; + +// ------------------------------------------------------------------------------------------------ +// Default constructor +MMDImporter::MMDImporter() +: m_Buffer() +, m_strAbsPath("") { + DefaultIOSystem io; + m_strAbsPath = io.getOsSeparator(); +} + +// ------------------------------------------------------------------------------------------------ +// Destructor. +MMDImporter::~MMDImporter() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Returns true, if file is an pmx file. +bool MMDImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, + bool checkSig) const { + if (!checkSig) // Check File Extension + { + return SimpleExtensionCheck(pFile, "pmx"); + } else // Check file Header + { + static const char *pTokens[] = {"PMX "}; + return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 1); + } +} + +// ------------------------------------------------------------------------------------------------ +const aiImporterDesc *MMDImporter::GetInfo() const { return &desc; } + +// ------------------------------------------------------------------------------------------------ +// MMD import implementation +void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene, + IOSystem * /*pIOHandler*/) { + // Read file by istream + std::filebuf fb; + if (!fb.open(file, std::ios::in | std::ios::binary)) { + throw DeadlyImportError("Failed to open file " + file + "."); + } + + std::istream fileStream(&fb); + + // Get the file-size and validate it, throwing an exception when fails + fileStream.seekg(0, fileStream.end); + size_t fileSize = static_cast(fileStream.tellg()); + fileStream.seekg(0, fileStream.beg); + + if (fileSize < sizeof(pmx::PmxModel)) { + throw DeadlyImportError(file + " is too small."); + } + + pmx::PmxModel model; + model.Read(&fileStream); + + CreateDataFromImport(&model, pScene); +} + +// ------------------------------------------------------------------------------------------------ +void MMDImporter::CreateDataFromImport(const pmx::PmxModel *pModel, + aiScene *pScene) { + if (pModel == NULL) { + return; + } + + aiNode *pNode = new aiNode; + if (!pModel->model_name.empty()) { + pNode->mName.Set(pModel->model_name); + } + + pScene->mRootNode = pNode; + + pNode = new aiNode; + pScene->mRootNode->addChildren(1, &pNode); + pNode->mName.Set(string(pModel->model_name) + string("_mesh")); + + // split mesh by materials + pNode->mNumMeshes = pModel->material_count; + pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; + for (unsigned int index = 0; index < pNode->mNumMeshes; index++) { + pNode->mMeshes[index] = index; + } + + pScene->mNumMeshes = pModel->material_count; + pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; + for (unsigned int i = 0, indexStart = 0; i < pScene->mNumMeshes; i++) { + const int indexCount = pModel->materials[i].index_count; + + pScene->mMeshes[i] = CreateMesh(pModel, indexStart, indexCount); + pScene->mMeshes[i]->mName = pModel->materials[i].material_name; + pScene->mMeshes[i]->mMaterialIndex = i; + indexStart += indexCount; + } + + // create node hierarchy for bone position + std::unique_ptr ppNode(new aiNode *[pModel->bone_count]); + for (auto i = 0; i < pModel->bone_count; i++) { + ppNode[i] = new aiNode(pModel->bones[i].bone_name); + } + + for (auto i = 0; i < pModel->bone_count; i++) { + const pmx::PmxBone &bone = pModel->bones[i]; + + if (bone.parent_index < 0) { + pScene->mRootNode->addChildren(1, ppNode.get() + i); + } else { + ppNode[bone.parent_index]->addChildren(1, ppNode.get() + i); + + aiVector3D v3 = aiVector3D( + bone.position[0] - pModel->bones[bone.parent_index].position[0], + bone.position[1] - pModel->bones[bone.parent_index].position[1], + bone.position[2] - pModel->bones[bone.parent_index].position[2]); + aiMatrix4x4::Translation(v3, ppNode[i]->mTransformation); + } + } + + // create materials + pScene->mNumMaterials = pModel->material_count; + pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; + for (unsigned int i = 0; i < pScene->mNumMaterials; i++) { + pScene->mMaterials[i] = CreateMaterial(&pModel->materials[i], pModel); + } + + // Convert everything to OpenGL space + MakeLeftHandedProcess convertProcess; + convertProcess.Execute(pScene); + + FlipUVsProcess uvFlipper; + uvFlipper.Execute(pScene); + + FlipWindingOrderProcess windingFlipper; + windingFlipper.Execute(pScene); +} + +// ------------------------------------------------------------------------------------------------ +aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel, + const int indexStart, const int indexCount) { + aiMesh *pMesh = new aiMesh; + + pMesh->mNumVertices = indexCount; + + pMesh->mNumFaces = indexCount / 3; + pMesh->mFaces = new aiFace[pMesh->mNumFaces]; + + const int numIndices = 3; // triangular face + for (unsigned int index = 0; index < pMesh->mNumFaces; index++) { + pMesh->mFaces[index].mNumIndices = numIndices; + unsigned int *indices = new unsigned int[numIndices]; + indices[0] = numIndices * index; + indices[1] = numIndices * index + 1; + indices[2] = numIndices * index + 2; + pMesh->mFaces[index].mIndices = indices; + } + + pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices]; + pMesh->mNumUVComponents[0] = 2; + + // additional UVs + for (int i = 1; i <= pModel->setting.uv; i++) { + pMesh->mTextureCoords[i] = new aiVector3D[pMesh->mNumVertices]; + pMesh->mNumUVComponents[i] = 4; + } + + map> bone_vertex_map; + + // fill in contents and create bones + for (int index = 0; index < indexCount; index++) { + const pmx::PmxVertex *v = + &pModel->vertices[pModel->indices[indexStart + index]]; + const float *position = v->position; + pMesh->mVertices[index].Set(position[0], position[1], position[2]); + const float *normal = v->normal; + + pMesh->mNormals[index].Set(normal[0], normal[1], normal[2]); + pMesh->mTextureCoords[0][index].x = v->uv[0]; + pMesh->mTextureCoords[0][index].y = v->uv[1]; + + for (int i = 1; i <= pModel->setting.uv; i++) { + // TODO: wrong here? use quaternion transform? + pMesh->mTextureCoords[i][index].x = v->uva[i][0]; + pMesh->mTextureCoords[i][index].y = v->uva[i][1]; + } + + // handle bone map + const auto vsBDEF1_ptr = + dynamic_cast(v->skinning.get()); + const auto vsBDEF2_ptr = + dynamic_cast(v->skinning.get()); + const auto vsBDEF4_ptr = + dynamic_cast(v->skinning.get()); + const auto vsSDEF_ptr = + dynamic_cast(v->skinning.get()); + switch (v->skinning_type) { + case pmx::PmxVertexSkinningType::BDEF1: + bone_vertex_map[vsBDEF1_ptr->bone_index].push_back( + aiVertexWeight(index, 1.0)); + break; + case pmx::PmxVertexSkinningType::BDEF2: + bone_vertex_map[vsBDEF2_ptr->bone_index1].push_back( + aiVertexWeight(index, vsBDEF2_ptr->bone_weight)); + bone_vertex_map[vsBDEF2_ptr->bone_index2].push_back( + aiVertexWeight(index, 1.0f - vsBDEF2_ptr->bone_weight)); + break; + case pmx::PmxVertexSkinningType::BDEF4: + bone_vertex_map[vsBDEF4_ptr->bone_index1].push_back( + aiVertexWeight(index, vsBDEF4_ptr->bone_weight1)); + bone_vertex_map[vsBDEF4_ptr->bone_index2].push_back( + aiVertexWeight(index, vsBDEF4_ptr->bone_weight2)); + bone_vertex_map[vsBDEF4_ptr->bone_index3].push_back( + aiVertexWeight(index, vsBDEF4_ptr->bone_weight3)); + bone_vertex_map[vsBDEF4_ptr->bone_index4].push_back( + aiVertexWeight(index, vsBDEF4_ptr->bone_weight4)); + break; + case pmx::PmxVertexSkinningType::SDEF: // TODO: how to use sdef_c, sdef_r0, + // sdef_r1? + bone_vertex_map[vsSDEF_ptr->bone_index1].push_back( + aiVertexWeight(index, vsSDEF_ptr->bone_weight)); + bone_vertex_map[vsSDEF_ptr->bone_index2].push_back( + aiVertexWeight(index, 1.0f - vsSDEF_ptr->bone_weight)); + break; + case pmx::PmxVertexSkinningType::QDEF: + const auto vsQDEF_ptr = + dynamic_cast(v->skinning.get()); + bone_vertex_map[vsQDEF_ptr->bone_index1].push_back( + aiVertexWeight(index, vsQDEF_ptr->bone_weight1)); + bone_vertex_map[vsQDEF_ptr->bone_index2].push_back( + aiVertexWeight(index, vsQDEF_ptr->bone_weight2)); + bone_vertex_map[vsQDEF_ptr->bone_index3].push_back( + aiVertexWeight(index, vsQDEF_ptr->bone_weight3)); + bone_vertex_map[vsQDEF_ptr->bone_index4].push_back( + aiVertexWeight(index, vsQDEF_ptr->bone_weight4)); + break; + } + } + + // make all bones for each mesh + // assign bone weights to skinned bones (otherwise just initialize) + auto bone_ptr_ptr = new aiBone *[pModel->bone_count]; + pMesh->mNumBones = pModel->bone_count; + pMesh->mBones = bone_ptr_ptr; + for (auto ii = 0; ii < pModel->bone_count; ++ii) { + auto pBone = new aiBone; + const auto &pmxBone = pModel->bones[ii]; + pBone->mName = pmxBone.bone_name; + aiVector3D pos(pmxBone.position[0], pmxBone.position[1], pmxBone.position[2]); + aiMatrix4x4::Translation(-pos, pBone->mOffsetMatrix); + auto it = bone_vertex_map.find(ii); + if (it != bone_vertex_map.end()) { + pBone->mNumWeights = static_cast(it->second.size()); + pBone->mWeights = new aiVertexWeight[pBone->mNumWeights]; + for (unsigned int j = 0; j < pBone->mNumWeights; j++) { + pBone->mWeights[j] = it->second[j]; + } + } + bone_ptr_ptr[ii] = pBone; + } + + return pMesh; +} + +// ------------------------------------------------------------------------------------------------ +aiMaterial *MMDImporter::CreateMaterial(const pmx::PmxMaterial *pMat, + const pmx::PmxModel *pModel) { + aiMaterial *mat = new aiMaterial(); + aiString name(pMat->material_english_name); + mat->AddProperty(&name, AI_MATKEY_NAME); + + aiColor3D diffuse(pMat->diffuse[0], pMat->diffuse[1], pMat->diffuse[2]); + mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + aiColor3D specular(pMat->specular[0], pMat->specular[1], pMat->specular[2]); + mat->AddProperty(&specular, 1, AI_MATKEY_COLOR_SPECULAR); + aiColor3D ambient(pMat->ambient[0], pMat->ambient[1], pMat->ambient[2]); + mat->AddProperty(&ambient, 1, AI_MATKEY_COLOR_AMBIENT); + + float opacity = pMat->diffuse[3]; + mat->AddProperty(&opacity, 1, AI_MATKEY_OPACITY); + float shininess = pMat->specularlity; + mat->AddProperty(&shininess, 1, AI_MATKEY_SHININESS_STRENGTH); + + if(pMat->diffuse_texture_index >= 0) { + aiString texture_path(pModel->textures[pMat->diffuse_texture_index]); + mat->AddProperty(&texture_path, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); + } + + int mapping_uvwsrc = 0; + mat->AddProperty(&mapping_uvwsrc, 1, + AI_MATKEY_UVWSRC(aiTextureType_DIFFUSE, 0)); + + return mat; +} + +// ------------------------------------------------------------------------------------------------ + +} // Namespace Assimp + +#endif // !! ASSIMP_BUILD_NO_MMD_IMPORTER diff --git a/thirdparty/assimp/code/MMD/MMDImporter.h b/thirdparty/assimp/code/MMD/MMDImporter.h new file mode 100644 index 0000000000..4ee94eeb00 --- /dev/null +++ b/thirdparty/assimp/code/MMD/MMDImporter.h @@ -0,0 +1,96 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2016, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef MMD_FILE_IMPORTER_H_INC +#define MMD_FILE_IMPORTER_H_INC + +#include +#include "MMDPmxParser.h" +#include +#include + +struct aiMesh; + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +/// \class MMDImporter +/// \brief Imports MMD a pmx/pmd/vmd file +// ------------------------------------------------------------------------------------------------ +class MMDImporter : public BaseImporter { +public: + /// \brief Default constructor + MMDImporter(); + + /// \brief Destructor + ~MMDImporter(); + +public: + /// \brief Returns whether the class can handle the format of the given file. + /// \remark See BaseImporter::CanRead() for details. + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; + +private: + //! \brief Appends the supported extension. + const aiImporterDesc* GetInfo () const; + + //! \brief File import implementation. + void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + + //! \brief Create the data from imported content. + void CreateDataFromImport(const pmx::PmxModel* pModel, aiScene* pScene); + + //! \brief Create the mesh + aiMesh* CreateMesh(const pmx::PmxModel* pModel, const int indexStart, const int indexCount); + + //! \brief Create the material + aiMaterial* CreateMaterial(const pmx::PmxMaterial* pMat, const pmx::PmxModel* pModel); + +private: + //! Data buffer + std::vector m_Buffer; + //! Absolute pathname of model in file system + std::string m_strAbsPath; +}; + +// ------------------------------------------------------------------------------------------------ + +} // Namespace Assimp + +#endif \ No newline at end of file diff --git a/thirdparty/assimp/code/MMD/MMDPmdParser.h b/thirdparty/assimp/code/MMD/MMDPmdParser.h new file mode 100644 index 0000000000..d2f2224aa1 --- /dev/null +++ b/thirdparty/assimp/code/MMD/MMDPmdParser.h @@ -0,0 +1,597 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include +#include +#include +#include +#include +#include "MMDCpp14.h" + +namespace pmd +{ + class PmdHeader + { + public: + std::string name; + std::string name_english; + std::string comment; + std::string comment_english; + + bool Read(std::ifstream* stream) + { + char buffer[256]; + stream->read(buffer, 20); + name = std::string(buffer); + stream->read(buffer, 256); + comment = std::string(buffer); + return true; + } + + bool ReadExtension(std::ifstream* stream) + { + char buffer[256]; + stream->read(buffer, 20); + name_english = std::string(buffer); + stream->read(buffer, 256); + comment_english = std::string(buffer); + return true; + } + }; + + class PmdVertex + { + public: + float position[3]; + + float normal[3]; + + float uv[2]; + + uint16_t bone_index[2]; + + uint8_t bone_weight; + + bool edge_invisible; + + bool Read(std::ifstream* stream) + { + stream->read((char*) position, sizeof(float) * 3); + stream->read((char*) normal, sizeof(float) * 3); + stream->read((char*) uv, sizeof(float) * 2); + stream->read((char*) bone_index, sizeof(uint16_t) * 2); + stream->read((char*) &bone_weight, sizeof(uint8_t)); + stream->read((char*) &edge_invisible, sizeof(uint8_t)); + return true; + } + }; + + class PmdMaterial + { + public: + float diffuse[4]; + float power; + float specular[3]; + float ambient[3]; + uint8_t toon_index; + uint8_t edge_flag; + uint32_t index_count; + std::string texture_filename; + std::string sphere_filename; + + bool Read(std::ifstream* stream) + { + char buffer[20]; + stream->read((char*) &diffuse, sizeof(float) * 4); + stream->read((char*) &power, sizeof(float)); + stream->read((char*) &specular, sizeof(float) * 3); + stream->read((char*) &ambient, sizeof(float) * 3); + stream->read((char*) &toon_index, sizeof(uint8_t)); + stream->read((char*) &edge_flag, sizeof(uint8_t)); + stream->read((char*) &index_count, sizeof(uint32_t)); + stream->read((char*) &buffer, sizeof(char) * 20); + char* pstar = strchr(buffer, '*'); + if (NULL == pstar) + { + texture_filename = std::string(buffer); + sphere_filename.clear(); + } + else { + *pstar = 0; + texture_filename = std::string(buffer); + sphere_filename = std::string(pstar+1); + } + return true; + } + }; + + enum class BoneType : uint8_t + { + Rotation, + RotationAndMove, + IkEffector, + Unknown, + IkEffectable, + RotationEffectable, + IkTarget, + Invisible, + Twist, + RotationMovement + }; + + class PmdBone + { + public: + std::string name; + std::string name_english; + uint16_t parent_bone_index; + uint16_t tail_pos_bone_index; + BoneType bone_type; + uint16_t ik_parent_bone_index; + float bone_head_pos[3]; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name = std::string(buffer); + stream->read((char*) &parent_bone_index, sizeof(uint16_t)); + stream->read((char*) &tail_pos_bone_index, sizeof(uint16_t)); + stream->read((char*) &bone_type, sizeof(uint8_t)); + stream->read((char*) &ik_parent_bone_index, sizeof(uint16_t)); + stream->read((char*) &bone_head_pos, sizeof(float) * 3); + } + + void ReadExpantion(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name_english = std::string(buffer); + } + }; + + class PmdIk + { + public: + uint16_t ik_bone_index; + uint16_t target_bone_index; + uint16_t interations; + float angle_limit; + std::vector ik_child_bone_index; + + void Read(std::istream *stream) + { + stream->read((char *) &ik_bone_index, sizeof(uint16_t)); + stream->read((char *) &target_bone_index, sizeof(uint16_t)); + uint8_t ik_chain_length; + stream->read((char*) &ik_chain_length, sizeof(uint8_t)); + stream->read((char *) &interations, sizeof(uint16_t)); + stream->read((char *) &angle_limit, sizeof(float)); + ik_child_bone_index.resize(ik_chain_length); + for (int i = 0; i < ik_chain_length; i++) + { + stream->read((char *) &ik_child_bone_index[i], sizeof(uint16_t)); + } + } + }; + + class PmdFaceVertex + { + public: + int vertex_index; + float position[3]; + + void Read(std::istream *stream) + { + stream->read((char *) &vertex_index, sizeof(int)); + stream->read((char *) position, sizeof(float) * 3); + } + }; + + enum class FaceCategory : uint8_t + { + Base, + Eyebrow, + Eye, + Mouth, + Other + }; + + class PmdFace + { + public: + std::string name; + FaceCategory type; + std::vector vertices; + std::string name_english; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name = std::string(buffer); + int vertex_count; + stream->read((char*) &vertex_count, sizeof(int)); + stream->read((char*) &type, sizeof(uint8_t)); + vertices.resize(vertex_count); + for (int i = 0; i < vertex_count; i++) + { + vertices[i].Read(stream); + } + } + + void ReadExpantion(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name_english = std::string(buffer); + } + }; + + class PmdBoneDispName + { + public: + std::string bone_disp_name; + std::string bone_disp_name_english; + + void Read(std::istream *stream) + { + char buffer[50]; + stream->read(buffer, 50); + bone_disp_name = std::string(buffer); + bone_disp_name_english.clear(); + } + void ReadExpantion(std::istream *stream) + { + char buffer[50]; + stream->read(buffer, 50); + bone_disp_name_english = std::string(buffer); + } + }; + + class PmdBoneDisp + { + public: + uint16_t bone_index; + uint8_t bone_disp_index; + + void Read(std::istream *stream) + { + stream->read((char*) &bone_index, sizeof(uint16_t)); + stream->read((char*) &bone_disp_index, sizeof(uint8_t)); + } + }; + + enum class RigidBodyShape : uint8_t + { + Sphere = 0, + Box = 1, + Cpusel = 2 + }; + + enum class RigidBodyType : uint8_t + { + BoneConnected = 0, + Physics = 1, + ConnectedPhysics = 2 + }; + + class PmdRigidBody + { + public: + std::string name; + uint16_t related_bone_index; + uint8_t group_index; + uint16_t mask; + RigidBodyShape shape; + float size[3]; + float position[3]; + float orientation[3]; + float weight; + float linear_damping; + float anglar_damping; + float restitution; + float friction; + RigidBodyType rigid_type; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, sizeof(char) * 20); + name = (std::string(buffer)); + stream->read((char*) &related_bone_index, sizeof(uint16_t)); + stream->read((char*) &group_index, sizeof(uint8_t)); + stream->read((char*) &mask, sizeof(uint16_t)); + stream->read((char*) &shape, sizeof(uint8_t)); + stream->read((char*) size, sizeof(float) * 3); + stream->read((char*) position, sizeof(float) * 3); + stream->read((char*) orientation, sizeof(float) * 3); + stream->read((char*) &weight, sizeof(float)); + stream->read((char*) &linear_damping, sizeof(float)); + stream->read((char*) &anglar_damping, sizeof(float)); + stream->read((char*) &restitution, sizeof(float)); + stream->read((char*) &friction, sizeof(float)); + stream->read((char*) &rigid_type, sizeof(char)); + } + }; + + class PmdConstraint + { + public: + std::string name; + uint32_t rigid_body_index_a; + uint32_t rigid_body_index_b; + float position[3]; + float orientation[3]; + float linear_lower_limit[3]; + float linear_upper_limit[3]; + float angular_lower_limit[3]; + float angular_upper_limit[3]; + float linear_stiffness[3]; + float angular_stiffness[3]; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name = std::string(buffer); + stream->read((char *) &rigid_body_index_a, sizeof(uint32_t)); + stream->read((char *) &rigid_body_index_b, sizeof(uint32_t)); + stream->read((char *) position, sizeof(float) * 3); + stream->read((char *) orientation, sizeof(float) * 3); + stream->read((char *) linear_lower_limit, sizeof(float) * 3); + stream->read((char *) linear_upper_limit, sizeof(float) * 3); + stream->read((char *) angular_lower_limit, sizeof(float) * 3); + stream->read((char *) angular_upper_limit, sizeof(float) * 3); + stream->read((char *) linear_stiffness, sizeof(float) * 3); + stream->read((char *) angular_stiffness, sizeof(float) * 3); + } + }; + + class PmdModel + { + public: + float version; + PmdHeader header; + std::vector vertices; + std::vector indices; + std::vector materials; + std::vector bones; + std::vector iks; + std::vector faces; + std::vector faces_indices; + std::vector bone_disp_name; + std::vector bone_disp; + std::vector toon_filenames; + std::vector rigid_bodies; + std::vector constraints; + + static std::unique_ptr LoadFromFile(const char *filename) + { + std::ifstream stream(filename, std::ios::binary); + if (stream.fail()) + { + std::cerr << "could not open \"" << filename << "\"" << std::endl; + return nullptr; + } + auto result = LoadFromStream(&stream); + stream.close(); + return result; + } + + static std::unique_ptr LoadFromStream(std::ifstream *stream) + { + auto result = mmd::make_unique(); + char buffer[100]; + + // magic + char magic[3]; + stream->read(magic, 3); + if (magic[0] != 'P' || magic[1] != 'm' || magic[2] != 'd') + { + std::cerr << "invalid file" << std::endl; + return nullptr; + } + + // version + stream->read((char*) &(result->version), sizeof(float)); + if (result ->version != 1.0f) + { + std::cerr << "invalid version" << std::endl; + return nullptr; + } + + // header + result->header.Read(stream); + + // vertices + uint32_t vertex_num; + stream->read((char*) &vertex_num, sizeof(uint32_t)); + result->vertices.resize(vertex_num); + for (uint32_t i = 0; i < vertex_num; i++) + { + result->vertices[i].Read(stream); + } + + // indices + uint32_t index_num; + stream->read((char*) &index_num, sizeof(uint32_t)); + result->indices.resize(index_num); + for (uint32_t i = 0; i < index_num; i++) + { + stream->read((char*) &result->indices[i], sizeof(uint16_t)); + } + + // materials + uint32_t material_num; + stream->read((char*) &material_num, sizeof(uint32_t)); + result->materials.resize(material_num); + for (uint32_t i = 0; i < material_num; i++) + { + result->materials[i].Read(stream); + } + + // bones + uint16_t bone_num; + stream->read((char*) &bone_num, sizeof(uint16_t)); + result->bones.resize(bone_num); + for (uint32_t i = 0; i < bone_num; i++) + { + result->bones[i].Read(stream); + } + + // iks + uint16_t ik_num; + stream->read((char*) &ik_num, sizeof(uint16_t)); + result->iks.resize(ik_num); + for (uint32_t i = 0; i < ik_num; i++) + { + result->iks[i].Read(stream); + } + + // faces + uint16_t face_num; + stream->read((char*) &face_num, sizeof(uint16_t)); + result->faces.resize(face_num); + for (uint32_t i = 0; i < face_num; i++) + { + result->faces[i].Read(stream); + } + + // face frames + uint8_t face_frame_num; + stream->read((char*) &face_frame_num, sizeof(uint8_t)); + result->faces_indices.resize(face_frame_num); + for (uint32_t i = 0; i < face_frame_num; i++) + { + stream->read((char*) &result->faces_indices[i], sizeof(uint16_t)); + } + + // bone names + uint8_t bone_disp_num; + stream->read((char*) &bone_disp_num, sizeof(uint8_t)); + result->bone_disp_name.resize(bone_disp_num); + for (uint32_t i = 0; i < bone_disp_num; i++) + { + result->bone_disp_name[i].Read(stream); + } + + // bone frame + uint32_t bone_frame_num; + stream->read((char*) &bone_frame_num, sizeof(uint32_t)); + result->bone_disp.resize(bone_frame_num); + for (uint32_t i = 0; i < bone_frame_num; i++) + { + result->bone_disp[i].Read(stream); + } + + // english name + bool english; + stream->read((char*) &english, sizeof(char)); + if (english) + { + result->header.ReadExtension(stream); + for (uint32_t i = 0; i < bone_num; i++) + { + result->bones[i].ReadExpantion(stream); + } + for (uint32_t i = 0; i < face_num; i++) + { + if (result->faces[i].type == pmd::FaceCategory::Base) + { + continue; + } + result->faces[i].ReadExpantion(stream); + } + for (uint32_t i = 0; i < result->bone_disp_name.size(); i++) + { + result->bone_disp_name[i].ReadExpantion(stream); + } + } + + // toon textures + if (stream->peek() == std::ios::traits_type::eof()) + { + result->toon_filenames.clear(); + } + else { + result->toon_filenames.resize(10); + for (uint32_t i = 0; i < 10; i++) + { + stream->read(buffer, 100); + result->toon_filenames[i] = std::string(buffer); + } + } + + // physics + if (stream->peek() == std::ios::traits_type::eof()) + { + result->rigid_bodies.clear(); + result->constraints.clear(); + } + else { + uint32_t rigid_body_num; + stream->read((char*) &rigid_body_num, sizeof(uint32_t)); + result->rigid_bodies.resize(rigid_body_num); + for (uint32_t i = 0; i < rigid_body_num; i++) + { + result->rigid_bodies[i].Read(stream); + } + uint32_t constraint_num; + stream->read((char*) &constraint_num, sizeof(uint32_t)); + result->constraints.resize(constraint_num); + for (uint32_t i = 0; i < constraint_num; i++) + { + result->constraints[i].Read(stream); + } + } + + if (stream->peek() != std::ios::traits_type::eof()) + { + std::cerr << "there is unknown data" << std::endl; + } + + return result; + } + }; +} diff --git a/thirdparty/assimp/code/MMD/MMDPmxParser.cpp b/thirdparty/assimp/code/MMD/MMDPmxParser.cpp new file mode 100644 index 0000000000..80f0986dd7 --- /dev/null +++ b/thirdparty/assimp/code/MMD/MMDPmxParser.cpp @@ -0,0 +1,608 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(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 +#include "MMDPmxParser.h" +#include +#ifdef ASSIMP_USE_HUNTER +# include +#else +# include "../contrib/utf8cpp/source/utf8.h" +#endif +#include + +namespace pmx +{ + int ReadIndex(std::istream *stream, int size) + { + switch (size) + { + case 1: + uint8_t tmp8; + stream->read((char*) &tmp8, sizeof(uint8_t)); + if (255 == tmp8) + { + return -1; + } + else { + return (int) tmp8; + } + case 2: + uint16_t tmp16; + stream->read((char*) &tmp16, sizeof(uint16_t)); + if (65535 == tmp16) + { + return -1; + } + else { + return (int) tmp16; + } + case 4: + int tmp32; + stream->read((char*) &tmp32, sizeof(int)); + return tmp32; + default: + return -1; + } + } + + std::string ReadString(std::istream *stream, uint8_t encoding) + { + int size; + stream->read((char*) &size, sizeof(int)); + std::vector buffer; + if (size == 0) + { + return std::string(""); + } + buffer.reserve(size); + stream->read((char*) buffer.data(), size); + if (encoding == 0) + { + // UTF16 to UTF8 + const uint16_t* sourceStart = (uint16_t*)buffer.data(); + const unsigned int targetSize = size * 3; // enough to encode + char *targetStart = new char[targetSize]; + std::memset(targetStart, 0, targetSize * sizeof(char)); + + utf8::utf16to8( sourceStart, sourceStart + size/2, targetStart ); + + std::string result(targetStart); + delete [] targetStart; + return result; + } + else + { + // the name is already UTF8 + return std::string((const char*)buffer.data(), size); + } + } + + void PmxSetting::Read(std::istream *stream) + { + uint8_t count; + stream->read((char*) &count, sizeof(uint8_t)); + if (count < 8) + { + throw DeadlyImportError("MMD: invalid size"); + } + stream->read((char*) &encoding, sizeof(uint8_t)); + stream->read((char*) &uv, sizeof(uint8_t)); + stream->read((char*) &vertex_index_size, sizeof(uint8_t)); + stream->read((char*) &texture_index_size, sizeof(uint8_t)); + stream->read((char*) &material_index_size, sizeof(uint8_t)); + stream->read((char*) &bone_index_size, sizeof(uint8_t)); + stream->read((char*) &morph_index_size, sizeof(uint8_t)); + stream->read((char*) &rigidbody_index_size, sizeof(uint8_t)); + uint8_t temp; + for (int i = 8; i < count; i++) + { + stream->read((char*)&temp, sizeof(uint8_t)); + } + } + + void PmxVertexSkinningBDEF1::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index = ReadIndex(stream, setting->bone_index_size); + } + + void PmxVertexSkinningBDEF2::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index1 = ReadIndex(stream, setting->bone_index_size); + this->bone_index2 = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->bone_weight, sizeof(float)); + } + + void PmxVertexSkinningBDEF4::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index1 = ReadIndex(stream, setting->bone_index_size); + this->bone_index2 = ReadIndex(stream, setting->bone_index_size); + this->bone_index3 = ReadIndex(stream, setting->bone_index_size); + this->bone_index4 = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->bone_weight1, sizeof(float)); + stream->read((char*) &this->bone_weight2, sizeof(float)); + stream->read((char*) &this->bone_weight3, sizeof(float)); + stream->read((char*) &this->bone_weight4, sizeof(float)); + } + + void PmxVertexSkinningSDEF::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index1 = ReadIndex(stream, setting->bone_index_size); + this->bone_index2 = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->bone_weight, sizeof(float)); + stream->read((char*) this->sdef_c, sizeof(float) * 3); + stream->read((char*) this->sdef_r0, sizeof(float) * 3); + stream->read((char*) this->sdef_r1, sizeof(float) * 3); + } + + void PmxVertexSkinningQDEF::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index1 = ReadIndex(stream, setting->bone_index_size); + this->bone_index2 = ReadIndex(stream, setting->bone_index_size); + this->bone_index3 = ReadIndex(stream, setting->bone_index_size); + this->bone_index4 = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->bone_weight1, sizeof(float)); + stream->read((char*) &this->bone_weight2, sizeof(float)); + stream->read((char*) &this->bone_weight3, sizeof(float)); + stream->read((char*) &this->bone_weight4, sizeof(float)); + } + + void PmxVertex::Read(std::istream *stream, PmxSetting *setting) + { + stream->read((char*) this->position, sizeof(float) * 3); + stream->read((char*) this->normal, sizeof(float) * 3); + stream->read((char*) this->uv, sizeof(float) * 2); + for (int i = 0; i < setting->uv; ++i) + { + stream->read((char*) this->uva[i], sizeof(float) * 4); + } + stream->read((char*) &this->skinning_type, sizeof(PmxVertexSkinningType)); + switch (this->skinning_type) + { + case PmxVertexSkinningType::BDEF1: + this->skinning = mmd::make_unique(); + break; + case PmxVertexSkinningType::BDEF2: + this->skinning = mmd::make_unique(); + break; + case PmxVertexSkinningType::BDEF4: + this->skinning = mmd::make_unique(); + break; + case PmxVertexSkinningType::SDEF: + this->skinning = mmd::make_unique(); + break; + case PmxVertexSkinningType::QDEF: + this->skinning = mmd::make_unique(); + break; + default: + throw "invalid skinning type"; + } + this->skinning->Read(stream, setting); + stream->read((char*) &this->edge, sizeof(float)); + } + + void PmxMaterial::Read(std::istream *stream, PmxSetting *setting) + { + this->material_name = ReadString(stream, setting->encoding); + this->material_english_name = ReadString(stream, setting->encoding); + stream->read((char*) this->diffuse, sizeof(float) * 4); + stream->read((char*) this->specular, sizeof(float) * 3); + stream->read((char*) &this->specularlity, sizeof(float)); + stream->read((char*) this->ambient, sizeof(float) * 3); + stream->read((char*) &this->flag, sizeof(uint8_t)); + stream->read((char*) this->edge_color, sizeof(float) * 4); + stream->read((char*) &this->edge_size, sizeof(float)); + this->diffuse_texture_index = ReadIndex(stream, setting->texture_index_size); + this->sphere_texture_index = ReadIndex(stream, setting->texture_index_size); + stream->read((char*) &this->sphere_op_mode, sizeof(uint8_t)); + stream->read((char*) &this->common_toon_flag, sizeof(uint8_t)); + if (this->common_toon_flag) + { + stream->read((char*) &this->toon_texture_index, sizeof(uint8_t)); + } + else { + this->toon_texture_index = ReadIndex(stream, setting->texture_index_size); + } + this->memo = ReadString(stream, setting->encoding); + stream->read((char*) &this->index_count, sizeof(int)); + } + + void PmxIkLink::Read(std::istream *stream, PmxSetting *setting) + { + this->link_target = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->angle_lock, sizeof(uint8_t)); + if (angle_lock == 1) + { + stream->read((char*) this->max_radian, sizeof(float) * 3); + stream->read((char*) this->min_radian, sizeof(float) * 3); + } + } + + void PmxBone::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_name = ReadString(stream, setting->encoding); + this->bone_english_name = ReadString(stream, setting->encoding); + stream->read((char*) this->position, sizeof(float) * 3); + this->parent_index = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->level, sizeof(int)); + stream->read((char*) &this->bone_flag, sizeof(uint16_t)); + if (this->bone_flag & 0x0001) { + this->target_index = ReadIndex(stream, setting->bone_index_size); + } + else { + stream->read((char*)this->offset, sizeof(float) * 3); + } + if (this->bone_flag & (0x0100 | 0x0200)) { + this->grant_parent_index = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->grant_weight, sizeof(float)); + } + if (this->bone_flag & 0x0400) { + stream->read((char*)this->lock_axis_orientation, sizeof(float) * 3); + } + if (this->bone_flag & 0x0800) { + stream->read((char*)this->local_axis_x_orientation, sizeof(float) * 3); + stream->read((char*)this->local_axis_y_orientation, sizeof(float) * 3); + } + if (this->bone_flag & 0x2000) { + stream->read((char*) &this->key, sizeof(int)); + } + if (this->bone_flag & 0x0020) { + this->ik_target_bone_index = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &ik_loop, sizeof(int)); + stream->read((char*) &ik_loop_angle_limit, sizeof(float)); + stream->read((char*) &ik_link_count, sizeof(int)); + this->ik_links = mmd::make_unique(ik_link_count); + for (int i = 0; i < ik_link_count; i++) { + ik_links[i].Read(stream, setting); + } + } + } + + void PmxMorphVertexOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->vertex_index = ReadIndex(stream, setting->vertex_index_size); + stream->read((char*)this->position_offset, sizeof(float) * 3); + } + + void PmxMorphUVOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->vertex_index = ReadIndex(stream, setting->vertex_index_size); + stream->read((char*)this->uv_offset, sizeof(float) * 4); + } + + void PmxMorphBoneOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index = ReadIndex(stream, setting->bone_index_size); + stream->read((char*)this->translation, sizeof(float) * 3); + stream->read((char*)this->rotation, sizeof(float) * 4); + } + + void PmxMorphMaterialOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->material_index = ReadIndex(stream, setting->material_index_size); + stream->read((char*) &this->offset_operation, sizeof(uint8_t)); + stream->read((char*)this->diffuse, sizeof(float) * 4); + stream->read((char*)this->specular, sizeof(float) * 3); + stream->read((char*) &this->specularity, sizeof(float)); + stream->read((char*)this->ambient, sizeof(float) * 3); + stream->read((char*)this->edge_color, sizeof(float) * 4); + stream->read((char*) &this->edge_size, sizeof(float)); + stream->read((char*)this->texture_argb, sizeof(float) * 4); + stream->read((char*)this->sphere_texture_argb, sizeof(float) * 4); + stream->read((char*)this->toon_texture_argb, sizeof(float) * 4); + } + + void PmxMorphGroupOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->morph_index = ReadIndex(stream, setting->morph_index_size); + stream->read((char*) &this->morph_weight, sizeof(float)); + } + + void PmxMorphFlipOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->morph_index = ReadIndex(stream, setting->morph_index_size); + stream->read((char*) &this->morph_value, sizeof(float)); + } + + void PmxMorphImplusOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->rigid_body_index = ReadIndex(stream, setting->rigidbody_index_size); + stream->read((char*) &this->is_local, sizeof(uint8_t)); + stream->read((char*)this->velocity, sizeof(float) * 3); + stream->read((char*)this->angular_torque, sizeof(float) * 3); + } + + void PmxMorph::Read(std::istream *stream, PmxSetting *setting) + { + this->morph_name = ReadString(stream, setting->encoding); + this->morph_english_name = ReadString(stream, setting->encoding); + stream->read((char*) &category, sizeof(MorphCategory)); + stream->read((char*) &morph_type, sizeof(MorphType)); + stream->read((char*) &this->offset_count, sizeof(int)); + switch (this->morph_type) + { + case MorphType::Group: + group_offsets = mmd::make_unique(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + group_offsets[i].Read(stream, setting); + } + break; + case MorphType::Vertex: + vertex_offsets = mmd::make_unique(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + vertex_offsets[i].Read(stream, setting); + } + break; + case MorphType::Bone: + bone_offsets = mmd::make_unique(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + bone_offsets[i].Read(stream, setting); + } + break; + case MorphType::Matrial: + material_offsets = mmd::make_unique(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + material_offsets[i].Read(stream, setting); + } + break; + case MorphType::UV: + case MorphType::AdditionalUV1: + case MorphType::AdditionalUV2: + case MorphType::AdditionalUV3: + case MorphType::AdditionalUV4: + uv_offsets = mmd::make_unique(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + uv_offsets[i].Read(stream, setting); + } + break; + default: + throw DeadlyImportError("MMD: unknown morth type"); + } + } + + void PmxFrameElement::Read(std::istream *stream, PmxSetting *setting) + { + stream->read((char*) &this->element_target, sizeof(uint8_t)); + if (this->element_target == 0x00) + { + this->index = ReadIndex(stream, setting->bone_index_size); + } + else { + this->index = ReadIndex(stream, setting->morph_index_size); + } + } + + void PmxFrame::Read(std::istream *stream, PmxSetting *setting) + { + this->frame_name = ReadString(stream, setting->encoding); + this->frame_english_name = ReadString(stream, setting->encoding); + stream->read((char*) &this->frame_flag, sizeof(uint8_t)); + stream->read((char*) &this->element_count, sizeof(int)); + this->elements = mmd::make_unique(this->element_count); + for (int i = 0; i < this->element_count; i++) + { + this->elements[i].Read(stream, setting); + } + } + + void PmxRigidBody::Read(std::istream *stream, PmxSetting *setting) + { + this->girid_body_name = ReadString(stream, setting->encoding); + this->girid_body_english_name = ReadString(stream, setting->encoding); + this->target_bone = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->group, sizeof(uint8_t)); + stream->read((char*) &this->mask, sizeof(uint16_t)); + stream->read((char*) &this->shape, sizeof(uint8_t)); + stream->read((char*) this->size, sizeof(float) * 3); + stream->read((char*) this->position, sizeof(float) * 3); + stream->read((char*) this->orientation, sizeof(float) * 3); + stream->read((char*) &this->mass, sizeof(float)); + stream->read((char*) &this->move_attenuation, sizeof(float)); + stream->read((char*) &this->rotation_attenuation, sizeof(float)); + stream->read((char*) &this->repulsion, sizeof(float)); + stream->read((char*) &this->friction, sizeof(float)); + stream->read((char*) &this->physics_calc_type, sizeof(uint8_t)); + } + + void PmxJointParam::Read(std::istream *stream, PmxSetting *setting) + { + this->rigid_body1 = ReadIndex(stream, setting->rigidbody_index_size); + this->rigid_body2 = ReadIndex(stream, setting->rigidbody_index_size); + stream->read((char*) this->position, sizeof(float) * 3); + stream->read((char*) this->orientaiton, sizeof(float) * 3); + stream->read((char*) this->move_limitation_min, sizeof(float) * 3); + stream->read((char*) this->move_limitation_max, sizeof(float) * 3); + stream->read((char*) this->rotation_limitation_min, sizeof(float) * 3); + stream->read((char*) this->rotation_limitation_max, sizeof(float) * 3); + stream->read((char*) this->spring_move_coefficient, sizeof(float) * 3); + stream->read((char*) this->spring_rotation_coefficient, sizeof(float) * 3); + } + + void PmxJoint::Read(std::istream *stream, PmxSetting *setting) + { + this->joint_name = ReadString(stream, setting->encoding); + this->joint_english_name = ReadString(stream, setting->encoding); + stream->read((char*) &this->joint_type, sizeof(uint8_t)); + this->param.Read(stream, setting); + } + + void PmxAncherRigidBody::Read(std::istream *stream, PmxSetting *setting) + { + this->related_rigid_body = ReadIndex(stream, setting->rigidbody_index_size); + this->related_vertex = ReadIndex(stream, setting->vertex_index_size); + stream->read((char*) &this->is_near, sizeof(uint8_t)); + } + + void PmxSoftBody::Read(std::istream * /*stream*/, PmxSetting * /*setting*/) + { + std::cerr << "Not Implemented Exception" << std::endl; + throw DeadlyImportError("MMD: Not Implemented Exception"); + } + + void PmxModel::Init() + { + this->version = 0.0f; + this->model_name.clear(); + this->model_english_name.clear(); + this->model_comment.clear(); + this->model_english_comment.clear(); + this->vertex_count = 0; + this->vertices = nullptr; + this->index_count = 0; + this->indices = nullptr; + this->texture_count = 0; + this->textures = nullptr; + this->material_count = 0; + this->materials = nullptr; + this->bone_count = 0; + this->bones = nullptr; + this->morph_count = 0; + this->morphs = nullptr; + this->frame_count = 0; + this->frames = nullptr; + this->rigid_body_count = 0; + this->rigid_bodies = nullptr; + this->joint_count = 0; + this->joints = nullptr; + this->soft_body_count = 0; + this->soft_bodies = nullptr; + } + + void PmxModel::Read(std::istream *stream) + { + char magic[4]; + stream->read((char*) magic, sizeof(char) * 4); + if (magic[0] != 0x50 || magic[1] != 0x4d || magic[2] != 0x58 || magic[3] != 0x20) + { + std::cerr << "invalid magic number." << std::endl; + throw DeadlyImportError("MMD: invalid magic number."); + } + stream->read((char*) &version, sizeof(float)); + if (version != 2.0f && version != 2.1f) + { + std::cerr << "this is not ver2.0 or ver2.1 but " << version << "." << std::endl; + throw DeadlyImportError("MMD: this is not ver2.0 or ver2.1 but " + to_string(version)); + } + this->setting.Read(stream); + + this->model_name = ReadString(stream, setting.encoding); + this->model_english_name = ReadString(stream, setting.encoding); + this->model_comment = ReadString(stream, setting.encoding); + this->model_english_comment = ReadString(stream, setting.encoding); + + // read vertices + stream->read((char*) &vertex_count, sizeof(int)); + this->vertices = mmd::make_unique(vertex_count); + for (int i = 0; i < vertex_count; i++) + { + vertices[i].Read(stream, &setting); + } + + // read indices + stream->read((char*) &index_count, sizeof(int)); + this->indices = mmd::make_unique(index_count); + for (int i = 0; i < index_count; i++) + { + this->indices[i] = ReadIndex(stream, setting.vertex_index_size); + } + + // read texture names + stream->read((char*) &texture_count, sizeof(int)); + this->textures = mmd::make_unique(texture_count); + for (int i = 0; i < texture_count; i++) + { + this->textures[i] = ReadString(stream, setting.encoding); + } + + // read materials + stream->read((char*) &material_count, sizeof(int)); + this->materials = mmd::make_unique(material_count); + for (int i = 0; i < material_count; i++) + { + this->materials[i].Read(stream, &setting); + } + + // read bones + stream->read((char*) &this->bone_count, sizeof(int)); + this->bones = mmd::make_unique(this->bone_count); + for (int i = 0; i < this->bone_count; i++) + { + this->bones[i].Read(stream, &setting); + } + + // read morphs + stream->read((char*) &this->morph_count, sizeof(int)); + this->morphs = mmd::make_unique(this->morph_count); + for (int i = 0; i < this->morph_count; i++) + { + this->morphs[i].Read(stream, &setting); + } + + // read display frames + stream->read((char*) &this->frame_count, sizeof(int)); + this->frames = mmd::make_unique(this->frame_count); + for (int i = 0; i < this->frame_count; i++) + { + this->frames[i].Read(stream, &setting); + } + + // read rigid bodies + stream->read((char*) &this->rigid_body_count, sizeof(int)); + this->rigid_bodies = mmd::make_unique(this->rigid_body_count); + for (int i = 0; i < this->rigid_body_count; i++) + { + this->rigid_bodies[i].Read(stream, &setting); + } + + // read joints + stream->read((char*) &this->joint_count, sizeof(int)); + this->joints = mmd::make_unique(this->joint_count); + for (int i = 0; i < this->joint_count; i++) + { + this->joints[i].Read(stream, &setting); + } + } +} diff --git a/thirdparty/assimp/code/MMD/MMDPmxParser.h b/thirdparty/assimp/code/MMD/MMDPmxParser.h new file mode 100644 index 0000000000..cf523a1298 --- /dev/null +++ b/thirdparty/assimp/code/MMD/MMDPmxParser.h @@ -0,0 +1,782 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include +#include +#include +#include +#include +#include "MMDCpp14.h" + +namespace pmx +{ + class PmxSetting + { + public: + PmxSetting() + : encoding(0) + , uv(0) + , vertex_index_size(0) + , texture_index_size(0) + , material_index_size(0) + , bone_index_size(0) + , morph_index_size(0) + , rigidbody_index_size(0) + {} + + uint8_t encoding; + uint8_t uv; + uint8_t vertex_index_size; + uint8_t texture_index_size; + uint8_t material_index_size; + uint8_t bone_index_size; + uint8_t morph_index_size; + uint8_t rigidbody_index_size; + void Read(std::istream *stream); + }; + + enum class PmxVertexSkinningType : uint8_t + { + BDEF1 = 0, + BDEF2 = 1, + BDEF4 = 2, + SDEF = 3, + QDEF = 4, + }; + + class PmxVertexSkinning + { + public: + virtual void Read(std::istream *stream, PmxSetting *setting) = 0; + virtual ~PmxVertexSkinning() {} + }; + + class PmxVertexSkinningBDEF1 : public PmxVertexSkinning + { + public: + PmxVertexSkinningBDEF1() + : bone_index(0) + {} + + int bone_index; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertexSkinningBDEF2 : public PmxVertexSkinning + { + public: + PmxVertexSkinningBDEF2() + : bone_index1(0) + , bone_index2(0) + , bone_weight(0.0f) + {} + + int bone_index1; + int bone_index2; + float bone_weight; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertexSkinningBDEF4 : public PmxVertexSkinning + { + public: + PmxVertexSkinningBDEF4() + : bone_index1(0) + , bone_index2(0) + , bone_index3(0) + , bone_index4(0) + , bone_weight1(0.0f) + , bone_weight2(0.0f) + , bone_weight3(0.0f) + , bone_weight4(0.0f) + {} + + int bone_index1; + int bone_index2; + int bone_index3; + int bone_index4; + float bone_weight1; + float bone_weight2; + float bone_weight3; + float bone_weight4; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertexSkinningSDEF : public PmxVertexSkinning + { + public: + PmxVertexSkinningSDEF() + : bone_index1(0) + , bone_index2(0) + , bone_weight(0.0f) + { + for (int i = 0; i < 3; ++i) { + sdef_c[i] = 0.0f; + sdef_r0[i] = 0.0f; + sdef_r1[i] = 0.0f; + } + } + + int bone_index1; + int bone_index2; + float bone_weight; + float sdef_c[3]; + float sdef_r0[3]; + float sdef_r1[3]; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertexSkinningQDEF : public PmxVertexSkinning + { + public: + PmxVertexSkinningQDEF() + : bone_index1(0) + , bone_index2(0) + , bone_index3(0) + , bone_index4(0) + , bone_weight1(0.0f) + , bone_weight2(0.0f) + , bone_weight3(0.0f) + , bone_weight4(0.0f) + {} + + int bone_index1; + int bone_index2; + int bone_index3; + int bone_index4; + float bone_weight1; + float bone_weight2; + float bone_weight3; + float bone_weight4; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertex + { + public: + PmxVertex() + : edge(0.0f) + { + uv[0] = uv[1] = 0.0f; + for (int i = 0; i < 3; ++i) { + position[i] = 0.0f; + normal[i] = 0.0f; + } + for (int i = 0; i < 4; ++i) { + for (int k = 0; k < 4; ++k) { + uva[i][k] = 0.0f; + } + } + } + + float position[3]; + float normal[3]; + float uv[2]; + float uva[4][4]; + PmxVertexSkinningType skinning_type; + std::unique_ptr skinning; + float edge; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxMaterial + { + public: + PmxMaterial() + : specularlity(0.0f) + , flag(0) + , edge_size(0.0f) + , diffuse_texture_index(0) + , sphere_texture_index(0) + , sphere_op_mode(0) + , common_toon_flag(0) + , toon_texture_index(0) + , index_count(0) + { + for (int i = 0; i < 3; ++i) { + specular[i] = 0.0f; + ambient[i] = 0.0f; + edge_color[i] = 0.0f; + } + for (int i = 0; i < 4; ++i) { + diffuse[i] = 0.0f; + } + } + + std::string material_name; + std::string material_english_name; + float diffuse[4]; + float specular[3]; + float specularlity; + float ambient[3]; + uint8_t flag; + float edge_color[4]; + float edge_size; + int diffuse_texture_index; + int sphere_texture_index; + uint8_t sphere_op_mode; + uint8_t common_toon_flag; + int toon_texture_index; + std::string memo; + int index_count; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxIkLink + { + public: + PmxIkLink() + : link_target(0) + , angle_lock(0) + { + for (int i = 0; i < 3; ++i) { + max_radian[i] = 0.0f; + min_radian[i] = 0.0f; + } + } + + int link_target; + uint8_t angle_lock; + float max_radian[3]; + float min_radian[3]; + void Read(std::istream *stream, PmxSetting *settingn); + }; + + class PmxBone + { + public: + PmxBone() + : parent_index(0) + , level(0) + , bone_flag(0) + , target_index(0) + , grant_parent_index(0) + , grant_weight(0.0f) + , key(0) + , ik_target_bone_index(0) + , ik_loop(0) + , ik_loop_angle_limit(0.0f) + , ik_link_count(0) + { + for (int i = 0; i < 3; ++i) { + position[i] = 0.0f; + offset[i] = 0.0f; + lock_axis_orientation[i] = 0.0f; + local_axis_x_orientation[i] = 0.0f; + local_axis_y_orientation[i] = 0.0f; + } + } + + std::string bone_name; + std::string bone_english_name; + float position[3]; + int parent_index; + int level; + uint16_t bone_flag; + float offset[3]; + int target_index; + int grant_parent_index; + float grant_weight; + float lock_axis_orientation[3]; + float local_axis_x_orientation[3]; + float local_axis_y_orientation[3]; + int key; + int ik_target_bone_index; + int ik_loop; + float ik_loop_angle_limit; + int ik_link_count; + std::unique_ptr ik_links; + void Read(std::istream *stream, PmxSetting *setting); + }; + + enum class MorphType : uint8_t + { + Group = 0, + Vertex = 1, + Bone = 2, + UV = 3, + AdditionalUV1 = 4, + AdditionalUV2 = 5, + AdditionalUV3 = 6, + AdditionalUV4 = 7, + Matrial = 8, + Flip = 9, + Implus = 10, + }; + + enum class MorphCategory : uint8_t + { + ReservedCategory = 0, + Eyebrow = 1, + Eye = 2, + Mouth = 3, + Other = 4, + }; + + class PmxMorphOffset + { + public: + void virtual Read(std::istream *stream, PmxSetting *setting) = 0; + }; + + class PmxMorphVertexOffset : public PmxMorphOffset + { + public: + PmxMorphVertexOffset() + : vertex_index(0) + { + for (int i = 0; i < 3; ++i) { + position_offset[i] = 0.0f; + } + } + int vertex_index; + float position_offset[3]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphUVOffset : public PmxMorphOffset + { + public: + PmxMorphUVOffset() + : vertex_index(0) + { + for (int i = 0; i < 4; ++i) { + uv_offset[i] = 0.0f; + } + } + int vertex_index; + float uv_offset[4]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphBoneOffset : public PmxMorphOffset + { + public: + PmxMorphBoneOffset() + : bone_index(0) + { + for (int i = 0; i < 3; ++i) { + translation[i] = 0.0f; + } + for (int i = 0; i < 4; ++i) { + rotation[i] = 0.0f; + } + } + int bone_index; + float translation[3]; + float rotation[4]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphMaterialOffset : public PmxMorphOffset + { + public: + PmxMorphMaterialOffset() + : specularity(0.0f) + , edge_size(0.0f) + { + for (int i = 0; i < 3; ++i) { + specular[i] = 0.0f; + ambient[i] = 0.0f; + } + for (int i = 0; i < 4; ++i) { + diffuse[i] = 0.0f; + edge_color[i] = 0.0f; + texture_argb[i] = 0.0f; + sphere_texture_argb[i] = 0.0f; + toon_texture_argb[i] = 0.0f; + } + } + int material_index; + uint8_t offset_operation; + float diffuse[4]; + float specular[3]; + float specularity; + float ambient[3]; + float edge_color[4]; + float edge_size; + float texture_argb[4]; + float sphere_texture_argb[4]; + float toon_texture_argb[4]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphGroupOffset : public PmxMorphOffset + { + public: + PmxMorphGroupOffset() + : morph_index(0) + , morph_weight(0.0f) + {} + int morph_index; + float morph_weight; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphFlipOffset : public PmxMorphOffset + { + public: + PmxMorphFlipOffset() + : morph_index(0) + , morph_value(0.0f) + {} + int morph_index; + float morph_value; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphImplusOffset : public PmxMorphOffset + { + public: + PmxMorphImplusOffset() + : rigid_body_index(0) + , is_local(0) + { + for (int i = 0; i < 3; ++i) { + velocity[i] = 0.0f; + angular_torque[i] = 0.0f; + } + } + int rigid_body_index; + uint8_t is_local; + float velocity[3]; + float angular_torque[3]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorph + { + public: + PmxMorph() + : offset_count(0) + { + } + std::string morph_name; + std::string morph_english_name; + MorphCategory category; + MorphType morph_type; + int offset_count; + std::unique_ptr vertex_offsets; + std::unique_ptr uv_offsets; + std::unique_ptr bone_offsets; + std::unique_ptr material_offsets; + std::unique_ptr group_offsets; + std::unique_ptr flip_offsets; + std::unique_ptr implus_offsets; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxFrameElement + { + public: + PmxFrameElement() + : element_target(0) + , index(0) + { + } + uint8_t element_target; + int index; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxFrame + { + public: + PmxFrame() + : frame_flag(0) + , element_count(0) + { + } + std::string frame_name; + std::string frame_english_name; + uint8_t frame_flag; + int element_count; + std::unique_ptr elements; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxRigidBody + { + public: + PmxRigidBody() + : target_bone(0) + , group(0) + , mask(0) + , shape(0) + , mass(0.0f) + , move_attenuation(0.0f) + , rotation_attenuation(0.0f) + , repulsion(0.0f) + , friction(0.0f) + , physics_calc_type(0) + { + for (int i = 0; i < 3; ++i) { + size[i] = 0.0f; + position[i] = 0.0f; + orientation[i] = 0.0f; + } + } + std::string girid_body_name; + std::string girid_body_english_name; + int target_bone; + uint8_t group; + uint16_t mask; + uint8_t shape; + float size[3]; + float position[3]; + float orientation[3]; + float mass; + float move_attenuation; + float rotation_attenuation; + float repulsion; + float friction; + uint8_t physics_calc_type; + void Read(std::istream *stream, PmxSetting *setting); + }; + + enum class PmxJointType : uint8_t + { + Generic6DofSpring = 0, + Generic6Dof = 1, + Point2Point = 2, + ConeTwist = 3, + Slider = 5, + Hinge = 6 + }; + + class PmxJointParam + { + public: + PmxJointParam() + : rigid_body1(0) + , rigid_body2(0) + { + for (int i = 0; i < 3; ++i) { + position[i] = 0.0f; + orientaiton[i] = 0.0f; + move_limitation_min[i] = 0.0f; + move_limitation_max[i] = 0.0f; + rotation_limitation_min[i] = 0.0f; + rotation_limitation_max[i] = 0.0f; + spring_move_coefficient[i] = 0.0f; + spring_rotation_coefficient[i] = 0.0f; + } + } + int rigid_body1; + int rigid_body2; + float position[3]; + float orientaiton[3]; + float move_limitation_min[3]; + float move_limitation_max[3]; + float rotation_limitation_min[3]; + float rotation_limitation_max[3]; + float spring_move_coefficient[3]; + float spring_rotation_coefficient[3]; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxJoint + { + public: + std::string joint_name; + std::string joint_english_name; + PmxJointType joint_type; + PmxJointParam param; + void Read(std::istream *stream, PmxSetting *setting); + }; + + enum PmxSoftBodyFlag : uint8_t + { + BLink = 0x01, + Cluster = 0x02, + Link = 0x04 + }; + + class PmxAncherRigidBody + { + public: + PmxAncherRigidBody() + : related_rigid_body(0) + , related_vertex(0) + , is_near(false) + {} + int related_rigid_body; + int related_vertex; + bool is_near; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxSoftBody + { + public: + PmxSoftBody() + : shape(0) + , target_material(0) + , group(0) + , mask(0) + , blink_distance(0) + , cluster_count(0) + , mass(0.0) + , collisioni_margin(0.0) + , aero_model(0) + , VCF(0.0f) + , DP(0.0f) + , DG(0.0f) + , LF(0.0f) + , PR(0.0f) + , VC(0.0f) + , DF(0.0f) + , MT(0.0f) + , CHR(0.0f) + , KHR(0.0f) + , SHR(0.0f) + , AHR(0.0f) + , SRHR_CL(0.0f) + , SKHR_CL(0.0f) + , SSHR_CL(0.0f) + , SR_SPLT_CL(0.0f) + , SK_SPLT_CL(0.0f) + , SS_SPLT_CL(0.0f) + , V_IT(0) + , P_IT(0) + , D_IT(0) + , C_IT(0) + , LST(0.0f) + , AST(0.0f) + , VST(0.0f) + , anchor_count(0) + , pin_vertex_count(0) + {} + std::string soft_body_name; + std::string soft_body_english_name; + uint8_t shape; + int target_material; + uint8_t group; + uint16_t mask; + PmxSoftBodyFlag flag; + int blink_distance; + int cluster_count; + float mass; + float collisioni_margin; + int aero_model; + float VCF; + float DP; + float DG; + float LF; + float PR; + float VC; + float DF; + float MT; + float CHR; + float KHR; + float SHR; + float AHR; + float SRHR_CL; + float SKHR_CL; + float SSHR_CL; + float SR_SPLT_CL; + float SK_SPLT_CL; + float SS_SPLT_CL; + int V_IT; + int P_IT; + int D_IT; + int C_IT; + float LST; + float AST; + float VST; + int anchor_count; + std::unique_ptr anchers; + int pin_vertex_count; + std::unique_ptr pin_vertices; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxModel + { + public: + PmxModel() + : version(0.0f) + , vertex_count(0) + , index_count(0) + , texture_count(0) + , material_count(0) + , bone_count(0) + , morph_count(0) + , frame_count(0) + , rigid_body_count(0) + , joint_count(0) + , soft_body_count(0) + {} + + float version; + PmxSetting setting; + std::string model_name; + std::string model_english_name; + std::string model_comment; + std::string model_english_comment; + int vertex_count; + std::unique_ptr vertices; + int index_count; + std::unique_ptr indices; + int texture_count; + std::unique_ptr< std::string []> textures; + int material_count; + std::unique_ptr materials; + int bone_count; + std::unique_ptr bones; + int morph_count; + std::unique_ptr morphs; + int frame_count; + std::unique_ptr frames; + int rigid_body_count; + std::unique_ptr rigid_bodies; + int joint_count; + std::unique_ptr joints; + int soft_body_count; + std::unique_ptr soft_bodies; + void Init(); + void Read(std::istream *stream); + //static std::unique_ptr ReadFromFile(const char *filename); + //static std::unique_ptr ReadFromStream(std::istream *stream); + }; +} diff --git a/thirdparty/assimp/code/MMD/MMDVmdParser.h b/thirdparty/assimp/code/MMD/MMDVmdParser.h new file mode 100644 index 0000000000..947c3a2422 --- /dev/null +++ b/thirdparty/assimp/code/MMD/MMDVmdParser.h @@ -0,0 +1,376 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "MMDCpp14.h" + +namespace vmd +{ + class VmdBoneFrame + { + public: + std::string name; + int frame; + float position[3]; + float orientation[4]; + char interpolation[4][4][4]; + + void Read(std::istream* stream) + { + char buffer[15]; + stream->read((char*) buffer, sizeof(char)*15); + name = std::string(buffer); + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) position, sizeof(float)*3); + stream->read((char*) orientation, sizeof(float)*4); + stream->read((char*) interpolation, sizeof(char) * 4 * 4 * 4); + } + + void Write(std::ostream* stream) + { + stream->write((char*)name.c_str(), sizeof(char) * 15); + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)position, sizeof(float) * 3); + stream->write((char*)orientation, sizeof(float) * 4); + stream->write((char*)interpolation, sizeof(char) * 4 * 4 * 4); + } + }; + + class VmdFaceFrame + { + public: + std::string face_name; + float weight; + uint32_t frame; + + void Read(std::istream* stream) + { + char buffer[15]; + stream->read((char*) &buffer, sizeof(char) * 15); + face_name = std::string(buffer); + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) &weight, sizeof(float)); + } + + void Write(std::ostream* stream) + { + stream->write((char*)face_name.c_str(), sizeof(char) * 15); + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)&weight, sizeof(float)); + } + }; + + class VmdCameraFrame + { + public: + int frame; + float distance; + float position[3]; + float orientation[3]; + char interpolation[6][4]; + float angle; + char unknown[3]; + + void Read(std::istream *stream) + { + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) &distance, sizeof(float)); + stream->read((char*) position, sizeof(float) * 3); + stream->read((char*) orientation, sizeof(float) * 3); + stream->read((char*) interpolation, sizeof(char) * 24); + stream->read((char*) &angle, sizeof(float)); + stream->read((char*) unknown, sizeof(char) * 3); + } + + void Write(std::ostream *stream) + { + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)&distance, sizeof(float)); + stream->write((char*)position, sizeof(float) * 3); + stream->write((char*)orientation, sizeof(float) * 3); + stream->write((char*)interpolation, sizeof(char) * 24); + stream->write((char*)&angle, sizeof(float)); + stream->write((char*)unknown, sizeof(char) * 3); + } + }; + + class VmdLightFrame + { + public: + int frame; + float color[3]; + float position[3]; + + void Read(std::istream* stream) + { + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) color, sizeof(float) * 3); + stream->read((char*) position, sizeof(float) * 3); + } + + void Write(std::ostream* stream) + { + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)color, sizeof(float) * 3); + stream->write((char*)position, sizeof(float) * 3); + } + }; + + class VmdIkEnable + { + public: + std::string ik_name; + bool enable; + }; + + class VmdIkFrame + { + public: + int frame; + bool display; + std::vector ik_enable; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) &display, sizeof(uint8_t)); + int ik_count; + stream->read((char*) &ik_count, sizeof(int)); + ik_enable.resize(ik_count); + for (int i = 0; i < ik_count; i++) + { + stream->read(buffer, 20); + ik_enable[i].ik_name = std::string(buffer); + stream->read((char*) &ik_enable[i].enable, sizeof(uint8_t)); + } + } + + void Write(std::ostream *stream) + { + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)&display, sizeof(uint8_t)); + int ik_count = static_cast(ik_enable.size()); + stream->write((char*)&ik_count, sizeof(int)); + for (int i = 0; i < ik_count; i++) + { + const VmdIkEnable& ik_enable = this->ik_enable.at(i); + stream->write(ik_enable.ik_name.c_str(), 20); + stream->write((char*)&ik_enable.enable, sizeof(uint8_t)); + } + } + }; + + class VmdMotion + { + public: + std::string model_name; + int version; + std::vector bone_frames; + std::vector face_frames; + std::vector camera_frames; + std::vector light_frames; + std::vector ik_frames; + + static std::unique_ptr LoadFromFile(char const *filename) + { + std::ifstream stream(filename, std::ios::binary); + auto result = LoadFromStream(&stream); + stream.close(); + return result; + } + + static std::unique_ptr LoadFromStream(std::ifstream *stream) + { + + char buffer[30]; + auto result = mmd::make_unique(); + + // magic and version + stream->read((char*) buffer, 30); + if (strncmp(buffer, "Vocaloid Motion Data", 20)) + { + std::cerr << "invalid vmd file." << std::endl; + return nullptr; + } + result->version = std::atoi(buffer + 20); + + // name + stream->read(buffer, 20); + result->model_name = std::string(buffer); + + // bone frames + int bone_frame_num; + stream->read((char*) &bone_frame_num, sizeof(int)); + result->bone_frames.resize(bone_frame_num); + for (int i = 0; i < bone_frame_num; i++) + { + result->bone_frames[i].Read(stream); + } + + // face frames + int face_frame_num; + stream->read((char*) &face_frame_num, sizeof(int)); + result->face_frames.resize(face_frame_num); + for (int i = 0; i < face_frame_num; i++) + { + result->face_frames[i].Read(stream); + } + + // camera frames + int camera_frame_num; + stream->read((char*) &camera_frame_num, sizeof(int)); + result->camera_frames.resize(camera_frame_num); + for (int i = 0; i < camera_frame_num; i++) + { + result->camera_frames[i].Read(stream); + } + + // light frames + int light_frame_num; + stream->read((char*) &light_frame_num, sizeof(int)); + result->light_frames.resize(light_frame_num); + for (int i = 0; i < light_frame_num; i++) + { + result->light_frames[i].Read(stream); + } + + // unknown2 + stream->read(buffer, 4); + + // ik frames + if (stream->peek() != std::ios::traits_type::eof()) + { + int ik_num; + stream->read((char*) &ik_num, sizeof(int)); + result->ik_frames.resize(ik_num); + for (int i = 0; i < ik_num; i++) + { + result->ik_frames[i].Read(stream); + } + } + + if (stream->peek() != std::ios::traits_type::eof()) + { + std::cerr << "vmd stream has unknown data." << std::endl; + } + + return result; + } + + bool SaveToFile(const std::u16string& /*filename*/) + { + // TODO: How to adapt u16string to string? + /* + std::ofstream stream(filename.c_str(), std::ios::binary); + auto result = SaveToStream(&stream); + stream.close(); + return result; + */ + return false; + } + + bool SaveToStream(std::ofstream *stream) + { + std::string magic = "Vocaloid Motion Data 0002\0"; + magic.resize(30); + + // magic and version + stream->write(magic.c_str(), 30); + + // name + stream->write(model_name.c_str(), 20); + + // bone frames + const int bone_frame_num = static_cast(bone_frames.size()); + stream->write(reinterpret_cast(&bone_frame_num), sizeof(int)); + for (int i = 0; i < bone_frame_num; i++) + { + bone_frames[i].Write(stream); + } + + // face frames + const int face_frame_num = static_cast(face_frames.size()); + stream->write(reinterpret_cast(&face_frame_num), sizeof(int)); + for (int i = 0; i < face_frame_num; i++) + { + face_frames[i].Write(stream); + } + + // camera frames + const int camera_frame_num = static_cast(camera_frames.size()); + stream->write(reinterpret_cast(&camera_frame_num), sizeof(int)); + for (int i = 0; i < camera_frame_num; i++) + { + camera_frames[i].Write(stream); + } + + // light frames + const int light_frame_num = static_cast(light_frames.size()); + stream->write(reinterpret_cast(&light_frame_num), sizeof(int)); + for (int i = 0; i < light_frame_num; i++) + { + light_frames[i].Write(stream); + } + + // self shadow datas + const int self_shadow_num = 0; + stream->write(reinterpret_cast(&self_shadow_num), sizeof(int)); + + // ik frames + const int ik_num = static_cast(ik_frames.size()); + stream->write(reinterpret_cast(&ik_num), sizeof(int)); + for (int i = 0; i < ik_num; i++) + { + ik_frames[i].Write(stream); + } + + return true; + } + }; +} diff --git a/thirdparty/assimp/code/MMDCpp14.h b/thirdparty/assimp/code/MMDCpp14.h deleted file mode 100644 index 638b0bfd2f..0000000000 --- a/thirdparty/assimp/code/MMDCpp14.h +++ /dev/null @@ -1,83 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -#pragma once - -#ifndef MMD_CPP14_H -#define MMD_CPP14_H - -#include -#include -#include -#include - -namespace mmd { - template struct _Unique_if { - typedef std::unique_ptr _Single_object; - }; - - template struct _Unique_if { - typedef std::unique_ptr _Unknown_bound; - }; - - template struct _Unique_if { - typedef void _Known_bound; - }; - - template - typename _Unique_if::_Single_object - make_unique(Args&&... args) { - return std::unique_ptr(new T(std::forward(args)...)); - } - - template - typename _Unique_if::_Unknown_bound - make_unique(size_t n) { - typedef typename std::remove_extent::type U; - return std::unique_ptr(new U[n]()); - } - - template - typename _Unique_if::_Known_bound - make_unique(Args&&...) = delete; -} - -#endif diff --git a/thirdparty/assimp/code/MMDImporter.cpp b/thirdparty/assimp/code/MMDImporter.cpp deleted file mode 100644 index 84b9e35a6b..0000000000 --- a/thirdparty/assimp/code/MMDImporter.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2016, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER - -#include "MMDImporter.h" -#include "MMDPmdParser.h" -#include "MMDPmxParser.h" -#include "MMDVmdParser.h" -#include "ConvertToLHProcess.h" -#include -#include -#include -#include -#include -#include -#include - -static const aiImporterDesc desc = {"MMD Importer", - "", - "", - "surfaces supported?", - aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "pmx"}; - -namespace Assimp { - -using namespace std; - -// ------------------------------------------------------------------------------------------------ -// Default constructor -MMDImporter::MMDImporter() -: m_Buffer() -, m_strAbsPath("") { - DefaultIOSystem io; - m_strAbsPath = io.getOsSeparator(); -} - -// ------------------------------------------------------------------------------------------------ -// Destructor. -MMDImporter::~MMDImporter() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Returns true, if file is an pmx file. -bool MMDImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, - bool checkSig) const { - if (!checkSig) // Check File Extension - { - return SimpleExtensionCheck(pFile, "pmx"); - } else // Check file Header - { - static const char *pTokens[] = {"PMX "}; - return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 1); - } -} - -// ------------------------------------------------------------------------------------------------ -const aiImporterDesc *MMDImporter::GetInfo() const { return &desc; } - -// ------------------------------------------------------------------------------------------------ -// MMD import implementation -void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene, - IOSystem * /*pIOHandler*/) { - // Read file by istream - std::filebuf fb; - if (!fb.open(file, std::ios::in | std::ios::binary)) { - throw DeadlyImportError("Failed to open file " + file + "."); - } - - std::istream fileStream(&fb); - - // Get the file-size and validate it, throwing an exception when fails - fileStream.seekg(0, fileStream.end); - size_t fileSize = static_cast(fileStream.tellg()); - fileStream.seekg(0, fileStream.beg); - - if (fileSize < sizeof(pmx::PmxModel)) { - throw DeadlyImportError(file + " is too small."); - } - - pmx::PmxModel model; - model.Read(&fileStream); - - CreateDataFromImport(&model, pScene); -} - -// ------------------------------------------------------------------------------------------------ -void MMDImporter::CreateDataFromImport(const pmx::PmxModel *pModel, - aiScene *pScene) { - if (pModel == NULL) { - return; - } - - aiNode *pNode = new aiNode; - if (!pModel->model_name.empty()) { - pNode->mName.Set(pModel->model_name); - } - - pScene->mRootNode = pNode; - - pNode = new aiNode; - pScene->mRootNode->addChildren(1, &pNode); - pNode->mName.Set(string(pModel->model_name) + string("_mesh")); - - // split mesh by materials - pNode->mNumMeshes = pModel->material_count; - pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; - for (unsigned int index = 0; index < pNode->mNumMeshes; index++) { - pNode->mMeshes[index] = index; - } - - pScene->mNumMeshes = pModel->material_count; - pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; - for (unsigned int i = 0, indexStart = 0; i < pScene->mNumMeshes; i++) { - const int indexCount = pModel->materials[i].index_count; - - pScene->mMeshes[i] = CreateMesh(pModel, indexStart, indexCount); - pScene->mMeshes[i]->mName = pModel->materials[i].material_name; - pScene->mMeshes[i]->mMaterialIndex = i; - indexStart += indexCount; - } - - // create node hierarchy for bone position - std::unique_ptr ppNode(new aiNode *[pModel->bone_count]); - for (auto i = 0; i < pModel->bone_count; i++) { - ppNode[i] = new aiNode(pModel->bones[i].bone_name); - } - - for (auto i = 0; i < pModel->bone_count; i++) { - const pmx::PmxBone &bone = pModel->bones[i]; - - if (bone.parent_index < 0) { - pScene->mRootNode->addChildren(1, ppNode.get() + i); - } else { - ppNode[bone.parent_index]->addChildren(1, ppNode.get() + i); - - aiVector3D v3 = aiVector3D( - bone.position[0] - pModel->bones[bone.parent_index].position[0], - bone.position[1] - pModel->bones[bone.parent_index].position[1], - bone.position[2] - pModel->bones[bone.parent_index].position[2]); - aiMatrix4x4::Translation(v3, ppNode[i]->mTransformation); - } - } - - // create materials - pScene->mNumMaterials = pModel->material_count; - pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; - for (unsigned int i = 0; i < pScene->mNumMaterials; i++) { - pScene->mMaterials[i] = CreateMaterial(&pModel->materials[i], pModel); - } - - // Convert everything to OpenGL space - MakeLeftHandedProcess convertProcess; - convertProcess.Execute(pScene); - - FlipUVsProcess uvFlipper; - uvFlipper.Execute(pScene); - - FlipWindingOrderProcess windingFlipper; - windingFlipper.Execute(pScene); -} - -// ------------------------------------------------------------------------------------------------ -aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel, - const int indexStart, const int indexCount) { - aiMesh *pMesh = new aiMesh; - - pMesh->mNumVertices = indexCount; - - pMesh->mNumFaces = indexCount / 3; - pMesh->mFaces = new aiFace[pMesh->mNumFaces]; - - const int numIndices = 3; // triangular face - for (unsigned int index = 0; index < pMesh->mNumFaces; index++) { - pMesh->mFaces[index].mNumIndices = numIndices; - unsigned int *indices = new unsigned int[numIndices]; - indices[0] = numIndices * index; - indices[1] = numIndices * index + 1; - indices[2] = numIndices * index + 2; - pMesh->mFaces[index].mIndices = indices; - } - - pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; - pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; - pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices]; - pMesh->mNumUVComponents[0] = 2; - - // additional UVs - for (int i = 1; i <= pModel->setting.uv; i++) { - pMesh->mTextureCoords[i] = new aiVector3D[pMesh->mNumVertices]; - pMesh->mNumUVComponents[i] = 4; - } - - map> bone_vertex_map; - - // fill in contents and create bones - for (int index = 0; index < indexCount; index++) { - const pmx::PmxVertex *v = - &pModel->vertices[pModel->indices[indexStart + index]]; - const float *position = v->position; - pMesh->mVertices[index].Set(position[0], position[1], position[2]); - const float *normal = v->normal; - - pMesh->mNormals[index].Set(normal[0], normal[1], normal[2]); - pMesh->mTextureCoords[0][index].x = v->uv[0]; - pMesh->mTextureCoords[0][index].y = v->uv[1]; - - for (int i = 1; i <= pModel->setting.uv; i++) { - // TODO: wrong here? use quaternion transform? - pMesh->mTextureCoords[i][index].x = v->uva[i][0]; - pMesh->mTextureCoords[i][index].y = v->uva[i][1]; - } - - // handle bone map - const auto vsBDEF1_ptr = - dynamic_cast(v->skinning.get()); - const auto vsBDEF2_ptr = - dynamic_cast(v->skinning.get()); - const auto vsBDEF4_ptr = - dynamic_cast(v->skinning.get()); - const auto vsSDEF_ptr = - dynamic_cast(v->skinning.get()); - switch (v->skinning_type) { - case pmx::PmxVertexSkinningType::BDEF1: - bone_vertex_map[vsBDEF1_ptr->bone_index].push_back( - aiVertexWeight(index, 1.0)); - break; - case pmx::PmxVertexSkinningType::BDEF2: - bone_vertex_map[vsBDEF2_ptr->bone_index1].push_back( - aiVertexWeight(index, vsBDEF2_ptr->bone_weight)); - bone_vertex_map[vsBDEF2_ptr->bone_index2].push_back( - aiVertexWeight(index, 1.0f - vsBDEF2_ptr->bone_weight)); - break; - case pmx::PmxVertexSkinningType::BDEF4: - bone_vertex_map[vsBDEF4_ptr->bone_index1].push_back( - aiVertexWeight(index, vsBDEF4_ptr->bone_weight1)); - bone_vertex_map[vsBDEF4_ptr->bone_index2].push_back( - aiVertexWeight(index, vsBDEF4_ptr->bone_weight2)); - bone_vertex_map[vsBDEF4_ptr->bone_index3].push_back( - aiVertexWeight(index, vsBDEF4_ptr->bone_weight3)); - bone_vertex_map[vsBDEF4_ptr->bone_index4].push_back( - aiVertexWeight(index, vsBDEF4_ptr->bone_weight4)); - break; - case pmx::PmxVertexSkinningType::SDEF: // TODO: how to use sdef_c, sdef_r0, - // sdef_r1? - bone_vertex_map[vsSDEF_ptr->bone_index1].push_back( - aiVertexWeight(index, vsSDEF_ptr->bone_weight)); - bone_vertex_map[vsSDEF_ptr->bone_index2].push_back( - aiVertexWeight(index, 1.0f - vsSDEF_ptr->bone_weight)); - break; - case pmx::PmxVertexSkinningType::QDEF: - const auto vsQDEF_ptr = - dynamic_cast(v->skinning.get()); - bone_vertex_map[vsQDEF_ptr->bone_index1].push_back( - aiVertexWeight(index, vsQDEF_ptr->bone_weight1)); - bone_vertex_map[vsQDEF_ptr->bone_index2].push_back( - aiVertexWeight(index, vsQDEF_ptr->bone_weight2)); - bone_vertex_map[vsQDEF_ptr->bone_index3].push_back( - aiVertexWeight(index, vsQDEF_ptr->bone_weight3)); - bone_vertex_map[vsQDEF_ptr->bone_index4].push_back( - aiVertexWeight(index, vsQDEF_ptr->bone_weight4)); - break; - } - } - - // make all bones for each mesh - // assign bone weights to skinned bones (otherwise just initialize) - auto bone_ptr_ptr = new aiBone *[pModel->bone_count]; - pMesh->mNumBones = pModel->bone_count; - pMesh->mBones = bone_ptr_ptr; - for (auto ii = 0; ii < pModel->bone_count; ++ii) { - auto pBone = new aiBone; - const auto &pmxBone = pModel->bones[ii]; - pBone->mName = pmxBone.bone_name; - aiVector3D pos(pmxBone.position[0], pmxBone.position[1], pmxBone.position[2]); - aiMatrix4x4::Translation(-pos, pBone->mOffsetMatrix); - auto it = bone_vertex_map.find(ii); - if (it != bone_vertex_map.end()) { - pBone->mNumWeights = static_cast(it->second.size()); - pBone->mWeights = new aiVertexWeight[pBone->mNumWeights]; - for (unsigned int j = 0; j < pBone->mNumWeights; j++) { - pBone->mWeights[j] = it->second[j]; - } - } - bone_ptr_ptr[ii] = pBone; - } - - return pMesh; -} - -// ------------------------------------------------------------------------------------------------ -aiMaterial *MMDImporter::CreateMaterial(const pmx::PmxMaterial *pMat, - const pmx::PmxModel *pModel) { - aiMaterial *mat = new aiMaterial(); - aiString name(pMat->material_english_name); - mat->AddProperty(&name, AI_MATKEY_NAME); - - aiColor3D diffuse(pMat->diffuse[0], pMat->diffuse[1], pMat->diffuse[2]); - mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - aiColor3D specular(pMat->specular[0], pMat->specular[1], pMat->specular[2]); - mat->AddProperty(&specular, 1, AI_MATKEY_COLOR_SPECULAR); - aiColor3D ambient(pMat->ambient[0], pMat->ambient[1], pMat->ambient[2]); - mat->AddProperty(&ambient, 1, AI_MATKEY_COLOR_AMBIENT); - - float opacity = pMat->diffuse[3]; - mat->AddProperty(&opacity, 1, AI_MATKEY_OPACITY); - float shininess = pMat->specularlity; - mat->AddProperty(&shininess, 1, AI_MATKEY_SHININESS_STRENGTH); - - if(pMat->diffuse_texture_index >= 0) { - aiString texture_path(pModel->textures[pMat->diffuse_texture_index]); - mat->AddProperty(&texture_path, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); - } - - int mapping_uvwsrc = 0; - mat->AddProperty(&mapping_uvwsrc, 1, - AI_MATKEY_UVWSRC(aiTextureType_DIFFUSE, 0)); - - return mat; -} - -// ------------------------------------------------------------------------------------------------ - -} // Namespace Assimp - -#endif // !! ASSIMP_BUILD_NO_MMD_IMPORTER diff --git a/thirdparty/assimp/code/MMDImporter.h b/thirdparty/assimp/code/MMDImporter.h deleted file mode 100644 index 4ee94eeb00..0000000000 --- a/thirdparty/assimp/code/MMDImporter.h +++ /dev/null @@ -1,96 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2016, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -#ifndef MMD_FILE_IMPORTER_H_INC -#define MMD_FILE_IMPORTER_H_INC - -#include -#include "MMDPmxParser.h" -#include -#include - -struct aiMesh; - -namespace Assimp { - -// ------------------------------------------------------------------------------------------------ -/// \class MMDImporter -/// \brief Imports MMD a pmx/pmd/vmd file -// ------------------------------------------------------------------------------------------------ -class MMDImporter : public BaseImporter { -public: - /// \brief Default constructor - MMDImporter(); - - /// \brief Destructor - ~MMDImporter(); - -public: - /// \brief Returns whether the class can handle the format of the given file. - /// \remark See BaseImporter::CanRead() for details. - bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; - -private: - //! \brief Appends the supported extension. - const aiImporterDesc* GetInfo () const; - - //! \brief File import implementation. - void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); - - //! \brief Create the data from imported content. - void CreateDataFromImport(const pmx::PmxModel* pModel, aiScene* pScene); - - //! \brief Create the mesh - aiMesh* CreateMesh(const pmx::PmxModel* pModel, const int indexStart, const int indexCount); - - //! \brief Create the material - aiMaterial* CreateMaterial(const pmx::PmxMaterial* pMat, const pmx::PmxModel* pModel); - -private: - //! Data buffer - std::vector m_Buffer; - //! Absolute pathname of model in file system - std::string m_strAbsPath; -}; - -// ------------------------------------------------------------------------------------------------ - -} // Namespace Assimp - -#endif \ No newline at end of file diff --git a/thirdparty/assimp/code/MMDPmdParser.h b/thirdparty/assimp/code/MMDPmdParser.h deleted file mode 100644 index d2f2224aa1..0000000000 --- a/thirdparty/assimp/code/MMDPmdParser.h +++ /dev/null @@ -1,597 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -#pragma once - -#include -#include -#include -#include -#include -#include "MMDCpp14.h" - -namespace pmd -{ - class PmdHeader - { - public: - std::string name; - std::string name_english; - std::string comment; - std::string comment_english; - - bool Read(std::ifstream* stream) - { - char buffer[256]; - stream->read(buffer, 20); - name = std::string(buffer); - stream->read(buffer, 256); - comment = std::string(buffer); - return true; - } - - bool ReadExtension(std::ifstream* stream) - { - char buffer[256]; - stream->read(buffer, 20); - name_english = std::string(buffer); - stream->read(buffer, 256); - comment_english = std::string(buffer); - return true; - } - }; - - class PmdVertex - { - public: - float position[3]; - - float normal[3]; - - float uv[2]; - - uint16_t bone_index[2]; - - uint8_t bone_weight; - - bool edge_invisible; - - bool Read(std::ifstream* stream) - { - stream->read((char*) position, sizeof(float) * 3); - stream->read((char*) normal, sizeof(float) * 3); - stream->read((char*) uv, sizeof(float) * 2); - stream->read((char*) bone_index, sizeof(uint16_t) * 2); - stream->read((char*) &bone_weight, sizeof(uint8_t)); - stream->read((char*) &edge_invisible, sizeof(uint8_t)); - return true; - } - }; - - class PmdMaterial - { - public: - float diffuse[4]; - float power; - float specular[3]; - float ambient[3]; - uint8_t toon_index; - uint8_t edge_flag; - uint32_t index_count; - std::string texture_filename; - std::string sphere_filename; - - bool Read(std::ifstream* stream) - { - char buffer[20]; - stream->read((char*) &diffuse, sizeof(float) * 4); - stream->read((char*) &power, sizeof(float)); - stream->read((char*) &specular, sizeof(float) * 3); - stream->read((char*) &ambient, sizeof(float) * 3); - stream->read((char*) &toon_index, sizeof(uint8_t)); - stream->read((char*) &edge_flag, sizeof(uint8_t)); - stream->read((char*) &index_count, sizeof(uint32_t)); - stream->read((char*) &buffer, sizeof(char) * 20); - char* pstar = strchr(buffer, '*'); - if (NULL == pstar) - { - texture_filename = std::string(buffer); - sphere_filename.clear(); - } - else { - *pstar = 0; - texture_filename = std::string(buffer); - sphere_filename = std::string(pstar+1); - } - return true; - } - }; - - enum class BoneType : uint8_t - { - Rotation, - RotationAndMove, - IkEffector, - Unknown, - IkEffectable, - RotationEffectable, - IkTarget, - Invisible, - Twist, - RotationMovement - }; - - class PmdBone - { - public: - std::string name; - std::string name_english; - uint16_t parent_bone_index; - uint16_t tail_pos_bone_index; - BoneType bone_type; - uint16_t ik_parent_bone_index; - float bone_head_pos[3]; - - void Read(std::istream *stream) - { - char buffer[20]; - stream->read(buffer, 20); - name = std::string(buffer); - stream->read((char*) &parent_bone_index, sizeof(uint16_t)); - stream->read((char*) &tail_pos_bone_index, sizeof(uint16_t)); - stream->read((char*) &bone_type, sizeof(uint8_t)); - stream->read((char*) &ik_parent_bone_index, sizeof(uint16_t)); - stream->read((char*) &bone_head_pos, sizeof(float) * 3); - } - - void ReadExpantion(std::istream *stream) - { - char buffer[20]; - stream->read(buffer, 20); - name_english = std::string(buffer); - } - }; - - class PmdIk - { - public: - uint16_t ik_bone_index; - uint16_t target_bone_index; - uint16_t interations; - float angle_limit; - std::vector ik_child_bone_index; - - void Read(std::istream *stream) - { - stream->read((char *) &ik_bone_index, sizeof(uint16_t)); - stream->read((char *) &target_bone_index, sizeof(uint16_t)); - uint8_t ik_chain_length; - stream->read((char*) &ik_chain_length, sizeof(uint8_t)); - stream->read((char *) &interations, sizeof(uint16_t)); - stream->read((char *) &angle_limit, sizeof(float)); - ik_child_bone_index.resize(ik_chain_length); - for (int i = 0; i < ik_chain_length; i++) - { - stream->read((char *) &ik_child_bone_index[i], sizeof(uint16_t)); - } - } - }; - - class PmdFaceVertex - { - public: - int vertex_index; - float position[3]; - - void Read(std::istream *stream) - { - stream->read((char *) &vertex_index, sizeof(int)); - stream->read((char *) position, sizeof(float) * 3); - } - }; - - enum class FaceCategory : uint8_t - { - Base, - Eyebrow, - Eye, - Mouth, - Other - }; - - class PmdFace - { - public: - std::string name; - FaceCategory type; - std::vector vertices; - std::string name_english; - - void Read(std::istream *stream) - { - char buffer[20]; - stream->read(buffer, 20); - name = std::string(buffer); - int vertex_count; - stream->read((char*) &vertex_count, sizeof(int)); - stream->read((char*) &type, sizeof(uint8_t)); - vertices.resize(vertex_count); - for (int i = 0; i < vertex_count; i++) - { - vertices[i].Read(stream); - } - } - - void ReadExpantion(std::istream *stream) - { - char buffer[20]; - stream->read(buffer, 20); - name_english = std::string(buffer); - } - }; - - class PmdBoneDispName - { - public: - std::string bone_disp_name; - std::string bone_disp_name_english; - - void Read(std::istream *stream) - { - char buffer[50]; - stream->read(buffer, 50); - bone_disp_name = std::string(buffer); - bone_disp_name_english.clear(); - } - void ReadExpantion(std::istream *stream) - { - char buffer[50]; - stream->read(buffer, 50); - bone_disp_name_english = std::string(buffer); - } - }; - - class PmdBoneDisp - { - public: - uint16_t bone_index; - uint8_t bone_disp_index; - - void Read(std::istream *stream) - { - stream->read((char*) &bone_index, sizeof(uint16_t)); - stream->read((char*) &bone_disp_index, sizeof(uint8_t)); - } - }; - - enum class RigidBodyShape : uint8_t - { - Sphere = 0, - Box = 1, - Cpusel = 2 - }; - - enum class RigidBodyType : uint8_t - { - BoneConnected = 0, - Physics = 1, - ConnectedPhysics = 2 - }; - - class PmdRigidBody - { - public: - std::string name; - uint16_t related_bone_index; - uint8_t group_index; - uint16_t mask; - RigidBodyShape shape; - float size[3]; - float position[3]; - float orientation[3]; - float weight; - float linear_damping; - float anglar_damping; - float restitution; - float friction; - RigidBodyType rigid_type; - - void Read(std::istream *stream) - { - char buffer[20]; - stream->read(buffer, sizeof(char) * 20); - name = (std::string(buffer)); - stream->read((char*) &related_bone_index, sizeof(uint16_t)); - stream->read((char*) &group_index, sizeof(uint8_t)); - stream->read((char*) &mask, sizeof(uint16_t)); - stream->read((char*) &shape, sizeof(uint8_t)); - stream->read((char*) size, sizeof(float) * 3); - stream->read((char*) position, sizeof(float) * 3); - stream->read((char*) orientation, sizeof(float) * 3); - stream->read((char*) &weight, sizeof(float)); - stream->read((char*) &linear_damping, sizeof(float)); - stream->read((char*) &anglar_damping, sizeof(float)); - stream->read((char*) &restitution, sizeof(float)); - stream->read((char*) &friction, sizeof(float)); - stream->read((char*) &rigid_type, sizeof(char)); - } - }; - - class PmdConstraint - { - public: - std::string name; - uint32_t rigid_body_index_a; - uint32_t rigid_body_index_b; - float position[3]; - float orientation[3]; - float linear_lower_limit[3]; - float linear_upper_limit[3]; - float angular_lower_limit[3]; - float angular_upper_limit[3]; - float linear_stiffness[3]; - float angular_stiffness[3]; - - void Read(std::istream *stream) - { - char buffer[20]; - stream->read(buffer, 20); - name = std::string(buffer); - stream->read((char *) &rigid_body_index_a, sizeof(uint32_t)); - stream->read((char *) &rigid_body_index_b, sizeof(uint32_t)); - stream->read((char *) position, sizeof(float) * 3); - stream->read((char *) orientation, sizeof(float) * 3); - stream->read((char *) linear_lower_limit, sizeof(float) * 3); - stream->read((char *) linear_upper_limit, sizeof(float) * 3); - stream->read((char *) angular_lower_limit, sizeof(float) * 3); - stream->read((char *) angular_upper_limit, sizeof(float) * 3); - stream->read((char *) linear_stiffness, sizeof(float) * 3); - stream->read((char *) angular_stiffness, sizeof(float) * 3); - } - }; - - class PmdModel - { - public: - float version; - PmdHeader header; - std::vector vertices; - std::vector indices; - std::vector materials; - std::vector bones; - std::vector iks; - std::vector faces; - std::vector faces_indices; - std::vector bone_disp_name; - std::vector bone_disp; - std::vector toon_filenames; - std::vector rigid_bodies; - std::vector constraints; - - static std::unique_ptr LoadFromFile(const char *filename) - { - std::ifstream stream(filename, std::ios::binary); - if (stream.fail()) - { - std::cerr << "could not open \"" << filename << "\"" << std::endl; - return nullptr; - } - auto result = LoadFromStream(&stream); - stream.close(); - return result; - } - - static std::unique_ptr LoadFromStream(std::ifstream *stream) - { - auto result = mmd::make_unique(); - char buffer[100]; - - // magic - char magic[3]; - stream->read(magic, 3); - if (magic[0] != 'P' || magic[1] != 'm' || magic[2] != 'd') - { - std::cerr << "invalid file" << std::endl; - return nullptr; - } - - // version - stream->read((char*) &(result->version), sizeof(float)); - if (result ->version != 1.0f) - { - std::cerr << "invalid version" << std::endl; - return nullptr; - } - - // header - result->header.Read(stream); - - // vertices - uint32_t vertex_num; - stream->read((char*) &vertex_num, sizeof(uint32_t)); - result->vertices.resize(vertex_num); - for (uint32_t i = 0; i < vertex_num; i++) - { - result->vertices[i].Read(stream); - } - - // indices - uint32_t index_num; - stream->read((char*) &index_num, sizeof(uint32_t)); - result->indices.resize(index_num); - for (uint32_t i = 0; i < index_num; i++) - { - stream->read((char*) &result->indices[i], sizeof(uint16_t)); - } - - // materials - uint32_t material_num; - stream->read((char*) &material_num, sizeof(uint32_t)); - result->materials.resize(material_num); - for (uint32_t i = 0; i < material_num; i++) - { - result->materials[i].Read(stream); - } - - // bones - uint16_t bone_num; - stream->read((char*) &bone_num, sizeof(uint16_t)); - result->bones.resize(bone_num); - for (uint32_t i = 0; i < bone_num; i++) - { - result->bones[i].Read(stream); - } - - // iks - uint16_t ik_num; - stream->read((char*) &ik_num, sizeof(uint16_t)); - result->iks.resize(ik_num); - for (uint32_t i = 0; i < ik_num; i++) - { - result->iks[i].Read(stream); - } - - // faces - uint16_t face_num; - stream->read((char*) &face_num, sizeof(uint16_t)); - result->faces.resize(face_num); - for (uint32_t i = 0; i < face_num; i++) - { - result->faces[i].Read(stream); - } - - // face frames - uint8_t face_frame_num; - stream->read((char*) &face_frame_num, sizeof(uint8_t)); - result->faces_indices.resize(face_frame_num); - for (uint32_t i = 0; i < face_frame_num; i++) - { - stream->read((char*) &result->faces_indices[i], sizeof(uint16_t)); - } - - // bone names - uint8_t bone_disp_num; - stream->read((char*) &bone_disp_num, sizeof(uint8_t)); - result->bone_disp_name.resize(bone_disp_num); - for (uint32_t i = 0; i < bone_disp_num; i++) - { - result->bone_disp_name[i].Read(stream); - } - - // bone frame - uint32_t bone_frame_num; - stream->read((char*) &bone_frame_num, sizeof(uint32_t)); - result->bone_disp.resize(bone_frame_num); - for (uint32_t i = 0; i < bone_frame_num; i++) - { - result->bone_disp[i].Read(stream); - } - - // english name - bool english; - stream->read((char*) &english, sizeof(char)); - if (english) - { - result->header.ReadExtension(stream); - for (uint32_t i = 0; i < bone_num; i++) - { - result->bones[i].ReadExpantion(stream); - } - for (uint32_t i = 0; i < face_num; i++) - { - if (result->faces[i].type == pmd::FaceCategory::Base) - { - continue; - } - result->faces[i].ReadExpantion(stream); - } - for (uint32_t i = 0; i < result->bone_disp_name.size(); i++) - { - result->bone_disp_name[i].ReadExpantion(stream); - } - } - - // toon textures - if (stream->peek() == std::ios::traits_type::eof()) - { - result->toon_filenames.clear(); - } - else { - result->toon_filenames.resize(10); - for (uint32_t i = 0; i < 10; i++) - { - stream->read(buffer, 100); - result->toon_filenames[i] = std::string(buffer); - } - } - - // physics - if (stream->peek() == std::ios::traits_type::eof()) - { - result->rigid_bodies.clear(); - result->constraints.clear(); - } - else { - uint32_t rigid_body_num; - stream->read((char*) &rigid_body_num, sizeof(uint32_t)); - result->rigid_bodies.resize(rigid_body_num); - for (uint32_t i = 0; i < rigid_body_num; i++) - { - result->rigid_bodies[i].Read(stream); - } - uint32_t constraint_num; - stream->read((char*) &constraint_num, sizeof(uint32_t)); - result->constraints.resize(constraint_num); - for (uint32_t i = 0; i < constraint_num; i++) - { - result->constraints[i].Read(stream); - } - } - - if (stream->peek() != std::ios::traits_type::eof()) - { - std::cerr << "there is unknown data" << std::endl; - } - - return result; - } - }; -} diff --git a/thirdparty/assimp/code/MMDPmxParser.cpp b/thirdparty/assimp/code/MMDPmxParser.cpp deleted file mode 100644 index 7425ceac22..0000000000 --- a/thirdparty/assimp/code/MMDPmxParser.cpp +++ /dev/null @@ -1,604 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -#include -#include "MMDPmxParser.h" -#include -#include "../contrib/utf8cpp/source/utf8.h" -#include - -namespace pmx -{ - int ReadIndex(std::istream *stream, int size) - { - switch (size) - { - case 1: - uint8_t tmp8; - stream->read((char*) &tmp8, sizeof(uint8_t)); - if (255 == tmp8) - { - return -1; - } - else { - return (int) tmp8; - } - case 2: - uint16_t tmp16; - stream->read((char*) &tmp16, sizeof(uint16_t)); - if (65535 == tmp16) - { - return -1; - } - else { - return (int) tmp16; - } - case 4: - int tmp32; - stream->read((char*) &tmp32, sizeof(int)); - return tmp32; - default: - return -1; - } - } - - std::string ReadString(std::istream *stream, uint8_t encoding) - { - int size; - stream->read((char*) &size, sizeof(int)); - std::vector buffer; - if (size == 0) - { - return std::string(""); - } - buffer.reserve(size); - stream->read((char*) buffer.data(), size); - if (encoding == 0) - { - // UTF16 to UTF8 - const uint16_t* sourceStart = (uint16_t*)buffer.data(); - const unsigned int targetSize = size * 3; // enough to encode - char *targetStart = new char[targetSize]; - std::memset(targetStart, 0, targetSize * sizeof(char)); - - utf8::utf16to8( sourceStart, sourceStart + size/2, targetStart ); - - std::string result(targetStart); - delete [] targetStart; - return result; - } - else - { - // the name is already UTF8 - return std::string((const char*)buffer.data(), size); - } - } - - void PmxSetting::Read(std::istream *stream) - { - uint8_t count; - stream->read((char*) &count, sizeof(uint8_t)); - if (count < 8) - { - throw DeadlyImportError("MMD: invalid size"); - } - stream->read((char*) &encoding, sizeof(uint8_t)); - stream->read((char*) &uv, sizeof(uint8_t)); - stream->read((char*) &vertex_index_size, sizeof(uint8_t)); - stream->read((char*) &texture_index_size, sizeof(uint8_t)); - stream->read((char*) &material_index_size, sizeof(uint8_t)); - stream->read((char*) &bone_index_size, sizeof(uint8_t)); - stream->read((char*) &morph_index_size, sizeof(uint8_t)); - stream->read((char*) &rigidbody_index_size, sizeof(uint8_t)); - uint8_t temp; - for (int i = 8; i < count; i++) - { - stream->read((char*)&temp, sizeof(uint8_t)); - } - } - - void PmxVertexSkinningBDEF1::Read(std::istream *stream, PmxSetting *setting) - { - this->bone_index = ReadIndex(stream, setting->bone_index_size); - } - - void PmxVertexSkinningBDEF2::Read(std::istream *stream, PmxSetting *setting) - { - this->bone_index1 = ReadIndex(stream, setting->bone_index_size); - this->bone_index2 = ReadIndex(stream, setting->bone_index_size); - stream->read((char*) &this->bone_weight, sizeof(float)); - } - - void PmxVertexSkinningBDEF4::Read(std::istream *stream, PmxSetting *setting) - { - this->bone_index1 = ReadIndex(stream, setting->bone_index_size); - this->bone_index2 = ReadIndex(stream, setting->bone_index_size); - this->bone_index3 = ReadIndex(stream, setting->bone_index_size); - this->bone_index4 = ReadIndex(stream, setting->bone_index_size); - stream->read((char*) &this->bone_weight1, sizeof(float)); - stream->read((char*) &this->bone_weight2, sizeof(float)); - stream->read((char*) &this->bone_weight3, sizeof(float)); - stream->read((char*) &this->bone_weight4, sizeof(float)); - } - - void PmxVertexSkinningSDEF::Read(std::istream *stream, PmxSetting *setting) - { - this->bone_index1 = ReadIndex(stream, setting->bone_index_size); - this->bone_index2 = ReadIndex(stream, setting->bone_index_size); - stream->read((char*) &this->bone_weight, sizeof(float)); - stream->read((char*) this->sdef_c, sizeof(float) * 3); - stream->read((char*) this->sdef_r0, sizeof(float) * 3); - stream->read((char*) this->sdef_r1, sizeof(float) * 3); - } - - void PmxVertexSkinningQDEF::Read(std::istream *stream, PmxSetting *setting) - { - this->bone_index1 = ReadIndex(stream, setting->bone_index_size); - this->bone_index2 = ReadIndex(stream, setting->bone_index_size); - this->bone_index3 = ReadIndex(stream, setting->bone_index_size); - this->bone_index4 = ReadIndex(stream, setting->bone_index_size); - stream->read((char*) &this->bone_weight1, sizeof(float)); - stream->read((char*) &this->bone_weight2, sizeof(float)); - stream->read((char*) &this->bone_weight3, sizeof(float)); - stream->read((char*) &this->bone_weight4, sizeof(float)); - } - - void PmxVertex::Read(std::istream *stream, PmxSetting *setting) - { - stream->read((char*) this->position, sizeof(float) * 3); - stream->read((char*) this->normal, sizeof(float) * 3); - stream->read((char*) this->uv, sizeof(float) * 2); - for (int i = 0; i < setting->uv; ++i) - { - stream->read((char*) this->uva[i], sizeof(float) * 4); - } - stream->read((char*) &this->skinning_type, sizeof(PmxVertexSkinningType)); - switch (this->skinning_type) - { - case PmxVertexSkinningType::BDEF1: - this->skinning = mmd::make_unique(); - break; - case PmxVertexSkinningType::BDEF2: - this->skinning = mmd::make_unique(); - break; - case PmxVertexSkinningType::BDEF4: - this->skinning = mmd::make_unique(); - break; - case PmxVertexSkinningType::SDEF: - this->skinning = mmd::make_unique(); - break; - case PmxVertexSkinningType::QDEF: - this->skinning = mmd::make_unique(); - break; - default: - throw "invalid skinning type"; - } - this->skinning->Read(stream, setting); - stream->read((char*) &this->edge, sizeof(float)); - } - - void PmxMaterial::Read(std::istream *stream, PmxSetting *setting) - { - this->material_name = ReadString(stream, setting->encoding); - this->material_english_name = ReadString(stream, setting->encoding); - stream->read((char*) this->diffuse, sizeof(float) * 4); - stream->read((char*) this->specular, sizeof(float) * 3); - stream->read((char*) &this->specularlity, sizeof(float)); - stream->read((char*) this->ambient, sizeof(float) * 3); - stream->read((char*) &this->flag, sizeof(uint8_t)); - stream->read((char*) this->edge_color, sizeof(float) * 4); - stream->read((char*) &this->edge_size, sizeof(float)); - this->diffuse_texture_index = ReadIndex(stream, setting->texture_index_size); - this->sphere_texture_index = ReadIndex(stream, setting->texture_index_size); - stream->read((char*) &this->sphere_op_mode, sizeof(uint8_t)); - stream->read((char*) &this->common_toon_flag, sizeof(uint8_t)); - if (this->common_toon_flag) - { - stream->read((char*) &this->toon_texture_index, sizeof(uint8_t)); - } - else { - this->toon_texture_index = ReadIndex(stream, setting->texture_index_size); - } - this->memo = ReadString(stream, setting->encoding); - stream->read((char*) &this->index_count, sizeof(int)); - } - - void PmxIkLink::Read(std::istream *stream, PmxSetting *setting) - { - this->link_target = ReadIndex(stream, setting->bone_index_size); - stream->read((char*) &this->angle_lock, sizeof(uint8_t)); - if (angle_lock == 1) - { - stream->read((char*) this->max_radian, sizeof(float) * 3); - stream->read((char*) this->min_radian, sizeof(float) * 3); - } - } - - void PmxBone::Read(std::istream *stream, PmxSetting *setting) - { - this->bone_name = ReadString(stream, setting->encoding); - this->bone_english_name = ReadString(stream, setting->encoding); - stream->read((char*) this->position, sizeof(float) * 3); - this->parent_index = ReadIndex(stream, setting->bone_index_size); - stream->read((char*) &this->level, sizeof(int)); - stream->read((char*) &this->bone_flag, sizeof(uint16_t)); - if (this->bone_flag & 0x0001) { - this->target_index = ReadIndex(stream, setting->bone_index_size); - } - else { - stream->read((char*)this->offset, sizeof(float) * 3); - } - if (this->bone_flag & (0x0100 | 0x0200)) { - this->grant_parent_index = ReadIndex(stream, setting->bone_index_size); - stream->read((char*) &this->grant_weight, sizeof(float)); - } - if (this->bone_flag & 0x0400) { - stream->read((char*)this->lock_axis_orientation, sizeof(float) * 3); - } - if (this->bone_flag & 0x0800) { - stream->read((char*)this->local_axis_x_orientation, sizeof(float) * 3); - stream->read((char*)this->local_axis_y_orientation, sizeof(float) * 3); - } - if (this->bone_flag & 0x2000) { - stream->read((char*) &this->key, sizeof(int)); - } - if (this->bone_flag & 0x0020) { - this->ik_target_bone_index = ReadIndex(stream, setting->bone_index_size); - stream->read((char*) &ik_loop, sizeof(int)); - stream->read((char*) &ik_loop_angle_limit, sizeof(float)); - stream->read((char*) &ik_link_count, sizeof(int)); - this->ik_links = mmd::make_unique(ik_link_count); - for (int i = 0; i < ik_link_count; i++) { - ik_links[i].Read(stream, setting); - } - } - } - - void PmxMorphVertexOffset::Read(std::istream *stream, PmxSetting *setting) - { - this->vertex_index = ReadIndex(stream, setting->vertex_index_size); - stream->read((char*)this->position_offset, sizeof(float) * 3); - } - - void PmxMorphUVOffset::Read(std::istream *stream, PmxSetting *setting) - { - this->vertex_index = ReadIndex(stream, setting->vertex_index_size); - stream->read((char*)this->uv_offset, sizeof(float) * 4); - } - - void PmxMorphBoneOffset::Read(std::istream *stream, PmxSetting *setting) - { - this->bone_index = ReadIndex(stream, setting->bone_index_size); - stream->read((char*)this->translation, sizeof(float) * 3); - stream->read((char*)this->rotation, sizeof(float) * 4); - } - - void PmxMorphMaterialOffset::Read(std::istream *stream, PmxSetting *setting) - { - this->material_index = ReadIndex(stream, setting->material_index_size); - stream->read((char*) &this->offset_operation, sizeof(uint8_t)); - stream->read((char*)this->diffuse, sizeof(float) * 4); - stream->read((char*)this->specular, sizeof(float) * 3); - stream->read((char*) &this->specularity, sizeof(float)); - stream->read((char*)this->ambient, sizeof(float) * 3); - stream->read((char*)this->edge_color, sizeof(float) * 4); - stream->read((char*) &this->edge_size, sizeof(float)); - stream->read((char*)this->texture_argb, sizeof(float) * 4); - stream->read((char*)this->sphere_texture_argb, sizeof(float) * 4); - stream->read((char*)this->toon_texture_argb, sizeof(float) * 4); - } - - void PmxMorphGroupOffset::Read(std::istream *stream, PmxSetting *setting) - { - this->morph_index = ReadIndex(stream, setting->morph_index_size); - stream->read((char*) &this->morph_weight, sizeof(float)); - } - - void PmxMorphFlipOffset::Read(std::istream *stream, PmxSetting *setting) - { - this->morph_index = ReadIndex(stream, setting->morph_index_size); - stream->read((char*) &this->morph_value, sizeof(float)); - } - - void PmxMorphImplusOffset::Read(std::istream *stream, PmxSetting *setting) - { - this->rigid_body_index = ReadIndex(stream, setting->rigidbody_index_size); - stream->read((char*) &this->is_local, sizeof(uint8_t)); - stream->read((char*)this->velocity, sizeof(float) * 3); - stream->read((char*)this->angular_torque, sizeof(float) * 3); - } - - void PmxMorph::Read(std::istream *stream, PmxSetting *setting) - { - this->morph_name = ReadString(stream, setting->encoding); - this->morph_english_name = ReadString(stream, setting->encoding); - stream->read((char*) &category, sizeof(MorphCategory)); - stream->read((char*) &morph_type, sizeof(MorphType)); - stream->read((char*) &this->offset_count, sizeof(int)); - switch (this->morph_type) - { - case MorphType::Group: - group_offsets = mmd::make_unique(this->offset_count); - for (int i = 0; i < offset_count; i++) - { - group_offsets[i].Read(stream, setting); - } - break; - case MorphType::Vertex: - vertex_offsets = mmd::make_unique(this->offset_count); - for (int i = 0; i < offset_count; i++) - { - vertex_offsets[i].Read(stream, setting); - } - break; - case MorphType::Bone: - bone_offsets = mmd::make_unique(this->offset_count); - for (int i = 0; i < offset_count; i++) - { - bone_offsets[i].Read(stream, setting); - } - break; - case MorphType::Matrial: - material_offsets = mmd::make_unique(this->offset_count); - for (int i = 0; i < offset_count; i++) - { - material_offsets[i].Read(stream, setting); - } - break; - case MorphType::UV: - case MorphType::AdditionalUV1: - case MorphType::AdditionalUV2: - case MorphType::AdditionalUV3: - case MorphType::AdditionalUV4: - uv_offsets = mmd::make_unique(this->offset_count); - for (int i = 0; i < offset_count; i++) - { - uv_offsets[i].Read(stream, setting); - } - break; - default: - throw DeadlyImportError("MMD: unknown morth type"); - } - } - - void PmxFrameElement::Read(std::istream *stream, PmxSetting *setting) - { - stream->read((char*) &this->element_target, sizeof(uint8_t)); - if (this->element_target == 0x00) - { - this->index = ReadIndex(stream, setting->bone_index_size); - } - else { - this->index = ReadIndex(stream, setting->morph_index_size); - } - } - - void PmxFrame::Read(std::istream *stream, PmxSetting *setting) - { - this->frame_name = ReadString(stream, setting->encoding); - this->frame_english_name = ReadString(stream, setting->encoding); - stream->read((char*) &this->frame_flag, sizeof(uint8_t)); - stream->read((char*) &this->element_count, sizeof(int)); - this->elements = mmd::make_unique(this->element_count); - for (int i = 0; i < this->element_count; i++) - { - this->elements[i].Read(stream, setting); - } - } - - void PmxRigidBody::Read(std::istream *stream, PmxSetting *setting) - { - this->girid_body_name = ReadString(stream, setting->encoding); - this->girid_body_english_name = ReadString(stream, setting->encoding); - this->target_bone = ReadIndex(stream, setting->bone_index_size); - stream->read((char*) &this->group, sizeof(uint8_t)); - stream->read((char*) &this->mask, sizeof(uint16_t)); - stream->read((char*) &this->shape, sizeof(uint8_t)); - stream->read((char*) this->size, sizeof(float) * 3); - stream->read((char*) this->position, sizeof(float) * 3); - stream->read((char*) this->orientation, sizeof(float) * 3); - stream->read((char*) &this->mass, sizeof(float)); - stream->read((char*) &this->move_attenuation, sizeof(float)); - stream->read((char*) &this->rotation_attenuation, sizeof(float)); - stream->read((char*) &this->repulsion, sizeof(float)); - stream->read((char*) &this->friction, sizeof(float)); - stream->read((char*) &this->physics_calc_type, sizeof(uint8_t)); - } - - void PmxJointParam::Read(std::istream *stream, PmxSetting *setting) - { - this->rigid_body1 = ReadIndex(stream, setting->rigidbody_index_size); - this->rigid_body2 = ReadIndex(stream, setting->rigidbody_index_size); - stream->read((char*) this->position, sizeof(float) * 3); - stream->read((char*) this->orientaiton, sizeof(float) * 3); - stream->read((char*) this->move_limitation_min, sizeof(float) * 3); - stream->read((char*) this->move_limitation_max, sizeof(float) * 3); - stream->read((char*) this->rotation_limitation_min, sizeof(float) * 3); - stream->read((char*) this->rotation_limitation_max, sizeof(float) * 3); - stream->read((char*) this->spring_move_coefficient, sizeof(float) * 3); - stream->read((char*) this->spring_rotation_coefficient, sizeof(float) * 3); - } - - void PmxJoint::Read(std::istream *stream, PmxSetting *setting) - { - this->joint_name = ReadString(stream, setting->encoding); - this->joint_english_name = ReadString(stream, setting->encoding); - stream->read((char*) &this->joint_type, sizeof(uint8_t)); - this->param.Read(stream, setting); - } - - void PmxAncherRigidBody::Read(std::istream *stream, PmxSetting *setting) - { - this->related_rigid_body = ReadIndex(stream, setting->rigidbody_index_size); - this->related_vertex = ReadIndex(stream, setting->vertex_index_size); - stream->read((char*) &this->is_near, sizeof(uint8_t)); - } - - void PmxSoftBody::Read(std::istream * /*stream*/, PmxSetting * /*setting*/) - { - std::cerr << "Not Implemented Exception" << std::endl; - throw DeadlyImportError("MMD: Not Implemented Exception"); - } - - void PmxModel::Init() - { - this->version = 0.0f; - this->model_name.clear(); - this->model_english_name.clear(); - this->model_comment.clear(); - this->model_english_comment.clear(); - this->vertex_count = 0; - this->vertices = nullptr; - this->index_count = 0; - this->indices = nullptr; - this->texture_count = 0; - this->textures = nullptr; - this->material_count = 0; - this->materials = nullptr; - this->bone_count = 0; - this->bones = nullptr; - this->morph_count = 0; - this->morphs = nullptr; - this->frame_count = 0; - this->frames = nullptr; - this->rigid_body_count = 0; - this->rigid_bodies = nullptr; - this->joint_count = 0; - this->joints = nullptr; - this->soft_body_count = 0; - this->soft_bodies = nullptr; - } - - void PmxModel::Read(std::istream *stream) - { - char magic[4]; - stream->read((char*) magic, sizeof(char) * 4); - if (magic[0] != 0x50 || magic[1] != 0x4d || magic[2] != 0x58 || magic[3] != 0x20) - { - std::cerr << "invalid magic number." << std::endl; - throw DeadlyImportError("MMD: invalid magic number."); - } - stream->read((char*) &version, sizeof(float)); - if (version != 2.0f && version != 2.1f) - { - std::cerr << "this is not ver2.0 or ver2.1 but " << version << "." << std::endl; - throw DeadlyImportError("MMD: this is not ver2.0 or ver2.1 but " + to_string(version)); - } - this->setting.Read(stream); - - this->model_name = ReadString(stream, setting.encoding); - this->model_english_name = ReadString(stream, setting.encoding); - this->model_comment = ReadString(stream, setting.encoding); - this->model_english_comment = ReadString(stream, setting.encoding); - - // read vertices - stream->read((char*) &vertex_count, sizeof(int)); - this->vertices = mmd::make_unique(vertex_count); - for (int i = 0; i < vertex_count; i++) - { - vertices[i].Read(stream, &setting); - } - - // read indices - stream->read((char*) &index_count, sizeof(int)); - this->indices = mmd::make_unique(index_count); - for (int i = 0; i < index_count; i++) - { - this->indices[i] = ReadIndex(stream, setting.vertex_index_size); - } - - // read texture names - stream->read((char*) &texture_count, sizeof(int)); - this->textures = mmd::make_unique(texture_count); - for (int i = 0; i < texture_count; i++) - { - this->textures[i] = ReadString(stream, setting.encoding); - } - - // read materials - stream->read((char*) &material_count, sizeof(int)); - this->materials = mmd::make_unique(material_count); - for (int i = 0; i < material_count; i++) - { - this->materials[i].Read(stream, &setting); - } - - // read bones - stream->read((char*) &this->bone_count, sizeof(int)); - this->bones = mmd::make_unique(this->bone_count); - for (int i = 0; i < this->bone_count; i++) - { - this->bones[i].Read(stream, &setting); - } - - // read morphs - stream->read((char*) &this->morph_count, sizeof(int)); - this->morphs = mmd::make_unique(this->morph_count); - for (int i = 0; i < this->morph_count; i++) - { - this->morphs[i].Read(stream, &setting); - } - - // read display frames - stream->read((char*) &this->frame_count, sizeof(int)); - this->frames = mmd::make_unique(this->frame_count); - for (int i = 0; i < this->frame_count; i++) - { - this->frames[i].Read(stream, &setting); - } - - // read rigid bodies - stream->read((char*) &this->rigid_body_count, sizeof(int)); - this->rigid_bodies = mmd::make_unique(this->rigid_body_count); - for (int i = 0; i < this->rigid_body_count; i++) - { - this->rigid_bodies[i].Read(stream, &setting); - } - - // read joints - stream->read((char*) &this->joint_count, sizeof(int)); - this->joints = mmd::make_unique(this->joint_count); - for (int i = 0; i < this->joint_count; i++) - { - this->joints[i].Read(stream, &setting); - } - } -} diff --git a/thirdparty/assimp/code/MMDPmxParser.h b/thirdparty/assimp/code/MMDPmxParser.h deleted file mode 100644 index cf523a1298..0000000000 --- a/thirdparty/assimp/code/MMDPmxParser.h +++ /dev/null @@ -1,782 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -#pragma once - -#include -#include -#include -#include -#include -#include "MMDCpp14.h" - -namespace pmx -{ - class PmxSetting - { - public: - PmxSetting() - : encoding(0) - , uv(0) - , vertex_index_size(0) - , texture_index_size(0) - , material_index_size(0) - , bone_index_size(0) - , morph_index_size(0) - , rigidbody_index_size(0) - {} - - uint8_t encoding; - uint8_t uv; - uint8_t vertex_index_size; - uint8_t texture_index_size; - uint8_t material_index_size; - uint8_t bone_index_size; - uint8_t morph_index_size; - uint8_t rigidbody_index_size; - void Read(std::istream *stream); - }; - - enum class PmxVertexSkinningType : uint8_t - { - BDEF1 = 0, - BDEF2 = 1, - BDEF4 = 2, - SDEF = 3, - QDEF = 4, - }; - - class PmxVertexSkinning - { - public: - virtual void Read(std::istream *stream, PmxSetting *setting) = 0; - virtual ~PmxVertexSkinning() {} - }; - - class PmxVertexSkinningBDEF1 : public PmxVertexSkinning - { - public: - PmxVertexSkinningBDEF1() - : bone_index(0) - {} - - int bone_index; - void Read(std::istream *stresam, PmxSetting *setting); - }; - - class PmxVertexSkinningBDEF2 : public PmxVertexSkinning - { - public: - PmxVertexSkinningBDEF2() - : bone_index1(0) - , bone_index2(0) - , bone_weight(0.0f) - {} - - int bone_index1; - int bone_index2; - float bone_weight; - void Read(std::istream *stresam, PmxSetting *setting); - }; - - class PmxVertexSkinningBDEF4 : public PmxVertexSkinning - { - public: - PmxVertexSkinningBDEF4() - : bone_index1(0) - , bone_index2(0) - , bone_index3(0) - , bone_index4(0) - , bone_weight1(0.0f) - , bone_weight2(0.0f) - , bone_weight3(0.0f) - , bone_weight4(0.0f) - {} - - int bone_index1; - int bone_index2; - int bone_index3; - int bone_index4; - float bone_weight1; - float bone_weight2; - float bone_weight3; - float bone_weight4; - void Read(std::istream *stresam, PmxSetting *setting); - }; - - class PmxVertexSkinningSDEF : public PmxVertexSkinning - { - public: - PmxVertexSkinningSDEF() - : bone_index1(0) - , bone_index2(0) - , bone_weight(0.0f) - { - for (int i = 0; i < 3; ++i) { - sdef_c[i] = 0.0f; - sdef_r0[i] = 0.0f; - sdef_r1[i] = 0.0f; - } - } - - int bone_index1; - int bone_index2; - float bone_weight; - float sdef_c[3]; - float sdef_r0[3]; - float sdef_r1[3]; - void Read(std::istream *stresam, PmxSetting *setting); - }; - - class PmxVertexSkinningQDEF : public PmxVertexSkinning - { - public: - PmxVertexSkinningQDEF() - : bone_index1(0) - , bone_index2(0) - , bone_index3(0) - , bone_index4(0) - , bone_weight1(0.0f) - , bone_weight2(0.0f) - , bone_weight3(0.0f) - , bone_weight4(0.0f) - {} - - int bone_index1; - int bone_index2; - int bone_index3; - int bone_index4; - float bone_weight1; - float bone_weight2; - float bone_weight3; - float bone_weight4; - void Read(std::istream *stresam, PmxSetting *setting); - }; - - class PmxVertex - { - public: - PmxVertex() - : edge(0.0f) - { - uv[0] = uv[1] = 0.0f; - for (int i = 0; i < 3; ++i) { - position[i] = 0.0f; - normal[i] = 0.0f; - } - for (int i = 0; i < 4; ++i) { - for (int k = 0; k < 4; ++k) { - uva[i][k] = 0.0f; - } - } - } - - float position[3]; - float normal[3]; - float uv[2]; - float uva[4][4]; - PmxVertexSkinningType skinning_type; - std::unique_ptr skinning; - float edge; - void Read(std::istream *stream, PmxSetting *setting); - }; - - class PmxMaterial - { - public: - PmxMaterial() - : specularlity(0.0f) - , flag(0) - , edge_size(0.0f) - , diffuse_texture_index(0) - , sphere_texture_index(0) - , sphere_op_mode(0) - , common_toon_flag(0) - , toon_texture_index(0) - , index_count(0) - { - for (int i = 0; i < 3; ++i) { - specular[i] = 0.0f; - ambient[i] = 0.0f; - edge_color[i] = 0.0f; - } - for (int i = 0; i < 4; ++i) { - diffuse[i] = 0.0f; - } - } - - std::string material_name; - std::string material_english_name; - float diffuse[4]; - float specular[3]; - float specularlity; - float ambient[3]; - uint8_t flag; - float edge_color[4]; - float edge_size; - int diffuse_texture_index; - int sphere_texture_index; - uint8_t sphere_op_mode; - uint8_t common_toon_flag; - int toon_texture_index; - std::string memo; - int index_count; - void Read(std::istream *stream, PmxSetting *setting); - }; - - class PmxIkLink - { - public: - PmxIkLink() - : link_target(0) - , angle_lock(0) - { - for (int i = 0; i < 3; ++i) { - max_radian[i] = 0.0f; - min_radian[i] = 0.0f; - } - } - - int link_target; - uint8_t angle_lock; - float max_radian[3]; - float min_radian[3]; - void Read(std::istream *stream, PmxSetting *settingn); - }; - - class PmxBone - { - public: - PmxBone() - : parent_index(0) - , level(0) - , bone_flag(0) - , target_index(0) - , grant_parent_index(0) - , grant_weight(0.0f) - , key(0) - , ik_target_bone_index(0) - , ik_loop(0) - , ik_loop_angle_limit(0.0f) - , ik_link_count(0) - { - for (int i = 0; i < 3; ++i) { - position[i] = 0.0f; - offset[i] = 0.0f; - lock_axis_orientation[i] = 0.0f; - local_axis_x_orientation[i] = 0.0f; - local_axis_y_orientation[i] = 0.0f; - } - } - - std::string bone_name; - std::string bone_english_name; - float position[3]; - int parent_index; - int level; - uint16_t bone_flag; - float offset[3]; - int target_index; - int grant_parent_index; - float grant_weight; - float lock_axis_orientation[3]; - float local_axis_x_orientation[3]; - float local_axis_y_orientation[3]; - int key; - int ik_target_bone_index; - int ik_loop; - float ik_loop_angle_limit; - int ik_link_count; - std::unique_ptr ik_links; - void Read(std::istream *stream, PmxSetting *setting); - }; - - enum class MorphType : uint8_t - { - Group = 0, - Vertex = 1, - Bone = 2, - UV = 3, - AdditionalUV1 = 4, - AdditionalUV2 = 5, - AdditionalUV3 = 6, - AdditionalUV4 = 7, - Matrial = 8, - Flip = 9, - Implus = 10, - }; - - enum class MorphCategory : uint8_t - { - ReservedCategory = 0, - Eyebrow = 1, - Eye = 2, - Mouth = 3, - Other = 4, - }; - - class PmxMorphOffset - { - public: - void virtual Read(std::istream *stream, PmxSetting *setting) = 0; - }; - - class PmxMorphVertexOffset : public PmxMorphOffset - { - public: - PmxMorphVertexOffset() - : vertex_index(0) - { - for (int i = 0; i < 3; ++i) { - position_offset[i] = 0.0f; - } - } - int vertex_index; - float position_offset[3]; - void Read(std::istream *stream, PmxSetting *setting); //override; - }; - - class PmxMorphUVOffset : public PmxMorphOffset - { - public: - PmxMorphUVOffset() - : vertex_index(0) - { - for (int i = 0; i < 4; ++i) { - uv_offset[i] = 0.0f; - } - } - int vertex_index; - float uv_offset[4]; - void Read(std::istream *stream, PmxSetting *setting); //override; - }; - - class PmxMorphBoneOffset : public PmxMorphOffset - { - public: - PmxMorphBoneOffset() - : bone_index(0) - { - for (int i = 0; i < 3; ++i) { - translation[i] = 0.0f; - } - for (int i = 0; i < 4; ++i) { - rotation[i] = 0.0f; - } - } - int bone_index; - float translation[3]; - float rotation[4]; - void Read(std::istream *stream, PmxSetting *setting); //override; - }; - - class PmxMorphMaterialOffset : public PmxMorphOffset - { - public: - PmxMorphMaterialOffset() - : specularity(0.0f) - , edge_size(0.0f) - { - for (int i = 0; i < 3; ++i) { - specular[i] = 0.0f; - ambient[i] = 0.0f; - } - for (int i = 0; i < 4; ++i) { - diffuse[i] = 0.0f; - edge_color[i] = 0.0f; - texture_argb[i] = 0.0f; - sphere_texture_argb[i] = 0.0f; - toon_texture_argb[i] = 0.0f; - } - } - int material_index; - uint8_t offset_operation; - float diffuse[4]; - float specular[3]; - float specularity; - float ambient[3]; - float edge_color[4]; - float edge_size; - float texture_argb[4]; - float sphere_texture_argb[4]; - float toon_texture_argb[4]; - void Read(std::istream *stream, PmxSetting *setting); //override; - }; - - class PmxMorphGroupOffset : public PmxMorphOffset - { - public: - PmxMorphGroupOffset() - : morph_index(0) - , morph_weight(0.0f) - {} - int morph_index; - float morph_weight; - void Read(std::istream *stream, PmxSetting *setting); //override; - }; - - class PmxMorphFlipOffset : public PmxMorphOffset - { - public: - PmxMorphFlipOffset() - : morph_index(0) - , morph_value(0.0f) - {} - int morph_index; - float morph_value; - void Read(std::istream *stream, PmxSetting *setting); //override; - }; - - class PmxMorphImplusOffset : public PmxMorphOffset - { - public: - PmxMorphImplusOffset() - : rigid_body_index(0) - , is_local(0) - { - for (int i = 0; i < 3; ++i) { - velocity[i] = 0.0f; - angular_torque[i] = 0.0f; - } - } - int rigid_body_index; - uint8_t is_local; - float velocity[3]; - float angular_torque[3]; - void Read(std::istream *stream, PmxSetting *setting); //override; - }; - - class PmxMorph - { - public: - PmxMorph() - : offset_count(0) - { - } - std::string morph_name; - std::string morph_english_name; - MorphCategory category; - MorphType morph_type; - int offset_count; - std::unique_ptr vertex_offsets; - std::unique_ptr uv_offsets; - std::unique_ptr bone_offsets; - std::unique_ptr material_offsets; - std::unique_ptr group_offsets; - std::unique_ptr flip_offsets; - std::unique_ptr implus_offsets; - void Read(std::istream *stream, PmxSetting *setting); - }; - - class PmxFrameElement - { - public: - PmxFrameElement() - : element_target(0) - , index(0) - { - } - uint8_t element_target; - int index; - void Read(std::istream *stream, PmxSetting *setting); - }; - - class PmxFrame - { - public: - PmxFrame() - : frame_flag(0) - , element_count(0) - { - } - std::string frame_name; - std::string frame_english_name; - uint8_t frame_flag; - int element_count; - std::unique_ptr elements; - void Read(std::istream *stream, PmxSetting *setting); - }; - - class PmxRigidBody - { - public: - PmxRigidBody() - : target_bone(0) - , group(0) - , mask(0) - , shape(0) - , mass(0.0f) - , move_attenuation(0.0f) - , rotation_attenuation(0.0f) - , repulsion(0.0f) - , friction(0.0f) - , physics_calc_type(0) - { - for (int i = 0; i < 3; ++i) { - size[i] = 0.0f; - position[i] = 0.0f; - orientation[i] = 0.0f; - } - } - std::string girid_body_name; - std::string girid_body_english_name; - int target_bone; - uint8_t group; - uint16_t mask; - uint8_t shape; - float size[3]; - float position[3]; - float orientation[3]; - float mass; - float move_attenuation; - float rotation_attenuation; - float repulsion; - float friction; - uint8_t physics_calc_type; - void Read(std::istream *stream, PmxSetting *setting); - }; - - enum class PmxJointType : uint8_t - { - Generic6DofSpring = 0, - Generic6Dof = 1, - Point2Point = 2, - ConeTwist = 3, - Slider = 5, - Hinge = 6 - }; - - class PmxJointParam - { - public: - PmxJointParam() - : rigid_body1(0) - , rigid_body2(0) - { - for (int i = 0; i < 3; ++i) { - position[i] = 0.0f; - orientaiton[i] = 0.0f; - move_limitation_min[i] = 0.0f; - move_limitation_max[i] = 0.0f; - rotation_limitation_min[i] = 0.0f; - rotation_limitation_max[i] = 0.0f; - spring_move_coefficient[i] = 0.0f; - spring_rotation_coefficient[i] = 0.0f; - } - } - int rigid_body1; - int rigid_body2; - float position[3]; - float orientaiton[3]; - float move_limitation_min[3]; - float move_limitation_max[3]; - float rotation_limitation_min[3]; - float rotation_limitation_max[3]; - float spring_move_coefficient[3]; - float spring_rotation_coefficient[3]; - void Read(std::istream *stream, PmxSetting *setting); - }; - - class PmxJoint - { - public: - std::string joint_name; - std::string joint_english_name; - PmxJointType joint_type; - PmxJointParam param; - void Read(std::istream *stream, PmxSetting *setting); - }; - - enum PmxSoftBodyFlag : uint8_t - { - BLink = 0x01, - Cluster = 0x02, - Link = 0x04 - }; - - class PmxAncherRigidBody - { - public: - PmxAncherRigidBody() - : related_rigid_body(0) - , related_vertex(0) - , is_near(false) - {} - int related_rigid_body; - int related_vertex; - bool is_near; - void Read(std::istream *stream, PmxSetting *setting); - }; - - class PmxSoftBody - { - public: - PmxSoftBody() - : shape(0) - , target_material(0) - , group(0) - , mask(0) - , blink_distance(0) - , cluster_count(0) - , mass(0.0) - , collisioni_margin(0.0) - , aero_model(0) - , VCF(0.0f) - , DP(0.0f) - , DG(0.0f) - , LF(0.0f) - , PR(0.0f) - , VC(0.0f) - , DF(0.0f) - , MT(0.0f) - , CHR(0.0f) - , KHR(0.0f) - , SHR(0.0f) - , AHR(0.0f) - , SRHR_CL(0.0f) - , SKHR_CL(0.0f) - , SSHR_CL(0.0f) - , SR_SPLT_CL(0.0f) - , SK_SPLT_CL(0.0f) - , SS_SPLT_CL(0.0f) - , V_IT(0) - , P_IT(0) - , D_IT(0) - , C_IT(0) - , LST(0.0f) - , AST(0.0f) - , VST(0.0f) - , anchor_count(0) - , pin_vertex_count(0) - {} - std::string soft_body_name; - std::string soft_body_english_name; - uint8_t shape; - int target_material; - uint8_t group; - uint16_t mask; - PmxSoftBodyFlag flag; - int blink_distance; - int cluster_count; - float mass; - float collisioni_margin; - int aero_model; - float VCF; - float DP; - float DG; - float LF; - float PR; - float VC; - float DF; - float MT; - float CHR; - float KHR; - float SHR; - float AHR; - float SRHR_CL; - float SKHR_CL; - float SSHR_CL; - float SR_SPLT_CL; - float SK_SPLT_CL; - float SS_SPLT_CL; - int V_IT; - int P_IT; - int D_IT; - int C_IT; - float LST; - float AST; - float VST; - int anchor_count; - std::unique_ptr anchers; - int pin_vertex_count; - std::unique_ptr pin_vertices; - void Read(std::istream *stream, PmxSetting *setting); - }; - - class PmxModel - { - public: - PmxModel() - : version(0.0f) - , vertex_count(0) - , index_count(0) - , texture_count(0) - , material_count(0) - , bone_count(0) - , morph_count(0) - , frame_count(0) - , rigid_body_count(0) - , joint_count(0) - , soft_body_count(0) - {} - - float version; - PmxSetting setting; - std::string model_name; - std::string model_english_name; - std::string model_comment; - std::string model_english_comment; - int vertex_count; - std::unique_ptr vertices; - int index_count; - std::unique_ptr indices; - int texture_count; - std::unique_ptr< std::string []> textures; - int material_count; - std::unique_ptr materials; - int bone_count; - std::unique_ptr bones; - int morph_count; - std::unique_ptr morphs; - int frame_count; - std::unique_ptr frames; - int rigid_body_count; - std::unique_ptr rigid_bodies; - int joint_count; - std::unique_ptr joints; - int soft_body_count; - std::unique_ptr soft_bodies; - void Init(); - void Read(std::istream *stream); - //static std::unique_ptr ReadFromFile(const char *filename); - //static std::unique_ptr ReadFromStream(std::istream *stream); - }; -} diff --git a/thirdparty/assimp/code/MMDVmdParser.h b/thirdparty/assimp/code/MMDVmdParser.h deleted file mode 100644 index 947c3a2422..0000000000 --- a/thirdparty/assimp/code/MMDVmdParser.h +++ /dev/null @@ -1,376 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -#pragma once - -#include -#include -#include -#include -#include -#include -#include "MMDCpp14.h" - -namespace vmd -{ - class VmdBoneFrame - { - public: - std::string name; - int frame; - float position[3]; - float orientation[4]; - char interpolation[4][4][4]; - - void Read(std::istream* stream) - { - char buffer[15]; - stream->read((char*) buffer, sizeof(char)*15); - name = std::string(buffer); - stream->read((char*) &frame, sizeof(int)); - stream->read((char*) position, sizeof(float)*3); - stream->read((char*) orientation, sizeof(float)*4); - stream->read((char*) interpolation, sizeof(char) * 4 * 4 * 4); - } - - void Write(std::ostream* stream) - { - stream->write((char*)name.c_str(), sizeof(char) * 15); - stream->write((char*)&frame, sizeof(int)); - stream->write((char*)position, sizeof(float) * 3); - stream->write((char*)orientation, sizeof(float) * 4); - stream->write((char*)interpolation, sizeof(char) * 4 * 4 * 4); - } - }; - - class VmdFaceFrame - { - public: - std::string face_name; - float weight; - uint32_t frame; - - void Read(std::istream* stream) - { - char buffer[15]; - stream->read((char*) &buffer, sizeof(char) * 15); - face_name = std::string(buffer); - stream->read((char*) &frame, sizeof(int)); - stream->read((char*) &weight, sizeof(float)); - } - - void Write(std::ostream* stream) - { - stream->write((char*)face_name.c_str(), sizeof(char) * 15); - stream->write((char*)&frame, sizeof(int)); - stream->write((char*)&weight, sizeof(float)); - } - }; - - class VmdCameraFrame - { - public: - int frame; - float distance; - float position[3]; - float orientation[3]; - char interpolation[6][4]; - float angle; - char unknown[3]; - - void Read(std::istream *stream) - { - stream->read((char*) &frame, sizeof(int)); - stream->read((char*) &distance, sizeof(float)); - stream->read((char*) position, sizeof(float) * 3); - stream->read((char*) orientation, sizeof(float) * 3); - stream->read((char*) interpolation, sizeof(char) * 24); - stream->read((char*) &angle, sizeof(float)); - stream->read((char*) unknown, sizeof(char) * 3); - } - - void Write(std::ostream *stream) - { - stream->write((char*)&frame, sizeof(int)); - stream->write((char*)&distance, sizeof(float)); - stream->write((char*)position, sizeof(float) * 3); - stream->write((char*)orientation, sizeof(float) * 3); - stream->write((char*)interpolation, sizeof(char) * 24); - stream->write((char*)&angle, sizeof(float)); - stream->write((char*)unknown, sizeof(char) * 3); - } - }; - - class VmdLightFrame - { - public: - int frame; - float color[3]; - float position[3]; - - void Read(std::istream* stream) - { - stream->read((char*) &frame, sizeof(int)); - stream->read((char*) color, sizeof(float) * 3); - stream->read((char*) position, sizeof(float) * 3); - } - - void Write(std::ostream* stream) - { - stream->write((char*)&frame, sizeof(int)); - stream->write((char*)color, sizeof(float) * 3); - stream->write((char*)position, sizeof(float) * 3); - } - }; - - class VmdIkEnable - { - public: - std::string ik_name; - bool enable; - }; - - class VmdIkFrame - { - public: - int frame; - bool display; - std::vector ik_enable; - - void Read(std::istream *stream) - { - char buffer[20]; - stream->read((char*) &frame, sizeof(int)); - stream->read((char*) &display, sizeof(uint8_t)); - int ik_count; - stream->read((char*) &ik_count, sizeof(int)); - ik_enable.resize(ik_count); - for (int i = 0; i < ik_count; i++) - { - stream->read(buffer, 20); - ik_enable[i].ik_name = std::string(buffer); - stream->read((char*) &ik_enable[i].enable, sizeof(uint8_t)); - } - } - - void Write(std::ostream *stream) - { - stream->write((char*)&frame, sizeof(int)); - stream->write((char*)&display, sizeof(uint8_t)); - int ik_count = static_cast(ik_enable.size()); - stream->write((char*)&ik_count, sizeof(int)); - for (int i = 0; i < ik_count; i++) - { - const VmdIkEnable& ik_enable = this->ik_enable.at(i); - stream->write(ik_enable.ik_name.c_str(), 20); - stream->write((char*)&ik_enable.enable, sizeof(uint8_t)); - } - } - }; - - class VmdMotion - { - public: - std::string model_name; - int version; - std::vector bone_frames; - std::vector face_frames; - std::vector camera_frames; - std::vector light_frames; - std::vector ik_frames; - - static std::unique_ptr LoadFromFile(char const *filename) - { - std::ifstream stream(filename, std::ios::binary); - auto result = LoadFromStream(&stream); - stream.close(); - return result; - } - - static std::unique_ptr LoadFromStream(std::ifstream *stream) - { - - char buffer[30]; - auto result = mmd::make_unique(); - - // magic and version - stream->read((char*) buffer, 30); - if (strncmp(buffer, "Vocaloid Motion Data", 20)) - { - std::cerr << "invalid vmd file." << std::endl; - return nullptr; - } - result->version = std::atoi(buffer + 20); - - // name - stream->read(buffer, 20); - result->model_name = std::string(buffer); - - // bone frames - int bone_frame_num; - stream->read((char*) &bone_frame_num, sizeof(int)); - result->bone_frames.resize(bone_frame_num); - for (int i = 0; i < bone_frame_num; i++) - { - result->bone_frames[i].Read(stream); - } - - // face frames - int face_frame_num; - stream->read((char*) &face_frame_num, sizeof(int)); - result->face_frames.resize(face_frame_num); - for (int i = 0; i < face_frame_num; i++) - { - result->face_frames[i].Read(stream); - } - - // camera frames - int camera_frame_num; - stream->read((char*) &camera_frame_num, sizeof(int)); - result->camera_frames.resize(camera_frame_num); - for (int i = 0; i < camera_frame_num; i++) - { - result->camera_frames[i].Read(stream); - } - - // light frames - int light_frame_num; - stream->read((char*) &light_frame_num, sizeof(int)); - result->light_frames.resize(light_frame_num); - for (int i = 0; i < light_frame_num; i++) - { - result->light_frames[i].Read(stream); - } - - // unknown2 - stream->read(buffer, 4); - - // ik frames - if (stream->peek() != std::ios::traits_type::eof()) - { - int ik_num; - stream->read((char*) &ik_num, sizeof(int)); - result->ik_frames.resize(ik_num); - for (int i = 0; i < ik_num; i++) - { - result->ik_frames[i].Read(stream); - } - } - - if (stream->peek() != std::ios::traits_type::eof()) - { - std::cerr << "vmd stream has unknown data." << std::endl; - } - - return result; - } - - bool SaveToFile(const std::u16string& /*filename*/) - { - // TODO: How to adapt u16string to string? - /* - std::ofstream stream(filename.c_str(), std::ios::binary); - auto result = SaveToStream(&stream); - stream.close(); - return result; - */ - return false; - } - - bool SaveToStream(std::ofstream *stream) - { - std::string magic = "Vocaloid Motion Data 0002\0"; - magic.resize(30); - - // magic and version - stream->write(magic.c_str(), 30); - - // name - stream->write(model_name.c_str(), 20); - - // bone frames - const int bone_frame_num = static_cast(bone_frames.size()); - stream->write(reinterpret_cast(&bone_frame_num), sizeof(int)); - for (int i = 0; i < bone_frame_num; i++) - { - bone_frames[i].Write(stream); - } - - // face frames - const int face_frame_num = static_cast(face_frames.size()); - stream->write(reinterpret_cast(&face_frame_num), sizeof(int)); - for (int i = 0; i < face_frame_num; i++) - { - face_frames[i].Write(stream); - } - - // camera frames - const int camera_frame_num = static_cast(camera_frames.size()); - stream->write(reinterpret_cast(&camera_frame_num), sizeof(int)); - for (int i = 0; i < camera_frame_num; i++) - { - camera_frames[i].Write(stream); - } - - // light frames - const int light_frame_num = static_cast(light_frames.size()); - stream->write(reinterpret_cast(&light_frame_num), sizeof(int)); - for (int i = 0; i < light_frame_num; i++) - { - light_frames[i].Write(stream); - } - - // self shadow datas - const int self_shadow_num = 0; - stream->write(reinterpret_cast(&self_shadow_num), sizeof(int)); - - // ik frames - const int ik_num = static_cast(ik_frames.size()); - stream->write(reinterpret_cast(&ik_num), sizeof(int)); - for (int i = 0; i < ik_num; i++) - { - ik_frames[i].Write(stream); - } - - return true; - } - }; -} diff --git a/thirdparty/assimp/code/MakeVerboseFormat.cpp b/thirdparty/assimp/code/MakeVerboseFormat.cpp deleted file mode 100644 index 50ff5ed93d..0000000000 --- a/thirdparty/assimp/code/MakeVerboseFormat.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ -/** @file Implementation of the post processing step "MakeVerboseFormat" -*/ - - -#include "MakeVerboseFormat.h" -#include -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -MakeVerboseFormatProcess::MakeVerboseFormatProcess() -{ - // nothing to do here -} -// ------------------------------------------------------------------------------------------------ -MakeVerboseFormatProcess::~MakeVerboseFormatProcess() -{ - // nothing to do here -} -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void MakeVerboseFormatProcess::Execute( aiScene* pScene) -{ - ai_assert(NULL != pScene); - ASSIMP_LOG_DEBUG("MakeVerboseFormatProcess begin"); - - bool bHas = false; - for( unsigned int a = 0; a < pScene->mNumMeshes; a++) - { - if( MakeVerboseFormat( pScene->mMeshes[a])) - bHas = true; - } - if (bHas) { - ASSIMP_LOG_INFO("MakeVerboseFormatProcess finished. There was much work to do ..."); - } else { - ASSIMP_LOG_DEBUG("MakeVerboseFormatProcess. There was nothing to do."); - } - - pScene->mFlags &= ~AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh) -{ - ai_assert(NULL != pcMesh); - - unsigned int iOldNumVertices = pcMesh->mNumVertices; - const unsigned int iNumVerts = pcMesh->mNumFaces*3; - - aiVector3D* pvPositions = new aiVector3D[ iNumVerts ]; - - aiVector3D* pvNormals = NULL; - if (pcMesh->HasNormals()) - { - pvNormals = new aiVector3D[iNumVerts]; - } - aiVector3D* pvTangents = NULL, *pvBitangents = NULL; - if (pcMesh->HasTangentsAndBitangents()) - { - pvTangents = new aiVector3D[iNumVerts]; - pvBitangents = new aiVector3D[iNumVerts]; - } - - aiVector3D* apvTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS] = {0}; - aiColor4D* apvColorSets[AI_MAX_NUMBER_OF_COLOR_SETS] = {0}; - - unsigned int p = 0; - while (pcMesh->HasTextureCoords(p)) - apvTextureCoords[p++] = new aiVector3D[iNumVerts]; - - p = 0; - while (pcMesh->HasVertexColors(p)) - apvColorSets[p++] = new aiColor4D[iNumVerts]; - - // allocate enough memory to hold output bones and vertex weights ... - std::vector* newWeights = new std::vector[pcMesh->mNumBones]; - for (unsigned int i = 0;i < pcMesh->mNumBones;++i) { - newWeights[i].reserve(pcMesh->mBones[i]->mNumWeights*3); - } - - // iterate through all faces and build a clean list - unsigned int iIndex = 0; - for (unsigned int a = 0; a< pcMesh->mNumFaces;++a) - { - aiFace* pcFace = &pcMesh->mFaces[a]; - for (unsigned int q = 0; q < pcFace->mNumIndices;++q,++iIndex) - { - // need to build a clean list of bones, too - for (unsigned int i = 0;i < pcMesh->mNumBones;++i) - { - for (unsigned int a = 0; a < pcMesh->mBones[i]->mNumWeights;a++) - { - const aiVertexWeight& w = pcMesh->mBones[i]->mWeights[a]; - if(pcFace->mIndices[q] == w.mVertexId) - { - aiVertexWeight wNew; - wNew.mVertexId = iIndex; - wNew.mWeight = w.mWeight; - newWeights[i].push_back(wNew); - } - } - } - - pvPositions[iIndex] = pcMesh->mVertices[pcFace->mIndices[q]]; - - if (pcMesh->HasNormals()) - { - pvNormals[iIndex] = pcMesh->mNormals[pcFace->mIndices[q]]; - } - if (pcMesh->HasTangentsAndBitangents()) - { - pvTangents[iIndex] = pcMesh->mTangents[pcFace->mIndices[q]]; - pvBitangents[iIndex] = pcMesh->mBitangents[pcFace->mIndices[q]]; - } - - unsigned int p = 0; - while (pcMesh->HasTextureCoords(p)) - { - apvTextureCoords[p][iIndex] = pcMesh->mTextureCoords[p][pcFace->mIndices[q]]; - ++p; - } - p = 0; - while (pcMesh->HasVertexColors(p)) - { - apvColorSets[p][iIndex] = pcMesh->mColors[p][pcFace->mIndices[q]]; - ++p; - } - pcFace->mIndices[q] = iIndex; - } - } - - - - // build output vertex weights - for (unsigned int i = 0;i < pcMesh->mNumBones;++i) - { - delete [] pcMesh->mBones[i]->mWeights; - if (!newWeights[i].empty()) { - pcMesh->mBones[i]->mWeights = new aiVertexWeight[newWeights[i].size()]; - aiVertexWeight *weightToCopy = &( newWeights[i][0] ); - memcpy(pcMesh->mBones[i]->mWeights, weightToCopy, - sizeof(aiVertexWeight) * newWeights[i].size()); - } else { - pcMesh->mBones[i]->mWeights = NULL; - } - } - delete[] newWeights; - - // delete the old members - delete[] pcMesh->mVertices; - pcMesh->mVertices = pvPositions; - - p = 0; - while (pcMesh->HasTextureCoords(p)) - { - delete[] pcMesh->mTextureCoords[p]; - pcMesh->mTextureCoords[p] = apvTextureCoords[p]; - ++p; - } - p = 0; - while (pcMesh->HasVertexColors(p)) - { - delete[] pcMesh->mColors[p]; - pcMesh->mColors[p] = apvColorSets[p]; - ++p; - } - pcMesh->mNumVertices = iNumVerts; - - if (pcMesh->HasNormals()) - { - delete[] pcMesh->mNormals; - pcMesh->mNormals = pvNormals; - } - if (pcMesh->HasTangentsAndBitangents()) - { - delete[] pcMesh->mTangents; - pcMesh->mTangents = pvTangents; - delete[] pcMesh->mBitangents; - pcMesh->mBitangents = pvBitangents; - } - return (pcMesh->mNumVertices != iOldNumVertices); -} diff --git a/thirdparty/assimp/code/MakeVerboseFormat.h b/thirdparty/assimp/code/MakeVerboseFormat.h deleted file mode 100644 index d12db63ae1..0000000000 --- a/thirdparty/assimp/code/MakeVerboseFormat.h +++ /dev/null @@ -1,105 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a post processing step to bring a given scene - into the verbose format that is expected by most postprocess steps. - This is the inverse of the "JoinIdenticalVertices" step. */ -#ifndef AI_MAKEVERBOSEFORMAT_H_INC -#define AI_MAKEVERBOSEFORMAT_H_INC - -#include "BaseProcess.h" -struct aiMesh; - -namespace Assimp { - -// --------------------------------------------------------------------------- -/** MakeVerboseFormatProcess: Class to convert an asset to the verbose - * format which is expected by most postprocess steps. - * - * This is the inverse of what the "JoinIdenticalVertices" step is doing. - * This step has no official flag (since it wouldn't make sense to run it - * during import). It is intended for applications intending to modify the - * returned aiScene. After this step has been executed, they can execute - * other postprocess steps on the data. The code might also be useful to - * quickly adapt code that doesn't result in a verbose representation of - * the scene data. - * The step has been added because it was required by the viewer, however - * it has been moved to the main library since others might find it - * useful, too. */ -class ASSIMP_API_WINONLY MakeVerboseFormatProcess : public BaseProcess -{ -public: - - - MakeVerboseFormatProcess(); - ~MakeVerboseFormatProcess(); - -public: - - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag field. - * @param pFlags The processing flags the importer was called with. A bitwise - * combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, false if not */ - bool IsActive( unsigned int /*pFlags*/ ) const - { - // NOTE: There is no direct flag that corresponds to - // this postprocess step. - return false; - } - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. */ - void Execute( aiScene* pScene); - - -private: - - //! Apply the postprocess step to a given submesh - bool MakeVerboseFormat (aiMesh* pcMesh); -}; - -} // end of namespace Assimp - -#endif // !!AI_KILLNORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/Material/MaterialSystem.cpp b/thirdparty/assimp/code/Material/MaterialSystem.cpp new file mode 100644 index 0000000000..d0b39093b6 --- /dev/null +++ b/thirdparty/assimp/code/Material/MaterialSystem.cpp @@ -0,0 +1,649 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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 +#include +#include +#include "MaterialSystem.h" +#include +#include +#include +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Get a specific property from a material +aiReturn aiGetMaterialProperty(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + const aiMaterialProperty** pPropOut) +{ + ai_assert( pMat != NULL ); + ai_assert( pKey != NULL ); + ai_assert( pPropOut != NULL ); + + /* Just search for a property with exactly this name .. + * could be improved by hashing, but it's possibly + * no worth the effort (we're bound to C structures, + * thus std::map or derivates are not applicable. */ + for ( unsigned int i = 0; i < pMat->mNumProperties; ++i ) { + aiMaterialProperty* prop = pMat->mProperties[i]; + + if (prop /* just for safety ... */ + && 0 == strcmp( prop->mKey.data, pKey ) + && (UINT_MAX == type || prop->mSemantic == type) /* UINT_MAX is a wild-card, but this is undocumented :-) */ + && (UINT_MAX == index || prop->mIndex == index)) + { + *pPropOut = pMat->mProperties[i]; + return AI_SUCCESS; + } + } + *pPropOut = NULL; + return AI_FAILURE; +} + +// ------------------------------------------------------------------------------------------------ +// Get an array of floating-point values from the material. +aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + ai_real* pOut, + unsigned int* pMax) +{ + ai_assert( pOut != nullptr ); + ai_assert( pMat != nullptr ); + + const aiMaterialProperty* prop; + aiGetMaterialProperty(pMat,pKey,type,index, (const aiMaterialProperty**) &prop); + if ( nullptr == prop) { + return AI_FAILURE; + } + + // data is given in floats, convert to ai_real + unsigned int iWrite = 0; + if( aiPTI_Float == prop->mType || aiPTI_Buffer == prop->mType) { + iWrite = prop->mDataLength / sizeof(float); + if (pMax) { + iWrite = std::min(*pMax,iWrite); ; + } + + for (unsigned int a = 0; a < iWrite; ++a) { + pOut[ a ] = static_cast ( reinterpret_cast(prop->mData)[a] ); + } + + if (pMax) { + *pMax = iWrite; + } + } + // data is given in doubles, convert to float + else if( aiPTI_Double == prop->mType) { + iWrite = prop->mDataLength / sizeof(double); + if (pMax) { + iWrite = std::min(*pMax,iWrite); ; + } + for (unsigned int a = 0; a < iWrite;++a) { + pOut[a] = static_cast ( reinterpret_cast(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 ( reinterpret_cast(prop->mData)[a] ); + } + if (pMax) { + *pMax = iWrite; + } + } + // a string ... read floats separated by spaces + else { + if (pMax) { + iWrite = *pMax; + } + // strings are zero-terminated with a 32 bit length prefix, so this is safe + const char *cur = prop->mData + 4; + ai_assert( prop->mDataLength >= 5 ); + ai_assert( !prop->mData[ prop->mDataLength - 1 ] ); + for ( unsigned int a = 0; ;++a) { + cur = fast_atoreal_move(cur,pOut[a]); + if ( a==iWrite-1 ) { + break; + } + if ( !IsSpace(*cur) ) { + ASSIMP_LOG_ERROR("Material property" + std::string(pKey) + + " is a string; failed to parse a float array out of it."); + return AI_FAILURE; + } + } + + if (pMax) { + *pMax = iWrite; + } + } + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +// Get an array if integers from the material +aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + int* pOut, + unsigned int* pMax) +{ + ai_assert( pOut != NULL ); + ai_assert( pMat != NULL ); + + const aiMaterialProperty* prop; + aiGetMaterialProperty(pMat,pKey,type,index,(const aiMaterialProperty**) &prop); + if (!prop) { + return AI_FAILURE; + } + + // data is given in ints, simply copy it + unsigned int iWrite = 0; + if( aiPTI_Integer == prop->mType || aiPTI_Buffer == prop->mType) { + iWrite = std::max(static_cast(prop->mDataLength / sizeof(int32_t)), 1u); + if (pMax) { + iWrite = std::min(*pMax,iWrite); + } + if (1 == prop->mDataLength) { + // bool type, 1 byte + *pOut = static_cast(*prop->mData); + } + else { + for (unsigned int a = 0; a < iWrite;++a) { + pOut[a] = static_cast(reinterpret_cast(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(reinterpret_cast(prop->mData)[a]); + } + if (pMax) { + *pMax = iWrite; + } + } + // it is a string ... no way to read something out of this + else { + if (pMax) { + iWrite = *pMax; + } + // strings are zero-terminated with a 32 bit length prefix, so this is safe + const char *cur = prop->mData+4; + ai_assert( prop->mDataLength >= 5 ); + ai_assert( !prop->mData[ prop->mDataLength - 1 ] ); + for (unsigned int a = 0; ;++a) { + pOut[a] = strtol10(cur,&cur); + if(a==iWrite-1) { + break; + } + if(!IsSpace(*cur)) { + ASSIMP_LOG_ERROR("Material property" + std::string(pKey) + + " is a string; failed to parse an integer array out of it."); + return AI_FAILURE; + } + } + + if (pMax) { + *pMax = iWrite; + } + } + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +// Get a color (3 or 4 floats) from the material +aiReturn aiGetMaterialColor(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + aiColor4D* pOut) +{ + unsigned int iMax = 4; + const aiReturn eRet = aiGetMaterialFloatArray(pMat,pKey,type,index,(ai_real*)pOut,&iMax); + + // if no alpha channel is defined: set it to 1.0 + if (3 == iMax) { + pOut->a = 1.0; + } + + return eRet; +} + +// ------------------------------------------------------------------------------------------------ +// Get a aiUVTransform (4 floats) from the material +aiReturn aiGetMaterialUVTransform(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + aiUVTransform* pOut) +{ + unsigned int iMax = 4; + return aiGetMaterialFloatArray(pMat,pKey,type,index,(ai_real*)pOut,&iMax); +} + +// ------------------------------------------------------------------------------------------------ +// Get a string from the material +aiReturn aiGetMaterialString(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + aiString* pOut) +{ + ai_assert (pOut != NULL); + + const aiMaterialProperty* prop; + aiGetMaterialProperty(pMat,pKey,type,index,(const aiMaterialProperty**)&prop); + if (!prop) { + return AI_FAILURE; + } + + if( aiPTI_String == prop->mType) { + ai_assert(prop->mDataLength>=5); + + // The string is stored as 32 but length prefix followed by zero-terminated UTF8 data + pOut->length = static_cast(*reinterpret_cast(prop->mData)); + + ai_assert( pOut->length+1+4==prop->mDataLength ); + ai_assert( !prop->mData[ prop->mDataLength - 1 ] ); + memcpy(pOut->data,prop->mData+4,pOut->length+1); + } + else { + // TODO - implement lexical cast as well + ASSIMP_LOG_ERROR("Material property" + std::string(pKey) + + " was found, but is no string" ); + return AI_FAILURE; + } + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +// Get the number of textures on a particular texture stack +unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial* pMat, + C_ENUM aiTextureType type) +{ + ai_assert (pMat != NULL); + + // Textures are always stored with ascending indices (ValidateDS provides a check, so we don't need to do it again) + unsigned int max = 0; + for (unsigned int i = 0; i < pMat->mNumProperties;++i) { + aiMaterialProperty* prop = pMat->mProperties[i]; + + if ( prop /* just a sanity check ... */ + && 0 == strcmp( prop->mKey.data, _AI_MATKEY_TEXTURE_BASE ) + && prop->mSemantic == type) { + + max = std::max(max,prop->mIndex+1); + } + } + return max; +} + +// ------------------------------------------------------------------------------------------------ +aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat, + aiTextureType type, + unsigned int index, + C_STRUCT aiString* path, + aiTextureMapping* _mapping /*= NULL*/, + unsigned int* uvindex /*= NULL*/, + ai_real* blend /*= NULL*/, + aiTextureOp* op /*= NULL*/, + aiTextureMapMode* mapmode /*= NULL*/, + unsigned int* flags /*= NULL*/ + ) +{ + ai_assert( NULL != mat ); + ai_assert( NULL != path ); + + // Get the path to the texture + if (AI_SUCCESS != aiGetMaterialString(mat,AI_MATKEY_TEXTURE(type,index),path)) { + return AI_FAILURE; + } + + // Determine mapping type + int mapping_ = static_cast(aiTextureMapping_UV); + aiGetMaterialInteger(mat,AI_MATKEY_MAPPING(type,index), &mapping_); + aiTextureMapping mapping = static_cast(mapping_); + if (_mapping) + *_mapping = mapping; + + // Get UV index + if (aiTextureMapping_UV == mapping && uvindex) { + aiGetMaterialInteger(mat,AI_MATKEY_UVWSRC(type,index),(int*)uvindex); + } + // Get blend factor + if (blend) { + aiGetMaterialFloat(mat,AI_MATKEY_TEXBLEND(type,index),blend); + } + // Get texture operation + if (op){ + aiGetMaterialInteger(mat,AI_MATKEY_TEXOP(type,index),(int*)op); + } + // Get texture mapping modes + if (mapmode) { + aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_U(type,index),(int*)&mapmode[0]); + aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_V(type,index),(int*)&mapmode[1]); + } + // Get texture flags + if (flags){ + aiGetMaterialInteger(mat,AI_MATKEY_TEXFLAGS(type,index),(int*)flags); + } + + return AI_SUCCESS; +} + + +static const unsigned int DefaultNumAllocated = 5; + +// ------------------------------------------------------------------------------------------------ +// Construction. Actually the one and only way to get an aiMaterial instance +aiMaterial::aiMaterial() +: mProperties( nullptr ) +, mNumProperties( 0 ) +, mNumAllocated( DefaultNumAllocated ) { + // Allocate 5 entries by default + mProperties = new aiMaterialProperty*[ DefaultNumAllocated ]; +} + +// ------------------------------------------------------------------------------------------------ +aiMaterial::~aiMaterial() +{ + Clear(); + + delete[] mProperties; +} + +// ------------------------------------------------------------------------------------------------ +aiString aiMaterial::GetName() { + aiString name; + Get(AI_MATKEY_NAME, name); + + return name; +} + +// ------------------------------------------------------------------------------------------------ +void aiMaterial::Clear() +{ + for ( unsigned int i = 0; i < mNumProperties; ++i ) { + // delete this entry + delete mProperties[ i ]; + AI_DEBUG_INVALIDATE_PTR(mProperties[i]); + } + mNumProperties = 0; + + // The array remains allocated, we just invalidated its contents +} + +// ------------------------------------------------------------------------------------------------ +aiReturn aiMaterial::RemoveProperty ( const char* pKey,unsigned int type, unsigned int index ) +{ + ai_assert( nullptr != pKey ); + + for (unsigned int i = 0; i < mNumProperties;++i) { + aiMaterialProperty* prop = mProperties[i]; + + if (prop && !strcmp( prop->mKey.data, pKey ) && + prop->mSemantic == type && prop->mIndex == index) + { + // Delete this entry + delete mProperties[i]; + + // collapse the array behind --. + --mNumProperties; + for (unsigned int a = i; a < mNumProperties;++a) { + mProperties[a] = mProperties[a+1]; + } + return AI_SUCCESS; + } + } + + return AI_FAILURE; +} + +// ------------------------------------------------------------------------------------------------ +aiReturn aiMaterial::AddBinaryProperty (const void* pInput, + unsigned int pSizeInBytes, + const char* pKey, + unsigned int type, + unsigned int index, + aiPropertyTypeInfo pType + ) +{ + ai_assert( pInput != NULL ); + ai_assert( pKey != NULL ); + ai_assert( 0 != pSizeInBytes ); + + if ( 0 == pSizeInBytes ) { + + } + + // first search the list whether there is already an entry with this key + unsigned int iOutIndex( UINT_MAX ); + for ( unsigned int i = 0; i < mNumProperties; ++i ) { + aiMaterialProperty *prop( mProperties[ i ] ); + + if (prop /* just for safety */ && !strcmp( prop->mKey.data, pKey ) && + prop->mSemantic == type && prop->mIndex == index){ + + delete mProperties[i]; + iOutIndex = i; + } + } + + // Allocate a new material property + aiMaterialProperty* pcNew = new aiMaterialProperty(); + + // .. and fill it + pcNew->mType = pType; + pcNew->mSemantic = type; + pcNew->mIndex = index; + + pcNew->mDataLength = pSizeInBytes; + pcNew->mData = new char[pSizeInBytes]; + memcpy (pcNew->mData,pInput,pSizeInBytes); + + pcNew->mKey.length = ::strlen(pKey); + ai_assert ( MAXLEN > pcNew->mKey.length); + strcpy( pcNew->mKey.data, pKey ); + + if (UINT_MAX != iOutIndex) { + mProperties[iOutIndex] = pcNew; + return AI_SUCCESS; + } + + // resize the array ... double the storage allocated + if (mNumProperties == mNumAllocated) { + const unsigned int iOld = mNumAllocated; + mNumAllocated *= 2; + + aiMaterialProperty** ppTemp; + try { + ppTemp = new aiMaterialProperty*[mNumAllocated]; + } catch (std::bad_alloc&) { + delete pcNew; + return AI_OUTOFMEMORY; + } + + // just copy all items over; then replace the old array + memcpy (ppTemp,mProperties,iOld * sizeof(void*)); + + delete[] mProperties; + mProperties = ppTemp; + } + // push back ... + mProperties[mNumProperties++] = pcNew; + + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +aiReturn aiMaterial::AddProperty (const aiString* pInput, + const char* pKey, + unsigned int type, + unsigned int index) +{ + // We don't want to add the whole buffer .. write a 32 bit length + // prefix followed by the zero-terminated UTF8 string. + // (HACK) I don't want to break the ABI now, but we definitely + // ought to change aiString::mLength to uint32_t one day. + if (sizeof(size_t) == 8) { + aiString copy = *pInput; + uint32_t* s = reinterpret_cast(©.length); + s[1] = static_cast(pInput->length); + + return AddBinaryProperty(s+1, + static_cast(pInput->length+1+4), + pKey, + type, + index, + aiPTI_String); + } + ai_assert(sizeof(size_t)==4); + return AddBinaryProperty(pInput, + static_cast(pInput->length+1+4), + pKey, + type, + index, + aiPTI_String); +} + +// ------------------------------------------------------------------------------------------------ +uint32_t Assimp::ComputeMaterialHash(const aiMaterial* mat, bool includeMatName /*= false*/) +{ + uint32_t hash = 1503; // magic start value, chosen to be my birthday :-) + for ( unsigned int i = 0; i < mat->mNumProperties; ++i ) { + aiMaterialProperty* prop; + + // Exclude all properties whose first character is '?' from the hash + // See doc for aiMaterialProperty. + if ((prop = mat->mProperties[i]) && (includeMatName || prop->mKey.data[0] != '?')) { + + hash = SuperFastHash(prop->mKey.data,(unsigned int)prop->mKey.length,hash); + hash = SuperFastHash(prop->mData,prop->mDataLength,hash); + + // Combine the semantic and the index with the hash + hash = SuperFastHash((const char*)&prop->mSemantic,sizeof(unsigned int),hash); + hash = SuperFastHash((const char*)&prop->mIndex,sizeof(unsigned int),hash); + } + } + return hash; +} + +// ------------------------------------------------------------------------------------------------ +void aiMaterial::CopyPropertyList(aiMaterial* pcDest, + const aiMaterial* pcSrc + ) +{ + ai_assert(NULL != pcDest); + ai_assert(NULL != pcSrc); + + unsigned int iOldNum = pcDest->mNumProperties; + pcDest->mNumAllocated += pcSrc->mNumAllocated; + pcDest->mNumProperties += pcSrc->mNumProperties; + + aiMaterialProperty** pcOld = pcDest->mProperties; + pcDest->mProperties = new aiMaterialProperty*[pcDest->mNumAllocated]; + + if (iOldNum && pcOld) { + for (unsigned int i = 0; i < iOldNum;++i) { + pcDest->mProperties[i] = pcOld[i]; + } + } + + if ( pcOld ) { + delete[] pcOld; + } + + for (unsigned int i = iOldNum; i< pcDest->mNumProperties;++i) { + aiMaterialProperty* propSrc = pcSrc->mProperties[i]; + + // search whether we have already a property with this name -> if yes, overwrite it + aiMaterialProperty* prop; + for ( unsigned int q = 0; q < iOldNum; ++q ) { + prop = pcDest->mProperties[q]; + if (prop /* just for safety */ && prop->mKey == propSrc->mKey && prop->mSemantic == propSrc->mSemantic + && prop->mIndex == propSrc->mIndex) { + delete prop; + + // collapse the whole array ... + memmove(&pcDest->mProperties[q],&pcDest->mProperties[q+1],i-q); + i--; + pcDest->mNumProperties--; + } + } + + // Allocate the output property and copy the source property + prop = pcDest->mProperties[i] = new aiMaterialProperty(); + prop->mKey = propSrc->mKey; + prop->mDataLength = propSrc->mDataLength; + prop->mType = propSrc->mType; + prop->mSemantic = propSrc->mSemantic; + prop->mIndex = propSrc->mIndex; + + prop->mData = new char[propSrc->mDataLength]; + memcpy(prop->mData,propSrc->mData,prop->mDataLength); + } +} diff --git a/thirdparty/assimp/code/Material/MaterialSystem.h b/thirdparty/assimp/code/Material/MaterialSystem.h new file mode 100644 index 0000000000..67d53578cb --- /dev/null +++ b/thirdparty/assimp/code/Material/MaterialSystem.h @@ -0,0 +1,72 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file MaterialSystem.h + * Now that #MaterialHelper is gone, this file only contains some + * internal material utility functions. + */ +#ifndef AI_MATERIALSYSTEM_H_INC +#define AI_MATERIALSYSTEM_H_INC + +#include + +struct aiMaterial; + +namespace Assimp { + +// ------------------------------------------------------------------------------ +/** Computes a hash (hopefully unique) from all material properties + * The hash value reflects the current property state, so if you add any + * property and call this method again, the resulting hash value will be + * different. The hash is not persistent across different builds and platforms. + * + * @param includeMatName Set to 'true' to take all properties with + * '?' as initial character in their name into account. + * Currently #AI_MATKEY_NAME is the only example. + * @return 32 Bit jash value for the material + */ +uint32_t ComputeMaterialHash(const aiMaterial* mat, bool includeMatName = false); + + +} // ! namespace Assimp + +#endif //!! AI_MATERIALSYSTEM_H_INC diff --git a/thirdparty/assimp/code/MaterialSystem.cpp b/thirdparty/assimp/code/MaterialSystem.cpp deleted file mode 100644 index 03d5a18a34..0000000000 --- a/thirdparty/assimp/code/MaterialSystem.cpp +++ /dev/null @@ -1,647 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file MaterialSystem.cpp - * @brief Implementation of the material system of the library - */ - -#include -#include -#include -#include "MaterialSystem.h" -#include -#include -#include -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Get a specific property from a material -aiReturn aiGetMaterialProperty(const aiMaterial* pMat, - const char* pKey, - unsigned int type, - unsigned int index, - const aiMaterialProperty** pPropOut) -{ - ai_assert( pMat != NULL ); - ai_assert( pKey != NULL ); - ai_assert( pPropOut != NULL ); - - /* Just search for a property with exactly this name .. - * could be improved by hashing, but it's possibly - * no worth the effort (we're bound to C structures, - * thus std::map or derivates are not applicable. */ - for ( unsigned int i = 0; i < pMat->mNumProperties; ++i ) { - aiMaterialProperty* prop = pMat->mProperties[i]; - - if (prop /* just for safety ... */ - && 0 == strcmp( prop->mKey.data, pKey ) - && (UINT_MAX == type || prop->mSemantic == type) /* UINT_MAX is a wild-card, but this is undocumented :-) */ - && (UINT_MAX == index || prop->mIndex == index)) - { - *pPropOut = pMat->mProperties[i]; - return AI_SUCCESS; - } - } - *pPropOut = NULL; - return AI_FAILURE; -} - -// ------------------------------------------------------------------------------------------------ -// Get an array of floating-point values from the material. -aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat, - const char* pKey, - unsigned int type, - unsigned int index, - ai_real* pOut, - unsigned int* pMax) -{ - ai_assert( pOut != NULL ); - ai_assert( pMat != NULL ); - - const aiMaterialProperty* prop; - aiGetMaterialProperty(pMat,pKey,type,index, (const aiMaterialProperty**) &prop); - if (!prop) { - return AI_FAILURE; - } - - // data is given in floats, convert to ai_real - unsigned int iWrite = 0; - if( aiPTI_Float == prop->mType || aiPTI_Buffer == prop->mType) { - iWrite = prop->mDataLength / sizeof(float); - if (pMax) { - iWrite = std::min(*pMax,iWrite); ; - } - for (unsigned int a = 0; a < iWrite;++a) { - pOut[a] = static_cast ( reinterpret_cast(prop->mData)[a] ); - } - if (pMax) { - *pMax = iWrite; - } - } - // data is given in doubles, convert to float - else if( aiPTI_Double == prop->mType) { - iWrite = prop->mDataLength / sizeof(double); - if (pMax) { - iWrite = std::min(*pMax,iWrite); ; - } - for (unsigned int a = 0; a < iWrite;++a) { - pOut[a] = static_cast ( reinterpret_cast(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 ( reinterpret_cast(prop->mData)[a] ); - } - if (pMax) { - *pMax = iWrite; - } - } - // a string ... read floats separated by spaces - else { - if (pMax) { - iWrite = *pMax; - } - // strings are zero-terminated with a 32 bit length prefix, so this is safe - const char *cur = prop->mData + 4; - ai_assert( prop->mDataLength >= 5 ); - ai_assert( !prop->mData[ prop->mDataLength - 1 ] ); - for ( unsigned int a = 0; ;++a) { - cur = fast_atoreal_move(cur,pOut[a]); - if ( a==iWrite-1 ) { - break; - } - if ( !IsSpace(*cur) ) { - ASSIMP_LOG_ERROR("Material property" + std::string(pKey) + - " is a string; failed to parse a float array out of it."); - return AI_FAILURE; - } - } - - if (pMax) { - *pMax = iWrite; - } - } - return AI_SUCCESS; -} - -// ------------------------------------------------------------------------------------------------ -// Get an array if integers from the material -aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat, - const char* pKey, - unsigned int type, - unsigned int index, - int* pOut, - unsigned int* pMax) -{ - ai_assert( pOut != NULL ); - ai_assert( pMat != NULL ); - - const aiMaterialProperty* prop; - aiGetMaterialProperty(pMat,pKey,type,index,(const aiMaterialProperty**) &prop); - if (!prop) { - return AI_FAILURE; - } - - // data is given in ints, simply copy it - unsigned int iWrite = 0; - if( aiPTI_Integer == prop->mType || aiPTI_Buffer == prop->mType) { - iWrite = std::max(static_cast(prop->mDataLength / sizeof(int32_t)), 1u); - if (pMax) { - iWrite = std::min(*pMax,iWrite); - } - if (1 == prop->mDataLength) { - // bool type, 1 byte - *pOut = static_cast(*prop->mData); - } - else { - for (unsigned int a = 0; a < iWrite;++a) { - pOut[a] = static_cast(reinterpret_cast(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(reinterpret_cast(prop->mData)[a]); - } - if (pMax) { - *pMax = iWrite; - } - } - // it is a string ... no way to read something out of this - else { - if (pMax) { - iWrite = *pMax; - } - // strings are zero-terminated with a 32 bit length prefix, so this is safe - const char *cur = prop->mData+4; - ai_assert( prop->mDataLength >= 5 ); - ai_assert( !prop->mData[ prop->mDataLength - 1 ] ); - for (unsigned int a = 0; ;++a) { - pOut[a] = strtol10(cur,&cur); - if(a==iWrite-1) { - break; - } - if(!IsSpace(*cur)) { - ASSIMP_LOG_ERROR("Material property" + std::string(pKey) + - " is a string; failed to parse an integer array out of it."); - return AI_FAILURE; - } - } - - if (pMax) { - *pMax = iWrite; - } - } - return AI_SUCCESS; -} - -// ------------------------------------------------------------------------------------------------ -// Get a color (3 or 4 floats) from the material -aiReturn aiGetMaterialColor(const aiMaterial* pMat, - const char* pKey, - unsigned int type, - unsigned int index, - aiColor4D* pOut) -{ - unsigned int iMax = 4; - const aiReturn eRet = aiGetMaterialFloatArray(pMat,pKey,type,index,(ai_real*)pOut,&iMax); - - // if no alpha channel is defined: set it to 1.0 - if (3 == iMax) { - pOut->a = 1.0; - } - - return eRet; -} - -// ------------------------------------------------------------------------------------------------ -// Get a aiUVTransform (4 floats) from the material -aiReturn aiGetMaterialUVTransform(const aiMaterial* pMat, - const char* pKey, - unsigned int type, - unsigned int index, - aiUVTransform* pOut) -{ - unsigned int iMax = 4; - return aiGetMaterialFloatArray(pMat,pKey,type,index,(ai_real*)pOut,&iMax); -} - -// ------------------------------------------------------------------------------------------------ -// Get a string from the material -aiReturn aiGetMaterialString(const aiMaterial* pMat, - const char* pKey, - unsigned int type, - unsigned int index, - aiString* pOut) -{ - ai_assert (pOut != NULL); - - const aiMaterialProperty* prop; - aiGetMaterialProperty(pMat,pKey,type,index,(const aiMaterialProperty**)&prop); - if (!prop) { - return AI_FAILURE; - } - - if( aiPTI_String == prop->mType) { - ai_assert(prop->mDataLength>=5); - - // The string is stored as 32 but length prefix followed by zero-terminated UTF8 data - pOut->length = static_cast(*reinterpret_cast(prop->mData)); - - ai_assert( pOut->length+1+4==prop->mDataLength ); - ai_assert( !prop->mData[ prop->mDataLength - 1 ] ); - memcpy(pOut->data,prop->mData+4,pOut->length+1); - } - else { - // TODO - implement lexical cast as well - ASSIMP_LOG_ERROR("Material property" + std::string(pKey) + - " was found, but is no string" ); - return AI_FAILURE; - } - return AI_SUCCESS; -} - -// ------------------------------------------------------------------------------------------------ -// Get the number of textures on a particular texture stack -unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial* pMat, - C_ENUM aiTextureType type) -{ - ai_assert (pMat != NULL); - - // Textures are always stored with ascending indices (ValidateDS provides a check, so we don't need to do it again) - unsigned int max = 0; - for (unsigned int i = 0; i < pMat->mNumProperties;++i) { - aiMaterialProperty* prop = pMat->mProperties[i]; - - if ( prop /* just a sanity check ... */ - && 0 == strcmp( prop->mKey.data, _AI_MATKEY_TEXTURE_BASE ) - && prop->mSemantic == type) { - - max = std::max(max,prop->mIndex+1); - } - } - return max; -} - -// ------------------------------------------------------------------------------------------------ -aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat, - aiTextureType type, - unsigned int index, - C_STRUCT aiString* path, - aiTextureMapping* _mapping /*= NULL*/, - unsigned int* uvindex /*= NULL*/, - ai_real* blend /*= NULL*/, - aiTextureOp* op /*= NULL*/, - aiTextureMapMode* mapmode /*= NULL*/, - unsigned int* flags /*= NULL*/ - ) -{ - ai_assert( NULL != mat ); - ai_assert( NULL != path ); - - // Get the path to the texture - if (AI_SUCCESS != aiGetMaterialString(mat,AI_MATKEY_TEXTURE(type,index),path)) { - return AI_FAILURE; - } - - // Determine mapping type - int mapping_ = static_cast(aiTextureMapping_UV); - aiGetMaterialInteger(mat,AI_MATKEY_MAPPING(type,index), &mapping_); - aiTextureMapping mapping = static_cast(mapping_); - if (_mapping) - *_mapping = mapping; - - // Get UV index - if (aiTextureMapping_UV == mapping && uvindex) { - aiGetMaterialInteger(mat,AI_MATKEY_UVWSRC(type,index),(int*)uvindex); - } - // Get blend factor - if (blend) { - aiGetMaterialFloat(mat,AI_MATKEY_TEXBLEND(type,index),blend); - } - // Get texture operation - if (op){ - aiGetMaterialInteger(mat,AI_MATKEY_TEXOP(type,index),(int*)op); - } - // Get texture mapping modes - if (mapmode) { - aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_U(type,index),(int*)&mapmode[0]); - aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_V(type,index),(int*)&mapmode[1]); - } - // Get texture flags - if (flags){ - aiGetMaterialInteger(mat,AI_MATKEY_TEXFLAGS(type,index),(int*)flags); - } - - return AI_SUCCESS; -} - - -static const unsigned int DefaultNumAllocated = 5; - -// ------------------------------------------------------------------------------------------------ -// Construction. Actually the one and only way to get an aiMaterial instance -aiMaterial::aiMaterial() -: mProperties( nullptr ) -, mNumProperties( 0 ) -, mNumAllocated( DefaultNumAllocated ) { - // Allocate 5 entries by default - mProperties = new aiMaterialProperty*[ DefaultNumAllocated ]; -} - -// ------------------------------------------------------------------------------------------------ -aiMaterial::~aiMaterial() -{ - Clear(); - - delete[] mProperties; -} - -// ------------------------------------------------------------------------------------------------ -aiString aiMaterial::GetName() { - aiString name; - Get(AI_MATKEY_NAME, name); - - return name; -} - -// ------------------------------------------------------------------------------------------------ -void aiMaterial::Clear() -{ - for ( unsigned int i = 0; i < mNumProperties; ++i ) { - // delete this entry - delete mProperties[ i ]; - AI_DEBUG_INVALIDATE_PTR(mProperties[i]); - } - mNumProperties = 0; - - // The array remains allocated, we just invalidated its contents -} - -// ------------------------------------------------------------------------------------------------ -aiReturn aiMaterial::RemoveProperty ( const char* pKey,unsigned int type, unsigned int index ) -{ - ai_assert( nullptr != pKey ); - - for (unsigned int i = 0; i < mNumProperties;++i) { - aiMaterialProperty* prop = mProperties[i]; - - if (prop && !strcmp( prop->mKey.data, pKey ) && - prop->mSemantic == type && prop->mIndex == index) - { - // Delete this entry - delete mProperties[i]; - - // collapse the array behind --. - --mNumProperties; - for (unsigned int a = i; a < mNumProperties;++a) { - mProperties[a] = mProperties[a+1]; - } - return AI_SUCCESS; - } - } - - return AI_FAILURE; -} - -// ------------------------------------------------------------------------------------------------ -aiReturn aiMaterial::AddBinaryProperty (const void* pInput, - unsigned int pSizeInBytes, - const char* pKey, - unsigned int type, - unsigned int index, - aiPropertyTypeInfo pType - ) -{ - ai_assert( pInput != NULL ); - ai_assert( pKey != NULL ); - ai_assert( 0 != pSizeInBytes ); - - if ( 0 == pSizeInBytes ) { - - } - - // first search the list whether there is already an entry with this key - unsigned int iOutIndex( UINT_MAX ); - for ( unsigned int i = 0; i < mNumProperties; ++i ) { - aiMaterialProperty *prop( mProperties[ i ] ); - - if (prop /* just for safety */ && !strcmp( prop->mKey.data, pKey ) && - prop->mSemantic == type && prop->mIndex == index){ - - delete mProperties[i]; - iOutIndex = i; - } - } - - // Allocate a new material property - aiMaterialProperty* pcNew = new aiMaterialProperty(); - - // .. and fill it - pcNew->mType = pType; - pcNew->mSemantic = type; - pcNew->mIndex = index; - - pcNew->mDataLength = pSizeInBytes; - pcNew->mData = new char[pSizeInBytes]; - memcpy (pcNew->mData,pInput,pSizeInBytes); - - pcNew->mKey.length = ::strlen(pKey); - ai_assert ( MAXLEN > pcNew->mKey.length); - strcpy( pcNew->mKey.data, pKey ); - - if (UINT_MAX != iOutIndex) { - mProperties[iOutIndex] = pcNew; - return AI_SUCCESS; - } - - // resize the array ... double the storage allocated - if (mNumProperties == mNumAllocated) { - const unsigned int iOld = mNumAllocated; - mNumAllocated *= 2; - - aiMaterialProperty** ppTemp; - try { - ppTemp = new aiMaterialProperty*[mNumAllocated]; - } catch (std::bad_alloc&) { - delete pcNew; - return AI_OUTOFMEMORY; - } - - // just copy all items over; then replace the old array - memcpy (ppTemp,mProperties,iOld * sizeof(void*)); - - delete[] mProperties; - mProperties = ppTemp; - } - // push back ... - mProperties[mNumProperties++] = pcNew; - - return AI_SUCCESS; -} - -// ------------------------------------------------------------------------------------------------ -aiReturn aiMaterial::AddProperty (const aiString* pInput, - const char* pKey, - unsigned int type, - unsigned int index) -{ - // We don't want to add the whole buffer .. write a 32 bit length - // prefix followed by the zero-terminated UTF8 string. - // (HACK) I don't want to break the ABI now, but we definitely - // ought to change aiString::mLength to uint32_t one day. - if (sizeof(size_t) == 8) { - aiString copy = *pInput; - uint32_t* s = reinterpret_cast(©.length); - s[1] = static_cast(pInput->length); - - return AddBinaryProperty(s+1, - static_cast(pInput->length+1+4), - pKey, - type, - index, - aiPTI_String); - } - ai_assert(sizeof(size_t)==4); - return AddBinaryProperty(pInput, - static_cast(pInput->length+1+4), - pKey, - type, - index, - aiPTI_String); -} - -// ------------------------------------------------------------------------------------------------ -uint32_t Assimp::ComputeMaterialHash(const aiMaterial* mat, bool includeMatName /*= false*/) -{ - uint32_t hash = 1503; // magic start value, chosen to be my birthday :-) - for ( unsigned int i = 0; i < mat->mNumProperties; ++i ) { - aiMaterialProperty* prop; - - // Exclude all properties whose first character is '?' from the hash - // See doc for aiMaterialProperty. - if ((prop = mat->mProperties[i]) && (includeMatName || prop->mKey.data[0] != '?')) { - - hash = SuperFastHash(prop->mKey.data,(unsigned int)prop->mKey.length,hash); - hash = SuperFastHash(prop->mData,prop->mDataLength,hash); - - // Combine the semantic and the index with the hash - hash = SuperFastHash((const char*)&prop->mSemantic,sizeof(unsigned int),hash); - hash = SuperFastHash((const char*)&prop->mIndex,sizeof(unsigned int),hash); - } - } - return hash; -} - -// ------------------------------------------------------------------------------------------------ -void aiMaterial::CopyPropertyList(aiMaterial* pcDest, - const aiMaterial* pcSrc - ) -{ - ai_assert(NULL != pcDest); - ai_assert(NULL != pcSrc); - - unsigned int iOldNum = pcDest->mNumProperties; - pcDest->mNumAllocated += pcSrc->mNumAllocated; - pcDest->mNumProperties += pcSrc->mNumProperties; - - aiMaterialProperty** pcOld = pcDest->mProperties; - pcDest->mProperties = new aiMaterialProperty*[pcDest->mNumAllocated]; - - if (iOldNum && pcOld) { - for (unsigned int i = 0; i < iOldNum;++i) { - pcDest->mProperties[i] = pcOld[i]; - } - } - - if ( pcOld ) { - delete[] pcOld; - } - - for (unsigned int i = iOldNum; i< pcDest->mNumProperties;++i) { - aiMaterialProperty* propSrc = pcSrc->mProperties[i]; - - // search whether we have already a property with this name -> if yes, overwrite it - aiMaterialProperty* prop; - for ( unsigned int q = 0; q < iOldNum; ++q ) { - prop = pcDest->mProperties[q]; - if (prop /* just for safety */ && prop->mKey == propSrc->mKey && prop->mSemantic == propSrc->mSemantic - && prop->mIndex == propSrc->mIndex) { - delete prop; - - // collapse the whole array ... - memmove(&pcDest->mProperties[q],&pcDest->mProperties[q+1],i-q); - i--; - pcDest->mNumProperties--; - } - } - - // Allocate the output property and copy the source property - prop = pcDest->mProperties[i] = new aiMaterialProperty(); - prop->mKey = propSrc->mKey; - prop->mDataLength = propSrc->mDataLength; - prop->mType = propSrc->mType; - prop->mSemantic = propSrc->mSemantic; - prop->mIndex = propSrc->mIndex; - - prop->mData = new char[propSrc->mDataLength]; - memcpy(prop->mData,propSrc->mData,prop->mDataLength); - } -} diff --git a/thirdparty/assimp/code/MaterialSystem.h b/thirdparty/assimp/code/MaterialSystem.h deleted file mode 100644 index 67d53578cb..0000000000 --- a/thirdparty/assimp/code/MaterialSystem.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file MaterialSystem.h - * Now that #MaterialHelper is gone, this file only contains some - * internal material utility functions. - */ -#ifndef AI_MATERIALSYSTEM_H_INC -#define AI_MATERIALSYSTEM_H_INC - -#include - -struct aiMaterial; - -namespace Assimp { - -// ------------------------------------------------------------------------------ -/** Computes a hash (hopefully unique) from all material properties - * The hash value reflects the current property state, so if you add any - * property and call this method again, the resulting hash value will be - * different. The hash is not persistent across different builds and platforms. - * - * @param includeMatName Set to 'true' to take all properties with - * '?' as initial character in their name into account. - * Currently #AI_MATKEY_NAME is the only example. - * @return 32 Bit jash value for the material - */ -uint32_t ComputeMaterialHash(const aiMaterial* mat, bool includeMatName = false); - - -} // ! namespace Assimp - -#endif //!! AI_MATERIALSYSTEM_H_INC diff --git a/thirdparty/assimp/code/OptimizeGraph.cpp b/thirdparty/assimp/code/OptimizeGraph.cpp deleted file mode 100644 index add9ab79e1..0000000000 --- a/thirdparty/assimp/code/OptimizeGraph.cpp +++ /dev/null @@ -1,353 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file OptimizeGraph.cpp - * @brief Implementation of the aiProcess_OptimizGraph step - */ - - -#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS - -#include "OptimizeGraph.h" -#include "ProcessHelper.h" -#include -#include -#include - -using namespace Assimp; - -#define AI_RESERVED_NODE_NAME "$Reserved_And_Evil" - -/* AI_OG_USE_HASHING enables the use of hashing to speed-up std::set lookups. - * The unhashed variant should be faster, except for *very* large data sets - */ -#ifdef AI_OG_USE_HASHING - // Use our standard hashing function to compute the hash -# define AI_OG_GETKEY(str) SuperFastHash(str.data,str.length) -#else - // Otherwise hope that std::string will utilize a static buffer - // for shorter node names. This would avoid endless heap copying. -# define AI_OG_GETKEY(str) std::string(str.data) -#endif - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -OptimizeGraphProcess::OptimizeGraphProcess() -: mScene() -, nodes_in() -, nodes_out() -, count_merged() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -OptimizeGraphProcess::~OptimizeGraphProcess() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const { - return (0 != (pFlags & aiProcess_OptimizeGraph)); -} - -// ------------------------------------------------------------------------------------------------ -// Setup properties for the post-processing step -void OptimizeGraphProcess::SetupProperties(const Importer* pImp) { - // Get value of AI_CONFIG_PP_OG_EXCLUDE_LIST - std::string tmp = pImp->GetPropertyString(AI_CONFIG_PP_OG_EXCLUDE_LIST,""); - AddLockedNodeList(tmp); -} - -// ------------------------------------------------------------------------------------------------ -// Collect new children -void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list& nodes) { - nodes_in += nd->mNumChildren; - - // Process children - std::list child_nodes; - for (unsigned int i = 0; i < nd->mNumChildren; ++i) { - CollectNewChildren(nd->mChildren[i],child_nodes); - nd->mChildren[i] = nullptr; - } - - // Check whether we need this node; if not we can replace it by our own children (warn, danger of incest). - if (locked.find(AI_OG_GETKEY(nd->mName)) == locked.end() ) { - for (std::list::iterator it = child_nodes.begin(); it != child_nodes.end();) { - - if (locked.find(AI_OG_GETKEY((*it)->mName)) == locked.end()) { - (*it)->mTransformation = nd->mTransformation * (*it)->mTransformation; - nodes.push_back(*it); - - it = child_nodes.erase(it); - continue; - } - ++it; - } - - if (nd->mNumMeshes || !child_nodes.empty()) { - nodes.push_back(nd); - } else { - delete nd; /* bye, node */ - return; - } - } else { - - // Retain our current position in the hierarchy - nodes.push_back(nd); - - // Now check for possible optimizations in our list of child nodes. join as many as possible - aiNode* join_master = NULL; - aiMatrix4x4 inv; - - const LockedSetType::const_iterator end = locked.end(); - - std::list join; - for (std::list::iterator it = child_nodes.begin(); it != child_nodes.end();) { - aiNode* child = *it; - if (child->mNumChildren == 0 && locked.find(AI_OG_GETKEY(child->mName)) == end) { - - // There may be no instanced meshes - unsigned int n = 0; - for (; n < child->mNumMeshes;++n) { - if (meshes[child->mMeshes[n]] > 1) { - break; - } - } - if (n == child->mNumMeshes) { - if (!join_master) { - join_master = child; - inv = join_master->mTransformation; - inv.Inverse(); - } else { - child->mTransformation = inv * child->mTransformation ; - - join.push_back(child); - it = child_nodes.erase(it); - continue; - } - } - } - ++it; - } - if (join_master && !join.empty()) { - join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i",count_merged++); - - unsigned int out_meshes = 0; - for (std::list::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::iterator it = join.begin(); it != join.end(); ++it) { - for (unsigned int n = 0; n < (*it)->mNumMeshes; ++n) { - - *tmp = (*it)->mMeshes[n]; - aiMesh* mesh = mScene->mMeshes[*tmp++]; - - // manually move the mesh into the right coordinate system - const aiMatrix3x3 IT = aiMatrix3x3( (*it)->mTransformation ).Inverse().Transpose(); - for (unsigned int a = 0; a < mesh->mNumVertices; ++a) { - - mesh->mVertices[a] *= (*it)->mTransformation; - - if (mesh->HasNormals()) - mesh->mNormals[a] *= IT; - - if (mesh->HasTangentsAndBitangents()) { - mesh->mTangents[a] *= IT; - mesh->mBitangents[a] *= IT; - } - } - } - delete *it; // bye, node - } - delete[] join_master->mMeshes; - join_master->mMeshes = meshes; - join_master->mNumMeshes += out_meshes; - } - } - } - // reassign children if something changed - if (child_nodes.empty() || child_nodes.size() > nd->mNumChildren) { - - delete[] nd->mChildren; - - if (!child_nodes.empty()) { - nd->mChildren = new aiNode*[child_nodes.size()]; - } - else nd->mChildren = nullptr; - } - - nd->mNumChildren = static_cast(child_nodes.size()); - - if (nd->mChildren) { - aiNode** tmp = nd->mChildren; - for (std::list::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) { - aiNode* node = *tmp++ = *it; - node->mParent = nd; - } - } - - nodes_out += static_cast(child_nodes.size()); -} - -// ------------------------------------------------------------------------------------------------ -// Execute the post-processing step on the given scene -void OptimizeGraphProcess::Execute( aiScene* pScene) { - ASSIMP_LOG_DEBUG("OptimizeGraphProcess begin"); - nodes_in = nodes_out = count_merged = 0; - mScene = pScene; - - meshes.resize(pScene->mNumMeshes,0); - FindInstancedMeshes(pScene->mRootNode); - - // build a blacklist of identifiers. If the name of a node matches one of these, we won't touch it - locked.clear(); - for (std::list::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 nodes; - CollectNewChildren (dummy_root,nodes); - - ai_assert(nodes.size() == 1); - - if (dummy_root->mNumChildren == 0) { - pScene->mRootNode = NULL; - throw DeadlyImportError("After optimizing the scene graph, no data remains"); - } - - if (dummy_root->mNumChildren > 1) { - pScene->mRootNode = dummy_root; - - // Keep the dummy node but assign the name of the old root node to it - pScene->mRootNode->mName = prev; - } - else { - - // Remove the dummy root node again. - pScene->mRootNode = dummy_root->mChildren[0]; - - dummy_root->mChildren[0] = NULL; - delete dummy_root; - } - - pScene->mRootNode->mParent = NULL; - if (!DefaultLogger::isNullLogger()) { - if ( nodes_in != nodes_out) { - ASSIMP_LOG_INFO_F("OptimizeGraphProcess finished; Input nodes: ", nodes_in, ", Output nodes: ", nodes_out); - } else { - ASSIMP_LOG_DEBUG("OptimizeGraphProcess finished"); - } - } - meshes.clear(); - locked.clear(); -} - -// ------------------------------------------------------------------------------------------------ -// Build a LUT of all instanced meshes -void OptimizeGraphProcess::FindInstancedMeshes (aiNode* pNode) -{ - for (unsigned int i = 0; i < pNode->mNumMeshes;++i) { - ++meshes[pNode->mMeshes[i]]; - } - - for (unsigned int i = 0; i < pNode->mNumChildren; ++i) - FindInstancedMeshes(pNode->mChildren[i]); -} - -#endif // !! ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS diff --git a/thirdparty/assimp/code/OptimizeGraph.h b/thirdparty/assimp/code/OptimizeGraph.h deleted file mode 100644 index e5bbed7679..0000000000 --- a/thirdparty/assimp/code/OptimizeGraph.h +++ /dev/null @@ -1,145 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file 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 - -struct aiMesh; -class OptimizeGraphProcessTest; -namespace Assimp { - -// ----------------------------------------------------------------------------- -/** @brief Postprocessing step to optimize the scenegraph - * - * The implementation tries to merge nodes, even if they use different - * transformations. Animations are preserved. - * - * @see aiProcess_OptimizeGraph for a detailed description of the - * algorithm being applied. - */ -class OptimizeGraphProcess : public BaseProcess -{ -public: - - OptimizeGraphProcess(); - ~OptimizeGraphProcess(); - -public: - // ------------------------------------------------------------------- - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - void SetupProperties(const Importer* pImp); - - - // ------------------------------------------------------------------- - /** @brief Add a list of node names to be locked and not modified. - * @param in List of nodes. See #AI_CONFIG_PP_OG_EXCLUDE_LIST for - * format explanations. - */ - inline void AddLockedNodeList(std::string& in) - { - ConvertListToStrings (in,locked_nodes); - } - - // ------------------------------------------------------------------- - /** @brief Add another node to be locked and not modified. - * @param name Name to be locked - */ - inline void AddLockedNode(std::string& name) - { - locked_nodes.push_back(name); - } - - // ------------------------------------------------------------------- - /** @brief Remove a node from the list of locked nodes. - * @param name Name to be unlocked - */ - inline void RemoveLockedNode(std::string& name) - { - locked_nodes.remove(name); - } - -protected: - - void CollectNewChildren(aiNode* nd, std::list& nodes); - void FindInstancedMeshes (aiNode* pNode); - -private: - -#ifdef AI_OG_USE_HASHING - typedef std::set LockedSetType; -#else - typedef std::set 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 locked_nodes; - - //! Node counters for logging purposes - unsigned int nodes_in,nodes_out, count_merged; - - //! Reference counters for meshes - std::vector meshes; -}; - -} // end of namespace Assimp - -#endif // AI_OPTIMIZEGRAPHPROCESS_H_INC diff --git a/thirdparty/assimp/code/OptimizeMeshes.cpp b/thirdparty/assimp/code/OptimizeMeshes.cpp deleted file mode 100644 index 3f6765f6ca..0000000000 --- a/thirdparty/assimp/code/OptimizeMeshes.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file OptimizeMeshes.cpp - * @brief Implementation of the aiProcess_OptimizeMeshes step - */ - - -#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS - - -#include "OptimizeMeshes.h" -#include "ProcessHelper.h" -#include -#include - -using namespace Assimp; - -static const unsigned int NotSet = 0xffffffff; -static const unsigned int DeadBeef = 0xdeadbeef; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -OptimizeMeshesProcess::OptimizeMeshesProcess() - : mScene() - , pts(false) - , max_verts( NotSet ) - , max_faces( NotSet ) { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -OptimizeMeshesProcess::~OptimizeMeshesProcess() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool OptimizeMeshesProcess::IsActive( unsigned int pFlags) const -{ - // Our behaviour needs to be different if the SortByPType or SplitLargeMeshes - // steps are active. Thus we need to query their flags here and store the - // information, although we're breaking const-correctness. - // That's a serious design flaw, consider redesign. - if( 0 != (pFlags & aiProcess_OptimizeMeshes) ) { - pts = (0 != (pFlags & aiProcess_SortByPType)); - max_verts = ( 0 != ( pFlags & aiProcess_SplitLargeMeshes ) ) ? DeadBeef : max_verts; - return true; - } - return false; -} - -// ------------------------------------------------------------------------------------------------ -// Setup properties for the post-processing step -void OptimizeMeshesProcess::SetupProperties(const Importer* pImp) -{ - if( max_verts == DeadBeef /* magic hack */ ) { - max_faces = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES); - max_verts = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES); - } -} - -// ------------------------------------------------------------------------------------------------ -// Execute step -void OptimizeMeshesProcess::Execute( aiScene* pScene) -{ - const unsigned int num_old = pScene->mNumMeshes; - if (num_old <= 1) { - ASSIMP_LOG_DEBUG("Skipping OptimizeMeshesProcess"); - return; - } - - ASSIMP_LOG_DEBUG("OptimizeMeshesProcess begin"); - mScene = pScene; - - // need to clear persistent members from previous runs - merge_list.resize( 0 ); - output.resize( 0 ); - - // ensure we have the right sizes - merge_list.reserve(pScene->mNumMeshes); - output.reserve(pScene->mNumMeshes); - - // Prepare lookup tables - meshes.resize(pScene->mNumMeshes); - FindInstancedMeshes(pScene->mRootNode); - if( max_verts == DeadBeef ) /* undo the magic hack */ - max_verts = NotSet; - - // ... instanced meshes are immediately processed and added to the output list - for (unsigned int i = 0, n = 0; i < pScene->mNumMeshes;++i) { - meshes[i].vertex_format = GetMeshVFormatUnique(pScene->mMeshes[i]); - - if (meshes[i].instance_cnt > 1 && meshes[i].output_id == NotSet ) { - meshes[i].output_id = n++; - output.push_back(mScene->mMeshes[i]); - } - } - - // and process all nodes in the scenegraph recursively - ProcessNode(pScene->mRootNode); - if (!output.size()) { - throw DeadlyImportError("OptimizeMeshes: No meshes remaining; there's definitely something wrong"); - } - - meshes.resize( 0 ); - ai_assert(output.size() <= num_old); - - mScene->mNumMeshes = static_cast(output.size()); - std::copy(output.begin(),output.end(),mScene->mMeshes); - - if (output.size() != num_old) { - ASSIMP_LOG_DEBUG_F("OptimizeMeshesProcess finished. Input meshes: ", num_old, ", Output meshes: ", pScene->mNumMeshes); - } else { - ASSIMP_LOG_DEBUG( "OptimizeMeshesProcess finished" ); - } -} - -// ------------------------------------------------------------------------------------------------ -// Process meshes for a single node -void OptimizeMeshesProcess::ProcessNode( aiNode* pNode) -{ - for (unsigned int i = 0; i < pNode->mNumMeshes;++i) { - unsigned int& im = pNode->mMeshes[i]; - - if (meshes[im].instance_cnt > 1) { - im = meshes[im].output_id; - } - else { - merge_list.resize( 0 ); - unsigned int verts = 0, faces = 0; - - // Find meshes to merge with us - for (unsigned int a = i+1; a < pNode->mNumMeshes;++a) { - unsigned int am = pNode->mMeshes[a]; - if (meshes[am].instance_cnt == 1 && CanJoin(im,am,verts,faces)) { - - merge_list.push_back(mScene->mMeshes[am]); - verts += mScene->mMeshes[am]->mNumVertices; - faces += mScene->mMeshes[am]->mNumFaces; - - pNode->mMeshes[a] = pNode->mMeshes[pNode->mNumMeshes - 1]; - --pNode->mNumMeshes; - --a; - } - } - - // and merge all meshes which we found, replace the old ones - if (!merge_list.empty()) { - merge_list.push_back(mScene->mMeshes[im]); - - aiMesh* out; - SceneCombiner::MergeMeshes(&out,0,merge_list.begin(),merge_list.end()); - output.push_back(out); - } else { - output.push_back(mScene->mMeshes[im]); - } - im = static_cast(output.size()-1); - } - } - - - for( unsigned int i = 0; i < pNode->mNumChildren; ++i ) { - ProcessNode( pNode->mChildren[ i ] ); - } -} - -// ------------------------------------------------------------------------------------------------ -// Check whether two meshes can be joined -bool OptimizeMeshesProcess::CanJoin ( unsigned int a, unsigned int b, unsigned int verts, unsigned int faces ) -{ - if (meshes[a].vertex_format != meshes[b].vertex_format) - return false; - - aiMesh* ma = mScene->mMeshes[a], *mb = mScene->mMeshes[b]; - - if ((NotSet != max_verts && verts+mb->mNumVertices > max_verts) || - (NotSet != max_faces && faces+mb->mNumFaces > max_faces)) { - return false; - } - - // Never merge unskinned meshes with skinned meshes - if (ma->mMaterialIndex != mb->mMaterialIndex || ma->HasBones() != mb->HasBones()) - return false; - - // Never merge meshes with different kinds of primitives if SortByPType did already - // do its work. We would destroy everything again ... - if (pts && ma->mPrimitiveTypes != mb->mPrimitiveTypes) - return false; - - // If both meshes are skinned, check whether we have many bones defined in both meshes. - // If yes, we can join them. - if (ma->HasBones()) { - // TODO - return false; - } - return true; -} - -// ------------------------------------------------------------------------------------------------ -// Build a LUT of all instanced meshes -void OptimizeMeshesProcess::FindInstancedMeshes (aiNode* pNode) -{ - for( unsigned int i = 0; i < pNode->mNumMeshes; ++i ) { - ++meshes[ pNode->mMeshes[ i ] ].instance_cnt; - } - - for( unsigned int i = 0; i < pNode->mNumChildren; ++i ) { - FindInstancedMeshes( pNode->mChildren[ i ] ); - } -} - -// ------------------------------------------------------------------------------------------------ - -#endif // !! ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS diff --git a/thirdparty/assimp/code/OptimizeMeshes.h b/thirdparty/assimp/code/OptimizeMeshes.h deleted file mode 100644 index 9f46f349b4..0000000000 --- a/thirdparty/assimp/code/OptimizeMeshes.h +++ /dev/null @@ -1,186 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file 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 - -struct aiMesh; -struct aiNode; -class OptimizeMeshesProcessTest; - -namespace Assimp { - -// --------------------------------------------------------------------------- -/** @brief Postprocessing step to optimize mesh usage - * - * The implementation looks for meshes that could be joined and joins them. - * Usually this will reduce the number of drawcalls. - * - * @note Instanced meshes are currently not processed. - */ -class OptimizeMeshesProcess : public BaseProcess -{ -public: - /// @brief The class constructor. - OptimizeMeshesProcess(); - - /// @brief The class destcructor, - ~OptimizeMeshesProcess(); - - - /** @brief Internal utility to store additional mesh info - */ - struct MeshInfo { - MeshInfo() AI_NO_EXCEPT - : instance_cnt(0) - , vertex_format(0) - , output_id(0xffffffff) { - // empty - } - - //! Number of times this mesh is referenced - unsigned int instance_cnt; - - //! Vertex format id - unsigned int vertex_format; - - //! Output ID - unsigned int output_id; - }; - -public: - // ------------------------------------------------------------------- - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - void SetupProperties(const Importer* pImp); - - - // ------------------------------------------------------------------- - /** @brief Specify whether you want meshes with different - * primitive types to be merged as well. - * - * IsActive() sets this property automatically to true if the - * aiProcess_SortByPType flag is found. - */ - void EnablePrimitiveTypeSorting(bool enable) { - pts = enable; - } - - // Getter - bool IsPrimitiveTypeSortingEnabled () const { - return pts; - } - - - // ------------------------------------------------------------------- - /** @brief Specify a maximum size of a single output mesh. - * - * If a single input mesh already exceeds this limit, it won't - * be split. - * @param verts Maximum number of vertices per mesh - * @param faces Maximum number of faces per mesh - */ - void SetPreferredMeshSizeLimit (unsigned int verts, unsigned int faces) - { - max_verts = verts; - max_faces = faces; - } - - -protected: - - // ------------------------------------------------------------------- - /** @brief Do the actual optimization on all meshes of this node - * @param pNode Node we're working with - */ - void ProcessNode( aiNode* pNode); - - // ------------------------------------------------------------------- - /** @brief Returns true if b can be joined with a - * - * @param verts Number of output verts up to now - * @param faces Number of output faces up to now - */ - bool CanJoin ( unsigned int a, unsigned int b, - unsigned int verts, unsigned int faces ); - - // ------------------------------------------------------------------- - /** @brief Find instanced meshes, for the moment we're excluding - * them from all optimizations - */ - void FindInstancedMeshes (aiNode* pNode); - -private: - - //! Scene we're working with - aiScene* mScene; - - //! Per mesh info - std::vector meshes; - - //! Output meshes - std::vector output; - - //! @see EnablePrimitiveTypeSorting - mutable bool pts; - - //! @see SetPreferredMeshSizeLimit - mutable unsigned int max_verts,max_faces; - - //! Temporary storage - std::vector merge_list; -}; - -} // end of namespace Assimp - -#endif // AI_CALCTANGENTSPROCESS_H_INC diff --git a/thirdparty/assimp/code/PolyTools.h b/thirdparty/assimp/code/PolyTools.h deleted file mode 100644 index fbbda0e7d1..0000000000 --- a/thirdparty/assimp/code/PolyTools.h +++ /dev/null @@ -1,229 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file PolyTools.h, various utilities for our dealings with arbitrary polygons */ - -#ifndef AI_POLYTOOLS_H_INCLUDED -#define AI_POLYTOOLS_H_INCLUDED - -#include -#include - -namespace Assimp { - -// ------------------------------------------------------------------------------- -/** Compute the signed area of a triangle. - * The function accepts an unconstrained template parameter for use with - * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ -template -inline double GetArea2D(const T& v1, const T& v2, const T& v3) -{ - return 0.5 * (v1.x * ((double)v3.y - v2.y) + v2.x * ((double)v1.y - v3.y) + v3.x * ((double)v2.y - v1.y)); -} - -// ------------------------------------------------------------------------------- -/** Test if a given point p2 is on the left side of the line formed by p0-p1. - * The function accepts an unconstrained template parameter for use with - * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ -template -inline bool OnLeftSideOfLine2D(const T& p0, const T& p1,const T& p2) -{ - return GetArea2D(p0,p2,p1) > 0; -} - -// ------------------------------------------------------------------------------- -/** Test if a given point is inside a given triangle in R2. - * The function accepts an unconstrained template parameter for use with - * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ -template -inline bool PointInTriangle2D(const T& p0, const T& p1,const T& p2, const T& pp) -{ - // Point in triangle test using baryzentric coordinates - const aiVector2D v0 = p1 - p0; - const aiVector2D v1 = p2 - p0; - const aiVector2D v2 = pp - p0; - - double dot00 = v0 * v0; - double dot01 = v0 * v1; - double dot02 = v0 * v2; - double dot11 = v1 * v1; - double dot12 = v1 * v2; - - const double invDenom = 1 / (dot00 * dot11 - dot01 * dot01); - dot11 = (dot11 * dot02 - dot01 * dot12) * invDenom; - dot00 = (dot00 * dot12 - dot01 * dot02) * invDenom; - - return (dot11 > 0) && (dot00 > 0) && (dot11 + dot00 < 1); -} - - -// ------------------------------------------------------------------------------- -/** Check whether the winding order of a given polygon is counter-clockwise. - * The function accepts an unconstrained template parameter, but is intended - * to be used only with aiVector2D and aiVector3D (z axis is ignored, only - * x and y are taken into account). - * @note Code taken from http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Ian/applet1.html and translated to C++ - */ -template -inline bool IsCCW(T* in, size_t npoints) { - double aa, bb, cc, b, c, theta; - double convex_turn; - double convex_sum = 0; - - ai_assert(npoints >= 3); - - for (size_t i = 0; i < npoints - 2; i++) { - aa = ((in[i+2].x - in[i].x) * (in[i+2].x - in[i].x)) + - ((-in[i+2].y + in[i].y) * (-in[i+2].y + in[i].y)); - - bb = ((in[i+1].x - in[i].x) * (in[i+1].x - in[i].x)) + - ((-in[i+1].y + in[i].y) * (-in[i+1].y + in[i].y)); - - cc = ((in[i+2].x - in[i+1].x) * - (in[i+2].x - in[i+1].x)) + - ((-in[i+2].y + in[i+1].y) * - (-in[i+2].y + in[i+1].y)); - - b = std::sqrt(bb); - c = std::sqrt(cc); - theta = std::acos((bb + cc - aa) / (2 * b * c)); - - if (OnLeftSideOfLine2D(in[i],in[i+2],in[i+1])) { - // if (convex(in[i].x, in[i].y, - // in[i+1].x, in[i+1].y, - // in[i+2].x, in[i+2].y)) { - convex_turn = AI_MATH_PI_F - theta; - convex_sum += convex_turn; - } - else { - convex_sum -= AI_MATH_PI_F - theta; - } - } - aa = ((in[1].x - in[npoints-2].x) * - (in[1].x - in[npoints-2].x)) + - ((-in[1].y + in[npoints-2].y) * - (-in[1].y + in[npoints-2].y)); - - bb = ((in[0].x - in[npoints-2].x) * - (in[0].x - in[npoints-2].x)) + - ((-in[0].y + in[npoints-2].y) * - (-in[0].y + in[npoints-2].y)); - - cc = ((in[1].x - in[0].x) * (in[1].x - in[0].x)) + - ((-in[1].y + in[0].y) * (-in[1].y + in[0].y)); - - b = std::sqrt(bb); - c = std::sqrt(cc); - theta = std::acos((bb + cc - aa) / (2 * b * c)); - - //if (convex(in[npoints-2].x, in[npoints-2].y, - // in[0].x, in[0].y, - // in[1].x, in[1].y)) { - if (OnLeftSideOfLine2D(in[npoints-2],in[1],in[0])) { - convex_turn = AI_MATH_PI_F - theta; - convex_sum += convex_turn; - } - else { - convex_sum -= AI_MATH_PI_F - theta; - } - - return convex_sum >= (2 * AI_MATH_PI_F); -} - - -// ------------------------------------------------------------------------------- -/** Compute the normal of an arbitrary polygon in R3. - * - * The code is based on Newell's formula, that is a polygons normal is the ratio - * of its area when projected onto the three coordinate axes. - * - * @param out Receives the output normal - * @param num Number of input vertices - * @param x X data source. x[ofs_x*n] is the n'th element. - * @param y Y data source. y[ofs_y*n] is the y'th element - * @param z Z data source. z[ofs_z*n] is the z'th element - * - * @note The data arrays must have storage for at least num+2 elements. Using - * this method is much faster than the 'other' NewellNormal() - */ -template -inline void NewellNormal (aiVector3t& out, int num, TReal* x, TReal* y, TReal* z) -{ - // Duplicate the first two vertices at the end - x[(num+0)*ofs_x] = x[0]; - x[(num+1)*ofs_x] = x[ofs_x]; - - y[(num+0)*ofs_y] = y[0]; - y[(num+1)*ofs_y] = y[ofs_y]; - - z[(num+0)*ofs_z] = z[0]; - z[(num+1)*ofs_z] = z[ofs_z]; - - TReal sum_xy = 0.0, sum_yz = 0.0, sum_zx = 0.0; - - TReal *xptr = x +ofs_x, *xlow = x, *xhigh = x + ofs_x*2; - TReal *yptr = y +ofs_y, *ylow = y, *yhigh = y + ofs_y*2; - TReal *zptr = z +ofs_z, *zlow = z, *zhigh = z + ofs_z*2; - - for (int tmp=0; tmp < num; tmp++) { - sum_xy += (*xptr) * ( (*yhigh) - (*ylow) ); - sum_yz += (*yptr) * ( (*zhigh) - (*zlow) ); - sum_zx += (*zptr) * ( (*xhigh) - (*xlow) ); - - xptr += ofs_x; - xlow += ofs_x; - xhigh += ofs_x; - - yptr += ofs_y; - ylow += ofs_y; - yhigh += ofs_y; - - zptr += ofs_z; - zlow += ofs_z; - zhigh += ofs_z; - } - out = aiVector3t(sum_yz,sum_zx,sum_xy); -} - -} // ! Assimp - -#endif diff --git a/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.cpp b/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.cpp new file mode 100644 index 0000000000..b30f39c274 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.cpp @@ -0,0 +1,319 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to calculate + * tangents and bitangents for all imported meshes + */ + +// internal headers +#include "CalcTangentsProcess.h" +#include "ProcessHelper.h" +#include +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +CalcTangentsProcess::CalcTangentsProcess() +: configMaxAngle( AI_DEG_TO_RAD(45.f) ) +, configSourceUV( 0 ) { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +CalcTangentsProcess::~CalcTangentsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool CalcTangentsProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_CalcTangentSpace) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void CalcTangentsProcess::SetupProperties(const Importer* pImp) +{ + ai_assert( NULL != pImp ); + + // get the current value of the property + configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f); + configMaxAngle = std::max(std::min(configMaxAngle,45.0f),0.0f); + configMaxAngle = AI_DEG_TO_RAD(configMaxAngle); + + configSourceUV = pImp->GetPropertyInteger(AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX,0); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void CalcTangentsProcess::Execute( aiScene* pScene) +{ + ai_assert( NULL != pScene ); + + ASSIMP_LOG_DEBUG("CalcTangentsProcess begin"); + + bool bHas = false; + for ( unsigned int a = 0; a < pScene->mNumMeshes; a++ ) { + if(ProcessMesh( pScene->mMeshes[a],a))bHas = true; + } + + if ( bHas ) { + ASSIMP_LOG_INFO("CalcTangentsProcess finished. Tangents have been calculated"); + } else { + ASSIMP_LOG_DEBUG("CalcTangentsProcess finished"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Calculates tangents and bi-tangents for the given mesh +bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) +{ + // we assume that the mesh is still in the verbose vertex format where each face has its own set + // of vertices and no vertices are shared between faces. Sadly I don't know any quick test to + // assert() it here. + // assert( must be verbose, dammit); + + if (pMesh->mTangents) // this implies that mBitangents is also there + return false; + + // If the mesh consists of lines and/or points but not of + // triangles or higher-order polygons the normal vectors + // are undefined. + if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) + { + ASSIMP_LOG_INFO("Tangents are undefined for line and point meshes"); + return false; + } + + // what we can check, though, is if the mesh has normals and texture coordinates. That's a requirement + if( pMesh->mNormals == NULL) + { + ASSIMP_LOG_ERROR("Failed to compute tangents; need normals"); + return false; + } + if( configSourceUV >= AI_MAX_NUMBER_OF_TEXTURECOORDS || !pMesh->mTextureCoords[configSourceUV] ) + { + ASSIMP_LOG_ERROR((Formatter::format("Failed to compute tangents; need UV data in channel"),configSourceUV)); + return false; + } + + const float angleEpsilon = 0.9999f; + + std::vector vertexDone( pMesh->mNumVertices, false); + const float qnan = get_qnan(); + + // create space for the tangents and bitangents + pMesh->mTangents = new aiVector3D[pMesh->mNumVertices]; + pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices]; + + const aiVector3D* meshPos = pMesh->mVertices; + const aiVector3D* meshNorm = pMesh->mNormals; + const aiVector3D* meshTex = pMesh->mTextureCoords[configSourceUV]; + aiVector3D* meshTang = pMesh->mTangents; + aiVector3D* meshBitang = pMesh->mBitangents; + + // calculate the tangent and bitangent for every face + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + const aiFace& face = pMesh->mFaces[a]; + if (face.mNumIndices < 3) + { + // There are less than three indices, thus the tangent vector + // is not defined. We are finished with these vertices now, + // their tangent vectors are set to qnan. + for (unsigned int i = 0; i < face.mNumIndices;++i) + { + unsigned int idx = face.mIndices[i]; + vertexDone [idx] = true; + meshTang [idx] = aiVector3D(qnan); + meshBitang [idx] = aiVector3D(qnan); + } + + continue; + } + + // triangle or polygon... we always use only the first three indices. A polygon + // is supposed to be planar anyways.... + // FIXME: (thom) create correct calculation for multi-vertex polygons maybe? + const unsigned int p0 = face.mIndices[0], p1 = face.mIndices[1], p2 = face.mIndices[2]; + + // position differences p1->p2 and p1->p3 + aiVector3D v = meshPos[p1] - meshPos[p0], w = meshPos[p2] - meshPos[p0]; + + // texture offset p1->p2 and p1->p3 + float sx = meshTex[p1].x - meshTex[p0].x, sy = meshTex[p1].y - meshTex[p0].y; + float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y; + float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f; + // when t1, t2, t3 in same position in UV space, just use default UV direction. + if ( sx * ty == sy * tx ) { + sx = 0.0; sy = 1.0; + tx = 1.0; ty = 0.0; + } + + // tangent points in the direction where to positive X axis of the texture coord's would point in model space + // bitangent's points along the positive Y axis of the texture coord's, respectively + aiVector3D tangent, bitangent; + tangent.x = (w.x * sy - v.x * ty) * dirCorrection; + tangent.y = (w.y * sy - v.y * ty) * dirCorrection; + tangent.z = (w.z * sy - v.z * ty) * dirCorrection; + bitangent.x = (w.x * sx - v.x * tx) * dirCorrection; + bitangent.y = (w.y * sx - v.y * tx) * dirCorrection; + bitangent.z = (w.z * sx - v.z * tx) * dirCorrection; + + // store for every vertex of that face + for( unsigned int b = 0; b < face.mNumIndices; ++b ) { + unsigned int p = face.mIndices[b]; + + // project tangent and bitangent into the plane formed by the vertex' normal + aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]); + aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]); + localTangent.Normalize(); localBitangent.Normalize(); + + // reconstruct tangent/bitangent according to normal and bitangent/tangent when it's infinite or NaN. + bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z); + bool invalid_bitangent = is_special_float(localBitangent.x) || is_special_float(localBitangent.y) || is_special_float(localBitangent.z); + if (invalid_tangent != invalid_bitangent) { + if (invalid_tangent) { + localTangent = meshNorm[p] ^ localBitangent; + localTangent.Normalize(); + } else { + localBitangent = localTangent ^ meshNorm[p]; + localBitangent.Normalize(); + } + } + + // and write it into the mesh. + meshTang[ p ] = localTangent; + meshBitang[ p ] = localBitangent; + } + } + + + // create a helper to quickly find locally close vertices among the vertex array + // FIX: check whether we can reuse the SpatialSort of a previous step + SpatialSort* vertexFinder = NULL; + SpatialSort _vertexFinder; + float posEpsilon; + if (shared) + { + std::vector >* avf; + shared->GetProperty(AI_SPP_SPATIAL_SORT,avf); + if (avf) + { + std::pair& 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 verticesFound; + + const float fLimit = std::cos(configMaxAngle); + std::vector closeVertices; + + // in the second pass we now smooth out all tangents and bitangents at the same local position + // if they are not too far off. + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + { + if( vertexDone[a]) + continue; + + const aiVector3D& origPos = pMesh->mVertices[a]; + const aiVector3D& origNorm = pMesh->mNormals[a]; + const aiVector3D& origTang = pMesh->mTangents[a]; + const aiVector3D& origBitang = pMesh->mBitangents[a]; + closeVertices.resize( 0 ); + + // find all vertices close to that position + vertexFinder->FindPositions( origPos, posEpsilon, verticesFound); + + closeVertices.reserve (verticesFound.size()+5); + closeVertices.push_back( a); + + // look among them for other vertices sharing the same normal and a close-enough tangent/bitangent + for( unsigned int b = 0; b < verticesFound.size(); b++) + { + unsigned int idx = verticesFound[b]; + if( vertexDone[idx]) + continue; + if( meshNorm[idx] * origNorm < angleEpsilon) + continue; + if( meshTang[idx] * origTang < fLimit) + continue; + if( meshBitang[idx] * origBitang < fLimit) + continue; + + // it's similar enough -> add it to the smoothing group + closeVertices.push_back( idx); + vertexDone[idx] = true; + } + + // smooth the tangents and bitangents of all vertices that were found to be close enough + aiVector3D smoothTangent( 0, 0, 0), smoothBitangent( 0, 0, 0); + for( unsigned int b = 0; b < closeVertices.size(); ++b) + { + smoothTangent += meshTang[ closeVertices[b] ]; + smoothBitangent += meshBitang[ closeVertices[b] ]; + } + smoothTangent.Normalize(); + smoothBitangent.Normalize(); + + // and write it back into all affected tangents + for( unsigned int b = 0; b < closeVertices.size(); ++b) + { + meshTang[ closeVertices[b] ] = smoothTangent; + meshBitang[ closeVertices[b] ] = smoothBitangent; + } + } + return true; +} diff --git a/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.h b/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.h new file mode 100644 index 0000000000..3568a624f8 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.h @@ -0,0 +1,117 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + + +/** @file Defines a post processing step to calculate tangents and + bi-tangents on all imported meshes.*/ +#ifndef AI_CALCTANGENTSPROCESS_H_INC +#define AI_CALCTANGENTSPROCESS_H_INC + +#include "Common/BaseProcess.h" + +struct aiMesh; + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The CalcTangentsProcess calculates the tangent and bitangent for any vertex + * of all meshes. It is expected to be run before the JoinVerticesProcess runs + * because the joining of vertices also considers tangents and bitangents for + * uniqueness. + */ +class ASSIMP_API_WINONLY CalcTangentsProcess : public BaseProcess +{ +public: + + CalcTangentsProcess(); + ~CalcTangentsProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. + * A bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + void SetupProperties(const Importer* pImp); + + + // setter for configMaxAngle + inline void SetMaxSmoothAngle(float f) + { + configMaxAngle =f; + } + +protected: + + // ------------------------------------------------------------------- + /** Calculates tangents and bitangents for a specific mesh. + * @param pMesh The mesh to process. + * @param meshIndex Index of the mesh + */ + bool ProcessMesh( aiMesh* pMesh, unsigned int meshIndex); + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + +private: + + /** Configuration option: maximum smoothing angle, in radians*/ + float configMaxAngle; + unsigned int configSourceUV; +}; + +} // end of namespace Assimp + +#endif // AI_CALCTANGENTSPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.cpp b/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.cpp new file mode 100644 index 0000000000..bb571a551b --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.cpp @@ -0,0 +1,506 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file GenUVCoords step */ + + +#include "ComputeUVMappingProcess.h" +#include "ProcessHelper.h" +#include + +using namespace Assimp; + +namespace { + + const static aiVector3D base_axis_y(0.0,1.0,0.0); + const static aiVector3D base_axis_x(1.0,0.0,0.0); + const static aiVector3D base_axis_z(0.0,0.0,1.0); + const static ai_real angle_epsilon = ai_real( 0.95 ); +} + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +ComputeUVMappingProcess::ComputeUVMappingProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +ComputeUVMappingProcess::~ComputeUVMappingProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool ComputeUVMappingProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_GenUVCoords) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Check whether a ray intersects a plane and find the intersection point +inline bool PlaneIntersect(const aiRay& ray, const aiVector3D& planePos, + const aiVector3D& planeNormal, aiVector3D& pos) +{ + const ai_real b = planeNormal * (planePos - ray.pos); + ai_real h = ray.dir * planeNormal; + if ((h < 10e-5 && h > -10e-5) || (h = b/h) < 0) + return false; + + pos = ray.pos + (ray.dir * h); + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Find the first empty UV channel in a mesh +inline unsigned int FindEmptyUVChannel (aiMesh* mesh) +{ + for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS;++m) + if (!mesh->mTextureCoords[m])return m; + + ASSIMP_LOG_ERROR("Unable to compute UV coordinates, no free UV slot found"); + return UINT_MAX; +} + +// ------------------------------------------------------------------------------------------------ +// Try to remove UV seams +void RemoveUVSeams (aiMesh* mesh, aiVector3D* out) +{ + // TODO: just a very rough algorithm. I think it could be done + // much easier, but I don't know how and am currently too tired to + // to think about a better solution. + + const static ai_real LOWER_LIMIT = ai_real( 0.1 ); + const static ai_real UPPER_LIMIT = ai_real( 0.9 ); + + const static ai_real LOWER_EPSILON = ai_real( 10e-3 ); + const static ai_real UPPER_EPSILON = ai_real( 1.0-10e-3 ); + + for (unsigned int fidx = 0; fidx < mesh->mNumFaces;++fidx) + { + const aiFace& face = mesh->mFaces[fidx]; + if (face.mNumIndices < 3) continue; // triangles and polygons only, please + + unsigned int small = face.mNumIndices, large = small; + bool zero = false, one = false, round_to_zero = false; + + // Check whether this face lies on a UV seam. We can just guess, + // but the assumption that a face with at least one very small + // on the one side and one very large U coord on the other side + // lies on a UV seam should work for most cases. + for (unsigned int n = 0; n < face.mNumIndices;++n) + { + if (out[face.mIndices[n]].x < LOWER_LIMIT) + { + small = n; + + // If we have a U value very close to 0 we can't + // round the others to 0, too. + if (out[face.mIndices[n]].x <= LOWER_EPSILON) + zero = true; + else round_to_zero = true; + } + if (out[face.mIndices[n]].x > UPPER_LIMIT) + { + large = n; + + // If we have a U value very close to 1 we can't + // round the others to 1, too. + if (out[face.mIndices[n]].x >= UPPER_EPSILON) + one = true; + } + } + if (small != face.mNumIndices && large != face.mNumIndices) + { + for (unsigned int n = 0; n < face.mNumIndices;++n) + { + // If the u value is over the upper limit and no other u + // value of that face is 0, round it to 0 + if (out[face.mIndices[n]].x > UPPER_LIMIT && !zero) + out[face.mIndices[n]].x = 0.0; + + // If the u value is below the lower limit and no other u + // value of that face is 1, round it to 1 + else if (out[face.mIndices[n]].x < LOWER_LIMIT && !one) + out[face.mIndices[n]].x = 1.0; + + // The face contains both 0 and 1 as UV coords. This can occur + // for faces which have an edge that lies directly on the seam. + // Due to numerical inaccuracies one U coord becomes 0, the + // other 1. But we do still have a third UV coord to determine + // to which side we must round to. + else if (one && zero) + { + if (round_to_zero && out[face.mIndices[n]].x >= UPPER_EPSILON) + out[face.mIndices[n]].x = 0.0; + else if (!round_to_zero && out[face.mIndices[n]].x <= LOWER_EPSILON) + out[face.mIndices[n]].x = 1.0; + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out) +{ + aiVector3D center, min, max; + FindMeshCenter(mesh, center, min, max); + + // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ... + // currently the mapping axis will always be one of x,y,z, except if the + // PretransformVertices step is used (it transforms the meshes into worldspace, + // thus changing the mapping axis) + if (axis * base_axis_x >= angle_epsilon) { + + // For each point get a normalized projection vector in the sphere, + // get its longitude and latitude and map them to their respective + // UV axes. Problems occur around the poles ... unsolvable. + // + // The spherical coordinate system looks like this: + // x = cos(lon)*cos(lat) + // y = sin(lon)*cos(lat) + // z = sin(lat) + // + // Thus we can derive: + // lat = arcsin (z) + // lon = arctan (y/x) + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize(); + out[pnt] = aiVector3D((std::atan2(diff.z, diff.y) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, + (std::asin (diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); + } + } + else if (axis * base_axis_y >= angle_epsilon) { + // ... just the same again + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize(); + out[pnt] = aiVector3D((std::atan2(diff.x, diff.z) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, + (std::asin (diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); + } + } + else if (axis * base_axis_z >= angle_epsilon) { + // ... just the same again + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize(); + out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, + (std::asin (diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); + } + } + // slower code path in case the mapping axis is not one of the coordinate system axes + else { + aiMatrix4x4 mTrafo; + aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo); + + // again the same, except we're applying a transformation now + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D diff = ((mTrafo*mesh->mVertices[pnt])-center).Normalize(); + out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, + (std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); + } + } + + + // Now find and remove UV seams. A seam occurs if a face has a tcoord + // close to zero on the one side, and a tcoord close to one on the + // other side. + RemoveUVSeams(mesh,out); +} + +// ------------------------------------------------------------------------------------------------ +void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out) +{ + aiVector3D center, min, max; + + // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ... + // currently the mapping axis will always be one of x,y,z, except if the + // PretransformVertices step is used (it transforms the meshes into worldspace, + // thus changing the mapping axis) + if (axis * base_axis_x >= angle_epsilon) { + FindMeshCenter(mesh, center, min, max); + const ai_real diff = max.x - min.x; + + // If the main axis is 'z', the z coordinate of a point 'p' is mapped + // directly to the texture V axis. The other axis is derived from + // the angle between ( p.x - c.x, p.y - c.y ) and (1,0), where + // 'c' is the center point of the mesh. + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D& pos = mesh->mVertices[pnt]; + aiVector3D& uv = out[pnt]; + + uv.y = (pos.x - min.x) / diff; + uv.x = (std::atan2( pos.z - center.z, pos.y - center.y) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI; + } + } + else if (axis * base_axis_y >= angle_epsilon) { + FindMeshCenter(mesh, center, min, max); + const ai_real diff = max.y - min.y; + + // just the same ... + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D& pos = mesh->mVertices[pnt]; + aiVector3D& uv = out[pnt]; + + uv.y = (pos.y - min.y) / diff; + uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI; + } + } + else if (axis * base_axis_z >= angle_epsilon) { + FindMeshCenter(mesh, center, min, max); + const ai_real diff = max.z - min.z; + + // just the same ... + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D& pos = mesh->mVertices[pnt]; + aiVector3D& uv = out[pnt]; + + uv.y = (pos.z - min.z) / diff; + uv.x = (std::atan2( pos.y - center.y, pos.x - center.x) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI; + } + } + // slower code path in case the mapping axis is not one of the coordinate system axes + else { + aiMatrix4x4 mTrafo; + aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo); + FindMeshCenterTransformed(mesh, center, min, max,mTrafo); + const ai_real diff = max.y - min.y; + + // again the same, except we're applying a transformation now + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt){ + const aiVector3D pos = mTrafo* mesh->mVertices[pnt]; + aiVector3D& uv = out[pnt]; + + uv.y = (pos.y - min.y) / diff; + uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI; + } + } + + // Now find and remove UV seams. A seam occurs if a face has a tcoord + // close to zero on the one side, and a tcoord close to one on the + // other side. + RemoveUVSeams(mesh,out); +} + +// ------------------------------------------------------------------------------------------------ +void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out) +{ + ai_real diffu,diffv; + aiVector3D center, min, max; + + // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ... + // currently the mapping axis will always be one of x,y,z, except if the + // PretransformVertices step is used (it transforms the meshes into worldspace, + // thus changing the mapping axis) + if (axis * base_axis_x >= angle_epsilon) { + FindMeshCenter(mesh, center, min, max); + diffu = max.z - min.z; + diffv = max.y - min.y; + + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D& pos = mesh->mVertices[pnt]; + out[pnt].Set((pos.z - min.z) / diffu,(pos.y - min.y) / diffv,0.0); + } + } + else if (axis * base_axis_y >= angle_epsilon) { + FindMeshCenter(mesh, center, min, max); + diffu = max.x - min.x; + diffv = max.z - min.z; + + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D& pos = mesh->mVertices[pnt]; + out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0); + } + } + else if (axis * base_axis_z >= angle_epsilon) { + FindMeshCenter(mesh, center, min, max); + diffu = max.y - min.y; + diffv = max.z - min.z; + + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D& pos = mesh->mVertices[pnt]; + out[pnt].Set((pos.y - min.y) / diffu,(pos.x - min.x) / diffv,0.0); + } + } + // slower code path in case the mapping axis is not one of the coordinate system axes + else + { + aiMatrix4x4 mTrafo; + aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo); + FindMeshCenterTransformed(mesh, center, min, max,mTrafo); + diffu = max.x - min.x; + diffv = max.z - min.z; + + // again the same, except we're applying a transformation now + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D pos = mTrafo * mesh->mVertices[pnt]; + out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0); + } + } + + // shouldn't be necessary to remove UV seams ... +} + +// ------------------------------------------------------------------------------------------------ +void ComputeUVMappingProcess::ComputeBoxMapping( aiMesh*, aiVector3D* ) +{ + ASSIMP_LOG_ERROR("Mapping type currently not implemented"); +} + +// ------------------------------------------------------------------------------------------------ +void ComputeUVMappingProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("GenUVCoordsProcess begin"); + char buffer[1024]; + + if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) + throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); + + std::list mappingStack; + + /* Iterate through all materials and search for non-UV mapped textures + */ + for (unsigned int i = 0; i < pScene->mNumMaterials;++i) + { + mappingStack.clear(); + aiMaterial* mat = pScene->mMaterials[i]; + for (unsigned int a = 0; a < mat->mNumProperties;++a) + { + aiMaterialProperty* prop = mat->mProperties[a]; + if (!::strcmp( prop->mKey.data, "$tex.mapping")) + { + aiTextureMapping& mapping = *((aiTextureMapping*)prop->mData); + if (aiTextureMapping_UV != mapping) + { + if (!DefaultLogger::isNullLogger()) + { + ai_snprintf(buffer, 1024, "Found non-UV mapped texture (%s,%u). Mapping type: %s", + TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex, + MappingTypeToString(mapping)); + + ASSIMP_LOG_INFO(buffer); + } + + if (aiTextureMapping_OTHER == mapping) + continue; + + MappingInfo info (mapping); + + // Get further properties - currently only the major axis + for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2) + { + aiMaterialProperty* prop2 = mat->mProperties[a2]; + if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex) + continue; + + if ( !::strcmp( prop2->mKey.data, "$tex.mapaxis")) { + info.axis = *((aiVector3D*)prop2->mData); + break; + } + } + + unsigned int idx( 99999999 ); + + // Check whether we have this mapping mode already + std::list::iterator it = std::find (mappingStack.begin(),mappingStack.end(), info); + if (mappingStack.end() != it) + { + idx = (*it).uv; + } + else + { + /* We have found a non-UV mapped texture. Now + * we need to find all meshes using this material + * that we can compute UV channels for them. + */ + for (unsigned int m = 0; m < pScene->mNumMeshes;++m) + { + aiMesh* mesh = pScene->mMeshes[m]; + unsigned int outIdx = 0; + if ( mesh->mMaterialIndex != i || ( outIdx = FindEmptyUVChannel(mesh) ) == UINT_MAX || + !mesh->mNumVertices) + { + continue; + } + + // Allocate output storage + aiVector3D* p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices]; + + switch (mapping) + { + case aiTextureMapping_SPHERE: + ComputeSphereMapping(mesh,info.axis,p); + break; + case aiTextureMapping_CYLINDER: + ComputeCylinderMapping(mesh,info.axis,p); + break; + case aiTextureMapping_PLANE: + ComputePlaneMapping(mesh,info.axis,p); + break; + case aiTextureMapping_BOX: + ComputeBoxMapping(mesh,p); + break; + default: + ai_assert(false); + } + if (m && idx != outIdx) + { + ASSIMP_LOG_WARN("UV index mismatch. Not all meshes assigned to " + "this material have equal numbers of UV channels. The UV index stored in " + "the material structure does therefore not apply for all meshes. "); + } + idx = outIdx; + } + info.uv = idx; + mappingStack.push_back(info); + } + + // Update the material property list + mapping = aiTextureMapping_UV; + ((aiMaterial*)mat)->AddProperty(&idx,1,AI_MATKEY_UVWSRC(prop->mSemantic,prop->mIndex)); + } + } + } + } + ASSIMP_LOG_DEBUG("GenUVCoordsProcess finished"); +} diff --git a/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.h b/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.h new file mode 100644 index 0000000000..a6d36e06ea --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.h @@ -0,0 +1,149 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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 "Common/BaseProcess.h" + +#include +#include +#include + +class ComputeUVMappingTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** ComputeUVMappingProcess - converts special mappings, such as spherical, + * cylindrical or boxed to proper UV coordinates for rendering. +*/ +class ComputeUVMappingProcess : public BaseProcess +{ +public: + ComputeUVMappingProcess(); + ~ComputeUVMappingProcess(); + +public: + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + +protected: + + // ------------------------------------------------------------------- + /** Computes spherical UV coordinates for a mesh + * + * @param mesh Mesh to be processed + * @param axis Main axis + * @param out Receives output UV coordinates + */ + void ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis, + aiVector3D* out); + + // ------------------------------------------------------------------- + /** Computes cylindrical UV coordinates for a mesh + * + * @param mesh Mesh to be processed + * @param axis Main axis + * @param out Receives output UV coordinates + */ + void ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis, + aiVector3D* out); + + // ------------------------------------------------------------------- + /** Computes planar UV coordinates for a mesh + * + * @param mesh Mesh to be processed + * @param axis Main axis + * @param out Receives output UV coordinates + */ + void ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis, + aiVector3D* out); + + // ------------------------------------------------------------------- + /** Computes cubic UV coordinates for a mesh + * + * @param mesh Mesh to be processed + * @param out Receives output UV coordinates + */ + void ComputeBoxMapping(aiMesh* mesh, aiVector3D* out); + +private: + + // temporary structure to describe a mapping + struct MappingInfo + { + explicit MappingInfo(aiTextureMapping _type) + : type (_type) + , axis (0.f,1.f,0.f) + , uv (0u) + {} + + aiTextureMapping type; + aiVector3D axis; + unsigned int uv; + + bool operator== (const MappingInfo& other) + { + return type == other.type && axis == other.axis; + } + }; +}; + +} // end of namespace Assimp + +#endif // AI_COMPUTEUVMAPPING_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.cpp b/thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.cpp new file mode 100644 index 0000000000..b7cd4f0bc6 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.cpp @@ -0,0 +1,414 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file MakeLeftHandedProcess.cpp + * @brief Implementation of the post processing step to convert all + * imported data to a left-handed coordinate system. + * + * Face order & UV flip are also implemented here, for the sake of a + * better location. + */ + + +#include "ConvertToLHProcess.h" +#include +#include +#include + +using namespace Assimp; + +#ifndef ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS + +namespace { + +template +void flipUVs(aiMeshType* pMesh) { + if (pMesh == nullptr) { return; } + // mirror texture y coordinate + for (unsigned int tcIdx = 0; tcIdx < AI_MAX_NUMBER_OF_TEXTURECOORDS; tcIdx++) { + if (!pMesh->HasTextureCoords(tcIdx)) { + break; + } + + for (unsigned int vIdx = 0; vIdx < pMesh->mNumVertices; vIdx++) { + pMesh->mTextureCoords[tcIdx][vIdx].y = 1.0f - pMesh->mTextureCoords[tcIdx][vIdx].y; + } + } +} + +} // namespace + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +MakeLeftHandedProcess::MakeLeftHandedProcess() +: BaseProcess() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +MakeLeftHandedProcess::~MakeLeftHandedProcess() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool MakeLeftHandedProcess::IsActive( unsigned int pFlags) const +{ + return 0 != (pFlags & aiProcess_MakeLeftHanded); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void MakeLeftHandedProcess::Execute( aiScene* pScene) +{ + // Check for an existent root node to proceed + ai_assert(pScene->mRootNode != NULL); + ASSIMP_LOG_DEBUG("MakeLeftHandedProcess begin"); + + // recursively convert all the nodes + ProcessNode( pScene->mRootNode, aiMatrix4x4()); + + // process the meshes accordingly + for ( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) { + ProcessMesh( pScene->mMeshes[ a ] ); + } + + // process the materials accordingly + for ( unsigned int a = 0; a < pScene->mNumMaterials; ++a ) { + ProcessMaterial( pScene->mMaterials[ a ] ); + } + + // transform all animation channels as well + for( unsigned int a = 0; a < pScene->mNumAnimations; a++) + { + aiAnimation* anim = pScene->mAnimations[a]; + for( unsigned int b = 0; b < anim->mNumChannels; b++) + { + aiNodeAnim* nodeAnim = anim->mChannels[b]; + ProcessAnimation( nodeAnim); + } + } + ASSIMP_LOG_DEBUG("MakeLeftHandedProcess finished"); +} + +// ------------------------------------------------------------------------------------------------ +// Recursively converts a node, all of its children and all of its meshes +void MakeLeftHandedProcess::ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation) +{ + // mirror all base vectors at the local Z axis + pNode->mTransformation.c1 = -pNode->mTransformation.c1; + pNode->mTransformation.c2 = -pNode->mTransformation.c2; + pNode->mTransformation.c3 = -pNode->mTransformation.c3; + pNode->mTransformation.c4 = -pNode->mTransformation.c4; + + // now invert the Z axis again to keep the matrix determinant positive. + // The local meshes will be inverted accordingly so that the result should look just fine again. + pNode->mTransformation.a3 = -pNode->mTransformation.a3; + pNode->mTransformation.b3 = -pNode->mTransformation.b3; + pNode->mTransformation.c3 = -pNode->mTransformation.c3; + pNode->mTransformation.d3 = -pNode->mTransformation.d3; // useless, but anyways... + + // continue for all children + for( size_t a = 0; a < pNode->mNumChildren; ++a ) { + ProcessNode( pNode->mChildren[ a ], pParentGlobalRotation * pNode->mTransformation ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts a single mesh to left handed coordinates. +void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) { + if ( nullptr == pMesh ) { + ASSIMP_LOG_ERROR( "Nullptr to mesh found." ); + return; + } + // mirror positions, normals and stuff along the Z axis + for( size_t a = 0; a < pMesh->mNumVertices; ++a) + { + pMesh->mVertices[a].z *= -1.0f; + if (pMesh->HasNormals()) { + pMesh->mNormals[a].z *= -1.0f; + } + if( pMesh->HasTangentsAndBitangents()) + { + pMesh->mTangents[a].z *= -1.0f; + pMesh->mBitangents[a].z *= -1.0f; + } + } + + // mirror anim meshes positions, normals and stuff along the Z axis + for (size_t m = 0; m < pMesh->mNumAnimMeshes; ++m) + { + for (size_t a = 0; a < pMesh->mAnimMeshes[m]->mNumVertices; ++a) + { + pMesh->mAnimMeshes[m]->mVertices[a].z *= -1.0f; + if (pMesh->mAnimMeshes[m]->HasNormals()) { + pMesh->mAnimMeshes[m]->mNormals[a].z *= -1.0f; + } + if (pMesh->mAnimMeshes[m]->HasTangentsAndBitangents()) + { + pMesh->mAnimMeshes[m]->mTangents[a].z *= -1.0f; + pMesh->mAnimMeshes[m]->mBitangents[a].z *= -1.0f; + } + } + } + + // mirror offset matrices of all bones + for( size_t a = 0; a < pMesh->mNumBones; ++a) + { + aiBone* bone = pMesh->mBones[a]; + bone->mOffsetMatrix.a3 = -bone->mOffsetMatrix.a3; + bone->mOffsetMatrix.b3 = -bone->mOffsetMatrix.b3; + bone->mOffsetMatrix.d3 = -bone->mOffsetMatrix.d3; + bone->mOffsetMatrix.c1 = -bone->mOffsetMatrix.c1; + bone->mOffsetMatrix.c2 = -bone->mOffsetMatrix.c2; + bone->mOffsetMatrix.c4 = -bone->mOffsetMatrix.c4; + } + + // mirror bitangents as well as they're derived from the texture coords + if( pMesh->HasTangentsAndBitangents()) + { + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + pMesh->mBitangents[a] *= -1.0f; + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts a single material to left handed coordinates. +void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) { + if ( nullptr == _mat ) { + ASSIMP_LOG_ERROR( "Nullptr to aiMaterial found." ); + return; + } + + aiMaterial* mat = (aiMaterial*)_mat; + for (unsigned int a = 0; a < mat->mNumProperties;++a) { + aiMaterialProperty* prop = mat->mProperties[a]; + + // Mapping axis for UV mappings? + if (!::strcmp( prop->mKey.data, "$tex.mapaxis")) { + ai_assert( prop->mDataLength >= sizeof(aiVector3D)); /* something is wrong with the validation if we end up here */ + aiVector3D* pff = (aiVector3D*)prop->mData; + pff->z *= -1.f; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts the given animation to LH coordinates. +void MakeLeftHandedProcess::ProcessAnimation( aiNodeAnim* pAnim) +{ + // position keys + for( unsigned int a = 0; a < pAnim->mNumPositionKeys; a++) + pAnim->mPositionKeys[a].mValue.z *= -1.0f; + + // rotation keys + for( unsigned int a = 0; a < pAnim->mNumRotationKeys; a++) + { + /* That's the safe version, but the float errors add up. So we try the short version instead + aiMatrix3x3 rotmat = pAnim->mRotationKeys[a].mValue.GetMatrix(); + rotmat.a3 = -rotmat.a3; rotmat.b3 = -rotmat.b3; + rotmat.c1 = -rotmat.c1; rotmat.c2 = -rotmat.c2; + aiQuaternion rotquat( rotmat); + pAnim->mRotationKeys[a].mValue = rotquat; + */ + pAnim->mRotationKeys[a].mValue.x *= -1.0f; + pAnim->mRotationKeys[a].mValue.y *= -1.0f; + } +} + +#endif // !! ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS +#ifndef ASSIMP_BUILD_NO_FLIPUVS_PROCESS +// # FlipUVsProcess + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +FlipUVsProcess::FlipUVsProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FlipUVsProcess::~FlipUVsProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool FlipUVsProcess::IsActive( unsigned int pFlags) const +{ + return 0 != (pFlags & aiProcess_FlipUVs); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void FlipUVsProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("FlipUVsProcess begin"); + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + ProcessMesh(pScene->mMeshes[i]); + + for (unsigned int i = 0; i < pScene->mNumMaterials;++i) + ProcessMaterial(pScene->mMaterials[i]); + ASSIMP_LOG_DEBUG("FlipUVsProcess finished"); +} + +// ------------------------------------------------------------------------------------------------ +// Converts a single material +void FlipUVsProcess::ProcessMaterial (aiMaterial* _mat) +{ + aiMaterial* mat = (aiMaterial*)_mat; + for (unsigned int a = 0; a < mat->mNumProperties;++a) { + aiMaterialProperty* prop = mat->mProperties[a]; + if( !prop ) { + ASSIMP_LOG_DEBUG( "Property is null" ); + continue; + } + + // UV transformation key? + if (!::strcmp( prop->mKey.data, "$tex.uvtrafo")) { + ai_assert( prop->mDataLength >= sizeof(aiUVTransform)); /* something is wrong with the validation if we end up here */ + aiUVTransform* uv = (aiUVTransform*)prop->mData; + + // just flip it, that's everything + uv->mTranslation.y *= -1.f; + uv->mRotation *= -1.f; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts a single mesh +void FlipUVsProcess::ProcessMesh( aiMesh* pMesh) +{ + flipUVs(pMesh); + for (unsigned int idx = 0; idx < pMesh->mNumAnimMeshes; idx++) { + flipUVs(pMesh->mAnimMeshes[idx]); + } +} + +#endif // !ASSIMP_BUILD_NO_FLIPUVS_PROCESS +#ifndef ASSIMP_BUILD_NO_FLIPWINDING_PROCESS +// # FlipWindingOrderProcess + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +FlipWindingOrderProcess::FlipWindingOrderProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FlipWindingOrderProcess::~FlipWindingOrderProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool FlipWindingOrderProcess::IsActive( unsigned int pFlags) const +{ + return 0 != (pFlags & aiProcess_FlipWindingOrder); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void FlipWindingOrderProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("FlipWindingOrderProcess begin"); + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + ProcessMesh(pScene->mMeshes[i]); + ASSIMP_LOG_DEBUG("FlipWindingOrderProcess finished"); +} + +// ------------------------------------------------------------------------------------------------ +// Converts a single mesh +void FlipWindingOrderProcess::ProcessMesh( aiMesh* pMesh) +{ + // invert the order of all faces in this mesh + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + aiFace& face = pMesh->mFaces[a]; + for (unsigned int b = 0; b < face.mNumIndices / 2; b++) { + std::swap(face.mIndices[b], face.mIndices[face.mNumIndices - 1 - b]); + } + } + + // invert the order of all components in this mesh anim meshes + for (unsigned int m = 0; m < pMesh->mNumAnimMeshes; m++) { + aiAnimMesh* animMesh = pMesh->mAnimMeshes[m]; + unsigned int numVertices = animMesh->mNumVertices; + if (animMesh->HasPositions()) { + for (unsigned int a = 0; a < numVertices; a++) + { + std::swap(animMesh->mVertices[a], animMesh->mVertices[numVertices - 1 - a]); + } + } + if (animMesh->HasNormals()) { + for (unsigned int a = 0; a < numVertices; a++) + { + std::swap(animMesh->mNormals[a], animMesh->mNormals[numVertices - 1 - a]); + } + } + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; i++) { + if (animMesh->HasTextureCoords(i)) { + for (unsigned int a = 0; a < numVertices; a++) + { + std::swap(animMesh->mTextureCoords[i][a], animMesh->mTextureCoords[i][numVertices - 1 - a]); + } + } + } + if (animMesh->HasTangentsAndBitangents()) { + for (unsigned int a = 0; a < numVertices; a++) + { + std::swap(animMesh->mTangents[a], animMesh->mTangents[numVertices - 1 - a]); + std::swap(animMesh->mBitangents[a], animMesh->mBitangents[numVertices - 1 - a]); + } + } + for (unsigned int v = 0; v < AI_MAX_NUMBER_OF_COLOR_SETS; v++) { + if (animMesh->HasVertexColors(v)) { + for (unsigned int a = 0; a < numVertices; a++) + { + std::swap(animMesh->mColors[v][a], animMesh->mColors[v][numVertices - 1 - a]); + } + } + } + } +} + +#endif // !! ASSIMP_BUILD_NO_FLIPWINDING_PROCESS diff --git a/thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.h b/thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.h new file mode 100644 index 0000000000..f32b91fc39 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.h @@ -0,0 +1,171 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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 "Common/BaseProcess.h" + +struct aiMesh; +struct aiNodeAnim; +struct aiNode; +struct aiMaterial; + +namespace Assimp { + +// ----------------------------------------------------------------------------------- +/** @brief The MakeLeftHandedProcess converts all imported data to a left-handed + * coordinate system. + * + * This implies a mirroring of the Z axis of the coordinate system. But to keep + * transformation matrices free from reflections we shift the reflection to other + * places. We mirror the meshes and adapt the rotations. + * + * @note RH-LH and LH-RH is the same, so this class can be used for both + */ +class MakeLeftHandedProcess : public BaseProcess +{ + + +public: + MakeLeftHandedProcess(); + ~MakeLeftHandedProcess(); + + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + +protected: + + // ------------------------------------------------------------------- + /** Recursively converts a node and all of its children + */ + void ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation); + + // ------------------------------------------------------------------- + /** Converts a single mesh to left handed coordinates. + * This means that positions, normals and tangents are mirrored at + * the local Z axis and the order of all faces are inverted. + * @param pMesh The mesh to convert. + */ + void ProcessMesh( aiMesh* pMesh); + + // ------------------------------------------------------------------- + /** Converts a single material to left-handed coordinates + * @param pMat Material to convert + */ + void ProcessMaterial( aiMaterial* pMat); + + // ------------------------------------------------------------------- + /** Converts the given animation to LH coordinates. + * The rotation and translation keys are transformed, the scale keys + * work in local space and can therefore be left untouched. + * @param pAnim The bone animation to transform + */ + void ProcessAnimation( aiNodeAnim* pAnim); +}; + + +// --------------------------------------------------------------------------- +/** Postprocessing step to flip the face order of the imported data + */ +class FlipWindingOrderProcess : public BaseProcess +{ + friend class Importer; + +public: + /** Constructor to be privately used by Importer */ + FlipWindingOrderProcess(); + + /** Destructor, private as well */ + ~FlipWindingOrderProcess(); + + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + +protected: + void ProcessMesh( aiMesh* pMesh); +}; + +// --------------------------------------------------------------------------- +/** Postprocessing step to flip the UV coordinate system of the import data + */ +class FlipUVsProcess : public BaseProcess +{ + friend class Importer; + +public: + /** Constructor to be privately used by Importer */ + FlipUVsProcess(); + + /** Destructor, private as well */ + ~FlipUVsProcess(); + + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + +protected: + void ProcessMesh( aiMesh* pMesh); + void ProcessMaterial( aiMaterial* mat); +}; + +} // end of namespace Assimp + +#endif // AI_CONVERTTOLHPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/DeboneProcess.cpp b/thirdparty/assimp/code/PostProcessing/DeboneProcess.cpp new file mode 100644 index 0000000000..83b8336bc9 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/DeboneProcess.cpp @@ -0,0 +1,465 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/// @file DeboneProcess.cpp +/** Implementation of the DeboneProcess post processing step */ + + + +// internal headers of the post-processing framework +#include "ProcessHelper.h" +#include "DeboneProcess.h" +#include + + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +DeboneProcess::DeboneProcess() +{ + mNumBones = 0; + mNumBonesCanDoWithout = 0; + + mThreshold = AI_DEBONE_THRESHOLD; + mAllOrNone = false; +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +DeboneProcess::~DeboneProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool DeboneProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_Debone) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void DeboneProcess::SetupProperties(const Importer* pImp) +{ + // get the current value of the property + mAllOrNone = pImp->GetPropertyInteger(AI_CONFIG_PP_DB_ALL_OR_NONE,0)?true:false; + mThreshold = pImp->GetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD,AI_DEBONE_THRESHOLD); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void DeboneProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("DeboneProcess begin"); + + if(!pScene->mNumMeshes) { + return; + } + + std::vector splitList(pScene->mNumMeshes); + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { + splitList[a] = ConsiderMesh( pScene->mMeshes[a] ); + } + + int numSplits = 0; + + if(!!mNumBonesCanDoWithout && (!mAllOrNone||mNumBonesCanDoWithout==mNumBones)) { + for(unsigned int a = 0; a < pScene->mNumMeshes; a++) { + if(splitList[a]) { + numSplits++; + } + } + } + + if(numSplits) { + // we need to do something. Let's go. + //mSubMeshIndices.clear(); // really needed? + mSubMeshIndices.resize(pScene->mNumMeshes); // because we're doing it here anyway + + // build a new array of meshes for the scene + std::vector meshes; + + for(unsigned int a=0;amNumMeshes;a++) + { + aiMesh* srcMesh = pScene->mMeshes[a]; + + std::vector > newMeshes; + + if(splitList[a]) { + SplitMesh(srcMesh,newMeshes); + } + + // mesh was split + if(!newMeshes.empty()) { + unsigned int out = 0, in = srcMesh->mNumBones; + + // store new meshes and indices of the new meshes + for(unsigned int b=0;bmName:0; + + aiNode *theNode = find?pScene->mRootNode->FindNode(*find):0; + std::pair push_pair(static_cast(meshes.size()),theNode); + + mSubMeshIndices[a].push_back(push_pair); + meshes.push_back(newMeshes[b].first); + + out+=newMeshes[b].first->mNumBones; + } + + if(!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_INFO_F("Removed %u bones. Input bones:", in - out, ". Output bones: ", out); + } + + // and destroy the source mesh. It should be completely contained inside the new submeshes + delete srcMesh; + } + else { + // Mesh is kept unchanged - store it's new place in the mesh array + mSubMeshIndices[a].push_back(std::pair(static_cast(meshes.size()),(aiNode*)0)); + meshes.push_back(srcMesh); + } + } + + // rebuild the scene's mesh array + pScene->mNumMeshes = static_cast(meshes.size()); + delete [] pScene->mMeshes; + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + std::copy( meshes.begin(), meshes.end(), pScene->mMeshes); + + // recurse through all nodes and translate the node's mesh indices to fit the new mesh array + UpdateNode( pScene->mRootNode); + } + + ASSIMP_LOG_DEBUG("DeboneProcess end"); +} + +// ------------------------------------------------------------------------------------------------ +// Counts bones total/removable in a given mesh. +bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh) +{ + if(!pMesh->HasBones()) { + return false; + } + + bool split = false; + + //interstitial faces not permitted + bool isInterstitialRequired = false; + + std::vector isBoneNecessary(pMesh->mNumBones,false); + std::vector vertexBones(pMesh->mNumVertices,UINT_MAX); + + const unsigned int cUnowned = UINT_MAX; + const unsigned int cCoowned = UINT_MAX-1; + + for(unsigned int i=0;imNumBones;i++) { + for(unsigned int j=0;jmBones[i]->mNumWeights;j++) { + float w = pMesh->mBones[i]->mWeights[j].mWeight; + + if(w==0.0f) { + continue; + } + + unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId; + if(w>=mThreshold) { + + if(vertexBones[vid]!=cUnowned) { + if(vertexBones[vid]==i) //double entry + { + ASSIMP_LOG_WARN("Encountered double entry in bone weights"); + } + else //TODO: track attraction in order to break tie + { + vertexBones[vid] = cCoowned; + } + } + else vertexBones[vid] = i; + } + + if(!isBoneNecessary[i]) { + isBoneNecessary[i] = wmNumFaces;i++) { + unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]]; + + for(unsigned int j=1;jmFaces[i].mNumIndices;j++) { + unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]]; + + if(v!=w) { + if(vmNumBones) isBoneNecessary[v] = true; + if(wmNumBones) isBoneNecessary[w] = true; + } + } + } + } + + for(unsigned int i=0;imNumBones;i++) { + if(!isBoneNecessary[i]) { + mNumBonesCanDoWithout++; + split = true; + } + + mNumBones++; + } + return split; +} + +// ------------------------------------------------------------------------------------------------ +// Splits the given mesh by bone count. +void DeboneProcess::SplitMesh( const aiMesh* pMesh, std::vector< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const +{ + // same deal here as ConsiderMesh basically + + std::vector isBoneNecessary(pMesh->mNumBones,false); + std::vector vertexBones(pMesh->mNumVertices,UINT_MAX); + + const unsigned int cUnowned = UINT_MAX; + const unsigned int cCoowned = UINT_MAX-1; + + for(unsigned int i=0;imNumBones;i++) { + for(unsigned int j=0;jmBones[i]->mNumWeights;j++) { + float w = pMesh->mBones[i]->mWeights[j].mWeight; + + if(w==0.0f) { + continue; + } + + unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId; + + if(w>=mThreshold) { + if(vertexBones[vid]!=cUnowned) { + if(vertexBones[vid]==i) //double entry + { + ASSIMP_LOG_WARN("Encountered double entry in bone weights"); + } + else //TODO: track attraction in order to break tie + { + vertexBones[vid] = cCoowned; + } + } + else vertexBones[vid] = i; + } + + if(!isBoneNecessary[i]) { + isBoneNecessary[i] = w faceBones(pMesh->mNumFaces,UINT_MAX); + std::vector facesPerBone(pMesh->mNumBones,0); + + for(unsigned int i=0;imNumFaces;i++) { + unsigned int nInterstitial = 1; + + unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]]; + + for(unsigned int j=1;jmFaces[i].mNumIndices;j++) { + unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]]; + + if(v!=w) { + if(vmNumBones) isBoneNecessary[v] = true; + if(wmNumBones) isBoneNecessary[w] = true; + } + else nInterstitial++; + } + + if(vmNumBones &&nInterstitial==pMesh->mFaces[i].mNumIndices) { + faceBones[i] = v; //primitive belongs to bone #v + facesPerBone[v]++; + } + else nFacesUnowned++; + } + + // invalidate any "cojoined" faces + for(unsigned int i=0;imNumFaces;i++) { + if(faceBones[i]mNumBones&&isBoneNecessary[faceBones[i]]) + { + ai_assert(facesPerBone[faceBones[i]]>0); + facesPerBone[faceBones[i]]--; + + nFacesUnowned++; + faceBones[i] = cUnowned; + } + } + + if(nFacesUnowned) { + std::vector subFaces; + + for(unsigned int i=0;imNumFaces;i++) { + if(faceBones[i]==cUnowned) { + subFaces.push_back(i); + } + } + + aiMesh *baseMesh = MakeSubmesh(pMesh,subFaces,0); + std::pair push_pair(baseMesh,(const aiBone*)0); + + poNewMeshes.push_back(push_pair); + } + + for(unsigned int i=0;imNumBones;i++) { + + if(!isBoneNecessary[i]&&facesPerBone[i]>0) { + std::vector subFaces; + + for(unsigned int j=0;jmNumFaces;j++) { + if(faceBones[j]==i) { + subFaces.push_back(j); + } + } + + unsigned int f = AI_SUBMESH_FLAGS_SANS_BONES; + aiMesh *subMesh =MakeSubmesh(pMesh,subFaces,f); + + //Lifted from PretransformVertices.cpp + ApplyTransform(subMesh,pMesh->mBones[i]->mOffsetMatrix); + std::pair push_pair(subMesh,pMesh->mBones[i]); + + poNewMeshes.push_back(push_pair); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Recursively updates the node's mesh list to account for the changed mesh list +void DeboneProcess::UpdateNode(aiNode* pNode) const +{ + // rebuild the node's mesh index list + + std::vector newMeshList; + + // this will require two passes + + unsigned int m = static_cast(pNode->mNumMeshes), n = static_cast(mSubMeshIndices.size()); + + // first pass, look for meshes which have not moved + + for(unsigned int a=0;amMeshes[a]; + const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[srcIndex]; + unsigned int nSubmeshes = static_cast(subMeshes.size()); + + for(unsigned int b=0;b > &subMeshes = mSubMeshIndices[a]; + unsigned int nSubmeshes = static_cast(subMeshes.size()); + + for(unsigned int b=0;bmNumMeshes > 0 ) { + delete [] pNode->mMeshes; pNode->mMeshes = NULL; + } + + pNode->mNumMeshes = static_cast(newMeshList.size()); + + if(pNode->mNumMeshes) { + pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; + std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes); + } + + // do that also recursively for all children + for( unsigned int a = 0; a < pNode->mNumChildren; ++a ) { + UpdateNode( pNode->mChildren[a]); + } +} + +// ------------------------------------------------------------------------------------------------ +// Apply the node transformation to a mesh +void DeboneProcess::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const +{ + // Check whether we need to transform the coordinates at all + if (!mat.IsIdentity()) { + + if (mesh->HasPositions()) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mVertices[i] = mat * mesh->mVertices[i]; + } + } + if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) { + aiMatrix4x4 mWorldIT = mat; + mWorldIT.Inverse().Transpose(); + + // TODO: implement Inverse() for aiMatrix3x3 + aiMatrix3x3 m = aiMatrix3x3(mWorldIT); + + if (mesh->HasNormals()) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize(); + } + } + if (mesh->HasTangentsAndBitangents()) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize(); + mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize(); + } + } + } + } +} diff --git a/thirdparty/assimp/code/PostProcessing/DeboneProcess.h b/thirdparty/assimp/code/PostProcessing/DeboneProcess.h new file mode 100644 index 0000000000..8b64c2acc6 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/DeboneProcess.h @@ -0,0 +1,131 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** Defines a post processing step to limit the number of bones affecting a single vertex. */ +#ifndef AI_DEBONEPROCESS_H_INC +#define AI_DEBONEPROCESS_H_INC + +#include "Common/BaseProcess.h" + +#include +#include + +#include +#include + +#// Forward declarations +class DeboneTest; + +namespace Assimp { + +#if (!defined AI_DEBONE_THRESHOLD) +# define AI_DEBONE_THRESHOLD 1.0f +#endif // !! AI_DEBONE_THRESHOLD + +// --------------------------------------------------------------------------- +/** This post processing step removes bones nearly losslessly or according to +* a configured threshold. In order to remove the bone, the primitives affected by +* the bone are split from the mesh. The split off (new) mesh is boneless. At any +* point in time, bones without affect upon a given mesh are to be removed. +*/ +class DeboneProcess : public BaseProcess { +public: + DeboneProcess(); + ~DeboneProcess(); + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. + * A bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + void SetupProperties(const Importer* pImp); + +protected: + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + /** Counts bones total/removable in a given mesh. + * @param pMesh The mesh to process. + */ + bool ConsiderMesh( const aiMesh* pMesh); + + /// Splits the given mesh by bone count. + /// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split. + /// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary. + void SplitMesh(const aiMesh* pMesh, std::vector< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const; + + /// Recursively updates the node's mesh list to account for the changed mesh list + void UpdateNode(aiNode* pNode) const; + + // ------------------------------------------------------------------- + // Apply transformation to a mesh + void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const; + +public: + /** Number of bones present in the scene. */ + unsigned int mNumBones; + unsigned int mNumBonesCanDoWithout; + + float mThreshold; + bool mAllOrNone; + + /// Per mesh index: Array of indices of the new submeshes. + std::vector< std::vector< std::pair< unsigned int,aiNode* > > > mSubMeshIndices; +}; + +} // end of namespace Assimp + +#endif // AI_DEBONEPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.cpp b/thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.cpp new file mode 100644 index 0000000000..b11615bb82 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.cpp @@ -0,0 +1,109 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to drop face +* normals for all imported faces. +*/ + + +#include "DropFaceNormalsProcess.h" +#include +#include +#include +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +DropFaceNormalsProcess::DropFaceNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +DropFaceNormalsProcess::~DropFaceNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool DropFaceNormalsProcess::IsActive( unsigned int pFlags) const { + return (pFlags & aiProcess_DropNormals) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void DropFaceNormalsProcess::Execute( aiScene* pScene) { + ASSIMP_LOG_DEBUG("DropFaceNormalsProcess begin"); + + if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { + throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); + } + + bool bHas = false; + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { + bHas |= this->DropMeshFaceNormals( pScene->mMeshes[a]); + } + if (bHas) { + ASSIMP_LOG_INFO("DropFaceNormalsProcess finished. " + "Face normals have been removed"); + } else { + ASSIMP_LOG_DEBUG("DropFaceNormalsProcess finished. " + "No normals were present"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* pMesh) { + if (NULL == pMesh->mNormals) { + return false; + } + + delete[] pMesh->mNormals; + pMesh->mNormals = nullptr; + return true; +} diff --git a/thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.h b/thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.h new file mode 100644 index 0000000000..c710c5a5ee --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.h @@ -0,0 +1,83 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to compute face normals for all loaded faces*/ +#ifndef AI_DROPFACENORMALPROCESS_H_INC +#define AI_DROPFACENORMALPROCESS_H_INC + +#include "Common/BaseProcess.h" + +#include + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** The DropFaceNormalsProcess computes face normals for all faces of all meshes +*/ +class ASSIMP_API_WINONLY DropFaceNormalsProcess : public BaseProcess { +public: + DropFaceNormalsProcess(); + ~DropFaceNormalsProcess(); + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + +private: + bool DropMeshFaceNormals(aiMesh* pcMesh); +}; + +} // end of namespace Assimp + +#endif // !!AI_DROPFACENORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.cpp b/thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.cpp new file mode 100644 index 0000000000..739382a057 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.cpp @@ -0,0 +1,152 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#include "EmbedTexturesProcess.h" +#include +#include "ProcessHelper.h" + +#include + +using namespace Assimp; + +EmbedTexturesProcess::EmbedTexturesProcess() +: BaseProcess() { +} + +EmbedTexturesProcess::~EmbedTexturesProcess() { +} + +bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const { + return (pFlags & aiProcess_EmbedTextures) != 0; +} + +void EmbedTexturesProcess::SetupProperties(const Importer* pImp) { + mRootPath = pImp->GetPropertyString("sourceFilePath"); + mRootPath = mRootPath.substr(0, mRootPath.find_last_of("\\/") + 1u); +} + +void EmbedTexturesProcess::Execute(aiScene* pScene) { + if (pScene == nullptr || pScene->mRootNode == nullptr) return; + + aiString path; + + uint32_t embeddedTexturesCount = 0u; + + for (auto matId = 0u; matId < pScene->mNumMaterials; ++matId) { + auto material = pScene->mMaterials[matId]; + + for (auto ttId = 1u; ttId < AI_TEXTURE_TYPE_MAX; ++ttId) { + auto tt = static_cast(ttId); + auto texturesCount = material->GetTextureCount(tt); + + for (auto texId = 0u; texId < texturesCount; ++texId) { + material->GetTexture(tt, texId, &path); + if (path.data[0] == '*') continue; // Already embedded + + // Indeed embed + if (addTexture(pScene, path.data)) { + auto embeddedTextureId = pScene->mNumTextures - 1u; + ::ai_snprintf(path.data, 1024, "*%u", embeddedTextureId); + material->AddProperty(&path, AI_MATKEY_TEXTURE(tt, texId)); + embeddedTexturesCount++; + } + } + } + } + + ASSIMP_LOG_INFO_F("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." ); +} + +bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { + std::streampos imageSize = 0; + std::string imagePath = path; + + // Test path directly + std::ifstream file(imagePath, std::ios::binary | std::ios::ate); + if ((imageSize = file.tellg()) == std::streampos(-1)) { + ASSIMP_LOG_WARN_F("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder."); + + // Test path in root path + imagePath = mRootPath + path; + file.open(imagePath, std::ios::binary | std::ios::ate); + if ((imageSize = file.tellg()) == std::streampos(-1)) { + // Test path basename in root path + imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u); + file.open(imagePath, std::ios::binary | std::ios::ate); + if ((imageSize = file.tellg()) == std::streampos(-1)) { + ASSIMP_LOG_ERROR_F("EmbedTexturesProcess: Unable to embed texture: ", path, "."); + return false; + } + } + } + + aiTexel* imageContent = new aiTexel[ 1ul + static_cast( imageSize ) / sizeof(aiTexel)]; + file.seekg(0, std::ios::beg); + file.read(reinterpret_cast(imageContent), imageSize); + + // Enlarging the textures table + unsigned int textureId = pScene->mNumTextures++; + auto oldTextures = pScene->mTextures; + pScene->mTextures = new aiTexture*[pScene->mNumTextures]; + ::memmove(pScene->mTextures, oldTextures, sizeof(aiTexture*) * (pScene->mNumTextures - 1u)); + + // Add the new texture + auto pTexture = new aiTexture; + pTexture->mHeight = 0; // Means that this is still compressed + pTexture->mWidth = static_cast(imageSize); + pTexture->pcData = imageContent; + + auto extension = path.substr(path.find_last_of('.') + 1u); + std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); + if (extension == "jpeg") { + extension = "jpg"; + } + + size_t len = extension.size(); + if (len > HINTMAXTEXTURELEN -1 ) { + len = HINTMAXTEXTURELEN - 1; + } + ::strncpy(pTexture->achFormatHint, extension.c_str(), len); + pScene->mTextures[textureId] = pTexture; + + return true; +} diff --git a/thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.h b/thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.h new file mode 100644 index 0000000000..3c4b2eab4e --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.h @@ -0,0 +1,85 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#pragma once + +#include "Common/BaseProcess.h" + +#include + +struct aiNode; + +namespace Assimp { + +/** + * Force embedding of textures (using the path = "*1" convention). + * If a texture's file does not exist at the specified path + * (due, for instance, to an absolute path generated on another system), + * it will check if a file with the same name exists at the root folder + * of the imported model. And if so, it uses that. + */ +class ASSIMP_API EmbedTexturesProcess : public BaseProcess { +public: + /// The default class constructor. + EmbedTexturesProcess(); + + /// The class destructor. + virtual ~EmbedTexturesProcess(); + + /// Overwritten, @see BaseProcess + virtual bool IsActive(unsigned int pFlags) const; + + /// Overwritten, @see BaseProcess + virtual void SetupProperties(const Importer* pImp); + + /// Overwritten, @see BaseProcess + virtual void Execute(aiScene* pScene); + +private: + // Resolve the path and add the file content to the scene as a texture. + bool addTexture(aiScene* pScene, std::string path) const; + +private: + std::string mRootPath; +}; + +} // namespace Assimp diff --git a/thirdparty/assimp/code/PostProcessing/FindDegenerates.cpp b/thirdparty/assimp/code/PostProcessing/FindDegenerates.cpp new file mode 100644 index 0000000000..50fac46dba --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/FindDegenerates.cpp @@ -0,0 +1,301 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file FindDegenerates.cpp + * @brief Implementation of the FindDegenerates post-process step. +*/ + + + +// internal headers +#include "ProcessHelper.h" +#include "FindDegenerates.h" +#include + +using namespace Assimp; + +//remove mesh at position 'index' from the scene +static void removeMesh(aiScene* pScene, unsigned const index); +//correct node indices to meshes and remove references to deleted mesh +static void updateSceneGraph(aiNode* pNode, unsigned const index); + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +FindDegeneratesProcess::FindDegeneratesProcess() +: mConfigRemoveDegenerates( false ) +, mConfigCheckAreaOfTriangle( false ){ + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FindDegeneratesProcess::~FindDegeneratesProcess() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const { + return 0 != (pFlags & aiProcess_FindDegenerates); +} + +// ------------------------------------------------------------------------------------------------ +// Setup import configuration +void FindDegeneratesProcess::SetupProperties(const Importer* pImp) { + // Get the current value of AI_CONFIG_PP_FD_REMOVE + mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0)); + mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) ); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void FindDegeneratesProcess::Execute( aiScene* pScene) { + ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin"); + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + { + //Do not process point cloud, ExecuteOnMesh works only with faces data + if ((pScene->mMeshes[i]->mPrimitiveTypes != aiPrimitiveType::aiPrimitiveType_POINT) && ExecuteOnMesh(pScene->mMeshes[i])) { + removeMesh(pScene, i); + --i; //the current i is removed, do not skip the next one + } + } + ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished"); +} + +static void removeMesh(aiScene* pScene, unsigned const index) { + //we start at index and copy the pointers one position forward + //save the mesh pointer to delete it later + auto delete_me = pScene->mMeshes[index]; + for (unsigned i = index; i < pScene->mNumMeshes - 1; ++i) { + pScene->mMeshes[i] = pScene->mMeshes[i+1]; + } + pScene->mMeshes[pScene->mNumMeshes - 1] = nullptr; + --(pScene->mNumMeshes); + delete delete_me; + + //removing a mesh also requires updating all references to it in the scene graph + updateSceneGraph(pScene->mRootNode, index); +} + +static void updateSceneGraph(aiNode* pNode, unsigned const index) { + for (unsigned i = 0; i < pNode->mNumMeshes; ++i) { + if (pNode->mMeshes[i] > index) { + --(pNode->mMeshes[i]); + continue; + } + if (pNode->mMeshes[i] == index) { + for (unsigned j = i; j < pNode->mNumMeshes -1; ++j) { + pNode->mMeshes[j] = pNode->mMeshes[j+1]; + } + --(pNode->mNumMeshes); + --i; + continue; + } + } + //recurse to all children + for (unsigned i = 0; i < pNode->mNumChildren; ++i) { + updateSceneGraph(pNode->mChildren[i], index); + } +} + +static ai_real heron( ai_real a, ai_real b, ai_real c ) { + ai_real s = (a + b + c) / 2; + ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 ); + return area; +} + +static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) { + const ai_real lx = ( vB.x - vA.x ); + const ai_real ly = ( vB.y - vA.y ); + const ai_real lz = ( vB.z - vA.z ); + ai_real a = lx*lx + ly*ly + lz*lz; + ai_real d = pow( a, (ai_real)0.5 ); + + return d; +} + +static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) { + ai_real area = 0; + + aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] ); + aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] ); + aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] ); + + ai_real a( distance3D( vA, vB ) ); + ai_real b( distance3D( vB, vC ) ); + ai_real c( distance3D( vC, vA ) ); + area = heron( a, b, c ); + + return area; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported mesh +bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) { + mesh->mPrimitiveTypes = 0; + + std::vector remove_me; + if (mConfigRemoveDegenerates) { + remove_me.resize( mesh->mNumFaces, false ); + } + + unsigned int deg = 0, limit; + for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) { + aiFace& face = mesh->mFaces[a]; + bool first = true; + + // check whether the face contains degenerated entries + for (unsigned int i = 0; i < face.mNumIndices; ++i) { + // Polygons with more than 4 points are allowed to have double points, that is + // simulating polygons with holes just with concave polygons. However, + // double points may not come directly after another. + limit = face.mNumIndices; + if (face.mNumIndices > 4) { + limit = std::min( limit, i+2 ); + } + + for (unsigned int t = i+1; t < limit; ++t) { + if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) { + // we have found a matching vertex position + // remove the corresponding index from the array + --face.mNumIndices; + --limit; + for (unsigned int m = t; m < face.mNumIndices; ++m) { + face.mIndices[ m ] = face.mIndices[ m+1 ]; + } + --t; + + // NOTE: we set the removed vertex index to an unique value + // to make sure the developer gets notified when his + // application attempts to access this data. + face.mIndices[ face.mNumIndices ] = 0xdeadbeef; + + if(first) { + ++deg; + first = false; + } + + if ( mConfigRemoveDegenerates ) { + remove_me[ a ] = true; + goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby! + } + } + } + + if ( mConfigCheckAreaOfTriangle ) { + if ( face.mNumIndices == 3 ) { + ai_real area = calculateAreaOfTriangle( face, mesh ); + if ( area < 1e-6 ) { + if ( mConfigRemoveDegenerates ) { + remove_me[ a ] = true; + ++deg; + goto evil_jump_outside; + } + + // todo: check for index which is corrupt. + } + } + } + } + + // We need to update the primitive flags array of the mesh. + switch (face.mNumIndices) + { + case 1u: + mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + case 2u: + mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + case 3u: + mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + default: + mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + break; + }; +evil_jump_outside: + continue; + } + + // If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import + if (mConfigRemoveDegenerates && deg) { + unsigned int n = 0; + for (unsigned int a = 0; a < mesh->mNumFaces; ++a) + { + aiFace& face_src = mesh->mFaces[a]; + if (!remove_me[a]) { + aiFace& face_dest = mesh->mFaces[n++]; + + // Do a manual copy, keep the index array + face_dest.mNumIndices = face_src.mNumIndices; + face_dest.mIndices = face_src.mIndices; + + if (&face_src != &face_dest) { + // clear source + face_src.mNumIndices = 0; + face_src.mIndices = nullptr; + } + } + else { + // Otherwise delete it if we don't need this face + delete[] face_src.mIndices; + face_src.mIndices = nullptr; + face_src.mNumIndices = 0; + } + } + // Just leave the rest of the array unreferenced, we don't care for now + mesh->mNumFaces = n; + if (!mesh->mNumFaces) { + //The whole mesh consists of degenerated faces + //signal upward, that this mesh should be deleted. + ASSIMP_LOG_DEBUG("FindDegeneratesProcess removed a mesh full of degenerated primitives"); + return true; + } + } + + if (deg && !DefaultLogger::isNullLogger()) { + ASSIMP_LOG_WARN_F( "Found ", deg, " degenerated primitives"); + } + return false; +} diff --git a/thirdparty/assimp/code/PostProcessing/FindDegenerates.h b/thirdparty/assimp/code/PostProcessing/FindDegenerates.h new file mode 100644 index 0000000000..7a15e77cf1 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/FindDegenerates.h @@ -0,0 +1,130 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to search all meshes for + degenerated faces */ +#ifndef AI_FINDDEGENERATESPROCESS_H_INC +#define AI_FINDDEGENERATESPROCESS_H_INC + +#include "Common/BaseProcess.h" + +#include + +class FindDegeneratesProcessTest; +namespace Assimp { + + +// --------------------------------------------------------------------------- +/** FindDegeneratesProcess: Searches a mesh for degenerated triangles. +*/ +class ASSIMP_API FindDegeneratesProcess : public BaseProcess { +public: + FindDegeneratesProcess(); + ~FindDegeneratesProcess(); + + // ------------------------------------------------------------------- + // Check whether step is active + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + // Execute step on a given scene + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + // Setup import settings + void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + // Execute step on a given mesh + ///@returns true if the current mesh should be deleted, false otherwise + bool ExecuteOnMesh( aiMesh* mesh); + + // ------------------------------------------------------------------- + /// @brief Enable the instant removal of degenerated primitives + /// @param enabled true for enabled. + void EnableInstantRemoval(bool enabled); + + // ------------------------------------------------------------------- + /// @brief Check whether instant removal is currently enabled + /// @return The instant removal state. + bool IsInstantRemoval() const; + + // ------------------------------------------------------------------- + /// @brief Enable the area check for triangles. + /// @param enabled true for enabled. + void EnableAreaCheck( bool enabled ); + + // ------------------------------------------------------------------- + /// @brief Check whether the area check is enabled. + /// @return The area check state. + bool isAreaCheckEnabled() const; + +private: + //! Configuration option: remove degenerates faces immediately + bool mConfigRemoveDegenerates; + //! Configuration option: check for area + bool mConfigCheckAreaOfTriangle; +}; + +inline +void FindDegeneratesProcess::EnableInstantRemoval(bool enabled) { + mConfigRemoveDegenerates = enabled; +} + +inline +bool FindDegeneratesProcess::IsInstantRemoval() const { + return mConfigRemoveDegenerates; +} + +inline +void FindDegeneratesProcess::EnableAreaCheck( bool enabled ) { + mConfigCheckAreaOfTriangle = enabled; +} + +inline +bool FindDegeneratesProcess::isAreaCheckEnabled() const { + return mConfigCheckAreaOfTriangle; +} + +} // Namespace Assimp + +#endif // !! AI_FINDDEGENERATESPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/FindInstancesProcess.cpp b/thirdparty/assimp/code/PostProcessing/FindInstancesProcess.cpp new file mode 100644 index 0000000000..64907458a1 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/FindInstancesProcess.cpp @@ -0,0 +1,277 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file FindInstancesProcess.cpp + * @brief Implementation of the aiProcess_FindInstances postprocessing step +*/ + + +#include "FindInstancesProcess.h" +#include +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +FindInstancesProcess::FindInstancesProcess() +: configSpeedFlag (false) +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FindInstancesProcess::~FindInstancesProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool FindInstancesProcess::IsActive( unsigned int pFlags) const +{ + // FindInstances makes absolutely no sense together with PreTransformVertices + // fixme: spawn error message somewhere else? + return 0 != (pFlags & aiProcess_FindInstances) && 0 == (pFlags & aiProcess_PreTransformVertices); +} + +// ------------------------------------------------------------------------------------------------ +// Setup properties for the step +void FindInstancesProcess::SetupProperties(const Importer* pImp) +{ + // AI_CONFIG_FAVOUR_SPEED + configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0)); +} + +// ------------------------------------------------------------------------------------------------ +// Compare the bones of two meshes +bool CompareBones(const aiMesh* orig, const aiMesh* inst) +{ + for (unsigned int i = 0; i < orig->mNumBones;++i) { + aiBone* aha = orig->mBones[i]; + aiBone* oha = inst->mBones[i]; + + if (aha->mNumWeights != oha->mNumWeights || + aha->mOffsetMatrix != oha->mOffsetMatrix) { + return false; + } + + // compare weight per weight --- + for (unsigned int n = 0; n < aha->mNumWeights;++n) { + if (aha->mWeights[n].mVertexId != oha->mWeights[n].mVertexId || + (aha->mWeights[n].mWeight - oha->mWeights[n].mWeight) < 10e-3f) { + return false; + } + } + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Update mesh indices in the node graph +void UpdateMeshIndices(aiNode* node, unsigned int* lookup) +{ + for (unsigned int n = 0; n < node->mNumMeshes;++n) + node->mMeshes[n] = lookup[node->mMeshes[n]]; + + for (unsigned int n = 0; n < node->mNumChildren;++n) + UpdateMeshIndices(node->mChildren[n],lookup); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void FindInstancesProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("FindInstancesProcess begin"); + if (pScene->mNumMeshes) { + + // use a pseudo hash for all meshes in the scene to quickly find + // the ones which are possibly equal. This step is executed early + // in the pipeline, so we could, depending on the file format, + // have several thousand small meshes. That's too much for a brute + // everyone-against-everyone check involving up to 10 comparisons + // each. + std::unique_ptr hashes (new uint64_t[pScene->mNumMeshes]); + std::unique_ptr 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); + + // Find an appropriate epsilon + // to compare position differences against + float epsilon = ComputePositionEpsilon(inst); + epsilon *= epsilon; + + 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. Now compare vertex positions, normals, + // tangents and bitangents using this epsilon. + if (orig->HasPositions()) { + if(!CompareArrays(orig->mVertices,inst->mVertices,orig->mNumVertices,epsilon)) + continue; + } + if (orig->HasNormals()) { + if(!CompareArrays(orig->mNormals,inst->mNormals,orig->mNumVertices,epsilon)) + continue; + } + if (orig->HasTangentsAndBitangents()) { + if (!CompareArrays(orig->mTangents,inst->mTangents,orig->mNumVertices,epsilon) || + !CompareArrays(orig->mBitangents,inst->mBitangents,orig->mNumVertices,epsilon)) + continue; + } + + // use a constant epsilon for colors and UV coordinates + static const float uvEpsilon = 10e-4f; + { + unsigned int j, end = orig->GetNumUVChannels(); + for(j = 0; j < end; ++j) { + if (!orig->mTextureCoords[j]) { + continue; + } + if(!CompareArrays(orig->mTextureCoords[j],inst->mTextureCoords[j],orig->mNumVertices,uvEpsilon)) { + break; + } + } + if (j != end) { + continue; + } + } + { + unsigned int j, end = orig->GetNumColorChannels(); + for(j = 0; j < end; ++j) { + if (!orig->mColors[j]) { + continue; + } + if(!CompareArrays(orig->mColors[j],inst->mColors[j],orig->mNumVertices,uvEpsilon)) { + break; + } + } + if (j != end) { + continue; + } + } + + // These two checks are actually quite expensive and almost *never* required. + // Almost. That's why they're still here. But there's no reason to do them + // in speed-targeted imports. + if (!configSpeedFlag) { + + // It seems to be strange, but we really need to check whether the + // bones are identical too. Although it's extremely unprobable + // that they're not if control reaches here, we need to deal + // with unprobable cases, too. It could still be that there are + // equal shapes which are deformed differently. + if (!CompareBones(orig,inst)) + continue; + + // For completeness ... compare even the index buffers for equality + // face order & winding order doesn't care. Input data is in verbose format. + std::unique_ptr ftbl_orig(new unsigned int[orig->mNumVertices]); + std::unique_ptr ftbl_inst(new unsigned int[orig->mNumVertices]); + + for (unsigned int tt = 0; tt < orig->mNumFaces;++tt) { + aiFace& f = orig->mFaces[tt]; + for (unsigned int nn = 0; nn < f.mNumIndices;++nn) + ftbl_orig[f.mIndices[nn]] = tt; + + aiFace& f2 = inst->mFaces[tt]; + for (unsigned int nn = 0; nn < f2.mNumIndices;++nn) + ftbl_inst[f2.mIndices[nn]] = tt; + } + if (0 != ::memcmp(ftbl_inst.get(),ftbl_orig.get(),orig->mNumVertices*sizeof(unsigned int))) + continue; + } + + // We're still here. Or in other words: 'inst' is an instance of 'orig'. + // Place a marker in our list that we can easily update mesh indices. + remapping[i] = remapping[a]; + + // Delete the instanced mesh, we don't need it anymore + delete inst; + pScene->mMeshes[i] = NULL; + break; + } + } + + // If we didn't find a match for the current mesh: keep it + if (pScene->mMeshes[i]) { + remapping[i] = numMeshesOut++; + } + } + ai_assert(0 != numMeshesOut); + if (numMeshesOut != pScene->mNumMeshes) { + + // Collapse the meshes array by removing all NULL entries + for (unsigned int real = 0, i = 0; real < numMeshesOut; ++i) { + if (pScene->mMeshes[i]) + pScene->mMeshes[real++] = pScene->mMeshes[i]; + } + + // And update the node graph with our nice lookup table + UpdateMeshIndices(pScene->mRootNode,remapping.get()); + + // write to log + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_INFO_F( "FindInstancesProcess finished. Found ", (pScene->mNumMeshes - numMeshesOut), " instances" ); + } + pScene->mNumMeshes = numMeshesOut; + } else { + ASSIMP_LOG_DEBUG("FindInstancesProcess finished. No instanced meshes found"); + } + } +} diff --git a/thirdparty/assimp/code/PostProcessing/FindInstancesProcess.h b/thirdparty/assimp/code/PostProcessing/FindInstancesProcess.h new file mode 100644 index 0000000000..64b838d7cc --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/FindInstancesProcess.h @@ -0,0 +1,137 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FindInstancesProcess.h + * @brief Declares the aiProcess_FindInstances post-process step + */ +#ifndef AI_FINDINSTANCES_H_INC +#define AI_FINDINSTANCES_H_INC + +#include "Common/BaseProcess.h" +#include "PostProcessing/ProcessHelper.h" + +class FindInstancesProcessTest; +namespace Assimp { + +// ------------------------------------------------------------------------------- +/** @brief Get a pseudo(!)-hash representing a mesh. + * + * The hash is built from number of vertices, faces, primitive types, + * .... but *not* from the real mesh data. The funcction is not a perfect hash. + * @param in Input mesh + * @return Hash. + */ +inline +uint64_t GetMeshHash(aiMesh* in) { + ai_assert(nullptr != in); + + // ... get an unique value representing the vertex format of the mesh + const unsigned int fhash = GetMeshVFormatUnique(in); + + // and bake it with number of vertices/faces/bones/matidx/ptypes + return ((uint64_t)fhash << 32u) | (( + (in->mNumBones << 16u) ^ (in->mNumVertices) ^ + (in->mNumFaces<<4u) ^ (in->mMaterialIndex<<15) ^ + (in->mPrimitiveTypes<<28)) & 0xffffffff ); +} + +// ------------------------------------------------------------------------------- +/** @brief Perform a component-wise comparison of two arrays + * + * @param first First array + * @param second Second array + * @param size Size of both arrays + * @param e Epsilon + * @return true if the arrays are identical + */ +inline +bool CompareArrays(const aiVector3D* first, const aiVector3D* second, + unsigned int size, float e) { + for (const aiVector3D* end = first+size; first != end; ++first,++second) { + if ( (*first - *second).SquareLength() >= e) + return false; + } + return true; +} + +// and the same for colors ... +inline bool CompareArrays(const aiColor4D* first, const aiColor4D* second, + unsigned int size, float e) +{ + for (const aiColor4D* end = first+size; first != end; ++first,++second) { + if ( GetColorDifference(*first,*second) >= e) + return false; + } + return true; +} + +// --------------------------------------------------------------------------- +/** @brief A post-processing steps to search for instanced meshes +*/ +class FindInstancesProcess : public BaseProcess +{ +public: + + FindInstancesProcess(); + ~FindInstancesProcess(); + +public: + // ------------------------------------------------------------------- + // Check whether step is active in given flags combination + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + // Execute step on a given scene + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + // Setup properties prior to executing the process + void SetupProperties(const Importer* pImp); + +private: + + bool configSpeedFlag; + +}; // ! end class FindInstancesProcess +} // ! end namespace Assimp + +#endif // !! AI_FINDINSTANCES_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.cpp b/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.cpp new file mode 100644 index 0000000000..433f042448 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.cpp @@ -0,0 +1,424 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to search an importer's output + for data that is obviously invalid */ + + + +#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS + +// internal headers +#include "FindInvalidDataProcess.h" +#include "ProcessHelper.h" + +#include +#include +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +FindInvalidDataProcess::FindInvalidDataProcess() +: configEpsilon(0.0) +, mIgnoreTexCoods( false ){ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FindInvalidDataProcess::~FindInvalidDataProcess() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool FindInvalidDataProcess::IsActive( unsigned int pFlags) const { + return 0 != (pFlags & aiProcess_FindInvalidData); +} + +// ------------------------------------------------------------------------------------------------ +// Setup import configuration +void FindInvalidDataProcess::SetupProperties(const Importer* pImp) { + // Get the current value of AI_CONFIG_PP_FID_ANIM_ACCURACY + configEpsilon = (0 != pImp->GetPropertyFloat(AI_CONFIG_PP_FID_ANIM_ACCURACY,0.f)); + mIgnoreTexCoods = pImp->GetPropertyBool(AI_CONFIG_PP_FID_IGNORE_TEXTURECOORDS, false); +} + +// ------------------------------------------------------------------------------------------------ +// Update mesh references in the node graph +void UpdateMeshReferences(aiNode* node, const std::vector& meshMapping) { + if (node->mNumMeshes) { + unsigned int out = 0; + for (unsigned int a = 0; a < node->mNumMeshes;++a) { + + unsigned int ref = node->mMeshes[a]; + if (UINT_MAX != (ref = meshMapping[ref])) { + node->mMeshes[out++] = ref; + } + } + // just let the members that are unused, that's much cheaper + // than a full array realloc'n'copy party ... + if(!(node->mNumMeshes = out)) { + + delete[] node->mMeshes; + node->mMeshes = NULL; + } + } + // recursively update all children + for (unsigned int i = 0; i < node->mNumChildren;++i) { + UpdateMeshReferences(node->mChildren[i],meshMapping); + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void FindInvalidDataProcess::Execute( aiScene* pScene) { + ASSIMP_LOG_DEBUG("FindInvalidDataProcess begin"); + + bool out = false; + std::vector meshMapping(pScene->mNumMeshes); + unsigned int real = 0; + + // Process meshes + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { + + int result; + if ((result = ProcessMesh( pScene->mMeshes[a]))) { + out = true; + + if (2 == result) { + // remove this mesh + delete pScene->mMeshes[a]; + AI_DEBUG_INVALIDATE_PTR(pScene->mMeshes[a]); + + meshMapping[a] = UINT_MAX; + continue; + } + } + pScene->mMeshes[real] = pScene->mMeshes[a]; + meshMapping[a] = real++; + } + + // Process animations + for (unsigned int a = 0; a < pScene->mNumAnimations;++a) { + ProcessAnimation( pScene->mAnimations[a]); + } + + + if (out) { + if ( real != pScene->mNumMeshes) { + if (!real) { + throw DeadlyImportError("No meshes remaining"); + } + + // we need to remove some meshes. + // therefore we'll also need to remove all references + // to them from the scenegraph + UpdateMeshReferences(pScene->mRootNode,meshMapping); + pScene->mNumMeshes = real; + } + + ASSIMP_LOG_INFO("FindInvalidDataProcess finished. Found issues ..."); + } else { + ASSIMP_LOG_DEBUG("FindInvalidDataProcess finished. Everything seems to be OK."); + } +} + +// ------------------------------------------------------------------------------------------------ +template +inline +const char* ValidateArrayContents(const T* /*arr*/, unsigned int /*size*/, + const std::vector& /*dirtyMask*/, bool /*mayBeIdentical = false*/, bool /*mayBeZero = true*/) +{ + return nullptr; +} + +// ------------------------------------------------------------------------------------------------ +template <> +inline +const char* ValidateArrayContents(const aiVector3D* arr, unsigned int size, + const std::vector& dirtyMask, bool mayBeIdentical , bool mayBeZero ) { + bool b = false; + unsigned int cnt = 0; + for (unsigned int i = 0; i < size;++i) { + + if (dirtyMask.size() && dirtyMask[i]) { + continue; + } + ++cnt; + + const aiVector3D& v = arr[i]; + if (is_special_float(v.x) || is_special_float(v.y) || is_special_float(v.z)) { + return "INF/NAN was found in a vector component"; + } + if (!mayBeZero && !v.x && !v.y && !v.z ) { + return "Found zero-length vector"; + } + if (i && v != arr[i-1])b = true; + } + if (cnt > 1 && !b && !mayBeIdentical) { + return "All vectors are identical"; + } + return nullptr; +} + +// ------------------------------------------------------------------------------------------------ +template +inline +bool ProcessArray(T*& in, unsigned int num,const char* name, + const std::vector& dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true) { + const char* err = ValidateArrayContents(in,num,dirtyMask,mayBeIdentical,mayBeZero); + if (err) { + ASSIMP_LOG_ERROR_F( "FindInvalidDataProcess fails on mesh ", name, ": ", err); + delete[] in; + in = NULL; + return true; + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE bool EpsilonCompare(const T& n, const T& s, ai_real epsilon); + +// ------------------------------------------------------------------------------------------------ +AI_FORCE_INLINE bool EpsilonCompare(ai_real n, ai_real s, ai_real epsilon) { + return std::fabs(n-s)>epsilon; +} + +// ------------------------------------------------------------------------------------------------ +template <> +bool EpsilonCompare(const aiVectorKey& n, const aiVectorKey& s, ai_real epsilon) { + return + EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) && + EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) && + EpsilonCompare(n.mValue.z,s.mValue.z,epsilon); +} + +// ------------------------------------------------------------------------------------------------ +template <> +bool EpsilonCompare(const aiQuatKey& n, const aiQuatKey& s, ai_real epsilon) { + return + EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) && + EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) && + EpsilonCompare(n.mValue.z,s.mValue.z,epsilon) && + EpsilonCompare(n.mValue.w,s.mValue.w,epsilon); +} + +// ------------------------------------------------------------------------------------------------ +template +inline +bool AllIdentical(T* in, unsigned int num, ai_real epsilon) { + if (num <= 1) { + return true; + } + + if (fabs(epsilon) > 0.f) { + for (unsigned int i = 0; i < num-1;++i) { + if (!EpsilonCompare(in[i],in[i+1],epsilon)) { + return false; + } + } + } else { + for (unsigned int i = 0; i < num-1;++i) { + if (in[i] != in[i+1]) { + return false; + } + } + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Search an animation for invalid content +void FindInvalidDataProcess::ProcessAnimation (aiAnimation* anim) { + // Process all animation channels + for ( unsigned int a = 0; a < anim->mNumChannels; ++a ) { + ProcessAnimationChannel( anim->mChannels[a]); + } +} + +// ------------------------------------------------------------------------------------------------ +void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) { + ai_assert( nullptr != anim ); + if (anim->mNumPositionKeys == 0 && anim->mNumRotationKeys == 0 && anim->mNumScalingKeys == 0) { + ai_assert_entry(); + return; + } + + // Check whether all values in a tracks are identical - in this case + // we can remove al keys except one. + // POSITIONS + int i = 0; + if (anim->mNumPositionKeys > 1 && AllIdentical(anim->mPositionKeys,anim->mNumPositionKeys,configEpsilon)) { + aiVectorKey v = anim->mPositionKeys[0]; + + // Reallocate ... we need just ONE element, it makes no sense to reuse the array + delete[] anim->mPositionKeys; + anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys = 1]; + anim->mPositionKeys[0] = v; + i = 1; + } + + // ROTATIONS + if (anim->mNumRotationKeys > 1 && AllIdentical(anim->mRotationKeys,anim->mNumRotationKeys,configEpsilon)) { + aiQuatKey v = anim->mRotationKeys[0]; + + // Reallocate ... we need just ONE element, it makes no sense to reuse the array + delete[] anim->mRotationKeys; + anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys = 1]; + anim->mRotationKeys[0] = v; + i = 1; + } + + // SCALINGS + if (anim->mNumScalingKeys > 1 && AllIdentical(anim->mScalingKeys,anim->mNumScalingKeys,configEpsilon)) { + aiVectorKey v = anim->mScalingKeys[0]; + + // Reallocate ... we need just ONE element, it makes no sense to reuse the array + delete[] anim->mScalingKeys; + anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys = 1]; + anim->mScalingKeys[0] = v; + i = 1; + } + if ( 1 == i ) { + ASSIMP_LOG_WARN("Simplified dummy tracks with just one key"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Search a mesh for invalid contents +int FindInvalidDataProcess::ProcessMesh(aiMesh* pMesh) +{ + bool ret = false; + std::vector dirtyMask(pMesh->mNumVertices, pMesh->mNumFaces != 0); + + // Ignore elements that are not referenced by vertices. + // (they are, for example, caused by the FindDegenerates step) + for (unsigned int m = 0; m < pMesh->mNumFaces; ++m) { + const aiFace& f = pMesh->mFaces[m]; + + for (unsigned int i = 0; i < f.mNumIndices; ++i) { + dirtyMask[f.mIndices[i]] = false; + } + } + + // Process vertex positions + if (pMesh->mVertices && ProcessArray(pMesh->mVertices, pMesh->mNumVertices, "positions", dirtyMask)) { + ASSIMP_LOG_ERROR("Deleting mesh: Unable to continue without vertex positions"); + + return 2; + } + + // process texture coordinates + if (!mIgnoreTexCoods) { + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS && pMesh->mTextureCoords[i]; ++i) { + if (ProcessArray(pMesh->mTextureCoords[i], pMesh->mNumVertices, "uvcoords", dirtyMask)) { + pMesh->mNumUVComponents[i] = 0; + + // delete all subsequent texture coordinate sets. + for (unsigned int a = i + 1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { + delete[] pMesh->mTextureCoords[a]; + pMesh->mTextureCoords[a] = NULL; + pMesh->mNumUVComponents[a] = 0; + } + + ret = true; + } + } + } + + // -- we don't validate vertex colors, it's difficult to say whether + // they are invalid or not. + + // Normals and tangents are undefined for point and line faces. + if (pMesh->mNormals || pMesh->mTangents) { + + if (aiPrimitiveType_POINT & pMesh->mPrimitiveTypes || + aiPrimitiveType_LINE & pMesh->mPrimitiveTypes) + { + if (aiPrimitiveType_TRIANGLE & pMesh->mPrimitiveTypes || + aiPrimitiveType_POLYGON & pMesh->mPrimitiveTypes) + { + // We need to update the lookup-table + for (unsigned int m = 0; m < pMesh->mNumFaces;++m) { + const aiFace& f = pMesh->mFaces[ m ]; + + if (f.mNumIndices < 3) { + dirtyMask[f.mIndices[0]] = true; + if (f.mNumIndices == 2) { + dirtyMask[f.mIndices[1]] = true; + } + } + } + } + // Normals, tangents and bitangents are undefined for + // the whole mesh (and should not even be there) + else { + return ret; + } + } + + // Process mesh normals + if (pMesh->mNormals && ProcessArray(pMesh->mNormals,pMesh->mNumVertices, + "normals",dirtyMask,true,false)) + ret = true; + + // Process mesh tangents + if (pMesh->mTangents && ProcessArray(pMesh->mTangents,pMesh->mNumVertices,"tangents",dirtyMask)) { + delete[] pMesh->mBitangents; pMesh->mBitangents = NULL; + ret = true; + } + + // Process mesh bitangents + if (pMesh->mBitangents && ProcessArray(pMesh->mBitangents,pMesh->mNumVertices,"bitangents",dirtyMask)) { + delete[] pMesh->mTangents; pMesh->mTangents = NULL; + ret = true; + } + } + return ret ? 1 : 0; +} + +#endif // !! ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS diff --git a/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.h b/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.h new file mode 100644 index 0000000000..ce7375f34f --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.h @@ -0,0 +1,106 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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 "Common/BaseProcess.h" + +#include +#include + +struct aiMesh; + +class FindInvalidDataProcessTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** The FindInvalidData post-processing step. It searches the mesh data + * for parts that are obviously invalid and removes them. + * + * Originally this was a workaround for some models written by Blender + * which have zero normal vectors. */ +class ASSIMP_API FindInvalidDataProcess : public BaseProcess { +public: + FindInvalidDataProcess(); + ~FindInvalidDataProcess(); + + // ------------------------------------------------------------------- + // + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + // Setup import settings + void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + // Run the step + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + /** Executes the post-processing step on the given mesh + * @param pMesh The mesh to process. + * @return 0 - nothing, 1 - removed sth, 2 - please delete me */ + int ProcessMesh( aiMesh* pMesh); + + // ------------------------------------------------------------------- + /** Executes the post-processing step on the given animation + * @param anim The animation to process. */ + void ProcessAnimation (aiAnimation* anim); + + // ------------------------------------------------------------------- + /** Executes the post-processing step on the given anim channel + * @param anim The animation channel to process.*/ + void ProcessAnimationChannel (aiNodeAnim* anim); + +private: + ai_real configEpsilon; + bool mIgnoreTexCoods; +}; + +} // end of namespace Assimp + +#endif // AI_AI_FINDINVALIDDATA_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/FixNormalsStep.cpp b/thirdparty/assimp/code/PostProcessing/FixNormalsStep.cpp new file mode 100644 index 0000000000..bbbe6899b4 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/FixNormalsStep.cpp @@ -0,0 +1,184 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to invert + * all normals in meshes with infacing normals. + */ + +// internal headers +#include "FixNormalsStep.h" +#include +#include +#include +#include +#include + + +using namespace Assimp; + + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +FixInfacingNormalsProcess::FixInfacingNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FixInfacingNormalsProcess::~FixInfacingNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool FixInfacingNormalsProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_FixInfacingNormals) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void FixInfacingNormalsProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess begin"); + + bool bHas( false ); + for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { + if (ProcessMesh(pScene->mMeshes[a], a)) { + bHas = true; + } + } + + if (bHas) { + ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess finished. Found issues."); + } else { + ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess finished. No changes to the scene."); + } +} + +// ------------------------------------------------------------------------------------------------ +// Apply the step to the mesh +bool FixInfacingNormalsProcess::ProcessMesh( aiMesh* pcMesh, unsigned int index) +{ + ai_assert(nullptr != pcMesh); + + // Nothing to do if there are no model normals + if (!pcMesh->HasNormals()) { + return false; + } + + // Compute the bounding box of both the model vertices + normals and + // the unmodified model vertices. Then check whether the first BB + // is smaller than the second. In this case we can assume that the + // normals need to be flipped, although there are a few special cases .. + // convex, concave, planar models ... + + aiVector3D vMin0 (1e10f,1e10f,1e10f); + aiVector3D vMin1 (1e10f,1e10f,1e10f); + aiVector3D vMax0 (-1e10f,-1e10f,-1e10f); + aiVector3D vMax1 (-1e10f,-1e10f,-1e10f); + + for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) + { + vMin1.x = std::min(vMin1.x,pcMesh->mVertices[i].x); + vMin1.y = std::min(vMin1.y,pcMesh->mVertices[i].y); + vMin1.z = std::min(vMin1.z,pcMesh->mVertices[i].z); + + vMax1.x = std::max(vMax1.x,pcMesh->mVertices[i].x); + vMax1.y = std::max(vMax1.y,pcMesh->mVertices[i].y); + vMax1.z = std::max(vMax1.z,pcMesh->mVertices[i].z); + + const aiVector3D vWithNormal = pcMesh->mVertices[i] + pcMesh->mNormals[i]; + + vMin0.x = std::min(vMin0.x,vWithNormal.x); + vMin0.y = std::min(vMin0.y,vWithNormal.y); + vMin0.z = std::min(vMin0.z,vWithNormal.z); + + vMax0.x = std::max(vMax0.x,vWithNormal.x); + vMax0.y = std::max(vMax0.y,vWithNormal.y); + vMax0.z = std::max(vMax0.z,vWithNormal.z); + } + + const float fDelta0_x = (vMax0.x - vMin0.x); + const float fDelta0_y = (vMax0.y - vMin0.y); + const float fDelta0_z = (vMax0.z - vMin0.z); + + const float fDelta1_x = (vMax1.x - vMin1.x); + const float fDelta1_y = (vMax1.y - vMin1.y); + const float fDelta1_z = (vMax1.z - vMin1.z); + + // Check whether the boxes are overlapping + if ((fDelta0_x > 0.0f) != (fDelta1_x > 0.0f))return false; + if ((fDelta0_y > 0.0f) != (fDelta1_y > 0.0f))return false; + if ((fDelta0_z > 0.0f) != (fDelta1_z > 0.0f))return false; + + // Check whether this is a planar surface + const float fDelta1_yz = fDelta1_y * fDelta1_z; + + if (fDelta1_x < 0.05f * std::sqrt( fDelta1_yz ))return false; + if (fDelta1_y < 0.05f * std::sqrt( fDelta1_z * fDelta1_x ))return false; + if (fDelta1_z < 0.05f * std::sqrt( fDelta1_y * fDelta1_x ))return false; + + // now compare the volumes of the bounding boxes + if (std::fabs(fDelta0_x * fDelta0_y * fDelta0_z) < std::fabs(fDelta1_x * fDelta1_yz)) { + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_INFO_F("Mesh ", index, ": Normals are facing inwards (or the mesh is planar)", index); + } + + // Invert normals + for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) + pcMesh->mNormals[i] *= -1.0f; + + // ... and flip faces + for (unsigned int i = 0; i < pcMesh->mNumFaces;++i) + { + aiFace& face = pcMesh->mFaces[i]; + for( unsigned int b = 0; b < face.mNumIndices / 2; b++) + std::swap( face.mIndices[b], face.mIndices[ face.mNumIndices - 1 - b]); + } + return true; + } + return false; +} diff --git a/thirdparty/assimp/code/PostProcessing/FixNormalsStep.h b/thirdparty/assimp/code/PostProcessing/FixNormalsStep.h new file mode 100644 index 0000000000..f60ce596a4 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/FixNormalsStep.h @@ -0,0 +1,91 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + + +/** @file Defines a post processing step to fix infacing normals */ +#ifndef AI_FIXNORMALSPROCESS_H_INC +#define AI_FIXNORMALSPROCESS_H_INC + +#include "Common/BaseProcess.h" + +struct aiMesh; + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The FixInfacingNormalsProcess tries to determine whether the normal + * vectors of an object are facing inwards. In this case they will be + * flipped. + */ +class FixInfacingNormalsProcess : public BaseProcess { +public: + FixInfacingNormalsProcess(); + ~FixInfacingNormalsProcess(); + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + +protected: + + // ------------------------------------------------------------------- + /** Executes the step on the given mesh + * @param pMesh The mesh to process. + */ + bool ProcessMesh( aiMesh* pMesh, unsigned int index); +}; + +} // end of namespace Assimp + +#endif // AI_FIXNORMALSPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/GenBoundingBoxesProcess.cpp b/thirdparty/assimp/code/PostProcessing/GenBoundingBoxesProcess.cpp new file mode 100644 index 0000000000..c013454fc3 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/GenBoundingBoxesProcess.cpp @@ -0,0 +1,115 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#ifndef ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS + +#include "PostProcessing/GenBoundingBoxesProcess.h" + +#include +#include + +namespace Assimp { + +GenBoundingBoxesProcess::GenBoundingBoxesProcess() +: BaseProcess() { + +} + +GenBoundingBoxesProcess::~GenBoundingBoxesProcess() { + // empty +} + +bool GenBoundingBoxesProcess::IsActive(unsigned int pFlags) const { + return 0 != ( pFlags & aiProcess_GenBoundingBoxes ); +} + +void checkMesh(aiMesh* mesh, aiVector3D& min, aiVector3D& max) { + ai_assert(nullptr != mesh); + + if (0 == mesh->mNumVertices) { + return; + } + + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + const aiVector3D &pos = mesh->mVertices[i]; + if (pos.x < min.x) { + min.x = pos.x; + } + if (pos.y < min.y) { + min.y = pos.y; + } + if (pos.z < min.z) { + min.z = pos.z; + } + + if (pos.x > max.x) { + max.x = pos.x; + } + if (pos.y > max.y) { + max.y = pos.y; + } + if (pos.z > max.z) { + max.z = pos.z; + } + } +} + +void GenBoundingBoxesProcess::Execute(aiScene* pScene) { + if (nullptr == pScene) { + return; + } + + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + aiMesh* mesh = pScene->mMeshes[i]; + if (nullptr == mesh) { + continue; + } + + aiVector3D min(999999, 999999, 999999), max(-999999, -999999, -999999); + checkMesh(mesh, min, max); + mesh->mAABB.mMin = min; + mesh->mAABB.mMax = max; + } +} + +} // Namespace Assimp + +#endif // ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS diff --git a/thirdparty/assimp/code/PostProcessing/GenBoundingBoxesProcess.h b/thirdparty/assimp/code/PostProcessing/GenBoundingBoxesProcess.h new file mode 100644 index 0000000000..4b43c82a42 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/GenBoundingBoxesProcess.h @@ -0,0 +1,76 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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 generate Axis-aligned bounding + * volumes for all meshes. + */ + +#pragma once + +#ifndef AI_GENBOUNDINGBOXESPROCESS_H_INC +#define AI_GENBOUNDINGBOXESPROCESS_H_INC + +#ifndef ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS + +#include "Common/BaseProcess.h" + +namespace Assimp { + +/** Post-processing process to find axis-aligned bounding volumes for amm meshes + * used in a scene + */ +class ASSIMP_API GenBoundingBoxesProcess : public BaseProcess { +public: + /// The class constructor. + GenBoundingBoxesProcess(); + /// The class destructor. + ~GenBoundingBoxesProcess(); + /// Will return true, if aiProcess_GenBoundingBoxes is defined. + bool IsActive(unsigned int pFlags) const override; + /// The execution callback. + void Execute(aiScene* pScene) override; +}; + +} // Namespace Assimp + +#endif // #ifndef ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS + +#endif // AI_GENBOUNDINGBOXESPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.cpp b/thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.cpp new file mode 100644 index 0000000000..028334dec7 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.cpp @@ -0,0 +1,146 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to generate face +* normals for all imported faces. +*/ + + +#include "GenFaceNormalsProcess.h" +#include +#include +#include +#include +#include + + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +GenFaceNormalsProcess::GenFaceNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +GenFaceNormalsProcess::~GenFaceNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool GenFaceNormalsProcess::IsActive( unsigned int pFlags) const { + force_ = (pFlags & aiProcess_ForceGenNormals) != 0; + return (pFlags & aiProcess_GenNormals) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void GenFaceNormalsProcess::Execute( aiScene* pScene) { + ASSIMP_LOG_DEBUG("GenFaceNormalsProcess begin"); + + if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { + throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); + } + + bool bHas = false; + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { + if(this->GenMeshFaceNormals( pScene->mMeshes[a])) { + bHas = true; + } + } + if (bHas) { + ASSIMP_LOG_INFO("GenFaceNormalsProcess finished. " + "Face normals have been calculated"); + } else { + ASSIMP_LOG_DEBUG("GenFaceNormalsProcess finished. " + "Normals are already there"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh) +{ + if (NULL != pMesh->mNormals) { + if (force_) delete[] pMesh->mNormals; + else return false; + } + + // If the mesh consists of lines and/or points but not of + // triangles or higher-order polygons the normal vectors + // are undefined. + if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) { + ASSIMP_LOG_INFO("Normal vectors are undefined for line and point meshes"); + return false; + } + + // allocate an array to hold the output normals + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + const float qnan = get_qnan(); + + // iterate through all faces and compute per-face normals but store them per-vertex. + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { + const aiFace& face = pMesh->mFaces[a]; + if (face.mNumIndices < 3) { + // either a point or a line -> no well-defined normal vector + for (unsigned int i = 0;i < face.mNumIndices;++i) { + pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan); + } + continue; + } + + const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]]; + const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]]; + const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]]; + const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe(); + + for (unsigned int i = 0;i < face.mNumIndices;++i) { + pMesh->mNormals[face.mIndices[i]] = vNor; + } + } + return true; +} diff --git a/thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.h b/thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.h new file mode 100644 index 0000000000..c641fd6353 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.h @@ -0,0 +1,87 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to compute face normals for all loaded faces*/ +#ifndef AI_GENFACENORMALPROCESS_H_INC +#define AI_GENFACENORMALPROCESS_H_INC + +#include "Common/BaseProcess.h" +#include + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The GenFaceNormalsProcess computes face normals for all faces of all meshes +*/ +class ASSIMP_API_WINONLY GenFaceNormalsProcess : public BaseProcess +{ +public: + + GenFaceNormalsProcess(); + ~GenFaceNormalsProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + +private: + bool GenMeshFaceNormals(aiMesh* pcMesh); + mutable bool force_ = false; +}; + +} // end of namespace Assimp + +#endif // !!AI_GENFACENORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.cpp b/thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.cpp new file mode 100644 index 0000000000..3f6c2f86bd --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.cpp @@ -0,0 +1,239 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to generate face +* normals for all imported faces. +*/ + + + +// internal headers +#include "GenVertexNormalsProcess.h" +#include "ProcessHelper.h" +#include +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +GenVertexNormalsProcess::GenVertexNormalsProcess() +: configMaxAngle( AI_DEG_TO_RAD( 175.f ) ) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +GenVertexNormalsProcess::~GenVertexNormalsProcess() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const +{ + force_ = (pFlags & aiProcess_ForceGenNormals) != 0; + return (pFlags & aiProcess_GenSmoothNormals) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void GenVertexNormalsProcess::SetupProperties(const Importer* pImp) +{ + // Get the current value of the AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE property + configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,(ai_real)175.0); + configMaxAngle = AI_DEG_TO_RAD(std::max(std::min(configMaxAngle,(ai_real)175.0),(ai_real)0.0)); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void GenVertexNormalsProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("GenVertexNormalsProcess begin"); + + if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { + throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); + } + + bool bHas = false; + for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) { + if(GenMeshVertexNormals( pScene->mMeshes[a],a)) + bHas = true; + } + + if (bHas) { + ASSIMP_LOG_INFO("GenVertexNormalsProcess finished. " + "Vertex normals have been calculated"); + } else { + ASSIMP_LOG_DEBUG("GenVertexNormalsProcess finished. " + "Normals are already there"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int meshIndex) +{ + if (NULL != pMesh->mNormals) { + if (force_) delete[] pMesh->mNormals; + else return false; + } + + // If the mesh consists of lines and/or points but not of + // triangles or higher-order polygons the normal vectors + // are undefined. + if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) + { + ASSIMP_LOG_INFO("Normal vectors are undefined for line and point meshes"); + return false; + } + + // Allocate the array to hold the output normals + const float qnan = std::numeric_limits::quiet_NaN(); + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + + // Compute per-face normals but store them per-vertex + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + const aiFace& face = pMesh->mFaces[a]; + if (face.mNumIndices < 3) + { + // either a point or a line -> no normal vector + for (unsigned int i = 0;i < face.mNumIndices;++i) { + pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan); + } + + continue; + } + + const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]]; + const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]]; + const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]]; + const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe(); + + for (unsigned int i = 0;i < face.mNumIndices;++i) { + pMesh->mNormals[face.mIndices[i]] = vNor; + } + } + + // Set up a SpatialSort to quickly find all vertices close to a given position + // check whether we can reuse the SpatialSort of a previous step. + SpatialSort* vertexFinder = NULL; + SpatialSort _vertexFinder; + ai_real posEpsilon = ai_real( 1e-5 ); + if (shared) { + std::vector >* avf; + shared->GetProperty(AI_SPP_SPATIAL_SORT,avf); + if (avf) + { + std::pair& 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 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 abHad(pMesh->mNumVertices,false); + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { + if (abHad[i]) { + continue; + } + + // Get all vertices that share this one ... + vertexFinder->FindPositions( pMesh->mVertices[i], posEpsilon, verticesFound); + + aiVector3D pcNor; + for (unsigned int a = 0; a < verticesFound.size(); ++a) { + const aiVector3D& v = pMesh->mNormals[verticesFound[a]]; + if (is_not_qnan(v.x))pcNor += v; + } + pcNor.NormalizeSafe(); + + // Write the smoothed normal back to all affected normals + for (unsigned int a = 0; a < verticesFound.size(); ++a) + { + unsigned int vidx = verticesFound[a]; + pcNew[vidx] = pcNor; + abHad[vidx] = true; + } + } + } + // Slower code path if a smooth angle is set. There are many ways to achieve + // the effect, this one is the most straightforward one. + else { + const ai_real fLimit = std::cos(configMaxAngle); + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { + // Get all vertices that share this one ... + vertexFinder->FindPositions( pMesh->mVertices[i] , posEpsilon, verticesFound); + + aiVector3D vr = pMesh->mNormals[i]; + + aiVector3D pcNor; + for (unsigned int a = 0; a < verticesFound.size(); ++a) { + aiVector3D v = pMesh->mNormals[verticesFound[a]]; + + // Check whether the angle between the two normals is not too large. + // Skip the angle check on our own normal to avoid false negatives + // (v*v is not guaranteed to be 1.0 for all unit vectors v) + if (is_not_qnan(v.x) && (verticesFound[a] == i || (v * vr >= fLimit))) + pcNor += v; + } + pcNew[i] = pcNor.NormalizeSafe(); + } + } + + delete[] pMesh->mNormals; + pMesh->mNormals = pcNew; + + return true; +} diff --git a/thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.h b/thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.h new file mode 100644 index 0000000000..2ceee17e85 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.h @@ -0,0 +1,111 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to compute vertex normals + for all loaded vertizes */ +#ifndef AI_GENVERTEXNORMALPROCESS_H_INC +#define AI_GENVERTEXNORMALPROCESS_H_INC + +#include "Common/assbin_chunks.h" +#include "Common/BaseProcess.h" + +#include + +// Forward declarations +class GenNormalsTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** The GenFaceNormalsProcess computes vertex normals for all vertices +*/ +class ASSIMP_API GenVertexNormalsProcess : public BaseProcess { +public: + GenVertexNormalsProcess(); + ~GenVertexNormalsProcess(); + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. + * A bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + + // setter for configMaxAngle + inline void SetMaxSmoothAngle(ai_real f) { + configMaxAngle =f; + } + + // ------------------------------------------------------------------- + /** Computes normals for a specific mesh + * @param pcMesh Mesh + * @param meshIndex Index of the mesh + * @return true if vertex normals have been computed + */ + bool GenMeshVertexNormals (aiMesh* pcMesh, unsigned int meshIndex); + +private: + /** Configuration option: maximum smoothing angle, in radians*/ + ai_real configMaxAngle; + mutable bool force_ = false; +}; + +} // end of namespace Assimp + +#endif // !!AI_GENVERTEXNORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.cpp b/thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.cpp new file mode 100644 index 0000000000..d0a016fa42 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.cpp @@ -0,0 +1,379 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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. + *
+ * The algorithm is roughly basing on this paper: + * http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf + * .. although overdraw reduction isn't implemented yet ... + */ + +// internal headers +#include "PostProcessing/ImproveCacheLocality.h" +#include "Common/VertexTriangleAdjacency.h" + +#include +#include +#include +#include +#include +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() +: mConfigCacheDepth(PP_ICL_PTCACHE_SIZE) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// 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 + mConfigCacheDepth = pImp->GetPropertyInteger(AI_CONFIG_PP_ICL_PTCACHE_SIZE,PP_ICL_PTCACHE_SIZE); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void ImproveCacheLocalityProcess::Execute( aiScene* pScene) { + if (!pScene->mNumMeshes) { + ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess skipped; there are no meshes"); + return; + } + + ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess begin"); + + float out = 0.f; + unsigned int numf = 0, numm = 0; + for( unsigned int a = 0; a < pScene->mNumMeshes; ++a ){ + const float res = ProcessMesh( pScene->mMeshes[a],a); + if (res) { + numf += pScene->mMeshes[a]->mNumFaces; + out += res; + ++numm; + } + } + if (!DefaultLogger::isNullLogger()) { + if (numf > 0) { + ASSIMP_LOG_INFO_F("Cache relevant are ", numm, " meshes (", numf, " faces). Average output ACMR is ", out / numf); + } + ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess finished. "); + } +} + +// ------------------------------------------------------------------------------------------------ +// Improves the cache coherency of a specific mesh +ai_real ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshNum) { + // TODO: rewrite this to use std::vector or boost::shared_array + ai_assert(nullptr != 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 static_cast(0.f); + + if (pMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) { + ASSIMP_LOG_ERROR("This algorithm works on triangle meshes only"); + return static_cast(0.f); + } + + if(pMesh->mNumVertices <= mConfigCacheDepth) { + return static_cast(0.f); + } + + ai_real 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[mConfigCacheDepth]; + memset(piFIFOStack,0xff,mConfigCacheDepth*sizeof(unsigned int)); + unsigned int* piCur = piFIFOStack; + const unsigned int* const piCurEnd = piFIFOStack + mConfigCacheDepth; + + // 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 = (ai_real) iCacheMisses / pMesh->mNumFaces; + if (3.0 == fACMR) { + char szBuff[128]; // should be sufficiently large in every case + + // the JoinIdenticalVertices process has not been executed on this + // mesh, otherwise this value would normally be at least minimally + // smaller than 3.0 ... + ai_snprintf(szBuff,128,"Mesh %u: Not suitable for vcache optimization",meshNum); + ASSIMP_LOG_WARN(szBuff); + return static_cast(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 abEmitted(pMesh->mNumFaces,false); + + // dead-end vertex index stack + std::stack > sDeadEndVStack; + + // create a copy of the piNumTriPtr buffer + unsigned int* const piNumTriPtr = adj.mLiveTriangles; + const std::vector piNumTriPtrNoModify(piNumTriPtr, piNumTriPtr + pMesh->mNumVertices); + + // get the largest number of referenced triangles and allocate the "candidate buffer" + unsigned int iMaxRefTris = 0; { + const unsigned int* piCur = adj.mLiveTriangles; + const unsigned int* const piCurEnd = adj.mLiveTriangles+pMesh->mNumVertices; + for (;piCur != piCurEnd;++piCur) { + iMaxRefTris = std::max(iMaxRefTris,*piCur); + } + } + ai_assert(iMaxRefTris > 0); + unsigned int* piCandidates = new unsigned int[iMaxRefTris*3]; + unsigned int iCacheMisses = 0; + + // ................................................................................... + /** PSEUDOCODE for the algorithm + + A = Build-Adjacency(I) Vertex-triangle adjacency + L = Get-Triangle-Counts(A) Per-vertex live triangle counts + C = Zero(Vertex-Count(I)) Per-vertex caching time stamps + D = Empty-Stack() Dead-end vertex stack + E = False(Triangle-Count(I)) Per triangle emitted flag + O = Empty-Index-Buffer() Empty output buffer + f = 0 Arbitrary starting vertex + s = k+1, i = 1 Time stamp and cursor + while f >= 0 For all valid fanning vertices + N = Empty-Set() 1-ring of next candidates + for each Triangle t in Neighbors(A, f) + if !Emitted(E,t) + for each Vertex v in t + Append(O,v) Output vertex + Push(D,v) Add to dead-end stack + Insert(N,v) Register as candidate + L[v] = L[v]-1 Decrease live triangle count + if s-C[v] > k If not in cache + C[v] = s Set time stamp + s = s+1 Increment time stamp + E[t] = true Flag triangle as emitted + Select next fanning vertex + f = Get-Next-Vertex(I,i,k,N,C,s,L,D) + return O + */ + // ................................................................................... + + int ivdx = 0; + int ics = 1; + int iStampCnt = mConfigCacheDepth+1; + while (ivdx >= 0) { + + unsigned int icnt = piNumTriPtrNoModify[ivdx]; + unsigned int* piList = adj.GetAdjacentTriangles(ivdx); + unsigned int* piCurCandidate = piCandidates; + + // get all triangles in the neighborhood + for (unsigned int tri = 0; tri < icnt;++tri) { + + // if they have not yet been emitted, add them to the output IB + const unsigned int fidx = *piList++; + if (!abEmitted[fidx]) { + + // so iterate through all vertices of the current triangle + const aiFace* pcFace = &pMesh->mFaces[ fidx ]; + unsigned nind = pcFace->mNumIndices; + for (unsigned ind = 0; ind < nind; ind++) { + unsigned dp = pcFace->mIndices[ind]; + + // the current vertex won't have any free triangles after this step + if (ivdx != (int)dp) { + // append the vertex to the dead-end stack + sDeadEndVStack.push(dp); + + // register as candidate for the next step + *piCurCandidate++ = dp; + + // decrease the per-vertex triangle counts + piNumTriPtr[dp]--; + } + + // append the vertex to the output index buffer + *piCSIter++ = dp; + + // if the vertex is not yet in cache, set its cache count + if (iStampCnt-piCachingStamps[dp] > mConfigCacheDepth) { + piCachingStamps[dp] = iStampCnt++; + ++iCacheMisses; + } + } + // flag triangle as emitted + abEmitted[fidx] = true; + } + } + + // the vertex has now no living adjacent triangles anymore + piNumTriPtr[ivdx] = 0; + + // get next fanning vertex + ivdx = -1; + int max_priority = -1; + for (unsigned int* piCur = piCandidates;piCur != piCurCandidate;++piCur) { + const unsigned int dp = *piCur; + + // must have live triangles + if (piNumTriPtr[dp] > 0) { + int priority = 0; + + // will the vertex be in cache, even after fanning occurs? + unsigned int tmp; + if ((tmp = iStampCnt-piCachingStamps[dp]) + 2*piNumTriPtr[dp] <= mConfigCacheDepth) { + 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; + } + } + } + } + } + ai_real fACMR2 = 0.0f; + if (!DefaultLogger::isNullLogger()) { + fACMR2 = (float)iCacheMisses / pMesh->mNumFaces; + + // very intense verbose logging ... prepare for much text if there are many meshes + if ( DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) { + ASSIMP_LOG_DEBUG_F("Mesh %u | ACMR in: ", meshNum, " out: ", fACMR, " | ~", fACMR2, ((fACMR - fACMR2) / fACMR) * 100.f); + } + + fACMR2 *= pMesh->mNumFaces; + } + // sort the output index buffer back to the input array + piCSIter = piIBOutput; + for (aiFace* pcFace = pMesh->mFaces; pcFace != pcEnd;++pcFace) { + unsigned nind = pcFace->mNumIndices; + unsigned * ind = pcFace->mIndices; + if (nind > 0) ind[0] = *piCSIter++; + if (nind > 1) ind[1] = *piCSIter++; + if (nind > 2) ind[2] = *piCSIter++; + } + + // delete temporary storage + delete[] piCachingStamps; + delete[] piIBOutput; + delete[] piCandidates; + + return fACMR2; +} diff --git a/thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.h b/thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.h new file mode 100644 index 0000000000..de25ecd9fb --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.h @@ -0,0 +1,101 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to reorder faces for + better cache locality*/ +#ifndef AI_IMPROVECACHELOCALITY_H_INC +#define AI_IMPROVECACHELOCALITY_H_INC + +#include "Common/BaseProcess.h" + +#include + +struct aiMesh; + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The ImproveCacheLocalityProcess reorders all faces for improved vertex + * cache locality. It tries to arrange all faces to fans and to render + * faces which share vertices directly one after the other. + * + * @note This step expects triagulated input data. + */ +class ImproveCacheLocalityProcess : public BaseProcess +{ +public: + + ImproveCacheLocalityProcess(); + ~ImproveCacheLocalityProcess(); + +public: + + // ------------------------------------------------------------------- + // Check whether the pp step is active + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + // Executes the pp step on a given scene + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + // Configures the pp step + void SetupProperties(const Importer* pImp); + +protected: + // ------------------------------------------------------------------- + /** Executes the postprocessing step on the given mesh + * @param pMesh The mesh to process. + * @param meshNum Index of the mesh to process + */ + ai_real ProcessMesh( aiMesh* pMesh, unsigned int meshNum); + +private: + //! Configuration parameter: specifies the size of the cache to + //! optimize the vertex data for. + unsigned int mConfigCacheDepth; +}; + +} // end of namespace Assimp + +#endif // AI_IMPROVECACHELOCALITY_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp b/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp new file mode 100644 index 0000000000..914ec05b46 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp @@ -0,0 +1,463 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to join identical vertices + * for all imported meshes + */ + + +#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS + +#include "JoinVerticesProcess.h" +#include "ProcessHelper.h" +#include +#include +#include +#include + +using namespace Assimp; +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +JoinVerticesProcess::JoinVerticesProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +JoinVerticesProcess::~JoinVerticesProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool JoinVerticesProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_JoinIdenticalVertices) != 0; +} +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void JoinVerticesProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("JoinVerticesProcess begin"); + + // get the total number of vertices BEFORE the step is executed + int iNumOldVertices = 0; + if (!DefaultLogger::isNullLogger()) { + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { + iNumOldVertices += pScene->mMeshes[a]->mNumVertices; + } + } + + // execute the step + int iNumVertices = 0; + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + iNumVertices += ProcessMesh( pScene->mMeshes[a],a); + + // if logging is active, print detailed statistics + if (!DefaultLogger::isNullLogger()) { + if (iNumOldVertices == iNumVertices) { + ASSIMP_LOG_DEBUG("JoinVerticesProcess finished "); + } else { + ASSIMP_LOG_INFO_F("JoinVerticesProcess finished | Verts in: ", iNumOldVertices, + " out: ", iNumVertices, " | ~", + ((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f ); + } + } + + pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; +} + +namespace { + +bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex) +{ + // A little helper to find locally close vertices faster. + // Try to reuse the lookup table from the last step. + const static float epsilon = 1e-5f; + // Squared because we check against squared length of the vector difference + static const float squareEpsilon = epsilon * epsilon; + + // Square compare is useful for animeshes vertices compare + if ((lhs.position - rhs.position).SquareLength() > squareEpsilon) { + return false; + } + + // We just test the other attributes even if they're not present in the mesh. + // In this case they're initialized to 0 so the comparison succeeds. + // By this method the non-present attributes are effectively ignored in the comparison. + if ((lhs.normal - rhs.normal).SquareLength() > squareEpsilon) { + return false; + } + + if ((lhs.texcoords[0] - rhs.texcoords[0]).SquareLength() > squareEpsilon) { + return false; + } + + if ((lhs.tangent - rhs.tangent).SquareLength() > squareEpsilon) { + return false; + } + + if ((lhs.bitangent - rhs.bitangent).SquareLength() > squareEpsilon) { + return false; + } + + // Usually we won't have vertex colors or multiple UVs, so we can skip from here + // Actually this increases runtime performance slightly, at least if branch + // prediction is on our side. + if (complex) { + for (int i = 0; i < 8; i++) { + if (i > 0 && (lhs.texcoords[i] - rhs.texcoords[i]).SquareLength() > squareEpsilon) { + return false; + } + if (GetColorDifference(lhs.colors[i], rhs.colors[i]) > squareEpsilon) { + return false; + } + } + } + return true; +} + +template +void updateXMeshVertices(XMesh *pMesh, std::vector &uniqueVertices) { + // replace vertex data with the unique data sets + pMesh->mNumVertices = (unsigned int)uniqueVertices.size(); + + // ---------------------------------------------------------------------------- + // NOTE - we're *not* calling Vertex::SortBack() because it would check for + // presence of every single vertex component once PER VERTEX. And our CPU + // dislikes branches, even if they're easily predictable. + // ---------------------------------------------------------------------------- + + // Position, if present (check made for aiAnimMesh) + if (pMesh->mVertices) + { + delete [] pMesh->mVertices; + pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; + for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { + pMesh->mVertices[a] = uniqueVertices[a].position; + } + } + + // Normals, if present + if (pMesh->mNormals) + { + delete [] pMesh->mNormals; + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { + pMesh->mNormals[a] = uniqueVertices[a].normal; + } + } + // Tangents, if present + if (pMesh->mTangents) + { + delete [] pMesh->mTangents; + pMesh->mTangents = new aiVector3D[pMesh->mNumVertices]; + for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { + pMesh->mTangents[a] = uniqueVertices[a].tangent; + } + } + // Bitangents as well + if (pMesh->mBitangents) + { + delete [] pMesh->mBitangents; + pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices]; + for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { + pMesh->mBitangents[a] = uniqueVertices[a].bitangent; + } + } + // Vertex colors + for (unsigned int a = 0; pMesh->HasVertexColors(a); a++) + { + delete [] pMesh->mColors[a]; + pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices]; + for( unsigned int b = 0; b < pMesh->mNumVertices; b++) { + pMesh->mColors[a][b] = uniqueVertices[b].colors[a]; + } + } + // Texture coords + for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++) + { + delete [] pMesh->mTextureCoords[a]; + pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices]; + for (unsigned int b = 0; b < pMesh->mNumVertices; b++) { + pMesh->mTextureCoords[a][b] = uniqueVertices[b].texcoords[a]; + } + } +} +} // namespace + +// ------------------------------------------------------------------------------------------------ +// Unites identical vertices in the given mesh +int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) +{ + static_assert( AI_MAX_NUMBER_OF_COLOR_SETS == 8, "AI_MAX_NUMBER_OF_COLOR_SETS == 8"); + static_assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 8, "AI_MAX_NUMBER_OF_TEXTURECOORDS == 8"); + + // Return early if we don't have any positions + if (!pMesh->HasPositions() || !pMesh->HasFaces()) { + return 0; + } + + // We should care only about used vertices, not all of them + // (this can happen due to original file vertices buffer being used by + // multiple meshes) + std::unordered_set usedVertexIndices; + usedVertexIndices.reserve(pMesh->mNumVertices); + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + aiFace& face = pMesh->mFaces[a]; + for( unsigned int b = 0; b < face.mNumIndices; b++) { + usedVertexIndices.insert(face.mIndices[b]); + } + } + + // We'll never have more vertices afterwards. + std::vector 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 and greatly enhances + // branching performance. + static_assert(AI_MAX_VERTICES == 0x7fffffff, "AI_MAX_VERTICES == 0x7fffffff"); + std::vector replaceIndex( pMesh->mNumVertices, 0xffffffff); + + // float posEpsilonSqr; + SpatialSort* vertexFinder = NULL; + SpatialSort _vertexFinder; + + typedef std::pair SpatPair; + if (shared) { + std::vector* avf; + shared->GetProperty(AI_SPP_SPATIAL_SORT,avf); + if (avf) { + SpatPair& blubb = (*avf)[meshIndex]; + vertexFinder = &blubb.first; + // posEpsilonSqr = blubb.second; + } + } + if (!vertexFinder) { + // bad, need to compute it. + _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D)); + vertexFinder = &_vertexFinder; + // posEpsilonSqr = ComputePositionEpsilon(pMesh); + } + + // Again, better waste some bytes than a realloc ... + std::vector verticesFound; + verticesFound.reserve(10); + + // Run an optimized code path if we don't have multiple UVs or vertex colors. + // This should yield false in more than 99% of all imports ... + const bool complex = ( pMesh->GetNumColorChannels() > 0 || pMesh->GetNumUVChannels() > 1); + const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0; + + // We'll never have more vertices afterwards. + std::vector> uniqueAnimatedVertices; + if (hasAnimMeshes) { + uniqueAnimatedVertices.resize(pMesh->mNumAnimMeshes); + for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { + uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices); + } + } + + // Now check each vertex if it brings something new to the table + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { + if (usedVertexIndices.find(a) == usedVertexIndices.end()) { + continue; + } + + // collect the vertex data + Vertex v(pMesh,a); + + // collect all vertices that are close enough to the given position + vertexFinder->FindIdenticalPositions( v.position, verticesFound); + unsigned int matchIndex = 0xffffffff; + + // check all unique vertices close to the position if this vertex is already present among them + for( unsigned int b = 0; b < verticesFound.size(); b++) { + const unsigned int vidx = verticesFound[b]; + const unsigned int uidx = replaceIndex[ vidx]; + if( uidx & 0x80000000) + continue; + + const Vertex& uv = uniqueVertices[ uidx]; + + if (!areVerticesEqual(v, uv, complex)) { + continue; + } + + if (hasAnimMeshes) { + // If given vertex is animated, then it has to be preserver 1 to 1 (base mesh and animated mesh require same topology) + // NOTE: not doing this totaly breaks anim meshes as they don't have their own faces (they use pMesh->mFaces) + bool breaksAnimMesh = false; + for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { + const Vertex& animatedUV = uniqueAnimatedVertices[animMeshIndex][ uidx]; + Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a); + if (!areVerticesEqual(aniMeshVertex, animatedUV, complex)) { + breaksAnimMesh = true; + break; + } + } + if (breaksAnimMesh) { + continue; + } + } + + // we're still here -> this vertex perfectly matches our given vertex + matchIndex = uidx; + break; + } + + // found a replacement vertex among the uniques? + if( matchIndex != 0xffffffff) + { + // store where to found the matching unique vertex + replaceIndex[a] = matchIndex | 0x80000000; + } + else + { + // no unique vertex matches it up to now -> so add it + replaceIndex[a] = (unsigned int)uniqueVertices.size(); + uniqueVertices.push_back( v); + if (hasAnimMeshes) { + for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { + Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a); + uniqueAnimatedVertices[animMeshIndex].push_back(aniMeshVertex); + } + } + } + } + + if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) { + ASSIMP_LOG_DEBUG_F( + "Mesh ",meshIndex, + " (", + (pMesh->mName.length ? pMesh->mName.data : "unnamed"), + ") | Verts in: ",pMesh->mNumVertices, + " out: ", + uniqueVertices.size(), + " | ~", + ((pMesh->mNumVertices - uniqueVertices.size()) / (float)pMesh->mNumVertices) * 100.f, + "%" + ); + } + + updateXMeshVertices(pMesh, uniqueVertices); + if (hasAnimMeshes) { + for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { + updateXMeshVertices(pMesh->mAnimMeshes[animMeshIndex], uniqueAnimatedVertices[animMeshIndex]); + } + } + + // adjust the indices in all faces + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + aiFace& face = pMesh->mFaces[a]; + for( unsigned int b = 0; b < face.mNumIndices; b++) { + face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000; + } + } + + // adjust bone vertex weights. + for( int a = 0; a < (int)pMesh->mNumBones; a++) { + aiBone* bone = pMesh->mBones[a]; + std::vector newWeights; + newWeights.reserve( bone->mNumWeights); + + if ( NULL != bone->mWeights ) { + for ( unsigned int b = 0; b < bone->mNumWeights; b++ ) { + const aiVertexWeight& ow = bone->mWeights[ b ]; + // if the vertex is a unique one, translate it + if ( !( replaceIndex[ ow.mVertexId ] & 0x80000000 ) ) { + aiVertexWeight nw; + nw.mVertexId = replaceIndex[ ow.mVertexId ]; + nw.mWeight = ow.mWeight; + newWeights.push_back( nw ); + } + } + } else { + ASSIMP_LOG_ERROR( "X-Export: aiBone shall contain weights, but pointer to them is NULL." ); + } + + if (newWeights.size() > 0) { + // kill the old and replace them with the translated weights + delete [] bone->mWeights; + bone->mNumWeights = (unsigned int)newWeights.size(); + + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; + memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight)); + } + else { + + /* NOTE: + * + * In the algorithm above we're assuming that there are no vertices + * with a different bone weight setup at the same position. That wouldn't + * make sense, but it is not absolutely impossible. SkeletonMeshBuilder + * for example generates such input data if two skeleton points + * share the same position. Again this doesn't make sense but is + * reality for some model formats (MD5 for example uses these special + * nodes as attachment tags for its weapons). + * + * Then it is possible that a bone has no weights anymore .... as a quick + * workaround, we're just removing these bones. If they're animated, + * model geometry might be modified but at least there's no risk of a crash. + */ + delete bone; + --pMesh->mNumBones; + for (unsigned int n = a; n < pMesh->mNumBones; ++n) { + pMesh->mBones[n] = pMesh->mBones[n+1]; + } + + --a; + ASSIMP_LOG_WARN("Removing bone -> no weights remaining"); + } + } + return pMesh->mNumVertices; +} + +#endif // !! ASSIMP_BUILD_NO_JOINVERTICES_PROCESS diff --git a/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.h b/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.h new file mode 100644 index 0000000000..e017ae62db --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.h @@ -0,0 +1,95 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to join identical vertices + on all imported meshes.*/ +#ifndef AI_JOINVERTICESPROCESS_H_INC +#define AI_JOINVERTICESPROCESS_H_INC + +#include "Common/BaseProcess.h" + +#include + +struct aiMesh; + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The JoinVerticesProcess unites identical vertices in all imported meshes. + * By default the importer returns meshes where each face addressed its own + * set of vertices even if that means that identical vertices are stored multiple + * times. The JoinVerticesProcess finds these identical vertices and + * erases all but one of the copies. This usually reduces the number of vertices + * in a mesh by a serious amount and is the standard form to render a mesh. + */ +class ASSIMP_API JoinVerticesProcess : public BaseProcess { +public: + JoinVerticesProcess(); + ~JoinVerticesProcess(); + + // ------------------------------------------------------------------- + /** 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); + + // ------------------------------------------------------------------- + /** 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); +}; + +} // end of namespace Assimp + +#endif // AI_CALCTANGENTSPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.cpp b/thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.cpp new file mode 100644 index 0000000000..d560f19287 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.cpp @@ -0,0 +1,201 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** Implementation of the LimitBoneWeightsProcess post processing step */ + + +#include "LimitBoneWeightsProcess.h" +#include +#include +#include +#include +#include + +using namespace Assimp; + + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +LimitBoneWeightsProcess::LimitBoneWeightsProcess() +{ + mMaxWeights = AI_LMW_MAX_WEIGHTS; +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +LimitBoneWeightsProcess::~LimitBoneWeightsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_LimitBoneWeights) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void LimitBoneWeightsProcess::Execute( aiScene* pScene) { + ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess begin"); + for (unsigned int a = 0; a < pScene->mNumMeshes; ++a ) { + ProcessMesh(pScene->mMeshes[a]); + } + + ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess end"); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp) +{ + // get the current value of the property + this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS); +} + +// ------------------------------------------------------------------------------------------------ +// Unites identical vertices in the given mesh +void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh) +{ + if( !pMesh->HasBones()) + return; + + // collect all bone weights per vertex + typedef std::vector< std::vector< Weight > > WeightsPerVertex; + WeightsPerVertex vertexWeights( pMesh->mNumVertices); + + // collect all weights per vertex + for( unsigned int a = 0; a < pMesh->mNumBones; a++) + { + const aiBone* bone = pMesh->mBones[a]; + for( unsigned int b = 0; b < bone->mNumWeights; b++) + { + const aiVertexWeight& w = bone->mWeights[b]; + vertexWeights[w.mVertexId].push_back( Weight( a, w.mWeight)); + } + } + + unsigned int removed = 0, old_bones = pMesh->mNumBones; + + // now cut the weight count if it exceeds the maximum + bool bChanged = false; + for( WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit) + { + if( vit->size() <= mMaxWeights) + continue; + + bChanged = true; + + // more than the defined maximum -> first sort by weight in descending order. That's + // why we defined the < operator in such a weird way. + std::sort( vit->begin(), vit->end()); + + // now kill everything beyond the maximum count + unsigned int m = static_cast(vit->size()); + vit->erase( vit->begin() + mMaxWeights, vit->end()); + removed += static_cast(m-vit->size()); + + // and renormalize the weights + float sum = 0.0f; + for( std::vector::const_iterator it = vit->begin(); it != vit->end(); ++it ) { + sum += it->mWeight; + } + if( 0.0f != sum ) { + const float invSum = 1.0f / sum; + for( std::vector::iterator it = vit->begin(); it != vit->end(); ++it ) { + it->mWeight *= invSum; + } + } + } + + if (bChanged) { + // rebuild the vertex weight array for all bones + typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone; + WeightsPerBone boneWeights( pMesh->mNumBones); + for( unsigned int a = 0; a < vertexWeights.size(); a++) + { + const std::vector& vw = vertexWeights[a]; + for( std::vector::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 abNoNeed(pMesh->mNumBones,false); + bChanged = false; + + for( unsigned int a = 0; a < pMesh->mNumBones; a++) + { + const std::vector& bw = boneWeights[a]; + aiBone* bone = pMesh->mBones[a]; + + if ( bw.empty() ) + { + abNoNeed[a] = bChanged = true; + continue; + } + + // copy the weight list. should always be less weights than before, so we don't need a new allocation + ai_assert( bw.size() <= bone->mNumWeights); + bone->mNumWeights = static_cast( 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::const_iterator iter = abNoNeed.begin();iter != abNoNeed.end() ;++iter) { + if (*iter) { + delete *ppcSrc; + --pMesh->mNumBones; + } + else *ppcCur++ = *ppcSrc; + ++ppcSrc; + } + } + + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_INFO_F("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones ); + } + } +} diff --git a/thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.h b/thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.h new file mode 100644 index 0000000000..73c2a68d53 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.h @@ -0,0 +1,138 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** 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 "Common/BaseProcess.h" + +// Forward declarations +struct aiMesh; + +class LimitBoneWeightsTest; + +namespace Assimp { + +// NOTE: If you change these limits, don't forget to change the +// corresponding values in all Assimp ports + +// ********************************************************** +// Java: ConfigProperty.java, +// ConfigProperty.DEFAULT_BONE_WEIGHT_LIMIT +// ********************************************************** + +#if (!defined AI_LMW_MAX_WEIGHTS) +# define AI_LMW_MAX_WEIGHTS 0x4 +#endif // !! AI_LMW_MAX_WEIGHTS + +// --------------------------------------------------------------------------- +/** This post processing step limits the number of bones affecting a vertex +* to a certain maximum value. If a vertex is affected by more than that number +* of bones, the bone weight with the least influence on this vertex are removed. +* The other weights on this bone are then renormalized to assure the sum weight +* to be 1. +*/ +class ASSIMP_API LimitBoneWeightsProcess : public BaseProcess { +public: + LimitBoneWeightsProcess(); + ~LimitBoneWeightsProcess(); + + // ------------------------------------------------------------------- + /** 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); + + // ------------------------------------------------------------------- + /** 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); + + // ------------------------------------------------------------------- + /** Describes a bone weight on a vertex */ + struct Weight { + unsigned int mBone; ///< Index of the bone + float mWeight; ///< Weight of that bone on this vertex + Weight() AI_NO_EXCEPT + : mBone(0) + , mWeight(0.0f) { + // empty + } + + Weight( unsigned int pBone, float pWeight) + : mBone(pBone) + , mWeight(pWeight) { + // empty + } + + /** Comparison operator to sort bone weights by descending weight */ + bool operator < (const Weight& pWeight) const { + return mWeight > pWeight.mWeight; + } + }; + + /** Maximum number of bones influencing any single vertex. */ + unsigned int mMaxWeights; +}; + +} // end of namespace Assimp + +#endif // AI_LIMITBONEWEIGHTSPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp new file mode 100644 index 0000000000..50ff5ed93d --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp @@ -0,0 +1,226 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file Implementation of the post processing step "MakeVerboseFormat" +*/ + + +#include "MakeVerboseFormat.h" +#include +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +MakeVerboseFormatProcess::MakeVerboseFormatProcess() +{ + // nothing to do here +} +// ------------------------------------------------------------------------------------------------ +MakeVerboseFormatProcess::~MakeVerboseFormatProcess() +{ + // nothing to do here +} +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void MakeVerboseFormatProcess::Execute( aiScene* pScene) +{ + ai_assert(NULL != pScene); + ASSIMP_LOG_DEBUG("MakeVerboseFormatProcess begin"); + + bool bHas = false; + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + { + if( MakeVerboseFormat( pScene->mMeshes[a])) + bHas = true; + } + if (bHas) { + ASSIMP_LOG_INFO("MakeVerboseFormatProcess finished. There was much work to do ..."); + } else { + ASSIMP_LOG_DEBUG("MakeVerboseFormatProcess. There was nothing to do."); + } + + pScene->mFlags &= ~AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh) +{ + ai_assert(NULL != pcMesh); + + unsigned int iOldNumVertices = pcMesh->mNumVertices; + const unsigned int iNumVerts = pcMesh->mNumFaces*3; + + aiVector3D* pvPositions = new aiVector3D[ iNumVerts ]; + + aiVector3D* pvNormals = NULL; + if (pcMesh->HasNormals()) + { + pvNormals = new aiVector3D[iNumVerts]; + } + aiVector3D* pvTangents = NULL, *pvBitangents = NULL; + if (pcMesh->HasTangentsAndBitangents()) + { + pvTangents = new aiVector3D[iNumVerts]; + pvBitangents = new aiVector3D[iNumVerts]; + } + + aiVector3D* apvTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS] = {0}; + aiColor4D* apvColorSets[AI_MAX_NUMBER_OF_COLOR_SETS] = {0}; + + unsigned int p = 0; + while (pcMesh->HasTextureCoords(p)) + apvTextureCoords[p++] = new aiVector3D[iNumVerts]; + + p = 0; + while (pcMesh->HasVertexColors(p)) + apvColorSets[p++] = new aiColor4D[iNumVerts]; + + // allocate enough memory to hold output bones and vertex weights ... + std::vector* newWeights = new std::vector[pcMesh->mNumBones]; + for (unsigned int i = 0;i < pcMesh->mNumBones;++i) { + newWeights[i].reserve(pcMesh->mBones[i]->mNumWeights*3); + } + + // iterate through all faces and build a clean list + unsigned int iIndex = 0; + for (unsigned int a = 0; a< pcMesh->mNumFaces;++a) + { + aiFace* pcFace = &pcMesh->mFaces[a]; + for (unsigned int q = 0; q < pcFace->mNumIndices;++q,++iIndex) + { + // need to build a clean list of bones, too + for (unsigned int i = 0;i < pcMesh->mNumBones;++i) + { + for (unsigned int a = 0; a < pcMesh->mBones[i]->mNumWeights;a++) + { + const aiVertexWeight& w = pcMesh->mBones[i]->mWeights[a]; + if(pcFace->mIndices[q] == w.mVertexId) + { + aiVertexWeight wNew; + wNew.mVertexId = iIndex; + wNew.mWeight = w.mWeight; + newWeights[i].push_back(wNew); + } + } + } + + pvPositions[iIndex] = pcMesh->mVertices[pcFace->mIndices[q]]; + + if (pcMesh->HasNormals()) + { + pvNormals[iIndex] = pcMesh->mNormals[pcFace->mIndices[q]]; + } + if (pcMesh->HasTangentsAndBitangents()) + { + pvTangents[iIndex] = pcMesh->mTangents[pcFace->mIndices[q]]; + pvBitangents[iIndex] = pcMesh->mBitangents[pcFace->mIndices[q]]; + } + + unsigned int p = 0; + while (pcMesh->HasTextureCoords(p)) + { + apvTextureCoords[p][iIndex] = pcMesh->mTextureCoords[p][pcFace->mIndices[q]]; + ++p; + } + p = 0; + while (pcMesh->HasVertexColors(p)) + { + apvColorSets[p][iIndex] = pcMesh->mColors[p][pcFace->mIndices[q]]; + ++p; + } + pcFace->mIndices[q] = iIndex; + } + } + + + + // build output vertex weights + for (unsigned int i = 0;i < pcMesh->mNumBones;++i) + { + delete [] pcMesh->mBones[i]->mWeights; + if (!newWeights[i].empty()) { + pcMesh->mBones[i]->mWeights = new aiVertexWeight[newWeights[i].size()]; + aiVertexWeight *weightToCopy = &( newWeights[i][0] ); + memcpy(pcMesh->mBones[i]->mWeights, weightToCopy, + sizeof(aiVertexWeight) * newWeights[i].size()); + } else { + pcMesh->mBones[i]->mWeights = NULL; + } + } + delete[] newWeights; + + // delete the old members + delete[] pcMesh->mVertices; + pcMesh->mVertices = pvPositions; + + p = 0; + while (pcMesh->HasTextureCoords(p)) + { + delete[] pcMesh->mTextureCoords[p]; + pcMesh->mTextureCoords[p] = apvTextureCoords[p]; + ++p; + } + p = 0; + while (pcMesh->HasVertexColors(p)) + { + delete[] pcMesh->mColors[p]; + pcMesh->mColors[p] = apvColorSets[p]; + ++p; + } + pcMesh->mNumVertices = iNumVerts; + + if (pcMesh->HasNormals()) + { + delete[] pcMesh->mNormals; + pcMesh->mNormals = pvNormals; + } + if (pcMesh->HasTangentsAndBitangents()) + { + delete[] pcMesh->mTangents; + pcMesh->mTangents = pvTangents; + delete[] pcMesh->mBitangents; + pcMesh->mBitangents = pvBitangents; + } + return (pcMesh->mNumVertices != iOldNumVertices); +} diff --git a/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h new file mode 100644 index 0000000000..1adf8e2f69 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h @@ -0,0 +1,106 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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 "Common/BaseProcess.h" + +struct aiMesh; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** MakeVerboseFormatProcess: Class to convert an asset to the verbose + * format which is expected by most postprocess steps. + * + * This is the inverse of what the "JoinIdenticalVertices" step is doing. + * This step has no official flag (since it wouldn't make sense to run it + * during import). It is intended for applications intending to modify the + * returned aiScene. After this step has been executed, they can execute + * other postprocess steps on the data. The code might also be useful to + * quickly adapt code that doesn't result in a verbose representation of + * the scene data. + * The step has been added because it was required by the viewer, however + * it has been moved to the main library since others might find it + * useful, too. */ +class ASSIMP_API_WINONLY MakeVerboseFormatProcess : public BaseProcess +{ +public: + + + MakeVerboseFormatProcess(); + ~MakeVerboseFormatProcess(); + +public: + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not */ + bool IsActive( unsigned int /*pFlags*/ ) const + { + // NOTE: There is no direct flag that corresponds to + // this postprocess step. + return false; + } + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. */ + void Execute( aiScene* pScene); + + +private: + + //! Apply the postprocess step to a given submesh + bool MakeVerboseFormat (aiMesh* pcMesh); +}; + +} // end of namespace Assimp + +#endif // !!AI_KILLNORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/OptimizeGraph.cpp b/thirdparty/assimp/code/PostProcessing/OptimizeGraph.cpp new file mode 100644 index 0000000000..5db51f58b6 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/OptimizeGraph.cpp @@ -0,0 +1,351 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file OptimizeGraph.cpp + * @brief Implementation of the aiProcess_OptimizGraph step + */ + + +#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS + +#include "OptimizeGraph.h" +#include "ProcessHelper.h" +#include +#include +#include + +using namespace Assimp; + +#define AI_RESERVED_NODE_NAME "$Reserved_And_Evil" + +/* AI_OG_USE_HASHING enables the use of hashing to speed-up std::set lookups. + * The unhashed variant should be faster, except for *very* large data sets + */ +#ifdef AI_OG_USE_HASHING + // Use our standard hashing function to compute the hash +# define AI_OG_GETKEY(str) SuperFastHash(str.data,str.length) +#else + // Otherwise hope that std::string will utilize a static buffer + // for shorter node names. This would avoid endless heap copying. +# define AI_OG_GETKEY(str) std::string(str.data) +#endif + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +OptimizeGraphProcess::OptimizeGraphProcess() +: mScene() +, nodes_in() +, nodes_out() +, count_merged() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +OptimizeGraphProcess::~OptimizeGraphProcess() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const { + return (0 != (pFlags & aiProcess_OptimizeGraph)); +} + +// ------------------------------------------------------------------------------------------------ +// Setup properties for the post-processing step +void OptimizeGraphProcess::SetupProperties(const Importer* pImp) { + // Get value of AI_CONFIG_PP_OG_EXCLUDE_LIST + std::string tmp = pImp->GetPropertyString(AI_CONFIG_PP_OG_EXCLUDE_LIST,""); + AddLockedNodeList(tmp); +} + +// ------------------------------------------------------------------------------------------------ +// Collect new children +void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list& nodes) { + nodes_in += nd->mNumChildren; + + // Process children + std::list child_nodes; + for (unsigned int i = 0; i < nd->mNumChildren; ++i) { + CollectNewChildren(nd->mChildren[i],child_nodes); + nd->mChildren[i] = nullptr; + } + + // Check whether we need this node; if not we can replace it by our own children (warn, danger of incest). + if (locked.find(AI_OG_GETKEY(nd->mName)) == locked.end() ) { + for (std::list::iterator it = child_nodes.begin(); it != child_nodes.end();) { + + if (locked.find(AI_OG_GETKEY((*it)->mName)) == locked.end()) { + (*it)->mTransformation = nd->mTransformation * (*it)->mTransformation; + nodes.push_back(*it); + + it = child_nodes.erase(it); + continue; + } + ++it; + } + + if (nd->mNumMeshes || !child_nodes.empty()) { + nodes.push_back(nd); + } else { + delete nd; /* bye, node */ + return; + } + } else { + + // Retain our current position in the hierarchy + nodes.push_back(nd); + + // Now check for possible optimizations in our list of child nodes. join as many as possible + aiNode* join_master = NULL; + aiMatrix4x4 inv; + + const LockedSetType::const_iterator end = locked.end(); + + std::list join; + for (std::list::iterator it = child_nodes.begin(); it != child_nodes.end();) { + aiNode* child = *it; + if (child->mNumChildren == 0 && locked.find(AI_OG_GETKEY(child->mName)) == end) { + + // There may be no instanced meshes + unsigned int n = 0; + for (; n < child->mNumMeshes;++n) { + if (meshes[child->mMeshes[n]] > 1) { + break; + } + } + if (n == child->mNumMeshes) { + if (!join_master) { + join_master = child; + inv = join_master->mTransformation; + inv.Inverse(); + } else { + child->mTransformation = inv * child->mTransformation ; + + join.push_back(child); + it = child_nodes.erase(it); + continue; + } + } + } + ++it; + } + if (join_master && !join.empty()) { + join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i",count_merged++); + + unsigned int out_meshes = 0; + for (std::list::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::iterator it = join.begin(); it != join.end(); ++it) { + for (unsigned int n = 0; n < (*it)->mNumMeshes; ++n) { + + *tmp = (*it)->mMeshes[n]; + aiMesh* mesh = mScene->mMeshes[*tmp++]; + + // manually move the mesh into the right coordinate system + const aiMatrix3x3 IT = aiMatrix3x3( (*it)->mTransformation ).Inverse().Transpose(); + for (unsigned int a = 0; a < mesh->mNumVertices; ++a) { + + mesh->mVertices[a] *= (*it)->mTransformation; + + if (mesh->HasNormals()) + mesh->mNormals[a] *= IT; + + if (mesh->HasTangentsAndBitangents()) { + mesh->mTangents[a] *= IT; + mesh->mBitangents[a] *= IT; + } + } + } + delete *it; // bye, node + } + delete[] join_master->mMeshes; + join_master->mMeshes = meshes; + join_master->mNumMeshes += out_meshes; + } + } + } + // reassign children if something changed + if (child_nodes.empty() || child_nodes.size() > nd->mNumChildren) { + + delete[] nd->mChildren; + + if (!child_nodes.empty()) { + nd->mChildren = new aiNode*[child_nodes.size()]; + } + else nd->mChildren = nullptr; + } + + nd->mNumChildren = static_cast(child_nodes.size()); + + if (nd->mChildren) { + aiNode** tmp = nd->mChildren; + for (std::list::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) { + aiNode* node = *tmp++ = *it; + node->mParent = nd; + } + } + + nodes_out += static_cast(child_nodes.size()); +} + +// ------------------------------------------------------------------------------------------------ +// Execute the post-processing step on the given scene +void OptimizeGraphProcess::Execute( aiScene* pScene) { + ASSIMP_LOG_DEBUG("OptimizeGraphProcess begin"); + nodes_in = nodes_out = count_merged = 0; + mScene = pScene; + + meshes.resize(pScene->mNumMeshes,0); + FindInstancedMeshes(pScene->mRootNode); + + // build a blacklist of identifiers. If the name of a node matches one of these, we won't touch it + locked.clear(); + for (std::list::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 nodes; + CollectNewChildren (dummy_root,nodes); + + ai_assert(nodes.size() == 1); + + if (dummy_root->mNumChildren == 0) { + pScene->mRootNode = NULL; + throw DeadlyImportError("After optimizing the scene graph, no data remains"); + } + + if (dummy_root->mNumChildren > 1) { + pScene->mRootNode = dummy_root; + + // Keep the dummy node but assign the name of the old root node to it + pScene->mRootNode->mName = prev; + } + else { + + // Remove the dummy root node again. + pScene->mRootNode = dummy_root->mChildren[0]; + + dummy_root->mChildren[0] = NULL; + delete dummy_root; + } + + pScene->mRootNode->mParent = NULL; + if (!DefaultLogger::isNullLogger()) { + if ( nodes_in != nodes_out) { + ASSIMP_LOG_INFO_F("OptimizeGraphProcess finished; Input nodes: ", nodes_in, ", Output nodes: ", nodes_out); + } else { + ASSIMP_LOG_DEBUG("OptimizeGraphProcess finished"); + } + } + meshes.clear(); + locked.clear(); +} + +// ------------------------------------------------------------------------------------------------ +// Build a LUT of all instanced meshes +void OptimizeGraphProcess::FindInstancedMeshes (aiNode* pNode) +{ + for (unsigned int i = 0; i < pNode->mNumMeshes;++i) { + ++meshes[pNode->mMeshes[i]]; + } + + for (unsigned int i = 0; i < pNode->mNumChildren; ++i) + FindInstancedMeshes(pNode->mChildren[i]); +} + +#endif // !! ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS diff --git a/thirdparty/assimp/code/PostProcessing/OptimizeGraph.h b/thirdparty/assimp/code/PostProcessing/OptimizeGraph.h new file mode 100644 index 0000000000..82cc5db3fe --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/OptimizeGraph.h @@ -0,0 +1,140 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file OptimizeGraph.h + * @brief Declares a post processing step to optimize the scenegraph + */ +#ifndef AI_OPTIMIZEGRAPHPROCESS_H_INC +#define AI_OPTIMIZEGRAPHPROCESS_H_INC + +#include "Common/BaseProcess.h" +#include "PostProcessing/ProcessHelper.h" + +#include + +#include + +// Forward declarations +struct aiMesh; + +class OptimizeGraphProcessTest; + +namespace Assimp { + +// ----------------------------------------------------------------------------- +/** @brief Postprocessing step to optimize the scenegraph + * + * The implementation tries to merge nodes, even if they use different + * transformations. Animations are preserved. + * + * @see aiProcess_OptimizeGraph for a detailed description of the + * algorithm being applied. + */ +class OptimizeGraphProcess : public BaseProcess { +public: + OptimizeGraphProcess(); + ~OptimizeGraphProcess(); + + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + /** @brief Add a list of node names to be locked and not modified. + * @param in List of nodes. See #AI_CONFIG_PP_OG_EXCLUDE_LIST for + * format explanations. + */ + inline void AddLockedNodeList(std::string& in) { + ConvertListToStrings (in,locked_nodes); + } + + // ------------------------------------------------------------------- + /** @brief Add another node to be locked and not modified. + * @param name Name to be locked + */ + inline void AddLockedNode(std::string& name) { + locked_nodes.push_back(name); + } + + // ------------------------------------------------------------------- + /** @brief Remove a node from the list of locked nodes. + * @param name Name to be unlocked + */ + inline void RemoveLockedNode(std::string& name) { + locked_nodes.remove(name); + } + +protected: + void CollectNewChildren(aiNode* nd, std::list& nodes); + void FindInstancedMeshes (aiNode* pNode); + +private: +#ifdef AI_OG_USE_HASHING + typedef std::set LockedSetType; +#else + typedef std::set 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 locked_nodes; + + //! Node counters for logging purposes + unsigned int nodes_in,nodes_out, count_merged; + + //! Reference counters for meshes + std::vector meshes; +}; + +} // end of namespace Assimp + +#endif // AI_OPTIMIZEGRAPHPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/OptimizeMeshes.cpp b/thirdparty/assimp/code/PostProcessing/OptimizeMeshes.cpp new file mode 100644 index 0000000000..3f6765f6ca --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/OptimizeMeshes.cpp @@ -0,0 +1,256 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file OptimizeMeshes.cpp + * @brief Implementation of the aiProcess_OptimizeMeshes step + */ + + +#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS + + +#include "OptimizeMeshes.h" +#include "ProcessHelper.h" +#include +#include + +using namespace Assimp; + +static const unsigned int NotSet = 0xffffffff; +static const unsigned int DeadBeef = 0xdeadbeef; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +OptimizeMeshesProcess::OptimizeMeshesProcess() + : mScene() + , pts(false) + , max_verts( NotSet ) + , max_faces( NotSet ) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +OptimizeMeshesProcess::~OptimizeMeshesProcess() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool OptimizeMeshesProcess::IsActive( unsigned int pFlags) const +{ + // Our behaviour needs to be different if the SortByPType or SplitLargeMeshes + // steps are active. Thus we need to query their flags here and store the + // information, although we're breaking const-correctness. + // That's a serious design flaw, consider redesign. + if( 0 != (pFlags & aiProcess_OptimizeMeshes) ) { + pts = (0 != (pFlags & aiProcess_SortByPType)); + max_verts = ( 0 != ( pFlags & aiProcess_SplitLargeMeshes ) ) ? DeadBeef : max_verts; + return true; + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Setup properties for the post-processing step +void OptimizeMeshesProcess::SetupProperties(const Importer* pImp) +{ + if( max_verts == DeadBeef /* magic hack */ ) { + max_faces = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES); + max_verts = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES); + } +} + +// ------------------------------------------------------------------------------------------------ +// Execute step +void OptimizeMeshesProcess::Execute( aiScene* pScene) +{ + const unsigned int num_old = pScene->mNumMeshes; + if (num_old <= 1) { + ASSIMP_LOG_DEBUG("Skipping OptimizeMeshesProcess"); + return; + } + + ASSIMP_LOG_DEBUG("OptimizeMeshesProcess begin"); + mScene = pScene; + + // need to clear persistent members from previous runs + merge_list.resize( 0 ); + output.resize( 0 ); + + // ensure we have the right sizes + merge_list.reserve(pScene->mNumMeshes); + output.reserve(pScene->mNumMeshes); + + // Prepare lookup tables + meshes.resize(pScene->mNumMeshes); + FindInstancedMeshes(pScene->mRootNode); + if( max_verts == DeadBeef ) /* undo the magic hack */ + max_verts = NotSet; + + // ... instanced meshes are immediately processed and added to the output list + for (unsigned int i = 0, n = 0; i < pScene->mNumMeshes;++i) { + meshes[i].vertex_format = GetMeshVFormatUnique(pScene->mMeshes[i]); + + if (meshes[i].instance_cnt > 1 && meshes[i].output_id == NotSet ) { + meshes[i].output_id = n++; + output.push_back(mScene->mMeshes[i]); + } + } + + // and process all nodes in the scenegraph recursively + ProcessNode(pScene->mRootNode); + if (!output.size()) { + throw DeadlyImportError("OptimizeMeshes: No meshes remaining; there's definitely something wrong"); + } + + meshes.resize( 0 ); + ai_assert(output.size() <= num_old); + + mScene->mNumMeshes = static_cast(output.size()); + std::copy(output.begin(),output.end(),mScene->mMeshes); + + if (output.size() != num_old) { + ASSIMP_LOG_DEBUG_F("OptimizeMeshesProcess finished. Input meshes: ", num_old, ", Output meshes: ", pScene->mNumMeshes); + } else { + ASSIMP_LOG_DEBUG( "OptimizeMeshesProcess finished" ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Process meshes for a single node +void OptimizeMeshesProcess::ProcessNode( aiNode* pNode) +{ + for (unsigned int i = 0; i < pNode->mNumMeshes;++i) { + unsigned int& im = pNode->mMeshes[i]; + + if (meshes[im].instance_cnt > 1) { + im = meshes[im].output_id; + } + else { + merge_list.resize( 0 ); + unsigned int verts = 0, faces = 0; + + // Find meshes to merge with us + for (unsigned int a = i+1; a < pNode->mNumMeshes;++a) { + unsigned int am = pNode->mMeshes[a]; + if (meshes[am].instance_cnt == 1 && CanJoin(im,am,verts,faces)) { + + merge_list.push_back(mScene->mMeshes[am]); + verts += mScene->mMeshes[am]->mNumVertices; + faces += mScene->mMeshes[am]->mNumFaces; + + pNode->mMeshes[a] = pNode->mMeshes[pNode->mNumMeshes - 1]; + --pNode->mNumMeshes; + --a; + } + } + + // and merge all meshes which we found, replace the old ones + if (!merge_list.empty()) { + merge_list.push_back(mScene->mMeshes[im]); + + aiMesh* out; + SceneCombiner::MergeMeshes(&out,0,merge_list.begin(),merge_list.end()); + output.push_back(out); + } else { + output.push_back(mScene->mMeshes[im]); + } + im = static_cast(output.size()-1); + } + } + + + for( unsigned int i = 0; i < pNode->mNumChildren; ++i ) { + ProcessNode( pNode->mChildren[ i ] ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Check whether two meshes can be joined +bool OptimizeMeshesProcess::CanJoin ( unsigned int a, unsigned int b, unsigned int verts, unsigned int faces ) +{ + if (meshes[a].vertex_format != meshes[b].vertex_format) + return false; + + aiMesh* ma = mScene->mMeshes[a], *mb = mScene->mMeshes[b]; + + if ((NotSet != max_verts && verts+mb->mNumVertices > max_verts) || + (NotSet != max_faces && faces+mb->mNumFaces > max_faces)) { + return false; + } + + // Never merge unskinned meshes with skinned meshes + if (ma->mMaterialIndex != mb->mMaterialIndex || ma->HasBones() != mb->HasBones()) + return false; + + // Never merge meshes with different kinds of primitives if SortByPType did already + // do its work. We would destroy everything again ... + if (pts && ma->mPrimitiveTypes != mb->mPrimitiveTypes) + return false; + + // If both meshes are skinned, check whether we have many bones defined in both meshes. + // If yes, we can join them. + if (ma->HasBones()) { + // TODO + return false; + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Build a LUT of all instanced meshes +void OptimizeMeshesProcess::FindInstancedMeshes (aiNode* pNode) +{ + for( unsigned int i = 0; i < pNode->mNumMeshes; ++i ) { + ++meshes[ pNode->mMeshes[ i ] ].instance_cnt; + } + + for( unsigned int i = 0; i < pNode->mNumChildren; ++i ) { + FindInstancedMeshes( pNode->mChildren[ i ] ); + } +} + +// ------------------------------------------------------------------------------------------------ + +#endif // !! ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS diff --git a/thirdparty/assimp/code/PostProcessing/OptimizeMeshes.h b/thirdparty/assimp/code/PostProcessing/OptimizeMeshes.h new file mode 100644 index 0000000000..dec4ab52de --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/OptimizeMeshes.h @@ -0,0 +1,186 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file OptimizeMeshes.h + * @brief Declares a post processing step to join meshes, if possible + */ +#ifndef AI_OPTIMIZEMESHESPROCESS_H_INC +#define AI_OPTIMIZEMESHESPROCESS_H_INC + +#include "Common/BaseProcess.h" + +#include + +#include + +struct aiMesh; +struct aiNode; +class OptimizeMeshesProcessTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** @brief Postprocessing step to optimize mesh usage + * + * The implementation looks for meshes that could be joined and joins them. + * Usually this will reduce the number of drawcalls. + * + * @note Instanced meshes are currently not processed. + */ +class OptimizeMeshesProcess : public BaseProcess { +public: + /// @brief The class constructor. + OptimizeMeshesProcess(); + + /// @brief The class destructor. + ~OptimizeMeshesProcess(); + + /** @brief Internal utility to store additional mesh info + */ + struct MeshInfo { + MeshInfo() AI_NO_EXCEPT + : instance_cnt(0) + , vertex_format(0) + , output_id(0xffffffff) { + // empty + } + + //! Number of times this mesh is referenced + unsigned int instance_cnt; + + //! Vertex format id + unsigned int vertex_format; + + //! Output ID + unsigned int output_id; + }; + +public: + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + void SetupProperties(const Importer* pImp); + + + // ------------------------------------------------------------------- + /** @brief Specify whether you want meshes with different + * primitive types to be merged as well. + * + * IsActive() sets this property automatically to true if the + * aiProcess_SortByPType flag is found. + */ + void EnablePrimitiveTypeSorting(bool enable) { + pts = enable; + } + + // Getter + bool IsPrimitiveTypeSortingEnabled () const { + return pts; + } + + + // ------------------------------------------------------------------- + /** @brief Specify a maximum size of a single output mesh. + * + * If a single input mesh already exceeds this limit, it won't + * be split. + * @param verts Maximum number of vertices per mesh + * @param faces Maximum number of faces per mesh + */ + void SetPreferredMeshSizeLimit (unsigned int verts, unsigned int faces) + { + max_verts = verts; + max_faces = faces; + } + + +protected: + + // ------------------------------------------------------------------- + /** @brief Do the actual optimization on all meshes of this node + * @param pNode Node we're working with + */ + void ProcessNode( aiNode* pNode); + + // ------------------------------------------------------------------- + /** @brief Returns true if b can be joined with a + * + * @param verts Number of output verts up to now + * @param faces Number of output faces up to now + */ + bool CanJoin ( unsigned int a, unsigned int b, + unsigned int verts, unsigned int faces ); + + // ------------------------------------------------------------------- + /** @brief Find instanced meshes, for the moment we're excluding + * them from all optimizations + */ + void FindInstancedMeshes (aiNode* pNode); + +private: + + //! Scene we're working with + aiScene* mScene; + + //! Per mesh info + std::vector meshes; + + //! Output meshes + std::vector output; + + //! @see EnablePrimitiveTypeSorting + mutable bool pts; + + //! @see SetPreferredMeshSizeLimit + mutable unsigned int max_verts,max_faces; + + //! Temporary storage + std::vector merge_list; +}; + +} // end of namespace Assimp + +#endif // AI_CALCTANGENTSPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/PretransformVertices.cpp b/thirdparty/assimp/code/PostProcessing/PretransformVertices.cpp new file mode 100644 index 0000000000..52001a0578 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/PretransformVertices.cpp @@ -0,0 +1,728 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file PretransformVertices.cpp + * @brief Implementation of the "PretransformVertices" post processing step +*/ + + +#include "PretransformVertices.h" +#include "ProcessHelper.h" +#include +#include + +using namespace Assimp; + +// some array offsets +#define AI_PTVS_VERTEX 0x0 +#define AI_PTVS_FACE 0x1 + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +PretransformVertices::PretransformVertices() +: configKeepHierarchy (false) +, configNormalize(false) +, configTransform(false) +, configTransformation() +, mConfigPointCloud( false ) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +PretransformVertices::~PretransformVertices() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool PretransformVertices::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_PreTransformVertices) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Setup import configuration +void PretransformVertices::SetupProperties(const Importer* pImp) +{ + // Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE, + // AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION + configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY,0)); + configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE,0)); + configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION,0)); + + configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4()); + + mConfigPointCloud = pImp->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); +} + +// ------------------------------------------------------------------------------------------------ +// Count the number of nodes +unsigned int PretransformVertices::CountNodes( aiNode* pcNode ) +{ + unsigned int iRet = 1; + for (unsigned int i = 0;i < pcNode->mNumChildren;++i) + { + iRet += CountNodes(pcNode->mChildren[i]); + } + return iRet; +} + +// ------------------------------------------------------------------------------------------------ +// Get a bitwise combination identifying the vertex format of a mesh +unsigned int PretransformVertices::GetMeshVFormat( aiMesh* pcMesh ) +{ + // the vertex format is stored in aiMesh::mBones for later retrieval. + // there isn't a good reason to compute it a few hundred times + // from scratch. The pointer is unused as animations are lost + // during PretransformVertices. + if (pcMesh->mBones) + return (unsigned int)(uint64_t)pcMesh->mBones; + + + const unsigned int iRet = GetMeshVFormatUnique(pcMesh); + + // store the value for later use + pcMesh->mBones = (aiBone**)(uint64_t)iRet; + return iRet; +} + +// ------------------------------------------------------------------------------------------------ +// Count the number of vertices in the whole scene and a given +// material index +void PretransformVertices::CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, + unsigned int iVFormat, unsigned int* piFaces, unsigned int* piVertices) +{ + for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) + { + aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ]; + if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh)) + { + *piVertices += pcMesh->mNumVertices; + *piFaces += pcMesh->mNumFaces; + } + } + for (unsigned int i = 0;i < pcNode->mNumChildren;++i) + { + CountVerticesAndFaces(pcScene,pcNode->mChildren[i],iMat, + iVFormat,piFaces,piVertices); + } +} + +// ------------------------------------------------------------------------------------------------ +// Collect vertex/face data +void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, + unsigned int iVFormat, aiMesh* pcMeshOut, + unsigned int aiCurrent[2], unsigned int* num_refs) +{ + // No need to multiply if there's no transformation + const bool identity = pcNode->mTransformation.IsIdentity(); + for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) + { + aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ]; + if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh)) + { + // Decrement mesh reference counter + unsigned int& num_ref = num_refs[pcNode->mMeshes[i]]; + ai_assert(0 != num_ref); + --num_ref; + // Save the name of the last mesh + if (num_ref==0) + { + pcMeshOut->mName = pcMesh->mName; + } + + if (identity) { + // copy positions without modifying them + ::memcpy(pcMeshOut->mVertices + aiCurrent[AI_PTVS_VERTEX], + pcMesh->mVertices, + pcMesh->mNumVertices * sizeof(aiVector3D)); + + if (iVFormat & 0x2) { + // copy normals without modifying them + ::memcpy(pcMeshOut->mNormals + aiCurrent[AI_PTVS_VERTEX], + pcMesh->mNormals, + pcMesh->mNumVertices * sizeof(aiVector3D)); + } + if (iVFormat & 0x4) + { + // copy tangents without modifying them + ::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX], + pcMesh->mTangents, + pcMesh->mNumVertices * sizeof(aiVector3D)); + // copy bitangents without modifying them + ::memcpy(pcMeshOut->mBitangents + aiCurrent[AI_PTVS_VERTEX], + pcMesh->mBitangents, + pcMesh->mNumVertices * sizeof(aiVector3D)); + } + } + else + { + // copy positions, transform them to worldspace + for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { + pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n]; + } + aiMatrix4x4 mWorldIT = pcNode->mTransformation; + mWorldIT.Inverse().Transpose(); + + // TODO: implement Inverse() for aiMatrix3x3 + aiMatrix3x3 m = aiMatrix3x3(mWorldIT); + + if (iVFormat & 0x2) + { + // copy normals, transform them to worldspace + for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { + pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] = + (m * pcMesh->mNormals[n]).Normalize(); + } + } + if (iVFormat & 0x4) + { + // copy tangents and bitangents, transform them to worldspace + for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { + pcMeshOut->mTangents [aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mTangents[n]).Normalize(); + pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mBitangents[n]).Normalize(); + } + } + } + unsigned int p = 0; + while (iVFormat & (0x100 << p)) + { + // copy texture coordinates + memcpy(pcMeshOut->mTextureCoords[p] + aiCurrent[AI_PTVS_VERTEX], + pcMesh->mTextureCoords[p], + pcMesh->mNumVertices * sizeof(aiVector3D)); + ++p; + } + p = 0; + while (iVFormat & (0x1000000 << p)) + { + // copy vertex colors + memcpy(pcMeshOut->mColors[p] + aiCurrent[AI_PTVS_VERTEX], + pcMesh->mColors[p], + pcMesh->mNumVertices * sizeof(aiColor4D)); + ++p; + } + // now we need to copy all faces. since we will delete the source mesh afterwards, + // we don't need to reallocate the array of indices except if this mesh is + // referenced multiple times. + for (unsigned int planck = 0;planck < pcMesh->mNumFaces;++planck) + { + aiFace& f_src = pcMesh->mFaces[planck]; + aiFace& f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck]; + + const unsigned int num_idx = f_src.mNumIndices; + + f_dst.mNumIndices = num_idx; + + unsigned int* pi; + if (!num_ref) { /* if last time the mesh is referenced -> no reallocation */ + pi = f_dst.mIndices = f_src.mIndices; + + // offset all vertex indices + for (unsigned int hahn = 0; hahn < num_idx;++hahn){ + pi[hahn] += aiCurrent[AI_PTVS_VERTEX]; + } + } + else { + pi = f_dst.mIndices = new unsigned int[num_idx]; + + // copy and offset all vertex indices + for (unsigned int hahn = 0; hahn < num_idx;++hahn){ + pi[hahn] = f_src.mIndices[hahn] + aiCurrent[AI_PTVS_VERTEX]; + } + } + + // Update the mPrimitiveTypes member of the mesh + switch (pcMesh->mFaces[planck].mNumIndices) + { + case 0x1: + pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + case 0x2: + pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + case 0x3: + pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + default: + pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + break; + }; + } + aiCurrent[AI_PTVS_VERTEX] += pcMesh->mNumVertices; + aiCurrent[AI_PTVS_FACE] += pcMesh->mNumFaces; + } + } + + // append all children of us + for (unsigned int i = 0;i < pcNode->mNumChildren;++i) { + CollectData(pcScene,pcNode->mChildren[i],iMat, + iVFormat,pcMeshOut,aiCurrent,num_refs); + } +} + +// ------------------------------------------------------------------------------------------------ +// Get a list of all vertex formats that occur for a given material index +// The output list contains duplicate elements +void PretransformVertices::GetVFormatList( aiScene* pcScene, unsigned int iMat, + std::list& aiOut) +{ + for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) + { + aiMesh* pcMesh = pcScene->mMeshes[ i ]; + if (iMat == pcMesh->mMaterialIndex) { + aiOut.push_back(GetMeshVFormat(pcMesh)); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Compute the absolute transformation matrices of each node +void PretransformVertices::ComputeAbsoluteTransform( aiNode* pcNode ) +{ + if (pcNode->mParent) { + pcNode->mTransformation = pcNode->mParent->mTransformation*pcNode->mTransformation; + } + + for (unsigned int i = 0;i < pcNode->mNumChildren;++i) { + ComputeAbsoluteTransform(pcNode->mChildren[i]); + } +} + +// ------------------------------------------------------------------------------------------------ +// Apply the node transformation to a mesh +void PretransformVertices::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat) +{ + // Check whether we need to transform the coordinates at all + if (!mat.IsIdentity()) { + + if (mesh->HasPositions()) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mVertices[i] = mat * mesh->mVertices[i]; + } + } + if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) { + aiMatrix4x4 mWorldIT = mat; + mWorldIT.Inverse().Transpose(); + + // TODO: implement Inverse() for aiMatrix3x3 + aiMatrix3x3 m = aiMatrix3x3(mWorldIT); + + if (mesh->HasNormals()) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize(); + } + } + if (mesh->HasTangentsAndBitangents()) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize(); + mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize(); + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Simple routine to build meshes in worldspace, no further optimization +void PretransformVertices::BuildWCSMeshes(std::vector& out, aiMesh** in, + unsigned int numIn, aiNode* node) +{ + // NOTE: + // aiMesh::mNumBones store original source mesh, or UINT_MAX if not a copy + // aiMesh::mBones store reference to abs. transform we multiplied with + + // process meshes + for (unsigned int i = 0; i < node->mNumMeshes;++i) { + aiMesh* mesh = in[node->mMeshes[i]]; + + // check whether we can operate on this mesh + if (!mesh->mBones || *reinterpret_cast(mesh->mBones) == node->mTransformation) { + // yes, we can. + mesh->mBones = reinterpret_cast (&node->mTransformation); + mesh->mNumBones = UINT_MAX; + } + else { + + // try to find us in the list of newly created meshes + for (unsigned int n = 0; n < out.size(); ++n) { + aiMesh* ctz = out[n]; + if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast(ctz->mBones) == node->mTransformation) { + + // ok, use this one. Update node mesh index + node->mMeshes[i] = numIn + n; + } + } + if (node->mMeshes[i] < numIn) { + // Worst case. Need to operate on a full copy of the mesh + ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms"); + aiMesh* ntz; + + const unsigned int tmp = mesh->mNumBones; // + mesh->mNumBones = 0; + SceneCombiner::Copy(&ntz,mesh); + mesh->mNumBones = tmp; + + ntz->mNumBones = node->mMeshes[i]; + ntz->mBones = reinterpret_cast (&node->mTransformation); + + out.push_back(ntz); + + node->mMeshes[i] = static_cast(numIn + out.size() - 1); + } + } + } + + // call children + for (unsigned int i = 0; i < node->mNumChildren;++i) + BuildWCSMeshes(out,in,numIn,node->mChildren[i]); +} + +// ------------------------------------------------------------------------------------------------ +// Reset transformation matrices to identity +void PretransformVertices::MakeIdentityTransform(aiNode* nd) +{ + nd->mTransformation = aiMatrix4x4(); + + // call children + for (unsigned int i = 0; i < nd->mNumChildren;++i) + MakeIdentityTransform(nd->mChildren[i]); +} + +// ------------------------------------------------------------------------------------------------ +// Build reference counters for all meshes +void PretransformVertices::BuildMeshRefCountArray(aiNode* nd, unsigned int * refs) +{ + for (unsigned int i = 0; i< nd->mNumMeshes;++i) + refs[nd->mMeshes[i]]++; + + // call children + for (unsigned int i = 0; i < nd->mNumChildren;++i) + BuildMeshRefCountArray(nd->mChildren[i],refs); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void PretransformVertices::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("PretransformVerticesProcess begin"); + + // Return immediately if we have no meshes + if (!pScene->mNumMeshes) + return; + + const unsigned int iOldMeshes = pScene->mNumMeshes; + const unsigned int iOldAnimationChannels = pScene->mNumAnimations; + const unsigned int iOldNodes = CountNodes(pScene->mRootNode); + + if(configTransform) { + pScene->mRootNode->mTransformation = configTransformation; + } + + // first compute absolute transformation matrices for all nodes + ComputeAbsoluteTransform(pScene->mRootNode); + + // Delete aiMesh::mBones for all meshes. The bones are + // removed during this step and we need the pointer as + // temporary storage + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { + aiMesh* mesh = pScene->mMeshes[i]; + + for (unsigned int a = 0; a < mesh->mNumBones;++a) + delete mesh->mBones[a]; + + delete[] mesh->mBones; + mesh->mBones = NULL; + } + + // now build a list of output meshes + std::vector apcOutMeshes; + + // Keep scene hierarchy? It's an easy job in this case ... + // we go on and transform all meshes, if one is referenced by nodes + // with different absolute transformations a depth copy of the mesh + // is required. + if( configKeepHierarchy ) { + + // Hack: store the matrix we're transforming a mesh with in aiMesh::mBones + BuildWCSMeshes(apcOutMeshes,pScene->mMeshes,pScene->mNumMeshes, pScene->mRootNode); + + // ... if new meshes have been generated, append them to the end of the scene + if (apcOutMeshes.size() > 0) { + aiMesh** npp = new aiMesh*[pScene->mNumMeshes + apcOutMeshes.size()]; + + memcpy(npp,pScene->mMeshes,sizeof(aiMesh*)*pScene->mNumMeshes); + memcpy(npp+pScene->mNumMeshes,&apcOutMeshes[0],sizeof(aiMesh*)*apcOutMeshes.size()); + + pScene->mNumMeshes += static_cast(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( 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 aiVFormats; + + std::vector 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::const_iterator j = aiVFormats.begin();j != aiVFormats.end();++j) { + unsigned int iVertices = 0; + unsigned int iFaces = 0; + CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices); + if (0 != iFaces && 0 != iVertices) + { + apcOutMeshes.push_back(new aiMesh()); + aiMesh* pcMesh = apcOutMeshes.back(); + pcMesh->mNumFaces = iFaces; + pcMesh->mNumVertices = iVertices; + pcMesh->mFaces = new aiFace[iFaces]; + pcMesh->mVertices = new aiVector3D[iVertices]; + pcMesh->mMaterialIndex = i; + if ((*j) & 0x2)pcMesh->mNormals = new aiVector3D[iVertices]; + if ((*j) & 0x4) + { + pcMesh->mTangents = new aiVector3D[iVertices]; + pcMesh->mBitangents = new aiVector3D[iVertices]; + } + iFaces = 0; + while ((*j) & (0x100 << iFaces)) + { + pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices]; + if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3; + else pcMesh->mNumUVComponents[iFaces] = 2; + iFaces++; + } + iFaces = 0; + while ((*j) & (0x1000000 << iFaces)) + pcMesh->mColors[iFaces++] = new aiColor4D[iVertices]; + + // fill the mesh ... + unsigned int aiTemp[2] = {0,0}; + CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp,&s[0]); + } + } + } + + // If no meshes are referenced in the node graph it is possible that we get no output meshes. + if (apcOutMeshes.empty()) { + + throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by any nodes"); + } + else + { + // now delete all meshes in the scene and build a new mesh list + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + { + aiMesh* mesh = pScene->mMeshes[i]; + mesh->mNumBones = 0; + mesh->mBones = NULL; + + // we're reusing the face index arrays. avoid destruction + for (unsigned int a = 0; a < mesh->mNumFaces; ++a) { + mesh->mFaces[a].mNumIndices = 0; + mesh->mFaces[a].mIndices = NULL; + } + + delete mesh; + + // Invalidate the contents of the old mesh array. We will most + // likely have less output meshes now, so the last entries of + // the mesh array are not overridden. We set them to NULL to + // make sure the developer gets notified when his application + // attempts to access these fields ... + mesh = NULL; + } + + // It is impossible that we have more output meshes than + // input meshes, so we can easily reuse the old mesh array + pScene->mNumMeshes = (unsigned int)apcOutMeshes.size(); + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { + pScene->mMeshes[i] = apcOutMeshes[i]; + } + } + } + + // remove all animations from the scene + for (unsigned int i = 0; i < pScene->mNumAnimations;++i) + delete pScene->mAnimations[i]; + delete[] pScene->mAnimations; + + pScene->mAnimations = NULL; + pScene->mNumAnimations = 0; + + // --- we need to keep all cameras and lights + for (unsigned int i = 0; i < pScene->mNumCameras;++i) + { + aiCamera* cam = pScene->mCameras[i]; + const aiNode* nd = pScene->mRootNode->FindNode(cam->mName); + ai_assert(NULL != nd); + + // multiply all properties of the camera with the absolute + // transformation of the corresponding node + cam->mPosition = nd->mTransformation * cam->mPosition; + cam->mLookAt = aiMatrix3x3( nd->mTransformation ) * cam->mLookAt; + cam->mUp = aiMatrix3x3( nd->mTransformation ) * cam->mUp; + } + + for (unsigned int i = 0; i < pScene->mNumLights;++i) + { + aiLight* l = pScene->mLights[i]; + const aiNode* nd = pScene->mRootNode->FindNode(l->mName); + ai_assert(NULL != nd); + + // multiply all properties of the camera with the absolute + // transformation of the corresponding node + l->mPosition = nd->mTransformation * l->mPosition; + l->mDirection = aiMatrix3x3( nd->mTransformation ) * l->mDirection; + l->mUp = aiMatrix3x3( nd->mTransformation ) * l->mUp; + } + + if( !configKeepHierarchy ) { + + // now delete all nodes in the scene and build a new + // flat node graph with a root node and some level 1 children + aiNode* newRoot = new aiNode(); + newRoot->mName = pScene->mRootNode->mName; + delete pScene->mRootNode; + pScene->mRootNode = newRoot; + + if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras) + { + pScene->mRootNode->mNumMeshes = 1; + pScene->mRootNode->mMeshes = new unsigned int[1]; + pScene->mRootNode->mMeshes[0] = 0; + } + else + { + pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras; + aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren]; + + // generate mesh nodes + for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes) + { + aiNode* pcNode = new aiNode(); + *nodes = pcNode; + pcNode->mParent = pScene->mRootNode; + pcNode->mName = pScene->mMeshes[i]->mName; + + // setup mesh indices + pcNode->mNumMeshes = 1; + pcNode->mMeshes = new unsigned int[1]; + pcNode->mMeshes[0] = i; + } + // generate light nodes + for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes) + { + aiNode* pcNode = new aiNode(); + *nodes = pcNode; + pcNode->mParent = pScene->mRootNode; + pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u",i); + pScene->mLights[i]->mName = pcNode->mName; + } + // generate camera nodes + for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes) + { + aiNode* pcNode = new aiNode(); + *nodes = pcNode; + pcNode->mParent = pScene->mRootNode; + pcNode->mName.length = ::ai_snprintf(pcNode->mName.data,MAXLEN,"cam_%u",i); + pScene->mCameras[i]->mName = pcNode->mName; + } + } + } + else { + // ... and finally set the transformation matrix of all nodes to identity + MakeIdentityTransform(pScene->mRootNode); + } + + if (configNormalize) { + // compute the boundary of all meshes + aiVector3D min,max; + MinMaxChooser ()(min,max); + + for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { + aiMesh* m = pScene->mMeshes[a]; + for (unsigned int i = 0; i < m->mNumVertices;++i) { + min = std::min(m->mVertices[i],min); + max = std::max(m->mVertices[i],max); + } + } + + // find the dominant axis + aiVector3D d = max-min; + const ai_real div = std::max(d.x,std::max(d.y,d.z))*ai_real( 0.5); + + d = min + d * (ai_real)0.5; + for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { + aiMesh* m = pScene->mMeshes[a]; + for (unsigned int i = 0; i < m->mNumVertices;++i) { + m->mVertices[i] = (m->mVertices[i]-d)/div; + } + } + } + + // print statistics + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished"); + + ASSIMP_LOG_INFO_F("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (", + CountNodes(pScene->mRootNode) ," output nodes)" ); + ASSIMP_LOG_INFO_F("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras." ); + ASSIMP_LOG_INFO_F("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")"); + } +} diff --git a/thirdparty/assimp/code/PostProcessing/PretransformVertices.h b/thirdparty/assimp/code/PostProcessing/PretransformVertices.h new file mode 100644 index 0000000000..b2982951e0 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/PretransformVertices.h @@ -0,0 +1,166 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file 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 "Common/BaseProcess.h" + +#include + +#include +#include + +// Forward declarations +struct aiNode; + +class PretransformVerticesTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** The PretransformVertices pre-transforms all vertices in the node tree + * and removes the whole graph. The output is a list of meshes, one for + * each material. +*/ +class ASSIMP_API PretransformVertices : public BaseProcess { +public: + PretransformVertices (); + ~PretransformVertices (); + + // ------------------------------------------------------------------- + // Check whether step is active + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + // Execute step on a given scene + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + // Setup import settings + void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + /** @brief Toggle the 'keep hierarchy' option + * @param keep true for keep configuration. + */ + void KeepHierarchy(bool keep) { + configKeepHierarchy = keep; + } + + // ------------------------------------------------------------------- + /** @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& 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& out, aiMesh** in, + unsigned int numIn, aiNode* node); + + // ------------------------------------------------------------------- + // Apply the node transformation to a mesh + void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat); + + // ------------------------------------------------------------------- + // Reset transformation matrices to identity + void MakeIdentityTransform(aiNode* nd); + + // ------------------------------------------------------------------- + // Build reference counters for all meshes + void BuildMeshRefCountArray(aiNode* nd, unsigned int * refs); + + //! Configuration option: keep scene hierarchy as long as possible + bool configKeepHierarchy; + bool configNormalize; + bool configTransform; + aiMatrix4x4 configTransformation; + bool mConfigPointCloud; +}; + +} // end of namespace Assimp + +#endif // !!AI_GENFACENORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/ProcessHelper.cpp b/thirdparty/assimp/code/PostProcessing/ProcessHelper.cpp new file mode 100644 index 0000000000..59869fdff7 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ProcessHelper.cpp @@ -0,0 +1,443 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/// @file ProcessHelper.cpp +/** Implement shared utility functions for postprocessing steps */ + + +#include "ProcessHelper.h" + + +#include + +namespace Assimp { + +// ------------------------------------------------------------------------------- +void ConvertListToStrings(const std::string& in, std::list& out) +{ + const char* s = in.c_str(); + while (*s) { + SkipSpacesAndLineEnd(&s); + if (*s == '\'') { + const char* base = ++s; + while (*s != '\'') { + ++s; + if (*s == '\0') { + ASSIMP_LOG_ERROR("ConvertListToString: String list is ill-formatted"); + return; + } + } + out.push_back(std::string(base,(size_t)(s-base))); + ++s; + } + else { + out.push_back(GetNextToken(s)); + } + } +} + +// ------------------------------------------------------------------------------- +void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max, + const aiMatrix4x4& m) +{ + min = aiVector3D ( ai_real( 10e10 ), ai_real( 10e10 ), ai_real( 10e10 ) ); + max = aiVector3D ( ai_real( -10e10 ), ai_real( -10e10 ), ai_real( -10e10 ) ); + for (unsigned int i = 0;i < mesh->mNumVertices;++i) + { + const aiVector3D v = m * mesh->mVertices[i]; + min = std::min(v,min); + max = std::max(v,max); + } +} + +// ------------------------------------------------------------------------------- +void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max) +{ + ArrayBounds(mesh->mVertices,mesh->mNumVertices, min,max); + out = min + (max-min)*(ai_real)0.5; +} + +// ------------------------------------------------------------------------------- +void FindSceneCenter (aiScene* scene, aiVector3D& out, aiVector3D& min, aiVector3D& max) { + if ( NULL == scene ) { + return; + } + + if ( 0 == scene->mNumMeshes ) { + return; + } + FindMeshCenter(scene->mMeshes[0], out, min, max); + for (unsigned int i = 1; i < scene->mNumMeshes; ++i) { + aiVector3D tout, tmin, tmax; + FindMeshCenter(scene->mMeshes[i], tout, tmin, tmax); + if (min[0] > tmin[0]) min[0] = tmin[0]; + if (min[1] > tmin[1]) min[1] = tmin[1]; + if (min[2] > tmin[2]) min[2] = tmin[2]; + if (max[0] < tmax[0]) max[0] = tmax[0]; + if (max[1] < tmax[1]) max[1] = tmax[1]; + if (max[2] < tmax[2]) max[2] = tmax[2]; + } + out = min + (max-min)*(ai_real)0.5; +} + + +// ------------------------------------------------------------------------------- +void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min, + aiVector3D& max, const aiMatrix4x4& m) +{ + FindAABBTransformed(mesh,min,max,m); + out = min + (max-min)*(ai_real)0.5; +} + +// ------------------------------------------------------------------------------- +void FindMeshCenter (aiMesh* mesh, aiVector3D& out) +{ + aiVector3D min,max; + FindMeshCenter(mesh,out,min,max); +} + +// ------------------------------------------------------------------------------- +void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, + const aiMatrix4x4& m) +{ + aiVector3D min,max; + FindMeshCenterTransformed(mesh,out,min,max,m); +} + +// ------------------------------------------------------------------------------- +ai_real ComputePositionEpsilon(const aiMesh* pMesh) +{ + const ai_real epsilon = ai_real( 1e-4 ); + + // calculate the position bounds so we have a reliable epsilon to check position differences against + aiVector3D minVec, maxVec; + ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,minVec,maxVec); + return (maxVec - minVec).Length() * epsilon; +} + +// ------------------------------------------------------------------------------- +ai_real ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num) +{ + ai_assert( NULL != pMeshes ); + + const ai_real epsilon = ai_real( 1e-4 ); + + // calculate the position bounds so we have a reliable epsilon to check position differences against + aiVector3D minVec, maxVec, mi, ma; + MinMaxChooser()(minVec,maxVec); + + for (size_t a = 0; a < num; ++a) { + const aiMesh* pMesh = pMeshes[a]; + ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,mi,ma); + + minVec = std::min(minVec,mi); + maxVec = std::max(maxVec,ma); + } + return (maxVec - minVec).Length() * epsilon; +} + + +// ------------------------------------------------------------------------------- +unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh) +{ + ai_assert(NULL != pcMesh); + + // FIX: the hash may never be 0. Otherwise a comparison against + // nullptr could be successful + unsigned int iRet = 1; + + // normals + if (pcMesh->HasNormals())iRet |= 0x2; + // tangents and bitangents + if (pcMesh->HasTangentsAndBitangents())iRet |= 0x4; + +#ifdef BOOST_STATIC_ASSERT + BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS); + BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS); +#endif + + // texture coordinates + unsigned int p = 0; + while (pcMesh->HasTextureCoords(p)) + { + iRet |= (0x100 << p); + if (3 == pcMesh->mNumUVComponents[p]) + iRet |= (0x10000 << p); + + ++p; + } + // vertex colors + p = 0; + while (pcMesh->HasVertexColors(p))iRet |= (0x1000000 << p++); + return iRet; +} + +// ------------------------------------------------------------------------------- +VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) +{ + if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) { + return NULL; + } + + VertexWeightTable* avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices]; + for (unsigned int i = 0; i < pMesh->mNumBones;++i) { + + aiBone* bone = pMesh->mBones[i]; + for (unsigned int a = 0; a < bone->mNumWeights;++a) { + const aiVertexWeight& weight = bone->mWeights[a]; + avPerVertexWeights[weight.mVertexId].push_back( std::pair(i,weight.mWeight) ); + } + } + return avPerVertexWeights; +} + + +// ------------------------------------------------------------------------------- +const char* TextureTypeToString(aiTextureType in) +{ + switch (in) + { + case aiTextureType_NONE: + return "n/a"; + case aiTextureType_DIFFUSE: + return "Diffuse"; + case aiTextureType_SPECULAR: + return "Specular"; + case aiTextureType_AMBIENT: + return "Ambient"; + case aiTextureType_EMISSIVE: + return "Emissive"; + case aiTextureType_OPACITY: + return "Opacity"; + case aiTextureType_NORMALS: + return "Normals"; + case aiTextureType_HEIGHT: + return "Height"; + case aiTextureType_SHININESS: + return "Shininess"; + case aiTextureType_DISPLACEMENT: + return "Displacement"; + case aiTextureType_LIGHTMAP: + return "Lightmap"; + case aiTextureType_REFLECTION: + return "Reflection"; + case aiTextureType_UNKNOWN: + return "Unknown"; + default: + break; + } + + ai_assert(false); + return "BUG"; +} + +// ------------------------------------------------------------------------------- +const char* MappingTypeToString(aiTextureMapping in) +{ + switch (in) + { + case aiTextureMapping_UV: + return "UV"; + case aiTextureMapping_BOX: + return "Box"; + case aiTextureMapping_SPHERE: + return "Sphere"; + case aiTextureMapping_CYLINDER: + return "Cylinder"; + case aiTextureMapping_PLANE: + return "Plane"; + case aiTextureMapping_OTHER: + return "Other"; + default: + break; + } + + ai_assert(false); + return "BUG"; +} + + +// ------------------------------------------------------------------------------- +aiMesh* MakeSubmesh(const aiMesh *pMesh, const std::vector &subMeshFaces, unsigned int subFlags) +{ + aiMesh *oMesh = new aiMesh(); + std::vector vMap(pMesh->mNumVertices,UINT_MAX); + + size_t numSubVerts = 0; + size_t numSubFaces = subMeshFaces.size(); + + for(unsigned int i=0;imFaces[subMeshFaces[i]]; + + for(unsigned int j=0;j(numSubVerts++); + } + } + } + + oMesh->mName = pMesh->mName; + + oMesh->mMaterialIndex = pMesh->mMaterialIndex; + oMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes; + + // create all the arrays for this mesh if the old mesh contained them + + oMesh->mNumFaces = static_cast(subMeshFaces.size()); + oMesh->mNumVertices = static_cast(numSubVerts); + oMesh->mVertices = new aiVector3D[numSubVerts]; + if( pMesh->HasNormals() ) { + oMesh->mNormals = new aiVector3D[numSubVerts]; + } + + if( pMesh->HasTangentsAndBitangents() ) { + oMesh->mTangents = new aiVector3D[numSubVerts]; + oMesh->mBitangents = new aiVector3D[numSubVerts]; + } + + for( size_t a = 0; pMesh->HasTextureCoords(static_cast(a)) ; ++a ) { + oMesh->mTextureCoords[a] = new aiVector3D[numSubVerts]; + oMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a]; + } + + for( size_t a = 0; pMesh->HasVertexColors( static_cast(a)); ++a ) { + oMesh->mColors[a] = new aiColor4D[numSubVerts]; + } + + // and copy over the data, generating faces with linear indices along the way + oMesh->mFaces = new aiFace[numSubFaces]; + + for(unsigned int a = 0; a < numSubFaces; ++a ) { + + const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]]; + aiFace& dstFace = oMesh->mFaces[a]; + dstFace.mNumIndices = srcFace.mNumIndices; + dstFace.mIndices = new unsigned int[dstFace.mNumIndices]; + + // accumulate linearly all the vertices of the source face + for( size_t b = 0; b < dstFace.mNumIndices; ++b ) { + dstFace.mIndices[b] = vMap[srcFace.mIndices[b]]; + } + } + + for(unsigned int srcIndex = 0; srcIndex < pMesh->mNumVertices; ++srcIndex ) { + unsigned int nvi = vMap[srcIndex]; + if(nvi==UINT_MAX) { + continue; + } + + oMesh->mVertices[nvi] = pMesh->mVertices[srcIndex]; + if( pMesh->HasNormals() ) { + oMesh->mNormals[nvi] = pMesh->mNormals[srcIndex]; + } + + if( pMesh->HasTangentsAndBitangents() ) { + oMesh->mTangents[nvi] = pMesh->mTangents[srcIndex]; + oMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex]; + } + for( size_t c = 0, cc = pMesh->GetNumUVChannels(); c < cc; ++c ) { + oMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex]; + } + for( size_t c = 0, cc = pMesh->GetNumColorChannels(); c < cc; ++c ) { + oMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex]; + } + } + + if(~subFlags&AI_SUBMESH_FLAGS_SANS_BONES) { + std::vector subBones(pMesh->mNumBones,0); + + for(unsigned int a=0;amNumBones;++a) { + const aiBone* bone = pMesh->mBones[a]; + + for(unsigned int b=0;bmNumWeights;b++) { + unsigned int v = vMap[bone->mWeights[b].mVertexId]; + + if(v!=UINT_MAX) { + subBones[a]++; + } + } + } + + for(unsigned int a=0;amNumBones;++a) { + if(subBones[a]>0) { + oMesh->mNumBones++; + } + } + + if(oMesh->mNumBones) { + oMesh->mBones = new aiBone*[oMesh->mNumBones](); + unsigned int nbParanoia = oMesh->mNumBones; + + oMesh->mNumBones = 0; //rewind + + for(unsigned int a=0;amNumBones;++a) { + if(subBones[a]==0) { + continue; + } + aiBone *newBone = new aiBone; + oMesh->mBones[oMesh->mNumBones++] = newBone; + + const aiBone* bone = pMesh->mBones[a]; + + newBone->mName = bone->mName; + newBone->mOffsetMatrix = bone->mOffsetMatrix; + newBone->mWeights = new aiVertexWeight[subBones[a]]; + + for(unsigned int b=0;bmNumWeights;b++) { + const unsigned int v = vMap[bone->mWeights[b].mVertexId]; + + if(v!=UINT_MAX) { + aiVertexWeight w(v,bone->mWeights[b].mWeight); + newBone->mWeights[newBone->mNumWeights++] = w; + } + } + } + + ai_assert(nbParanoia==oMesh->mNumBones); + (void)nbParanoia; // remove compiler warning on release build + } + } + + return oMesh; +} + +} // namespace Assimp diff --git a/thirdparty/assimp/code/PostProcessing/ProcessHelper.h b/thirdparty/assimp/code/PostProcessing/ProcessHelper.h new file mode 100644 index 0000000000..0afcc41420 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ProcessHelper.h @@ -0,0 +1,386 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_PROCESS_HELPER_H_INCLUDED +#define AI_PROCESS_HELPER_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +#include +#include "Common/BaseProcess.h" +#include + +#include + +// ------------------------------------------------------------------------------- +// Some extensions to std namespace. Mainly std::min and std::max for all +// flat data types in the aiScene. They're used to quickly determine the +// min/max bounds of data arrays. +#ifdef __cplusplus +namespace std { + + // std::min for aiVector3D + template + inline ::aiVector3t min (const ::aiVector3t& a, const ::aiVector3t& b) { + return ::aiVector3t (min(a.x,b.x),min(a.y,b.y),min(a.z,b.z)); + } + + // std::max for aiVector3t + template + inline ::aiVector3t max (const ::aiVector3t& a, const ::aiVector3t& b) { + return ::aiVector3t (max(a.x,b.x),max(a.y,b.y),max(a.z,b.z)); + } + + // std::min for aiVector2t + template + inline ::aiVector2t min (const ::aiVector2t& a, const ::aiVector2t& b) { + return ::aiVector2t (min(a.x,b.x),min(a.y,b.y)); + } + + // std::max for aiVector2t + template + inline ::aiVector2t max (const ::aiVector2t& a, const ::aiVector2t& b) { + return ::aiVector2t (max(a.x,b.x),max(a.y,b.y)); + } + + // std::min for aiColor4D + template + inline ::aiColor4t min (const ::aiColor4t& a, const ::aiColor4t& b) { + return ::aiColor4t (min(a.r,b.r),min(a.g,b.g),min(a.b,b.b),min(a.a,b.a)); + } + + // std::max for aiColor4D + template + inline ::aiColor4t max (const ::aiColor4t& a, const ::aiColor4t& b) { + return ::aiColor4t (max(a.r,b.r),max(a.g,b.g),max(a.b,b.b),max(a.a,b.a)); + } + + + // std::min for aiQuaterniont + template + inline ::aiQuaterniont min (const ::aiQuaterniont& a, const ::aiQuaterniont& b) { + return ::aiQuaterniont (min(a.w,b.w),min(a.x,b.x),min(a.y,b.y),min(a.z,b.z)); + } + + // std::max for aiQuaterniont + template + inline ::aiQuaterniont max (const ::aiQuaterniont& a, const ::aiQuaterniont& b) { + return ::aiQuaterniont (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 for all supported Ts +template +struct MinMaxChooser; + +template <> struct MinMaxChooser { + void operator ()(float& min,float& max) { + max = -1e10f; + min = 1e10f; +}}; +template <> struct MinMaxChooser { + void operator ()(double& min,double& max) { + max = -1e10; + min = 1e10; +}}; +template <> struct MinMaxChooser { + void operator ()(unsigned int& min,unsigned int& max) { + max = 0; + min = (1u<<(sizeof(unsigned int)*8-1)); +}}; + +template struct MinMaxChooser< aiVector3t > { + void operator ()(aiVector3t& min,aiVector3t& max) { + max = aiVector3t(-1e10f,-1e10f,-1e10f); + min = aiVector3t( 1e10f, 1e10f, 1e10f); +}}; +template struct MinMaxChooser< aiVector2t > { + void operator ()(aiVector2t& min,aiVector2t& max) { + max = aiVector2t(-1e10f,-1e10f); + min = aiVector2t( 1e10f, 1e10f); + }}; +template struct MinMaxChooser< aiColor4t > { + void operator ()(aiColor4t& min,aiColor4t& max) { + max = aiColor4t(-1e10f,-1e10f,-1e10f,-1e10f); + min = aiColor4t( 1e10f, 1e10f, 1e10f, 1e10f); +}}; + +template struct MinMaxChooser< aiQuaterniont > { + void operator ()(aiQuaterniont& min,aiQuaterniont& max) { + max = aiQuaterniont(-1e10f,-1e10f,-1e10f,-1e10f); + min = aiQuaterniont( 1e10f, 1e10f, 1e10f, 1e10f); +}}; + +template <> struct MinMaxChooser { + void operator ()(aiVectorKey& min,aiVectorKey& max) { + MinMaxChooser()(min.mTime,max.mTime); + MinMaxChooser()(min.mValue,max.mValue); +}}; +template <> struct MinMaxChooser { + void operator ()(aiQuatKey& min,aiQuatKey& max) { + MinMaxChooser()(min.mTime,max.mTime); + MinMaxChooser()(min.mValue,max.mValue); +}}; + +template <> struct MinMaxChooser { + void operator ()(aiVertexWeight& min,aiVertexWeight& max) { + MinMaxChooser()(min.mVertexId,max.mVertexId); + MinMaxChooser()(min.mWeight,max.mWeight); +}}; + +// ------------------------------------------------------------------------------- +/** @brief Find the min/max values of an array of Ts + * @param in Input array + * @param size Number of elements to process + * @param[out] min minimum value + * @param[out] max maximum value + */ +template +inline void ArrayBounds(const T* in, unsigned int size, T& min, T& max) +{ + MinMaxChooser ()(min,max); + for (unsigned int i = 0; i < size;++i) { + min = std::min(in[i],min); + max = std::max(in[i],max); + } +} + + +// ------------------------------------------------------------------------------- +/** Little helper function to calculate the quadratic difference + * of two colours. + * @param pColor1 First color + * @param pColor2 second color + * @return Quadratic color difference */ +inline ai_real GetColorDifference( const aiColor4D& pColor1, const aiColor4D& pColor2) +{ + const aiColor4D c (pColor1.r - pColor2.r, pColor1.g - pColor2.g, pColor1.b - pColor2.b, pColor1.a - pColor2.a); + return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a; +} + + +// ------------------------------------------------------------------------------- +/** @brief Extract single strings from a list of identifiers + * @param in Input string list. + * @param out Receives a list of clean output strings + * @sdee #AI_CONFIG_PP_OG_EXCLUDE_LIST */ +void ConvertListToStrings(const std::string& in, std::list& out); + + +// ------------------------------------------------------------------------------- +/** @brief Compute the AABB of a mesh after applying a given transform + * @param mesh Input mesh + * @param[out] min Receives minimum transformed vertex + * @param[out] max Receives maximum transformed vertex + * @param m Transformation matrix to be applied */ +void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max, const aiMatrix4x4& m); + + +// ------------------------------------------------------------------------------- +/** @brief Helper function to determine the 'real' center of a mesh + * + * That is the center of its axis-aligned bounding box. + * @param mesh Input mesh + * @param[out] min Minimum vertex of the mesh + * @param[out] max maximum vertex of the mesh + * @param[out] out Center point */ +void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max); + +// ------------------------------------------------------------------------------- +/** @brief Helper function to determine the 'real' center of a scene + * + * That is the center of its axis-aligned bounding box. + * @param scene Input scene + * @param[out] min Minimum vertex of the scene + * @param[out] max maximum vertex of the scene + * @param[out] out Center point */ +void FindSceneCenter (aiScene* scene, aiVector3D& out, aiVector3D& min, aiVector3D& max); + + +// ------------------------------------------------------------------------------- +// Helper function to determine the 'real' center of a mesh after applying a given transform +void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,aiVector3D& max, const aiMatrix4x4& m); + + +// ------------------------------------------------------------------------------- +// Helper function to determine the 'real' center of a mesh +void FindMeshCenter (aiMesh* mesh, aiVector3D& out); + + +// ------------------------------------------------------------------------------- +// Helper function to determine the 'real' center of a mesh after applying a given transform +void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,const aiMatrix4x4& m); + + +// ------------------------------------------------------------------------------- +// Compute a good epsilon value for position comparisons on a mesh +ai_real ComputePositionEpsilon(const aiMesh* pMesh); + + +// ------------------------------------------------------------------------------- +// Compute a good epsilon value for position comparisons on a array of meshes +ai_real ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num); + + +// ------------------------------------------------------------------------------- +// Compute an unique value for the vertex format of a mesh +unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh); + + +// defs for ComputeVertexBoneWeightTable() +typedef std::pair PerVertexWeight; +typedef std::vector VertexWeightTable; + +// ------------------------------------------------------------------------------- +// Compute a per-vertex bone weight table +VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh); + + +// ------------------------------------------------------------------------------- +// Get a string for a given aiTextureType +const char* TextureTypeToString(aiTextureType in); + + +// ------------------------------------------------------------------------------- +// Get a string for a given aiTextureMapping +const char* MappingTypeToString(aiTextureMapping in); + + +// flags for MakeSubmesh() +#define AI_SUBMESH_FLAGS_SANS_BONES 0x1 + +// ------------------------------------------------------------------------------- +// Split a mesh given a list of faces to be contained in the sub mesh +aiMesh* MakeSubmesh(const aiMesh *superMesh, const std::vector &subMeshFaces, unsigned int subFlags); + +// ------------------------------------------------------------------------------- +// Utility postprocess step to share the spatial sort tree between +// all steps which use it to speedup its computations. +class ComputeSpatialSortProcess : public BaseProcess +{ + bool IsActive( unsigned int pFlags) const + { + return NULL != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace | + aiProcess_GenNormals | aiProcess_JoinIdenticalVertices)); + } + + void Execute( aiScene* pScene) + { + typedef std::pair _Type; + ASSIMP_LOG_DEBUG("Generate spatially-sorted vertex cache"); + + std::vector<_Type>* p = new std::vector<_Type>(pScene->mNumMeshes); + std::vector<_Type>::iterator it = p->begin(); + + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i, ++it) { + aiMesh* mesh = pScene->mMeshes[i]; + _Type& blubb = *it; + blubb.first.Fill(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D)); + blubb.second = ComputePositionEpsilon(mesh); + } + + shared->AddProperty(AI_SPP_SPATIAL_SORT,p); + } +}; + +// ------------------------------------------------------------------------------- +// ... and the same again to cleanup the whole stuff +class DestroySpatialSortProcess : public BaseProcess +{ + bool IsActive( unsigned int pFlags) const + { + return NULL != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace | + aiProcess_GenNormals | aiProcess_JoinIdenticalVertices)); + } + + void Execute( aiScene* /*pScene*/) + { + shared->RemoveProperty(AI_SPP_SPATIAL_SORT); + } +}; + + + +} // ! namespace Assimp +#endif // !! AI_PROCESS_HELPER_H_INCLUDED diff --git a/thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.cpp b/thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.cpp new file mode 100644 index 0000000000..49ec8f5c47 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.cpp @@ -0,0 +1,221 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file RemoveRedundantMaterials.cpp + * @brief Implementation of the "RemoveRedundantMaterials" post processing step +*/ + +// internal headers + +#include "RemoveRedundantMaterials.h" +#include +#include "ProcessHelper.h" +#include "Material/MaterialSystem.h" +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +RemoveRedundantMatsProcess::RemoveRedundantMatsProcess() +: mConfigFixedMaterials() { + // 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 + mConfigFixedMaterials = pImp->GetPropertyString(AI_CONFIG_PP_RRM_EXCLUDE_LIST,""); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void RemoveRedundantMatsProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess begin"); + + unsigned int redundantRemoved = 0, unreferencedRemoved = 0; + if (pScene->mNumMaterials) + { + // Find out which materials are referenced by meshes + std::vector 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 (mConfigFixedMaterials.length()) { + + std::list strings; + ConvertListToStrings(mConfigFixedMaterials,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::const_iterator it = std::find(strings.begin(), strings.end(), name.data); + if (it != strings.end()) { + + // Our brilliant 'salt': A single material property with ~ as first + // character to mark it as internal and temporary. + const int dummy = 1; + ((aiMaterial*)mat)->AddProperty(&dummy,1,"~RRM.UniqueMaterial",0,0); + + // Keep this material even if no mesh references it + abReferenced[i] = true; + ASSIMP_LOG_DEBUG_F( "Found positive match in exclusion list: \'", name.data, "\'"); + } + } + } + } + + // TODO: re-implement this algorithm to work in-place + unsigned int *aiMappingTable = new unsigned int[pScene->mNumMaterials]; + for ( unsigned int i=0; imNumMaterials; i++ ) { + aiMappingTable[ i ] = 0; + } + unsigned int iNewNum = 0; + + // Iterate through all materials and calculate a hash for them + // store all hashes in a list and so a quick search whether + // we do already have a specific hash. This allows us to + // determine which materials are identical. + uint32_t *aiHashes = new uint32_t[ pScene->mNumMaterials ];; + for (unsigned int i = 0; i < pScene->mNumMaterials;++i) + { + // No mesh is referencing this material, remove it. + if (!abReferenced[i]) { + ++unreferencedRemoved; + delete pScene->mMaterials[i]; + pScene->mMaterials[i] = nullptr; + continue; + } + + // Check all previously mapped materials for a matching hash. + // On a match we can delete this material and just make it ref to the same index. + uint32_t me = aiHashes[i] = ComputeMaterialHash(pScene->mMaterials[i]); + for (unsigned int a = 0; a < i;++a) + { + if (abReferenced[a] && me == aiHashes[a]) { + ++redundantRemoved; + me = 0; + aiMappingTable[i] = aiMappingTable[a]; + delete pScene->mMaterials[i]; + pScene->mMaterials[i] = nullptr; + break; + } + } + // This is a new material that is referenced, add to the map. + if (me) { + aiMappingTable[i] = iNewNum++; + } + } + // If the new material count differs from the original, + // we need to rebuild the material list and remap mesh material indexes. + if (iNewNum != pScene->mNumMaterials) { + ai_assert(iNewNum > 0); + aiMaterial** ppcMaterials = new aiMaterial*[iNewNum]; + ::memset(ppcMaterials,0,sizeof(void*)*iNewNum); + for (unsigned int p = 0; p < pScene->mNumMaterials;++p) + { + // if the material is not referenced ... remove it + if (!abReferenced[p]) { + continue; + } + + // generate new names for modified materials that had no names + const unsigned int idx = aiMappingTable[p]; + if (ppcMaterials[idx]) { + aiString sz; + if( ppcMaterials[idx]->Get(AI_MATKEY_NAME, sz) != AI_SUCCESS ) { + sz.length = ::ai_snprintf(sz.data,MAXLEN,"JoinedMaterial_#%u",p); + ((aiMaterial*)ppcMaterials[idx])->AddProperty(&sz,AI_MATKEY_NAME); + } + } else { + ppcMaterials[idx] = pScene->mMaterials[p]; + } + } + // update all material indices + for (unsigned int p = 0; p < pScene->mNumMeshes;++p) { + aiMesh* mesh = pScene->mMeshes[p]; + ai_assert( NULL!=mesh ); + mesh->mMaterialIndex = aiMappingTable[mesh->mMaterialIndex]; + } + // delete the old material list + delete[] pScene->mMaterials; + pScene->mMaterials = ppcMaterials; + pScene->mNumMaterials = iNewNum; + } + // delete temporary storage + delete[] aiHashes; + delete[] aiMappingTable; + } + if (redundantRemoved == 0 && unreferencedRemoved == 0) + { + ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess finished "); + } + else + { + ASSIMP_LOG_INFO_F("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", + unreferencedRemoved, " unused materials."); + } +} diff --git a/thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.h b/thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.h new file mode 100644 index 0000000000..1f32a0abfb --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.h @@ -0,0 +1,103 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file RemoveRedundantMaterials.h + * @brief Defines a post processing step to remove redundant materials + */ +#ifndef AI_REMOVEREDUNDANTMATERIALS_H_INC +#define AI_REMOVEREDUNDANTMATERIALS_H_INC + +#include "Common/BaseProcess.h" +#include + +class RemoveRedundantMatsTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** RemoveRedundantMatsProcess: Post-processing step to remove redundant + * materials from the imported scene. + */ +class ASSIMP_API RemoveRedundantMatsProcess : public BaseProcess { +public: + /// The default class constructor. + RemoveRedundantMatsProcess(); + + /// The class destructor. + ~RemoveRedundantMatsProcess(); + + // ------------------------------------------------------------------- + // 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 (inmutable) materials + * @param fixed See #AI_CONFIG_PP_RRM_EXCLUDE_LIST + */ + void SetFixedMaterialsString(const std::string& fixed = "") { + mConfigFixedMaterials = fixed; + } + + // ------------------------------------------------------------------- + /** @brief Get list of fixed (inmutable) materials + * @return See #AI_CONFIG_PP_RRM_EXCLUDE_LIST + */ + const std::string& GetFixedMaterialsString() const { + return mConfigFixedMaterials; + } + +private: + //! Configuration option: list of all fixed materials + std::string mConfigFixedMaterials; +}; + +} // end of namespace Assimp + +#endif // !!AI_REMOVEREDUNDANTMATERIALS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/RemoveVCProcess.cpp b/thirdparty/assimp/code/PostProcessing/RemoveVCProcess.cpp new file mode 100644 index 0000000000..99fd47a3aa --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/RemoveVCProcess.cpp @@ -0,0 +1,337 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file Implementation of the post processing step to remove + * any parts of the mesh structure from the imported data. +*/ + + +#include "RemoveVCProcess.h" +#include +#include +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +RemoveVCProcess::RemoveVCProcess() : + configDeleteFlags() + , mScene() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +RemoveVCProcess::~RemoveVCProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool RemoveVCProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_RemoveComponent) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Small helper function to delete all elements in a T** aray using delete +template +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& childsOfParent,bool root) +{ + bool b = false; + + std::list 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::iterator it = mine.begin(), end = mine.end(); + it != end; ++it) + { + *ptr++ = *it; + } + delete[] node->mChildren; + node->mChildren = children; + return false; + } + } + return b; +} +#endif + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void RemoveVCProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("RemoveVCProcess begin"); + bool bHas = false; //,bMasked = false; + + mScene = pScene; + + // handle animations + if ( configDeleteFlags & aiComponent_ANIMATIONS) + { + + bHas = true; + ArrayDelete(pScene->mAnimations,pScene->mNumAnimations); + } + + // handle textures + if ( configDeleteFlags & aiComponent_TEXTURES) + { + bHas = true; + ArrayDelete(pScene->mTextures,pScene->mNumTextures); + } + + // handle materials + if ( configDeleteFlags & aiComponent_MATERIALS && pScene->mNumMaterials) + { + bHas = true; + for (unsigned int i = 1;i < pScene->mNumMaterials;++i) + delete pScene->mMaterials[i]; + + pScene->mNumMaterials = 1; + aiMaterial* helper = (aiMaterial*) pScene->mMaterials[0]; + ai_assert(NULL != helper); + helper->Clear(); + + // gray + aiColor3D clr(0.6f,0.6f,0.6f); + helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE); + + // add a small ambient color value + clr = aiColor3D(0.05f,0.05f,0.05f); + helper->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT); + + aiString s; + s.Set("Dummy_MaterialsRemoved"); + helper->AddProperty(&s,AI_MATKEY_NAME); + } + + // handle light sources + if ( configDeleteFlags & aiComponent_LIGHTS) + { + bHas = true; + ArrayDelete(pScene->mLights,pScene->mNumLights); + } + + // handle camneras + if ( configDeleteFlags & aiComponent_CAMERAS) + { + bHas = true; + ArrayDelete(pScene->mCameras,pScene->mNumCameras); + } + + // handle meshes + if (configDeleteFlags & aiComponent_MESHES) + { + bHas = true; + ArrayDelete(pScene->mMeshes,pScene->mNumMeshes); + } + else + { + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + { + if( ProcessMesh( pScene->mMeshes[a])) + bHas = true; + } + } + + + // now check whether the result is still a full scene + if (!pScene->mNumMeshes || !pScene->mNumMaterials) + { + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + ASSIMP_LOG_DEBUG("Setting AI_SCENE_FLAGS_INCOMPLETE flag"); + + // If we have no meshes anymore we should also clear another flag ... + if (!pScene->mNumMeshes) + pScene->mFlags &= ~AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; + } + + if (bHas) { + ASSIMP_LOG_INFO("RemoveVCProcess finished. Data structure cleanup has been done."); + } else { + ASSIMP_LOG_DEBUG("RemoveVCProcess finished. Nothing to be done ..."); + } +} + +// ------------------------------------------------------------------------------------------------ +// Setup configuration properties for the step +void RemoveVCProcess::SetupProperties(const Importer* pImp) +{ + configDeleteFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS,0x0); + if (!configDeleteFlags) + { + ASSIMP_LOG_WARN("RemoveVCProcess: AI_CONFIG_PP_RVC_FLAGS is zero."); + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh) +{ + bool ret = false; + + // if all materials have been deleted let the material + // index of the mesh point to the created default material + if ( configDeleteFlags & aiComponent_MATERIALS) + pMesh->mMaterialIndex = 0; + + // handle normals + if (configDeleteFlags & aiComponent_NORMALS && pMesh->mNormals) + { + delete[] pMesh->mNormals; + pMesh->mNormals = NULL; + ret = true; + } + + // handle tangents and bitangents + if (configDeleteFlags & aiComponent_TANGENTS_AND_BITANGENTS && pMesh->mTangents) + { + delete[] pMesh->mTangents; + pMesh->mTangents = NULL; + + delete[] pMesh->mBitangents; + pMesh->mBitangents = NULL; + ret = true; + } + + // handle texture coordinates + bool b = (0 != (configDeleteFlags & aiComponent_TEXCOORDS)); + for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++real) + { + if (!pMesh->mTextureCoords[i])break; + if (configDeleteFlags & aiComponent_TEXCOORDSn(real) || b) + { + delete [] pMesh->mTextureCoords[i]; + pMesh->mTextureCoords[i] = NULL; + ret = true; + + if (!b) + { + // collapse the rest of the array + for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) + pMesh->mTextureCoords[a-1] = pMesh->mTextureCoords[a]; + + pMesh->mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS-1] = NULL; + continue; + } + } + ++i; + } + + // handle vertex colors + b = (0 != (configDeleteFlags & aiComponent_COLORS)); + for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_COLOR_SETS; ++real) + { + if (!pMesh->mColors[i])break; + if (configDeleteFlags & aiComponent_COLORSn(i) || b) + { + delete [] pMesh->mColors[i]; + pMesh->mColors[i] = NULL; + ret = true; + + if (!b) + { + // collapse the rest of the array + for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) + pMesh->mColors[a-1] = pMesh->mColors[a]; + + pMesh->mColors[AI_MAX_NUMBER_OF_COLOR_SETS-1] = NULL; + continue; + } + } + ++i; + } + + // handle bones + if (configDeleteFlags & aiComponent_BONEWEIGHTS && pMesh->mBones) + { + ArrayDelete(pMesh->mBones,pMesh->mNumBones); + ret = true; + } + return ret; +} diff --git a/thirdparty/assimp/code/PostProcessing/RemoveVCProcess.h b/thirdparty/assimp/code/PostProcessing/RemoveVCProcess.h new file mode 100644 index 0000000000..7bb21a8330 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/RemoveVCProcess.h @@ -0,0 +1,124 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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 "Common/BaseProcess.h" + +#include + +class RemoveVCProcessTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** RemoveVCProcess: Class to exclude specific parts of the data structure + * from further processing by removing them, +*/ +class ASSIMP_API RemoveVCProcess : public BaseProcess { +public: + /// The default class constructor. + RemoveVCProcess(); + + /// The class destructor. + ~RemoveVCProcess(); + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + virtual void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + /** Manually setup the configuration flags for the step + * + * @param Bitwise combination of the #aiComponent enumerated values. + */ + void SetDeleteFlags(unsigned int f) + { + configDeleteFlags = f; + } + + // ------------------------------------------------------------------- + /** Query the current configuration. + */ + unsigned int GetDeleteFlags() const + { + return configDeleteFlags; + } + +private: + + bool ProcessMesh (aiMesh* pcMesh); + + /** Configuration flag + */ + unsigned int configDeleteFlags; + + /** The scene we're working with + */ + aiScene* mScene; +}; + +// --------------------------------------------------------------------------- + +} // end of namespace Assimp + +#endif // !!AI_REMOVEVCPROCESS_H_INCLUDED diff --git a/thirdparty/assimp/code/PostProcessing/ScaleProcess.cpp b/thirdparty/assimp/code/PostProcessing/ScaleProcess.cpp new file mode 100644 index 0000000000..6d458c4b11 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ScaleProcess.cpp @@ -0,0 +1,103 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS + +#include "ScaleProcess.h" + +#include +#include + +namespace Assimp { + +ScaleProcess::ScaleProcess() +: BaseProcess() +, mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) { + // empty +} + +ScaleProcess::~ScaleProcess() { + // empty +} + +void ScaleProcess::setScale( ai_real scale ) { + mScale = scale; +} + +ai_real ScaleProcess::getScale() const { + return mScale; +} + +bool ScaleProcess::IsActive( unsigned int pFlags ) const { + return ( pFlags & aiProcess_GlobalScale ) != 0; +} + +void ScaleProcess::SetupProperties( const Importer* pImp ) { + mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 0 ); +} + +void ScaleProcess::Execute( aiScene* pScene ) { + if ( nullptr == pScene ) { + return; + } + + if ( nullptr == pScene->mRootNode ) { + return; + } + + traverseNodes( pScene->mRootNode ); +} + +void ScaleProcess::traverseNodes( aiNode *node ) { + applyScaling( node ); +} + +void ScaleProcess::applyScaling( aiNode *currentNode ) { + if ( nullptr != currentNode ) { + currentNode->mTransformation.a1 = currentNode->mTransformation.a1 * mScale; + currentNode->mTransformation.b2 = currentNode->mTransformation.b2 * mScale; + currentNode->mTransformation.c3 = currentNode->mTransformation.c3 * mScale; + } +} + +} // Namespace Assimp + +#endif // !! ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS diff --git a/thirdparty/assimp/code/PostProcessing/ScaleProcess.h b/thirdparty/assimp/code/PostProcessing/ScaleProcess.h new file mode 100644 index 0000000000..2567378759 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ScaleProcess.h @@ -0,0 +1,88 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include "Common/BaseProcess.h" + +struct aiNode; + +#if (!defined AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT) +# define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f +#endif // !! AI_DEBONE_THRESHOLD + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** ScaleProcess: Class to rescale the whole model. +*/ +class ASSIMP_API ScaleProcess : public BaseProcess { +public: + /// The default class constructor. + ScaleProcess(); + + /// The class destructor. + virtual ~ScaleProcess(); + + /// Will set the scale manually. + void setScale( ai_real scale ); + + /// Returns the current scaling value. + ai_real getScale() const; + + /// Overwritten, @see BaseProcess + virtual bool IsActive( unsigned int pFlags ) const; + + /// Overwritten, @see BaseProcess + virtual void SetupProperties( const Importer* pImp ); + + /// Overwritten, @see BaseProcess + virtual void Execute( aiScene* pScene ); + +private: + void traverseNodes( aiNode *currentNode ); + void applyScaling( aiNode *currentNode ); + +private: + ai_real mScale; +}; + +} // Namespace Assimp diff --git a/thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.cpp b/thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.cpp new file mode 100644 index 0000000000..be8405a17b --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.cpp @@ -0,0 +1,403 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the DeterminePTypeHelperProcess and + * SortByPTypeProcess post-process steps. +*/ + + + +// internal headers +#include "ProcessHelper.h" +#include "SortByPTypeProcess.h" +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +SortByPTypeProcess::SortByPTypeProcess() +: mConfigRemoveMeshes( 0 ) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// 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) +{ + mConfigRemoveMeshes = pImp->GetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE,0); +} + +// ------------------------------------------------------------------------------------------------ +// Update changed meshes in all nodes +void UpdateNodes(const std::vector& replaceMeshIndex, aiNode* node) +{ + if (node->mNumMeshes) + { + unsigned int newSize = 0; + for (unsigned int m = 0; m< node->mNumMeshes; ++m) + { + unsigned int add = node->mMeshes[m]<<2; + for (unsigned int i = 0; i < 4;++i) + { + if (UINT_MAX != replaceMeshIndex[add+i])++newSize; + } + } + if (!newSize) + { + delete[] node->mMeshes; + node->mNumMeshes = 0; + node->mMeshes = NULL; + } + else + { + // Try to reuse the old array if possible + unsigned int* newMeshes = (newSize > node->mNumMeshes + ? new unsigned int[newSize] : node->mMeshes); + + for (unsigned int m = 0; m< node->mNumMeshes; ++m) + { + unsigned int add = node->mMeshes[m]<<2; + for (unsigned int i = 0; i < 4;++i) + { + if (UINT_MAX != replaceMeshIndex[add+i]) + *newMeshes++ = replaceMeshIndex[add+i]; + } + } + if (newSize > node->mNumMeshes) + delete[] node->mMeshes; + + node->mMeshes = newMeshes-(node->mNumMeshes = newSize); + } + } + + // call all subnodes recursively + for (unsigned int m = 0; m < node->mNumChildren; ++m) + UpdateNodes(replaceMeshIndex,node->mChildren[m]); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SortByPTypeProcess::Execute( aiScene* pScene) { + if ( 0 == pScene->mNumMeshes) { + ASSIMP_LOG_DEBUG("SortByPTypeProcess skipped, there are no meshes"); + return; + } + + ASSIMP_LOG_DEBUG("SortByPTypeProcess begin"); + + unsigned int aiNumMeshesPerPType[4] = {0,0,0,0}; + + std::vector outMeshes; + outMeshes.reserve(pScene->mNumMeshes<<1u); + + bool bAnyChanges = false; + + std::vector replaceMeshIndex(pScene->mNumMeshes*4,UINT_MAX); + std::vector::iterator meshIdx = replaceMeshIndex.begin(); + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + aiMesh* const mesh = pScene->mMeshes[i]; + ai_assert(0 != mesh->mPrimitiveTypes); + + // if there's just one primitive type in the mesh there's nothing to do for us + unsigned int num = 0; + if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT) { + ++aiNumMeshesPerPType[0]; + ++num; + } + if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE) { + ++aiNumMeshesPerPType[1]; + ++num; + } + if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) { + ++aiNumMeshesPerPType[2]; + ++num; + } + if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON) { + ++aiNumMeshesPerPType[3]; + ++num; + } + + if (1 == num) { + if (!(mConfigRemoveMeshes & mesh->mPrimitiveTypes)) { + *meshIdx = static_cast( outMeshes.size() ); + outMeshes.push_back(mesh); + } else { + delete mesh; + pScene->mMeshes[ i ] = nullptr; + bAnyChanges = true; + } + + meshIdx += 4; + continue; + } + bAnyChanges = true; + + // reuse our current mesh arrays for the submesh + // with the largest number of primitives + unsigned int aiNumPerPType[4] = {0,0,0,0}; + aiFace* pFirstFace = mesh->mFaces; + aiFace* const pLastFace = pFirstFace + mesh->mNumFaces; + + unsigned int numPolyVerts = 0; + for (;pFirstFace != pLastFace; ++pFirstFace) { + if (pFirstFace->mNumIndices <= 3) + ++aiNumPerPType[pFirstFace->mNumIndices-1]; + else + { + ++aiNumPerPType[3]; + numPolyVerts += pFirstFace-> mNumIndices; + } + } + + VertexWeightTable* avw = ComputeVertexBoneWeightTable(mesh); + for (unsigned int real = 0; real < 4; ++real,++meshIdx) + { + if ( !aiNumPerPType[real] || mConfigRemoveMeshes & (1u << real)) + { + continue; + } + + *meshIdx = (unsigned int) outMeshes.size(); + outMeshes.push_back(new aiMesh()); + aiMesh* out = outMeshes.back(); + + // the name carries the adjacency information between the meshes + out->mName = mesh->mName; + + // copy data members + out->mPrimitiveTypes = 1u << real; + out->mMaterialIndex = mesh->mMaterialIndex; + + // allocate output storage + out->mNumFaces = aiNumPerPType[real]; + aiFace* outFaces = out->mFaces = new aiFace[out->mNumFaces]; + + out->mNumVertices = (3 == real ? numPolyVerts : out->mNumFaces * (real+1)); + + aiVector3D *vert(nullptr), *nor(nullptr), *tan(nullptr), *bit(nullptr); + aiVector3D *uv [AI_MAX_NUMBER_OF_TEXTURECOORDS]; + aiColor4D *cols [AI_MAX_NUMBER_OF_COLOR_SETS]; + + if (mesh->mVertices) { + vert = out->mVertices = new aiVector3D[out->mNumVertices]; + } + + if (mesh->mNormals) { + nor = out->mNormals = new aiVector3D[out->mNumVertices]; + } + + if (mesh->mTangents) { + tan = out->mTangents = new aiVector3D[out->mNumVertices]; + bit = out->mBitangents = new aiVector3D[out->mNumVertices]; + } + + for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_TEXTURECOORDS;++j) { + uv[j] = nullptr; + if (mesh->mTextureCoords[j]) { + uv[j] = out->mTextureCoords[j] = new aiVector3D[out->mNumVertices]; + } + + out->mNumUVComponents[j] = mesh->mNumUVComponents[j]; + } + + for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_COLOR_SETS;++j) { + cols[j] = nullptr; + if (mesh->mColors[j]) { + cols[j] = out->mColors[j] = new aiColor4D[out->mNumVertices]; + } + } + + typedef std::vector< aiVertexWeight > TempBoneInfo; + std::vector< TempBoneInfo > tempBones(mesh->mNumBones); + + // try to guess how much storage we'll need + for (unsigned int q = 0; q < mesh->mNumBones;++q) + { + tempBones[q].reserve(mesh->mBones[q]->mNumWeights / (num-1)); + } + + unsigned int outIdx = 0; + for (unsigned int m = 0; m < mesh->mNumFaces; ++m) + { + aiFace& in = mesh->mFaces[m]; + if ((real == 3 && in.mNumIndices <= 3) || (real != 3 && in.mNumIndices != real+1)) + { + continue; + } + + outFaces->mNumIndices = in.mNumIndices; + outFaces->mIndices = in.mIndices; + + for (unsigned int q = 0; q < in.mNumIndices; ++q) + { + unsigned int idx = in.mIndices[q]; + + // process all bones of this index + if (avw) + { + VertexWeightTable& tbl = avw[idx]; + for (VertexWeightTable::const_iterator it = tbl.begin(), end = tbl.end(); + it != end; ++it) + { + tempBones[ (*it).first ].push_back( aiVertexWeight(outIdx, (*it).second) ); + } + } + + if (vert) + { + *vert++ = mesh->mVertices[idx]; + //mesh->mVertices[idx].x = get_qnan(); + } + if (nor )*nor++ = mesh->mNormals[idx]; + if (tan ) + { + *tan++ = mesh->mTangents[idx]; + *bit++ = mesh->mBitangents[idx]; + } + + for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++pp) + { + if (!uv[pp])break; + *uv[pp]++ = mesh->mTextureCoords[pp][idx]; + } + + for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_COLOR_SETS; ++pp) + { + if (!cols[pp])break; + *cols[pp]++ = mesh->mColors[pp][idx]; + } + + in.mIndices[q] = outIdx++; + } + + in.mIndices = nullptr; + ++outFaces; + } + ai_assert(outFaces == out->mFaces + out->mNumFaces); + + // now generate output bones + for (unsigned int q = 0; q < mesh->mNumBones;++q) + if (!tempBones[q].empty())++out->mNumBones; + + if (out->mNumBones) + { + out->mBones = new aiBone*[out->mNumBones]; + for (unsigned int q = 0, real = 0; q < mesh->mNumBones;++q) + { + TempBoneInfo& in = tempBones[q]; + if (in.empty())continue; + + aiBone* srcBone = mesh->mBones[q]; + aiBone* bone = out->mBones[real] = new aiBone(); + + bone->mName = srcBone->mName; + bone->mOffsetMatrix = srcBone->mOffsetMatrix; + + bone->mNumWeights = (unsigned int)in.size(); + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; + + ::memcpy(bone->mWeights,&in[0],bone->mNumWeights*sizeof(aiVertexWeight)); + + ++real; + } + } + } + + // delete the per-vertex bone weights table + delete[] avw; + + // delete the input mesh + delete mesh; + + // avoid invalid pointer + pScene->mMeshes[i] = NULL; + } + + if (outMeshes.empty()) + { + // This should not occur + throw DeadlyImportError("No meshes remaining"); + } + + // If we added at least one mesh process all nodes in the node + // graph and update their respective mesh indices. + if (bAnyChanges) + { + UpdateNodes(replaceMeshIndex,pScene->mRootNode); + } + + if (outMeshes.size() != pScene->mNumMeshes) + { + delete[] pScene->mMeshes; + pScene->mNumMeshes = (unsigned int)outMeshes.size(); + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + } + ::memcpy(pScene->mMeshes,&outMeshes[0],pScene->mNumMeshes*sizeof(void*)); + + if (!DefaultLogger::isNullLogger()) + { + char buffer[1024]; + ::ai_snprintf(buffer,1024,"Points: %u%s, Lines: %u%s, Triangles: %u%s, Polygons: %u%s (Meshes, X = removed)", + aiNumMeshesPerPType[0], ((mConfigRemoveMeshes & aiPrimitiveType_POINT) ? "X" : ""), + aiNumMeshesPerPType[1], ((mConfigRemoveMeshes & aiPrimitiveType_LINE) ? "X" : ""), + aiNumMeshesPerPType[2], ((mConfigRemoveMeshes & aiPrimitiveType_TRIANGLE) ? "X" : ""), + aiNumMeshesPerPType[3], ((mConfigRemoveMeshes & aiPrimitiveType_POLYGON) ? "X" : "")); + ASSIMP_LOG_INFO(buffer); + ASSIMP_LOG_DEBUG("SortByPTypeProcess finished"); + } +} + diff --git a/thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.h b/thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.h new file mode 100644 index 0000000000..1d7ccfc152 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.h @@ -0,0 +1,82 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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 "Common/BaseProcess.h" +#include + +class SortByPTypeProcessTest; + +namespace Assimp { + + +// --------------------------------------------------------------------------- +/** SortByPTypeProcess: Sorts meshes by the types of primitives they contain. + * A mesh with 5 lines, 3 points and 145 triangles would be split in 3 + * submeshes. +*/ +class ASSIMP_API SortByPTypeProcess : public BaseProcess { +public: + SortByPTypeProcess(); + ~SortByPTypeProcess(); + + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + void SetupProperties(const Importer* pImp); + +private: + int mConfigRemoveMeshes; +}; + + +} // end of namespace Assimp + +#endif // !!AI_SORTBYPTYPEPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.cpp b/thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.cpp new file mode 100644 index 0000000000..1797b28d5a --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.cpp @@ -0,0 +1,623 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** + * @file Implementation of the SplitLargeMeshes postprocessing step + */ + +// internal headers of the post-processing framework +#include "SplitLargeMeshes.h" +#include "ProcessHelper.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +SplitLargeMeshesProcess_Triangle::SplitLargeMeshesProcess_Triangle() { + LIMIT = AI_SLM_DEFAULT_MAX_TRIANGLES; +} + +// ------------------------------------------------------------------------------------------------ +SplitLargeMeshesProcess_Triangle::~SplitLargeMeshesProcess_Triangle() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool SplitLargeMeshesProcess_Triangle::IsActive( unsigned int pFlags) const { + return (pFlags & aiProcess_SplitLargeMeshes) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SplitLargeMeshesProcess_Triangle::Execute( aiScene* pScene) { + if (0xffffffff == this->LIMIT || nullptr == pScene ) { + return; + } + + ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle begin"); + std::vector > avList; + + for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) { + this->SplitMesh(a, pScene->mMeshes[a],avList); + } + + if (avList.size() != pScene->mNumMeshes) { + // it seems something has been split. rebuild the mesh list + delete[] pScene->mMeshes; + pScene->mNumMeshes = (unsigned int)avList.size(); + pScene->mMeshes = new aiMesh*[avList.size()]; + + for (unsigned int i = 0; i < avList.size();++i) { + pScene->mMeshes[i] = avList[i].first; + } + + // now we need to update all nodes + this->UpdateNode(pScene->mRootNode,avList); + ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Triangle finished. Meshes have been split"); + } else { + ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle finished. There was nothing to do"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Setup properties +void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp) { + // get the current value of the split property + this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES); +} + +// ------------------------------------------------------------------------------------------------ +// Update a node after some meshes have been split +void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode, + const std::vector >& avList) { + // for every index in out list build a new entry + std::vector aiEntries; + aiEntries.reserve(pcNode->mNumMeshes + 1); + for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) { + for (unsigned int a = 0; a < avList.size();++a) { + if (avList[a].second == pcNode->mMeshes[i]) { + aiEntries.push_back(a); + } + } + } + + // now build the new list + delete[] pcNode->mMeshes; + pcNode->mNumMeshes = (unsigned int)aiEntries.size(); + pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; + + for (unsigned int b = 0; b < pcNode->mNumMeshes;++b) { + pcNode->mMeshes[b] = aiEntries[b]; + } + + // recusively update all other nodes + for (unsigned int i = 0; i < pcNode->mNumChildren;++i) { + UpdateNode ( pcNode->mChildren[i], avList ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SplitLargeMeshesProcess_Triangle::SplitMesh( + unsigned int a, + aiMesh* pMesh, + std::vector >& avList) { + if (pMesh->mNumFaces > SplitLargeMeshesProcess_Triangle::LIMIT) { + ASSIMP_LOG_INFO("Mesh exceeds the triangle limit. It will be split ..."); + + // we need to split this mesh into sub meshes + // determine the size of a submesh + const unsigned int iSubMeshes = (pMesh->mNumFaces / LIMIT) + 1; + + const unsigned int iOutFaceNum = pMesh->mNumFaces / iSubMeshes; + const unsigned int iOutVertexNum = iOutFaceNum * 3; + + // now generate all submeshes + for (unsigned int i = 0; i < iSubMeshes;++i) { + aiMesh* pcMesh = new aiMesh; + pcMesh->mNumFaces = iOutFaceNum; + pcMesh->mMaterialIndex = pMesh->mMaterialIndex; + + // the name carries the adjacency information between the meshes + pcMesh->mName = pMesh->mName; + + if (i == iSubMeshes-1) { + pcMesh->mNumFaces = iOutFaceNum + ( + pMesh->mNumFaces - iOutFaceNum * iSubMeshes); + } + // copy the list of faces + pcMesh->mFaces = new aiFace[pcMesh->mNumFaces]; + + const unsigned int iBase = iOutFaceNum * i; + + // get the total number of indices + unsigned int iCnt = 0; + for (unsigned int p = iBase; p < pcMesh->mNumFaces + iBase;++p) { + iCnt += pMesh->mFaces[p].mNumIndices; + } + pcMesh->mNumVertices = iCnt; + + // allocate storage + if (pMesh->mVertices != nullptr) { + pcMesh->mVertices = new aiVector3D[iCnt]; + } + + if (pMesh->HasNormals()) { + pcMesh->mNormals = new aiVector3D[iCnt]; + } + + if (pMesh->HasTangentsAndBitangents()) { + pcMesh->mTangents = new aiVector3D[iCnt]; + pcMesh->mBitangents = new aiVector3D[iCnt]; + } + + // texture coordinates + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { + pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c]; + if (pMesh->HasTextureCoords( c)) { + pcMesh->mTextureCoords[c] = new aiVector3D[iCnt]; + } + } + + // vertex colors + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { + if (pMesh->HasVertexColors( c)) { + pcMesh->mColors[c] = new aiColor4D[iCnt]; + } + } + + if (pMesh->HasBones()) { + // assume the number of bones won't change in most cases + pcMesh->mBones = new aiBone*[pMesh->mNumBones]; + + // iterate through all bones of the mesh and find those which + // need to be copied to the split mesh + std::vector avTempWeights; + for (unsigned int p = 0; p < pcMesh->mNumBones;++p) { + aiBone* const bone = pcMesh->mBones[p]; + avTempWeights.clear(); + avTempWeights.reserve(bone->mNumWeights / iSubMeshes); + + for (unsigned int q = 0; q < bone->mNumWeights;++q) { + aiVertexWeight& weight = bone->mWeights[q]; + if(weight.mVertexId >= iBase && weight.mVertexId < iBase + iOutVertexNum) { + avTempWeights.push_back(weight); + weight = avTempWeights.back(); + weight.mVertexId -= iBase; + } + } + + if (!avTempWeights.empty()) { + // we'll need this bone. Copy it ... + aiBone* pc = new aiBone(); + pcMesh->mBones[pcMesh->mNumBones++] = pc; + pc->mName = aiString(bone->mName); + pc->mNumWeights = (unsigned int)avTempWeights.size(); + pc->mOffsetMatrix = bone->mOffsetMatrix; + + // no need to reallocate the array for the last submesh. + // Here we can reuse the (large) source array, although + // we'll waste some memory + if (iSubMeshes-1 == i) { + pc->mWeights = bone->mWeights; + bone->mWeights = nullptr; + } else { + pc->mWeights = new aiVertexWeight[pc->mNumWeights]; + } + + // copy the weights + ::memcpy(pc->mWeights,&avTempWeights[0],sizeof(aiVertexWeight)*pc->mNumWeights); + } + } + } + + // (we will also need to copy the array of indices) + unsigned int iCurrent = 0; + for (unsigned int p = 0; p < pcMesh->mNumFaces;++p) { + pcMesh->mFaces[p].mNumIndices = 3; + // allocate a new array + const unsigned int iTemp = p + iBase; + const unsigned int iNumIndices = pMesh->mFaces[iTemp].mNumIndices; + + // setup face type and number of indices + pcMesh->mFaces[p].mNumIndices = iNumIndices; + unsigned int* pi = pMesh->mFaces[iTemp].mIndices; + unsigned int* piOut = pcMesh->mFaces[p].mIndices = new unsigned int[iNumIndices]; + + // need to update the output primitive types + switch (iNumIndices) { + case 1: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + case 2: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + case 3: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + default: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + } + + // and copy the contents of the old array, offset by current base + for (unsigned int v = 0; v < iNumIndices;++v) { + unsigned int iIndex = pi[v]; + unsigned int iIndexOut = iCurrent++; + piOut[v] = iIndexOut; + + // copy positions + if (pMesh->mVertices != nullptr) { + pcMesh->mVertices[iIndexOut] = pMesh->mVertices[iIndex]; + } + + // copy normals + if (pMesh->HasNormals()) { + pcMesh->mNormals[iIndexOut] = pMesh->mNormals[iIndex]; + } + + // copy tangents/bitangents + if (pMesh->HasTangentsAndBitangents()) { + pcMesh->mTangents[iIndexOut] = pMesh->mTangents[iIndex]; + pcMesh->mBitangents[iIndexOut] = pMesh->mBitangents[iIndex]; + } + + // texture coordinates + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { + if (pMesh->HasTextureCoords( c ) ) { + pcMesh->mTextureCoords[c][iIndexOut] = pMesh->mTextureCoords[c][iIndex]; + } + } + // vertex colors + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { + if (pMesh->HasVertexColors( c)) { + pcMesh->mColors[c][iIndexOut] = pMesh->mColors[c][iIndex]; + } + } + } + } + + // add the newly created mesh to the list + avList.push_back(std::pair(pcMesh,a)); + } + + // now delete the old mesh data + delete pMesh; + } else { + avList.push_back(std::pair(pMesh,a)); + } +} + +// ------------------------------------------------------------------------------------------------ +SplitLargeMeshesProcess_Vertex::SplitLargeMeshesProcess_Vertex() { + LIMIT = AI_SLM_DEFAULT_MAX_VERTICES; +} + +// ------------------------------------------------------------------------------------------------ +SplitLargeMeshesProcess_Vertex::~SplitLargeMeshesProcess_Vertex() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool SplitLargeMeshesProcess_Vertex::IsActive( unsigned int pFlags) const { + return (pFlags & aiProcess_SplitLargeMeshes) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene) { + if (0xffffffff == this->LIMIT || nullptr == pScene ) { + return; + } + + ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Vertex begin"); + + std::vector > avList; + + //Check for point cloud first, + //Do not process point cloud, splitMesh works only with faces data + for (unsigned int a = 0; a < pScene->mNumMeshes; a++) { + if ( pScene->mMeshes[a]->mPrimitiveTypes == aiPrimitiveType_POINT ) { + return; + } + } + + for( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) { + this->SplitMesh(a, pScene->mMeshes[a], avList); + } + + if (avList.size() != pScene->mNumMeshes) { + // it seems something has been split. rebuild the mesh list + delete[] pScene->mMeshes; + pScene->mNumMeshes = (unsigned int)avList.size(); + pScene->mMeshes = new aiMesh*[avList.size()]; + + for (unsigned int i = 0; i < avList.size();++i) { + pScene->mMeshes[i] = avList[i].first; + } + + // now we need to update all nodes + SplitLargeMeshesProcess_Triangle::UpdateNode(pScene->mRootNode,avList); + ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Vertex finished. Meshes have been split"); + } else { + ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Vertex finished. There was nothing to do"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Setup properties +void SplitLargeMeshesProcess_Vertex::SetupProperties( const Importer* pImp) { + this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SplitLargeMeshesProcess_Vertex::SplitMesh( + unsigned int a, + aiMesh* pMesh, + std::vector >& avList) { + if (pMesh->mNumVertices > SplitLargeMeshesProcess_Vertex::LIMIT) { + typedef std::vector< std::pair > VertexWeightTable; + + // build a per-vertex weight list if necessary + VertexWeightTable* avPerVertexWeights = ComputeVertexBoneWeightTable(pMesh); + + // we need to split this mesh into sub meshes + // determine the estimated size of a submesh + // (this could be too large. Max waste is a single digit percentage) + const unsigned int iSubMeshes = (pMesh->mNumVertices / SplitLargeMeshesProcess_Vertex::LIMIT) + 1; + + // create a std::vector to indicate which vertices + // have already been copied + std::vector 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 BoneWeightList; + if (pMesh->HasBones()) { + pcMesh->mBones = new aiBone*[pMesh->mNumBones]; + ::memset(pcMesh->mBones,0,sizeof(void*)*pMesh->mNumBones); + } + + // clear the temporary helper array + if (iBase) { + // we can't use memset here we unsigned int needn' be 32 bits + for (auto &elem : avWasCopied) { + elem = 0xffffffff; + } + } + + // output vectors + std::vector vFaces; + + // reserve enough storage for most cases + if (pMesh->HasPositions()) { + pcMesh->mVertices = new aiVector3D[iOutVertexNum]; + } + if (pMesh->HasNormals()) { + pcMesh->mNormals = new aiVector3D[iOutVertexNum]; + } + if (pMesh->HasTangentsAndBitangents()) { + pcMesh->mTangents = new aiVector3D[iOutVertexNum]; + pcMesh->mBitangents = new aiVector3D[iOutVertexNum]; + } + for (unsigned int c = 0; pMesh->HasVertexColors(c);++c) { + pcMesh->mColors[c] = new aiColor4D[iOutVertexNum]; + } + for (unsigned int c = 0; pMesh->HasTextureCoords(c);++c) { + pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c]; + pcMesh->mTextureCoords[c] = new aiVector3D[iOutVertexNum]; + } + vFaces.reserve(iEstimatedSize); + + // (we will also need to copy the array of indices) + while (iBase < pMesh->mNumFaces) { + // allocate a new array + const unsigned int iNumIndices = pMesh->mFaces[iBase].mNumIndices; + + // doesn't catch degenerates but is quite fast + unsigned int iNeed = 0; + for (unsigned int v = 0; v < iNumIndices;++v) { + unsigned int iIndex = pMesh->mFaces[iBase].mIndices[v]; + + // check whether we do already have this vertex + if (0xFFFFFFFF == avWasCopied[iIndex]) { + iNeed++; + } + } + if (pcMesh->mNumVertices + iNeed > iOutVertexNum) { + // don't use this face + break; + } + + vFaces.push_back(aiFace()); + aiFace& rFace = vFaces.back(); + + // setup face type and number of indices + rFace.mNumIndices = iNumIndices; + rFace.mIndices = new unsigned int[iNumIndices]; + + // need to update the output primitive types + switch (rFace.mNumIndices) { + case 1: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + case 2: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + case 3: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + default: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + } + + // and copy the contents of the old array, offset by current base + for (unsigned int v = 0; v < iNumIndices;++v) { + unsigned int iIndex = pMesh->mFaces[iBase].mIndices[v]; + + // check whether we do already have this vertex + if (0xFFFFFFFF != avWasCopied[iIndex]) { + rFace.mIndices[v] = avWasCopied[iIndex]; + continue; + } + + // copy positions + pcMesh->mVertices[pcMesh->mNumVertices] = (pMesh->mVertices[iIndex]); + + // copy normals + if (pMesh->HasNormals()) { + pcMesh->mNormals[pcMesh->mNumVertices] = (pMesh->mNormals[iIndex]); + } + + // copy tangents/bitangents + if (pMesh->HasTangentsAndBitangents()) { + pcMesh->mTangents[pcMesh->mNumVertices] = (pMesh->mTangents[iIndex]); + pcMesh->mBitangents[pcMesh->mNumVertices] = (pMesh->mBitangents[iIndex]); + } + + // texture coordinates + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { + if (pMesh->HasTextureCoords( c)) { + pcMesh->mTextureCoords[c][pcMesh->mNumVertices] = pMesh->mTextureCoords[c][iIndex]; + } + } + // vertex colors + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { + if (pMesh->HasVertexColors( c)) { + pcMesh->mColors[c][pcMesh->mNumVertices] = pMesh->mColors[c][iIndex]; + } + } + // check whether we have bone weights assigned to this vertex + rFace.mIndices[v] = pcMesh->mNumVertices; + if (avPerVertexWeights) { + VertexWeightTable& table = avPerVertexWeights[ pcMesh->mNumVertices ]; + if( !table.empty() ) { + for (VertexWeightTable::const_iterator iter = table.begin(); + iter != table.end();++iter) { + // allocate the bone weight array if necessary + BoneWeightList* pcWeightList = (BoneWeightList*)pcMesh->mBones[(*iter).first]; + if (nullptr == pcWeightList) { + pcMesh->mBones[(*iter).first] = (aiBone*)(pcWeightList = new BoneWeightList()); + } + pcWeightList->push_back(aiVertexWeight(pcMesh->mNumVertices,(*iter).second)); + } + } + } + + avWasCopied[iIndex] = pcMesh->mNumVertices; + pcMesh->mNumVertices++; + } + ++iBase; + if(pcMesh->mNumVertices == iOutVertexNum) { + // break here. The face is only added if it was complete + break; + } + } + + // check which bones we'll need to create for this submesh + if (pMesh->HasBones()) { + aiBone** ppCurrent = pcMesh->mBones; + for (unsigned int k = 0; k < pMesh->mNumBones;++k) { + // check whether the bone is existing + BoneWeightList* pcWeightList; + if ((pcWeightList = (BoneWeightList*)pcMesh->mBones[k])) { + aiBone* pcOldBone = pMesh->mBones[k]; + aiBone* pcOut( nullptr ); + *ppCurrent++ = pcOut = new aiBone(); + pcOut->mName = aiString(pcOldBone->mName); + pcOut->mOffsetMatrix = pcOldBone->mOffsetMatrix; + pcOut->mNumWeights = (unsigned int)pcWeightList->size(); + pcOut->mWeights = new aiVertexWeight[pcOut->mNumWeights]; + + // copy the vertex weights + ::memcpy(pcOut->mWeights,&pcWeightList->operator[](0), + pcOut->mNumWeights * sizeof(aiVertexWeight)); + + // delete the temporary bone weight list + delete pcWeightList; + pcMesh->mNumBones++; + } + } + } + + // copy the face list to the mesh + pcMesh->mFaces = new aiFace[vFaces.size()]; + pcMesh->mNumFaces = (unsigned int)vFaces.size(); + + for (unsigned int p = 0; p < pcMesh->mNumFaces;++p) { + pcMesh->mFaces[p] = vFaces[p]; + } + + // add the newly created mesh to the list + avList.push_back(std::pair(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(pMesh,a)); +} diff --git a/thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.h b/thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.h new file mode 100644 index 0000000000..3f90576ea9 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.h @@ -0,0 +1,209 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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 sub-meshes + */ +#ifndef AI_SPLITLARGEMESHES_H_INC +#define AI_SPLITLARGEMESHES_H_INC + +#include +#include "Common/BaseProcess.h" + +#include +#include + +// Forward declarations +class SplitLargeMeshesTest; + +namespace Assimp { + +class SplitLargeMeshesProcess_Triangle; +class SplitLargeMeshesProcess_Vertex; + +// NOTE: If you change these limits, don't forget to change the +// corresponding values in all Assimp ports + +// ********************************************************** +// Java: ConfigProperty.java, +// ConfigProperty.DEFAULT_VERTEX_SPLIT_LIMIT +// ConfigProperty.DEFAULT_TRIANGLE_SPLIT_LIMIT +// ********************************************************** + +// default limit for vertices +#if (!defined AI_SLM_DEFAULT_MAX_VERTICES) +# define AI_SLM_DEFAULT_MAX_VERTICES 1000000 +#endif + +// default limit for triangles +#if (!defined AI_SLM_DEFAULT_MAX_TRIANGLES) +# define AI_SLM_DEFAULT_MAX_TRIANGLES 1000000 +#endif + +// --------------------------------------------------------------------------- +/** Post-processing filter to split large meshes into sub-meshes + * + * Applied BEFORE the JoinVertices-Step occurs. + * Returns NON-UNIQUE vertices, splits by triangle number. +*/ +class ASSIMP_API SplitLargeMeshesProcess_Triangle : public BaseProcess +{ + friend class SplitLargeMeshesProcess_Vertex; + +public: + + SplitLargeMeshesProcess_Triangle(); + ~SplitLargeMeshesProcess_Triangle(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. A + * bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + bool IsActive( unsigned int pFlags) const; + + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + virtual void SetupProperties(const Importer* pImp); + + + //! Set the split limit - needed for unit testing + inline void SetLimit(unsigned int l) + {LIMIT = l;} + + //! Get the split limit + inline unsigned int GetLimit() const + {return LIMIT;} + +public: + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + //! Apply the algorithm to a given mesh + void SplitMesh (unsigned int a, aiMesh* pcMesh, + std::vector >& avList); + + // ------------------------------------------------------------------- + //! Update a node in the asset after a few of its meshes + //! have been split + static void UpdateNode(aiNode* pcNode, + const std::vector >& avList); + +public: + //! Triangle limit + unsigned int LIMIT; +}; + + +// --------------------------------------------------------------------------- +/** Post-processing filter to split large meshes into sub-meshes + * + * Applied AFTER the JoinVertices-Step occurs. + * Returns UNIQUE vertices, splits by vertex number. +*/ +class ASSIMP_API SplitLargeMeshesProcess_Vertex : public BaseProcess +{ +public: + + SplitLargeMeshesProcess_Vertex(); + ~SplitLargeMeshesProcess_Vertex(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + virtual void SetupProperties(const Importer* pImp); + + + //! Set the split limit - needed for unit testing + inline void SetLimit(unsigned int l) + {LIMIT = l;} + + //! Get the split limit + inline unsigned int GetLimit() const + {return LIMIT;} + +public: + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + //! Apply the algorithm to a given mesh + void SplitMesh (unsigned int a, aiMesh* pcMesh, + std::vector >& avList); + + // NOTE: Reuse SplitLargeMeshesProcess_Triangle::UpdateNode() + +public: + //! Triangle limit + unsigned int LIMIT; +}; + +} // end of namespace Assimp + +#endif // !!AI_SPLITLARGEMESHES_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/TextureTransform.cpp b/thirdparty/assimp/code/PostProcessing/TextureTransform.cpp new file mode 100644 index 0000000000..8ae2ba7218 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/TextureTransform.cpp @@ -0,0 +1,566 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file A helper class that processes texture transformations */ + + + +#include +#include +#include +#include + +#include "TextureTransform.h" +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +TextureTransformStep::TextureTransformStep() : + configFlags() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +TextureTransformStep::~TextureTransformStep() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool TextureTransformStep::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_TransformUVCoords) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Setup properties +void TextureTransformStep::SetupProperties(const Importer* pImp) +{ + configFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_TUV_EVALUATE,AI_UVTRAFO_ALL); +} + +// ------------------------------------------------------------------------------------------------ +void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info) +{ + /* This function tries to simplify the input UV transformation. + * That's very important as it allows us to reduce the number + * of output UV channels. The order in which the transformations + * are applied is - as always - scaling, rotation, translation. + */ + + char szTemp[512]; + int rounded = 0; + + + /* Optimize the rotation angle. That's slightly difficult as + * we have an inprecise floating-point number (when comparing + * UV transformations we'll take that into account by using + * an epsilon of 5 degrees). If there is a rotation value, we can't + * perform any further optimizations. + */ + if (info.mRotation) + { + float out = info.mRotation; + if ((rounded = static_cast((info.mRotation / static_cast(AI_MATH_TWO_PI))))) + { + out -= rounded * static_cast(AI_MATH_PI); + ASSIMP_LOG_INFO_F("Texture coordinate rotation ", info.mRotation, " can be simplified to ", out); + } + + // Next step - convert negative rotation angles to positives + if (out < 0.f) + out = (float)AI_MATH_TWO_PI * 2 + out; + + info.mRotation = out; + return; + } + + + /* Optimize UV translation in the U direction. To determine whether + * or not we can optimize we need to look at the requested mapping + * type (e.g. if mirroring is active there IS a difference between + * offset 2 and 3) + */ + if ((rounded = (int)info.mTranslation.x)) { + float out = 0.0f; + szTemp[0] = 0; + if (aiTextureMapMode_Wrap == info.mapU) { + // Wrap - simple take the fraction of the field + out = info.mTranslation.x-(float)rounded; + ai_snprintf(szTemp, 512, "[w] UV U offset %f can be simplified to %f", info.mTranslation.x, out); + } + else if (aiTextureMapMode_Mirror == info.mapU && 1 != rounded) { + // Mirror + if (rounded % 2) + rounded--; + out = info.mTranslation.x-(float)rounded; + + ai_snprintf(szTemp,512,"[m/d] UV U offset %f can be simplified to %f",info.mTranslation.x,out); + } + else if (aiTextureMapMode_Clamp == info.mapU || aiTextureMapMode_Decal == info.mapU) { + // Clamp - translations beyond 1,1 are senseless + ai_snprintf(szTemp,512,"[c] UV U offset %f can be clamped to 1.0f",info.mTranslation.x); + + out = 1.f; + } + if (szTemp[0]) { + ASSIMP_LOG_INFO(szTemp); + info.mTranslation.x = out; + } + } + + /* Optimize UV translation in the V direction. To determine whether + * or not we can optimize we need to look at the requested mapping + * type (e.g. if mirroring is active there IS a difference between + * offset 2 and 3) + */ + if ((rounded = (int)info.mTranslation.y)) { + float out = 0.0f; + szTemp[0] = 0; + if (aiTextureMapMode_Wrap == info.mapV) { + // Wrap - simple take the fraction of the field + out = info.mTranslation.y-(float)rounded; + ::ai_snprintf(szTemp,512,"[w] UV V offset %f can be simplified to %f",info.mTranslation.y,out); + } + else if (aiTextureMapMode_Mirror == info.mapV && 1 != rounded) { + // Mirror + if (rounded % 2) + rounded--; + out = info.mTranslation.x-(float)rounded; + + ::ai_snprintf(szTemp,512,"[m/d] UV V offset %f can be simplified to %f",info.mTranslation.y,out); + } + else if (aiTextureMapMode_Clamp == info.mapV || aiTextureMapMode_Decal == info.mapV) { + // Clamp - translations beyond 1,1 are senseless + ::ai_snprintf(szTemp,512,"[c] UV V offset %f canbe clamped to 1.0f",info.mTranslation.y); + + out = 1.f; + } + if (szTemp[0]) { + ASSIMP_LOG_INFO(szTemp); + info.mTranslation.y = out; + } + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void UpdateUVIndex(const std::list& l, unsigned int n) +{ + // Don't set if == 0 && wasn't set before + for (std::list::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*)&n,1,AI_MATKEY_UVWSRC(info.semantic,info.index)); + } + } +} + +// ------------------------------------------------------------------------------------------------ +inline const char* MappingModeToChar(aiTextureMapMode map) +{ + if (aiTextureMapMode_Wrap == map) + return "-w"; + + if (aiTextureMapMode_Mirror == map) + return "-m"; + + return "-c"; +} + +// ------------------------------------------------------------------------------------------------ +void TextureTransformStep::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("TransformUVCoordsProcess begin"); + + + /* We build a per-mesh list of texture transformations we'll need + * to apply. To achieve this, we iterate through all materials, + * find all textures and get their transformations and UV indices. + * Then we search for all meshes using this material. + */ + typedef std::list MeshTrafoList; + std::vector meshLists(pScene->mNumMeshes); + + for (unsigned int i = 0; i < pScene->mNumMaterials;++i) { + + aiMaterial* mat = pScene->mMaterials[i]; + for (unsigned int a = 0; a < mat->mNumProperties;++a) { + + aiMaterialProperty* prop = mat->mProperties[a]; + if (!::strcmp( prop->mKey.data, "$tex.file")) { + STransformVecInfo info; + + // Setup a shortcut structure to allow for a fast updating + // of the UV index later + TTUpdateInfo update; + update.mat = (aiMaterial*) mat; + update.semantic = prop->mSemantic; + update.index = prop->mIndex; + + // Get textured properties and transform + for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2) { + aiMaterialProperty* prop2 = mat->mProperties[a2]; + if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex) { + continue; + } + + if ( !::strcmp( prop2->mKey.data, "$tex.uvwsrc")) { + info.uvIndex = *((int*)prop2->mData); + + // Store a direct pointer for later use + update.directShortcut = (unsigned int*) prop2->mData; + } + + else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodeu")) { + info.mapU = *((aiTextureMapMode*)prop2->mData); + } + else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodev")) { + info.mapV = *((aiTextureMapMode*)prop2->mData); + } + else if ( !::strcmp( prop2->mKey.data, "$tex.uvtrafo")) { + // ValidateDS should check this + ai_assert(prop2->mDataLength >= 20); + ::memcpy(&info.mTranslation.x,prop2->mData,sizeof(float)*5); + + // Directly remove this property from the list + mat->mNumProperties--; + for (unsigned int a3 = a2; a3 < mat->mNumProperties;++a3) { + mat->mProperties[a3] = mat->mProperties[a3+1]; + } + + delete prop2; + + // Warn: could be an underflow, but this does not invoke undefined behaviour + --a2; + } + } + + // Find out which transformations are to be evaluated + if (!(configFlags & AI_UVTRAFO_ROTATION)) { + info.mRotation = 0.f; + } + if (!(configFlags & AI_UVTRAFO_SCALING)) { + info.mScaling = aiVector2D(1.f,1.f); + } + if (!(configFlags & AI_UVTRAFO_TRANSLATION)) { + info.mTranslation = aiVector2D(0.f,0.f); + } + + // Do some preprocessing + PreProcessUVTransform(info); + info.uvIndex = std::min(info.uvIndex,AI_MAX_NUMBER_OF_TEXTURECOORDS -1u); + + // Find out whether this material is used by more than + // one mesh. This will make our task much, much more difficult! + unsigned int cnt = 0; + for (unsigned int n = 0; n < pScene->mNumMeshes;++n) { + if (pScene->mMeshes[n]->mMaterialIndex == i) + ++cnt; + } + + if (!cnt) + continue; + else if (1 != cnt) { + // This material is referenced by more than one mesh! + // So we need to make sure the UV index for the texture + // is identical for each of it ... + info.lockedPos = AI_TT_UV_IDX_LOCK_TBD; + } + + // Get all corresponding meshes + for (unsigned int n = 0; n < pScene->mNumMeshes;++n) { + aiMesh* mesh = pScene->mMeshes[n]; + if (mesh->mMaterialIndex != i || !mesh->mTextureCoords[0]) + continue; + + unsigned int uv = info.uvIndex; + if (!mesh->mTextureCoords[uv]) { + // If the requested UV index is not available, take the first one instead. + uv = 0; + } + + if (mesh->mNumUVComponents[info.uvIndex] >= 3){ + ASSIMP_LOG_WARN("UV transformations on 3D mapping channels are not supported"); + continue; + } + + MeshTrafoList::iterator it; + + // Check whether we have this transform setup already + for (it = meshLists[n].begin();it != meshLists[n].end(); ++it) { + + if ((*it) == info && (*it).uvIndex == uv) { + (*it).updateList.push_back(update); + break; + } + } + + if (it == meshLists[n].end()) { + meshLists[n].push_back(info); + meshLists[n].back().uvIndex = uv; + meshLists[n].back().updateList.push_back(update); + } + } + } + } + } + + char buffer[1024]; // should be sufficiently large + unsigned int outChannels = 0, inChannels = 0, transformedChannels = 0; + + // Now process all meshes. Important: we don't remove unreferenced UV channels. + // This is a job for the RemoveUnreferencedData-Step. + for (unsigned int q = 0; q < pScene->mNumMeshes;++q) { + + aiMesh* mesh = pScene->mMeshes[q]; + MeshTrafoList& trafo = meshLists[q]; + + inChannels += mesh->GetNumUVChannels(); + + if (!mesh->mTextureCoords[0] || trafo.empty() || (trafo.size() == 1 && trafo.begin()->IsUntransformed())) { + outChannels += mesh->GetNumUVChannels(); + continue; + } + + // Move untransformed UV channels to the first position in the list .... + // except if we need a new locked index which should be as small as possible + bool veto = false, need = false; + unsigned int cnt = 0; + unsigned int untransformed = 0; + + MeshTrafoList::iterator it,it2; + for (it = trafo.begin();it != trafo.end(); ++it,++cnt) { + + if (!(*it).IsUntransformed()) { + need = true; + } + + if ((*it).lockedPos == AI_TT_UV_IDX_LOCK_TBD) { + // Lock this index and make sure it won't be changed + (*it).lockedPos = cnt; + veto = true; + continue; + } + + if (!veto && it != trafo.begin() && (*it).IsUntransformed()) { + for (it2 = trafo.begin();it2 != it; ++it2) { + if (!(*it2).IsUntransformed()) + break; + } + trafo.insert(it2,*it); + trafo.erase(it); + break; + } + } + if (!need) + continue; + + // Find all that are not at their 'locked' position and move them to it. + // Conflicts are possible but quite unlikely. + cnt = 0; + for (it = trafo.begin();it != trafo.end(); ++it,++cnt) { + if ((*it).lockedPos != AI_TT_UV_IDX_LOCK_NONE && (*it).lockedPos != cnt) { + it2 = trafo.begin();unsigned int t = 0; + while (t != (*it).lockedPos) + ++it2; + + if ((*it2).lockedPos != AI_TT_UV_IDX_LOCK_NONE) { + ASSIMP_LOG_ERROR("Channel mismatch, can't compute all transformations properly [design bug]"); + continue; + } + + std::swap(*it2,*it); + if ((*it).lockedPos == untransformed) + untransformed = cnt; + } + } + + // ... and add dummies for all unreferenced channels + // at the end of the list + bool ref[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) + ref[n] = (!mesh->mTextureCoords[n] ? true : false); + + for (it = trafo.begin();it != trafo.end(); ++it) + ref[(*it).uvIndex] = true; + + for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) { + if (ref[n]) + continue; + trafo.push_back(STransformVecInfo()); + trafo.back().uvIndex = n; + } + + // Then check whether this list breaks the channel limit. + // The unimportant ones are at the end of the list, so + // it shouldn't be too worse if we remove them. + unsigned int size = (unsigned int)trafo.size(); + if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) { + + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_ERROR_F(static_cast(trafo.size()), " UV channels required but just ", + AI_MAX_NUMBER_OF_TEXTURECOORDS, " available"); + } + size = AI_MAX_NUMBER_OF_TEXTURECOORDS; + } + + + aiVector3D* old[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) + old[n] = mesh->mTextureCoords[n]; + + // Now continue and generate the output channels. Channels + // that we're not going to need later can be overridden. + it = trafo.begin(); + for (unsigned int n = 0; n < trafo.size();++n,++it) { + + if (n >= size) { + // Try to use an untransformed channel for all channels we threw over board + UpdateUVIndex((*it).updateList,untransformed); + continue; + } + + outChannels++; + + // Write to the log + if (!DefaultLogger::isNullLogger()) { + ::ai_snprintf(buffer,1024,"Mesh %u, channel %u: t(%.3f,%.3f), s(%.3f,%.3f), r(%.3f), %s%s", + q,n, + (*it).mTranslation.x, + (*it).mTranslation.y, + (*it).mScaling.x, + (*it).mScaling.y, + AI_RAD_TO_DEG( (*it).mRotation), + MappingModeToChar ((*it).mapU), + MappingModeToChar ((*it).mapV)); + + ASSIMP_LOG_INFO(buffer); + } + + // Check whether we need a new buffer here + if (mesh->mTextureCoords[n]) { + + it2 = it;++it2; + for (unsigned int m = n+1; m < size;++m, ++it2) { + + if ((*it2).uvIndex == n){ + it2 = trafo.begin(); + break; + } + } + if (it2 == trafo.begin()){ + mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices]; + } + } + else mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices]; + + aiVector3D* src = old[(*it).uvIndex]; + aiVector3D* dest, *end; + dest = mesh->mTextureCoords[n]; + + ai_assert(NULL != src); + + // Copy the data to the destination array + if (dest != src) + ::memcpy(dest,src,sizeof(aiVector3D)*mesh->mNumVertices); + + end = dest + mesh->mNumVertices; + + // Build a transformation matrix and transform all UV coords with it + if (!(*it).IsUntransformed()) { + const aiVector2D& trl = (*it).mTranslation; + const aiVector2D& scl = (*it).mScaling; + + // fixme: simplify .. + ++transformedChannels; + aiMatrix3x3 matrix; + + aiMatrix3x3 m2,m3,m4,m5; + + m4.a1 = scl.x; + m4.b2 = scl.y; + + m2.a3 = m2.b3 = 0.5f; + m3.a3 = m3.b3 = -0.5f; + + if ((*it).mRotation > AI_TT_ROTATION_EPSILON ) + aiMatrix3x3::RotationZ((*it).mRotation,matrix); + + m5.a3 += trl.x; m5.b3 += trl.y; + matrix = m2 * m4 * matrix * m3 * m5; + + for (src = dest; src != end; ++src) { /* manual homogenious divide */ + src->z = 1.f; + *src = matrix * *src; + src->x /= src->z; + src->y /= src->z; + src->z = 0.f; + } + } + + // Update all UV indices + UpdateUVIndex((*it).updateList,n); + } + } + + // Print some detailed statistics into the log + if (!DefaultLogger::isNullLogger()) { + + if (transformedChannels) { + ASSIMP_LOG_INFO_F("TransformUVCoordsProcess end: ", outChannels, " output channels (in: ", inChannels, ", modified: ", transformedChannels,")"); + } else { + ASSIMP_LOG_DEBUG("TransformUVCoordsProcess finished"); + } + } +} + + diff --git a/thirdparty/assimp/code/PostProcessing/TextureTransform.h b/thirdparty/assimp/code/PostProcessing/TextureTransform.h new file mode 100644 index 0000000000..2a5d623d7f --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/TextureTransform.h @@ -0,0 +1,232 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Definition of a helper step that processes texture transformations */ +#ifndef AI_TEXTURE_TRANSFORM_H_INCLUDED +#define AI_TEXTURE_TRANSFORM_H_INCLUDED + +#include +#include "Common/BaseProcess.h" + +#include +#include + +struct aiNode; +struct aiMaterial; + +namespace Assimp { + +#define AI_TT_UV_IDX_LOCK_TBD 0xffffffff +#define AI_TT_UV_IDX_LOCK_NONE 0xeeeeeeee + + +#define AI_TT_ROTATION_EPSILON ((float)AI_DEG_TO_RAD(0.5)) + +// --------------------------------------------------------------------------- +/** Small helper structure representing a shortcut into the material list + * to be able to update some values quickly. +*/ +struct TTUpdateInfo { + TTUpdateInfo() AI_NO_EXCEPT + : directShortcut(nullptr) + , mat(nullptr) + , semantic(0) + , index(0) { + // empty + } + + //! Direct shortcut, if available + unsigned int* directShortcut; + + //! Material + aiMaterial *mat; + + //! Texture type and index + unsigned int semantic, index; +}; + + +// --------------------------------------------------------------------------- +/** Helper class representing texture coordinate transformations +*/ +struct STransformVecInfo : public aiUVTransform { + STransformVecInfo() AI_NO_EXCEPT + : uvIndex(0) + , mapU(aiTextureMapMode_Wrap) + , mapV(aiTextureMapMode_Wrap) + , lockedPos(AI_TT_UV_IDX_LOCK_NONE) { + // empty + } + + //! Source texture coordinate index + unsigned int uvIndex; + + //! Texture mapping mode in the u, v direction + aiTextureMapMode mapU,mapV; + + //! Locked destination UV index + //! AI_TT_UV_IDX_LOCK_TBD - to be determined + //! AI_TT_UV_IDX_LOCK_NONE - none (default) + unsigned int lockedPos; + + //! Update info - shortcuts into all materials + //! that are referencing this transform setup + std::list updateList; + + + // ------------------------------------------------------------------- + /** Compare two transform setups + */ + inline bool operator== (const STransformVecInfo& other) const + { + // We use a small epsilon here + const static float epsilon = 0.05f; + + if (std::fabs( mTranslation.x - other.mTranslation.x ) > epsilon || + std::fabs( mTranslation.y - other.mTranslation.y ) > epsilon) + { + return false; + } + + if (std::fabs( mScaling.x - other.mScaling.x ) > epsilon || + std::fabs( mScaling.y - other.mScaling.y ) > epsilon) + { + return false; + } + + if (std::fabs( mRotation - other.mRotation) > epsilon) + { + return false; + } + return true; + } + + inline bool operator!= (const STransformVecInfo& other) const + { + return !(*this == other); + } + + + // ------------------------------------------------------------------- + /** Returns whether this is an untransformed texture coordinate set + */ + inline bool IsUntransformed() const + { + return (1.0f == mScaling.x && 1.f == mScaling.y && + !mTranslation.x && !mTranslation.y && + mRotation < AI_TT_ROTATION_EPSILON); + } + + // ------------------------------------------------------------------- + /** Build a 3x3 matrix from the transformations + */ + inline void GetMatrix(aiMatrix3x3& mOut) + { + mOut = aiMatrix3x3(); + + if (1.0f != mScaling.x || 1.0f != mScaling.y) + { + aiMatrix3x3 mScale; + mScale.a1 = mScaling.x; + mScale.b2 = mScaling.y; + mOut = mScale; + } + if (mRotation) + { + aiMatrix3x3 mRot; + mRot.a1 = mRot.b2 = std::cos(mRotation); + mRot.a2 = mRot.b1 = std::sin(mRotation); + mRot.a2 = -mRot.a2; + mOut *= mRot; + } + if (mTranslation.x || mTranslation.y) + { + aiMatrix3x3 mTrans; + mTrans.a3 = mTranslation.x; + mTrans.b3 = mTranslation.y; + mOut *= mTrans; + } + } +}; + + +// --------------------------------------------------------------------------- +/** Helper step to compute final UV coordinate sets if there are scalings + * or rotations in the original data read from the file. +*/ +class TextureTransformStep : public BaseProcess +{ +public: + + TextureTransformStep(); + ~TextureTransformStep(); + +public: + + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + void SetupProperties(const Importer* pImp); + + +protected: + + + // ------------------------------------------------------------------- + /** Preprocess a specific UV transformation setup + * + * @param info Transformation setup to be preprocessed. + */ + void PreProcessUVTransform(STransformVecInfo& info); + +private: + + unsigned int configFlags; +}; + +} + +#endif //! AI_TEXTURE_TRANSFORM_H_INCLUDED diff --git a/thirdparty/assimp/code/PostProcessing/TriangulateProcess.cpp b/thirdparty/assimp/code/PostProcessing/TriangulateProcess.cpp new file mode 100644 index 0000000000..1040836bbe --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/TriangulateProcess.cpp @@ -0,0 +1,530 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file TriangulateProcess.cpp + * @brief Implementation of the post processing step to split up + * all faces with more than three indices into triangles. + * + * + * The triangulation algorithm will handle concave or convex polygons. + * Self-intersecting or non-planar polygons are not rejected, but + * they're probably not triangulated correctly. + * + * DEBUG SWITCHES - do not enable any of them in release builds: + * + * AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING + * - generates vertex colors to represent the face winding order. + * the first vertex of a polygon becomes red, the last blue. + * AI_BUILD_TRIANGULATE_DEBUG_POLYS + * - dump all polygons and their triangulation sequences to + * a file + */ +#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS + +#include "PostProcessing/TriangulateProcess.h" +#include "PostProcessing/ProcessHelper.h" +#include "Common/PolyTools.h" + +#include + +//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING +//#define AI_BUILD_TRIANGULATE_DEBUG_POLYS + +#define POLY_GRID_Y 40 +#define POLY_GRID_X 70 +#define POLY_GRID_XPAD 20 +#define POLY_OUTPUT_FILE "assimp_polygons_debug.txt" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +TriangulateProcess::TriangulateProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +TriangulateProcess::~TriangulateProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool TriangulateProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_Triangulate) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void TriangulateProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("TriangulateProcess begin"); + + bool bHas = false; + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + { + if (pScene->mMeshes[ a ]) { + if ( TriangulateMesh( pScene->mMeshes[ a ] ) ) { + bHas = true; + } + } + } + if ( bHas ) { + ASSIMP_LOG_INFO( "TriangulateProcess finished. All polygons have been triangulated." ); + } else { + ASSIMP_LOG_DEBUG( "TriangulateProcess finished. There was nothing to be done." ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Triangulates the given mesh. +bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) +{ + // Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases + if (!pMesh->mPrimitiveTypes) { + bool bNeed = false; + + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { + const aiFace& face = pMesh->mFaces[a]; + + if( face.mNumIndices != 3) { + bNeed = true; + } + } + if (!bNeed) + return false; + } + else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) { + return false; + } + + // Find out how many output faces we'll get + unsigned int numOut = 0, max_out = 0; + bool get_normals = true; + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { + aiFace& face = pMesh->mFaces[a]; + if (face.mNumIndices <= 4) { + get_normals = false; + } + if( face.mNumIndices <= 3) { + numOut++; + + } + else { + numOut += face.mNumIndices-2; + max_out = std::max(max_out,face.mNumIndices); + } + } + + // Just another check whether aiMesh::mPrimitiveTypes is correct + ai_assert(numOut != pMesh->mNumFaces); + + aiVector3D* nor_out = NULL; + + // if we don't have normals yet, but expect them to be a cheap side + // product of triangulation anyway, allocate storage for them. + if (!pMesh->mNormals && get_normals) { + // XXX need a mechanism to inform the GenVertexNormals process to treat these normals as preprocessed per-face normals + // nor_out = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + } + + // the output mesh will contain triangles, but no polys anymore + pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON; + + aiFace* out = new aiFace[numOut](), *curOut = out; + std::vector temp_verts3d(max_out+2); /* temporary storage for vertices */ + std::vector temp_verts(max_out+2); + + // Apply vertex colors to represent the face winding? +#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING + if (!pMesh->mColors[0]) + pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices]; + else + new(pMesh->mColors[0]) aiColor4D[pMesh->mNumVertices]; + + aiColor4D* clr = pMesh->mColors[0]; +#endif + +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS + FILE* fout = fopen(POLY_OUTPUT_FILE,"a"); +#endif + + const aiVector3D* verts = pMesh->mVertices; + + // use std::unique_ptr to avoid slow std::vector specialiations + std::unique_ptr done(new bool[max_out]); + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { + aiFace& face = pMesh->mFaces[a]; + + unsigned int* idx = face.mIndices; + int num = (int)face.mNumIndices, ear = 0, tmp, prev = num-1, next = 0, max = num; + + // Apply vertex colors to represent the face winding? +#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING + for (unsigned int i = 0; i < face.mNumIndices; ++i) { + aiColor4D& c = clr[idx[i]]; + c.r = (i+1) / (float)max; + c.b = 1.f - c.r; + } +#endif + + aiFace* const last_face = curOut; + + // if it's a simple point,line or triangle: just copy it + if( face.mNumIndices <= 3) + { + aiFace& nface = *curOut++; + nface.mNumIndices = face.mNumIndices; + nface.mIndices = face.mIndices; + + face.mIndices = NULL; + continue; + } + // optimized code for quadrilaterals + else if ( face.mNumIndices == 4) { + + // quads can have at maximum one concave vertex. Determine + // this vertex (if it exists) and start tri-fanning from + // it. + unsigned int start_vertex = 0; + for (unsigned int i = 0; i < 4; ++i) { + const aiVector3D& v0 = verts[face.mIndices[(i+3) % 4]]; + const aiVector3D& v1 = verts[face.mIndices[(i+2) % 4]]; + const aiVector3D& v2 = verts[face.mIndices[(i+1) % 4]]; + + const aiVector3D& v = verts[face.mIndices[i]]; + + aiVector3D left = (v0-v); + aiVector3D diag = (v1-v); + aiVector3D right = (v2-v); + + left.Normalize(); + diag.Normalize(); + right.Normalize(); + + const float angle = std::acos(left*diag) + std::acos(right*diag); + if (angle > AI_MATH_PI_F) { + // this is the concave point + start_vertex = i; + break; + } + } + + const unsigned int temp[] = {face.mIndices[0], face.mIndices[1], face.mIndices[2], face.mIndices[3]}; + + aiFace& nface = *curOut++; + nface.mNumIndices = 3; + nface.mIndices = face.mIndices; + + nface.mIndices[0] = temp[start_vertex]; + nface.mIndices[1] = temp[(start_vertex + 1) % 4]; + nface.mIndices[2] = temp[(start_vertex + 2) % 4]; + + aiFace& sface = *curOut++; + sface.mNumIndices = 3; + sface.mIndices = new unsigned int[3]; + + sface.mIndices[0] = temp[start_vertex]; + sface.mIndices[1] = temp[(start_vertex + 2) % 4]; + sface.mIndices[2] = temp[(start_vertex + 3) % 4]; + + // prevent double deletion of the indices field + face.mIndices = NULL; + continue; + } + else + { + // A polygon with more than 3 vertices can be either concave or convex. + // Usually everything we're getting is convex and we could easily + // triangulate by tri-fanning. However, LightWave is probably the only + // modeling suite to make extensive use of highly concave, monster polygons ... + // so we need to apply the full 'ear cutting' algorithm to get it right. + + // RERQUIREMENT: polygon is expected to be simple and *nearly* planar. + // We project it onto a plane to get a 2d triangle. + + // Collect all vertices of of the polygon. + for (tmp = 0; tmp < max; ++tmp) { + temp_verts3d[tmp] = verts[idx[tmp]]; + } + + // Get newell normal of the polygon. Store it for future use if it's a polygon-only mesh + aiVector3D n; + NewellNormal<3,3,3>(n,max,&temp_verts3d.front().x,&temp_verts3d.front().y,&temp_verts3d.front().z); + if (nor_out) { + for (tmp = 0; tmp < max; ++tmp) + nor_out[idx[tmp]] = n; + } + + // Select largest normal coordinate to ignore for projection + const float ax = (n.x>0 ? n.x : -n.x); + const float ay = (n.y>0 ? n.y : -n.y); + const float az = (n.z>0 ? n.z : -n.z); + + unsigned int ac = 0, bc = 1; /* no z coord. projection to xy */ + float inv = n.z; + if (ax > ay) { + if (ax > az) { /* no x coord. projection to yz */ + ac = 1; bc = 2; + inv = n.x; + } + } + else if (ay > az) { /* no y coord. projection to zy */ + ac = 2; bc = 0; + inv = n.y; + } + + // Swap projection axes to take the negated projection vector into account + if (inv < 0.f) { + std::swap(ac,bc); + } + + for (tmp =0; tmp < max; ++tmp) { + temp_verts[tmp].x = verts[idx[tmp]][ac]; + temp_verts[tmp].y = verts[idx[tmp]][bc]; + done[tmp] = false; + } + +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS + // plot the plane onto which we mapped the polygon to a 2D ASCII pic + aiVector2D bmin,bmax; + ArrayBounds(&temp_verts[0],max,bmin,bmax); + + char grid[POLY_GRID_Y][POLY_GRID_X+POLY_GRID_XPAD]; + std::fill_n((char*)grid,POLY_GRID_Y*(POLY_GRID_X+POLY_GRID_XPAD),' '); + + for (int i =0; i < max; ++i) { + const aiVector2D& v = (temp_verts[i] - bmin) / (bmax-bmin); + const size_t x = static_cast(v.x*(POLY_GRID_X-1)), y = static_cast(v.y*(POLY_GRID_Y-1)); + char* loc = grid[y]+x; + if (grid[y][x] != ' ') { + for(;*loc != ' '; ++loc); + *loc++ = '_'; + } + *(loc+::ai_snprintf(loc, POLY_GRID_XPAD,"%i",i)) = ' '; + } + + + for(size_t y = 0; y < POLY_GRID_Y; ++y) { + grid[y][POLY_GRID_X+POLY_GRID_XPAD-1] = '\0'; + fprintf(fout,"%s\n",grid[y]); + } + + fprintf(fout,"\ntriangulation sequence: "); +#endif + + // + // FIXME: currently this is the slow O(kn) variant with a worst case + // complexity of O(n^2) (I think). Can be done in O(n). + while (num > 3) { + + // Find the next ear of the polygon + int num_found = 0; + for (ear = next;;prev = ear,ear = next) { + + // break after we looped two times without a positive match + for (next=ear+1;done[(next>=max?next=0:next)];++next); + if (next < ear) { + if (++num_found == 2) { + break; + } + } + const aiVector2D* pnt1 = &temp_verts[ear], + *pnt0 = &temp_verts[prev], + *pnt2 = &temp_verts[next]; + + // Must be a convex point. Assuming ccw winding, it must be on the right of the line between p-1 and p+1. + if (OnLeftSideOfLine2D(*pnt0,*pnt2,*pnt1)) { + continue; + } + + // and no other point may be contained in this triangle + for ( tmp = 0; tmp < max; ++tmp) { + + // We need to compare the actual values because it's possible that multiple indexes in + // the polygon are referring to the same position. concave_polygon.obj is a sample + // + // FIXME: Use 'epsiloned' comparisons instead? Due to numeric inaccuracies in + // PointInTriangle() I'm guessing that it's actually possible to construct + // input data that would cause us to end up with no ears. The problem is, + // which epsilon? If we chose a too large value, we'd get wrong results + const aiVector2D& vtmp = temp_verts[tmp]; + if ( vtmp != *pnt1 && vtmp != *pnt2 && vtmp != *pnt0 && PointInTriangle2D(*pnt0,*pnt1,*pnt2,vtmp)) { + break; + } + } + if (tmp != max) { + continue; + } + + // this vertex is an ear + break; + } + if (num_found == 2) { + + // Due to the 'two ear theorem', every simple polygon with more than three points must + // have 2 'ears'. Here's definitely something wrong ... but we don't give up yet. + // + + // Instead we're continuing with the standard tri-fanning algorithm which we'd + // use if we had only convex polygons. That's life. + ASSIMP_LOG_ERROR("Failed to triangulate polygon (no ear found). Probably not a simple polygon?"); + +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS + fprintf(fout,"critical error here, no ear found! "); +#endif + num = 0; + break; + + curOut -= (max-num); /* undo all previous work */ + for (tmp = 0; tmp < max-2; ++tmp) { + aiFace& nface = *curOut++; + + nface.mNumIndices = 3; + if (!nface.mIndices) + nface.mIndices = new unsigned int[3]; + + nface.mIndices[0] = 0; + nface.mIndices[1] = tmp+1; + nface.mIndices[2] = tmp+2; + + } + num = 0; + break; + } + + aiFace& nface = *curOut++; + nface.mNumIndices = 3; + + if (!nface.mIndices) { + nface.mIndices = new unsigned int[3]; + } + + // setup indices for the new triangle ... + nface.mIndices[0] = prev; + nface.mIndices[1] = ear; + nface.mIndices[2] = next; + + // exclude the ear from most further processing + done[ear] = true; + --num; + } + if (num > 0) { + // We have three indices forming the last 'ear' remaining. Collect them. + aiFace& nface = *curOut++; + nface.mNumIndices = 3; + if (!nface.mIndices) { + nface.mIndices = new unsigned int[3]; + } + + for (tmp = 0; done[tmp]; ++tmp); + nface.mIndices[0] = tmp; + + for (++tmp; done[tmp]; ++tmp); + nface.mIndices[1] = tmp; + + for (++tmp; done[tmp]; ++tmp); + nface.mIndices[2] = tmp; + + } + } + +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS + + for(aiFace* f = last_face; f != curOut; ++f) { + unsigned int* i = f->mIndices; + fprintf(fout," (%i %i %i)",i[0],i[1],i[2]); + } + + fprintf(fout,"\n*********************************************************************\n"); + fflush(fout); + +#endif + + for(aiFace* f = last_face; f != curOut; ) { + unsigned int* i = f->mIndices; + + // drop dumb 0-area triangles - deactivated for now: + //FindDegenerates post processing step can do the same thing + //if (std::fabs(GetArea2D(temp_verts[i[0]],temp_verts[i[1]],temp_verts[i[2]])) < 1e-5f) { + // ASSIMP_LOG_DEBUG("Dropping triangle with area 0"); + // --curOut; + + // delete[] f->mIndices; + // f->mIndices = nullptr; + + // for(aiFace* ff = f; ff != curOut; ++ff) { + // ff->mNumIndices = (ff+1)->mNumIndices; + // ff->mIndices = (ff+1)->mIndices; + // (ff+1)->mIndices = nullptr; + // } + // continue; + //} + + i[0] = idx[i[0]]; + i[1] = idx[i[1]]; + i[2] = idx[i[2]]; + ++f; + } + + delete[] face.mIndices; + face.mIndices = NULL; + } + +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS + fclose(fout); +#endif + + // kill the old faces + delete [] pMesh->mFaces; + + // ... and store the new ones + pMesh->mFaces = out; + pMesh->mNumFaces = (unsigned int)(curOut-out); /* not necessarily equal to numOut */ + return true; +} + +#endif // !! ASSIMP_BUILD_NO_TRIANGULATE_PROCESS diff --git a/thirdparty/assimp/code/PostProcessing/TriangulateProcess.h b/thirdparty/assimp/code/PostProcessing/TriangulateProcess.h new file mode 100644 index 0000000000..916b5103dd --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/TriangulateProcess.h @@ -0,0 +1,91 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to triangulate all faces + with more than three vertices. + */ +#ifndef AI_TRIANGULATEPROCESS_H_INC +#define AI_TRIANGULATEPROCESS_H_INC + +#include "Common/BaseProcess.h" + +struct aiMesh; + +class TriangulateProcessTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** The TriangulateProcess splits up all faces with more than three indices + * into triangles. You usually want this to happen because the graphics cards + * need their data as triangles. + */ +class ASSIMP_API TriangulateProcess : public BaseProcess { +public: + TriangulateProcess(); + ~TriangulateProcess(); + + // ------------------------------------------------------------------- + /** 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); + + // ------------------------------------------------------------------- + /** Triangulates the given mesh. + * @param pMesh The mesh to triangulate. + */ + bool TriangulateMesh( aiMesh* pMesh); +}; + +} // end of namespace Assimp + +#endif // AI_TRIANGULATEPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.cpp b/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.cpp new file mode 100644 index 0000000000..712fd6943d --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.cpp @@ -0,0 +1,979 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file ValidateDataStructure.cpp + * @brief Implementation of the post processing step to validate + * the data structure returned by Assimp. + */ + +// internal headers +#include "ValidateDataStructure.h" +#include +#include +#include "ProcessHelper.h" +#include + +// CRT headers +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +ValidateDSProcess::ValidateDSProcess() : + mScene() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +ValidateDSProcess::~ValidateDSProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool ValidateDSProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_ValidateDataStructure) != 0; +} +// ------------------------------------------------------------------------------------------------ +AI_WONT_RETURN void ValidateDSProcess::ReportError(const char* msg,...) +{ + ai_assert(NULL != msg); + + va_list args; + va_start(args,msg); + + char szBuffer[3000]; + const int iLen = vsprintf(szBuffer,msg,args); + ai_assert(iLen > 0); + + va_end(args); + + throw DeadlyImportError("Validation failed: " + std::string(szBuffer,iLen)); +} +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::ReportWarning(const char* msg,...) +{ + ai_assert(NULL != msg); + + va_list args; + va_start(args,msg); + + char szBuffer[3000]; + const int iLen = vsprintf(szBuffer,msg,args); + ai_assert(iLen > 0); + + va_end(args); + ASSIMP_LOG_WARN("Validation warning: " + std::string(szBuffer,iLen)); +} + +// ------------------------------------------------------------------------------------------------ +inline +int HasNameMatch(const aiString& in, aiNode* node) { + int result = (node->mName == in ? 1 : 0 ); + for (unsigned int i = 0; i < node->mNumChildren;++i) { + result += HasNameMatch(in,node->mChildren[i]); + } + return result; +} + +// ------------------------------------------------------------------------------------------------ +template +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 +inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size, + const char* firstName, const char* secondName) +{ + // validate all entries + if (size) + { + if (!parray) { + ReportError("aiScene::%s is NULL (aiScene::%s is %i)", + firstName, secondName, size); + } + for (unsigned int i = 0; i < size;++i) + { + if (!parray[i]) + { + ReportError("aiScene::%s[%u] is NULL (aiScene::%s is %u)", + firstName,i,secondName,size); + } + Validate(parray[i]); + + // check whether there are duplicate names + for (unsigned int a = i+1; a < size;++a) + { + if (parray[i]->mName == parray[a]->mName) + { + ReportError("aiScene::%s[%u] has the same name as " + "aiScene::%s[%u]",firstName, i,secondName, a); + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +template +inline +void ValidateDSProcess::DoValidationWithNameCheck(T** array, unsigned int size, const char* firstName, + const char* secondName) { + // validate all entries + DoValidationEx(array,size,firstName,secondName); + + for (unsigned int i = 0; i < size;++i) { + int res = HasNameMatch(array[i]->mName,mScene->mRootNode); + if (0 == res) { + const std::string name = static_cast(array[i]->mName.data); + ReportError("aiScene::%s[%i] has no corresponding node in the scene graph (%s)", + firstName,i, name.c_str()); + } else if (1 != res) { + const std::string name = static_cast(array[i]->mName.data); + ReportError("aiScene::%s[%i]: there are more than one nodes with %s as name", + firstName,i, name.c_str()); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void ValidateDSProcess::Execute( aiScene* pScene) { + mScene = pScene; + ASSIMP_LOG_DEBUG("ValidateDataStructureProcess begin"); + + // validate the node graph of the scene + Validate(pScene->mRootNode); + + // validate all meshes + if (pScene->mNumMeshes) { + DoValidation(pScene->mMeshes,pScene->mNumMeshes,"mMeshes","mNumMeshes"); + } + else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { + ReportError("aiScene::mNumMeshes is 0. At least one mesh must be there"); + } + else if (pScene->mMeshes) { + ReportError("aiScene::mMeshes is non-null although there are no meshes"); + } + + // validate all animations + if (pScene->mNumAnimations) { + DoValidation(pScene->mAnimations,pScene->mNumAnimations, + "mAnimations","mNumAnimations"); + } + else if (pScene->mAnimations) { + ReportError("aiScene::mAnimations is non-null although there are no animations"); + } + + // validate all cameras + if (pScene->mNumCameras) { + DoValidationWithNameCheck(pScene->mCameras,pScene->mNumCameras, + "mCameras","mNumCameras"); + } + else if (pScene->mCameras) { + ReportError("aiScene::mCameras is non-null although there are no cameras"); + } + + // validate all lights + if (pScene->mNumLights) { + DoValidationWithNameCheck(pScene->mLights,pScene->mNumLights, + "mLights","mNumLights"); + } + else if (pScene->mLights) { + ReportError("aiScene::mLights is non-null although there are no lights"); + } + + // validate all textures + if (pScene->mNumTextures) { + DoValidation(pScene->mTextures,pScene->mNumTextures, + "mTextures","mNumTextures"); + } + else if (pScene->mTextures) { + ReportError("aiScene::mTextures is non-null although there are no textures"); + } + + // validate all materials + if (pScene->mNumMaterials) { + DoValidation(pScene->mMaterials,pScene->mNumMaterials,"mMaterials","mNumMaterials"); + } +#if 0 + // NOTE: ScenePreprocessor generates a default material if none is there + else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { + ReportError("aiScene::mNumMaterials is 0. At least one material must be there"); + } +#endif + else if (pScene->mMaterials) { + ReportError("aiScene::mMaterials is non-null although there are no materials"); + } + +// if (!has)ReportError("The aiScene data structure is empty"); + ASSIMP_LOG_DEBUG("ValidateDataStructureProcess end"); +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiLight* pLight) +{ + if (pLight->mType == aiLightSource_UNDEFINED) + ReportWarning("aiLight::mType is aiLightSource_UNDEFINED"); + + if (!pLight->mAttenuationConstant && + !pLight->mAttenuationLinear && + !pLight->mAttenuationQuadratic) { + ReportWarning("aiLight::mAttenuationXXX - all are zero"); + } + + if (pLight->mAngleInnerCone > pLight->mAngleOuterCone) + ReportError("aiLight::mAngleInnerCone is larger than aiLight::mAngleOuterCone"); + + if (pLight->mColorDiffuse.IsBlack() && pLight->mColorAmbient.IsBlack() + && pLight->mColorSpecular.IsBlack()) + { + ReportWarning("aiLight::mColorXXX - all are black and won't have any influence"); + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiCamera* pCamera) +{ + if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear) + ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear"); + + // FIX: there are many 3ds files with invalid FOVs. No reason to + // reject them at all ... a warning is appropriate. + if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI) + ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV",pCamera->mHorizontalFOV); +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiMesh* pMesh) +{ + // validate the material index of the mesh + if (mScene->mNumMaterials && pMesh->mMaterialIndex >= mScene->mNumMaterials) + { + ReportError("aiMesh::mMaterialIndex is invalid (value: %i maximum: %i)", + pMesh->mMaterialIndex,mScene->mNumMaterials-1); + } + + Validate(&pMesh->mName); + + for (unsigned int i = 0; i < pMesh->mNumFaces; ++i) + { + aiFace& face = pMesh->mFaces[i]; + + if (pMesh->mPrimitiveTypes) + { + switch (face.mNumIndices) + { + case 0: + ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i); + break; + case 1: + if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT)) + { + ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimitiveTypes " + "does not report the POINT flag",i); + } + break; + case 2: + if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_LINE)) + { + ReportError("aiMesh::mFaces[%i] is a LINE but aiMesh::mPrimitiveTypes " + "does not report the LINE flag",i); + } + break; + case 3: + if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE)) + { + ReportError("aiMesh::mFaces[%i] is a TRIANGLE but aiMesh::mPrimitiveTypes " + "does not report the TRIANGLE flag",i); + } + break; + default: + if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) + { + this->ReportError("aiMesh::mFaces[%i] is a POLYGON but aiMesh::mPrimitiveTypes " + "does not report the POLYGON flag",i); + } + break; + }; + } + + if (!face.mIndices) + ReportError("aiMesh::mFaces[%i].mIndices is NULL",i); + } + + // positions must always be there ... + if (!pMesh->mNumVertices || (!pMesh->mVertices && !mScene->mFlags)) { + ReportError("The mesh %s contains no vertices", pMesh->mName.C_Str()); + } + + if (pMesh->mNumVertices > AI_MAX_VERTICES) { + ReportError("Mesh has too many vertices: %u, but the limit is %u",pMesh->mNumVertices,AI_MAX_VERTICES); + } + if (pMesh->mNumFaces > AI_MAX_FACES) { + ReportError("Mesh has too many faces: %u, but the limit is %u",pMesh->mNumFaces,AI_MAX_FACES); + } + + // if tangents are there there must also be bitangent vectors ... + if ((pMesh->mTangents != NULL) != (pMesh->mBitangents != NULL)) { + ReportError("If there are tangents, bitangent vectors must be present as well"); + } + + // faces, too + if (!pMesh->mNumFaces || (!pMesh->mFaces && !mScene->mFlags)) { + ReportError("Mesh %s contains no faces", pMesh->mName.C_Str()); + } + + // now check whether the face indexing layout is correct: + // unique vertices, pseudo-indexed. + std::vector abRefList; + abRefList.resize(pMesh->mNumVertices,false); + for (unsigned int i = 0; i < pMesh->mNumFaces;++i) + { + aiFace& face = pMesh->mFaces[i]; + if (face.mNumIndices > AI_MAX_FACE_INDICES) { + ReportError("Face %u has too many faces: %u, but the limit is %u",i,face.mNumIndices,AI_MAX_FACE_INDICES); + } + + for (unsigned int a = 0; a < face.mNumIndices;++a) + { + if (face.mIndices[a] >= pMesh->mNumVertices) { + ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a); + } + // the MSB flag is temporarily used by the extra verbose + // mode to tell us that the JoinVerticesProcess might have + // been executed already. + /*if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && !(this->mScene->mFlags & AI_SCENE_FLAGS_ALLOW_SHARED) && + abRefList[face.mIndices[a]]) + { + ReportError("aiMesh::mVertices[%i] is referenced twice - second " + "time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a); + }*/ + abRefList[face.mIndices[a]] = true; + } + } + + // check whether there are vertices that aren't referenced by a face + bool b = false; + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { + if (!abRefList[i])b = true; + } + abRefList.clear(); + if (b) { + ReportWarning("There are unreferenced vertices"); + } + + // texture channel 2 may not be set if channel 1 is zero ... + { + unsigned int i = 0; + for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) + { + if (!pMesh->HasTextureCoords(i))break; + } + for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) + if (pMesh->HasTextureCoords(i)) + { + ReportError("Texture coordinate channel %i exists " + "although the previous channel was NULL.",i); + } + } + // the same for the vertex colors + { + unsigned int i = 0; + for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i) + { + if (!pMesh->HasVertexColors(i))break; + } + for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i) + if (pMesh->HasVertexColors(i)) + { + ReportError("Vertex color channel %i is exists " + "although the previous channel was NULL.",i); + } + } + + + // now validate all bones + if (pMesh->mNumBones) + { + if (!pMesh->mBones) + { + ReportError("aiMesh::mBones is NULL (aiMesh::mNumBones is %i)", + pMesh->mNumBones); + } + std::unique_ptr afSum(nullptr); + if (pMesh->mNumVertices) + { + afSum.reset(new float[pMesh->mNumVertices]); + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) + afSum[i] = 0.0f; + } + + // check whether there are duplicate bone names + for (unsigned int i = 0; i < pMesh->mNumBones;++i) + { + const aiBone* bone = pMesh->mBones[i]; + if (bone->mNumWeights > AI_MAX_BONE_WEIGHTS) { + ReportError("Bone %u has too many weights: %u, but the limit is %u",i,bone->mNumWeights,AI_MAX_BONE_WEIGHTS); + } + + if (!pMesh->mBones[i]) + { + ReportError("aiMesh::mBones[%i] is NULL (aiMesh::mNumBones is %i)", + i,pMesh->mNumBones); + } + Validate(pMesh,pMesh->mBones[i],afSum.get()); + + for (unsigned int a = i+1; a < pMesh->mNumBones;++a) + { + if (pMesh->mBones[i]->mName == pMesh->mBones[a]->mName) + { + const char *name = "unknown"; + if (nullptr != pMesh->mBones[ i ]->mName.C_Str()) { + name = pMesh->mBones[ i ]->mName.C_Str(); + } + ReportError("aiMesh::mBones[%i], name = \"%s\" has the same name as " + "aiMesh::mBones[%i]", i, name, a ); + } + } + } + // check whether all bone weights for a vertex sum to 1.0 ... + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) + { + if (afSum[i] && (afSum[i] <= 0.94 || afSum[i] >= 1.05)) { + ReportWarning("aiMesh::mVertices[%i]: bone weight sum != 1.0 (sum is %f)",i,afSum[i]); + } + } + } + else if (pMesh->mBones) + { + ReportError("aiMesh::mBones is non-null although there are no bones"); + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiMesh* pMesh, const aiBone* pBone,float* afSum) { + this->Validate(&pBone->mName); + + if (!pBone->mNumWeights) { + //ReportError("aiBone::mNumWeights is zero"); + } + + // check whether all vertices affected by this bone are valid + for (unsigned int i = 0; i < pBone->mNumWeights;++i) + { + if (pBone->mWeights[i].mVertexId >= pMesh->mNumVertices) { + ReportError("aiBone::mWeights[%i].mVertexId is out of range",i); + } + else if (!pBone->mWeights[i].mWeight || pBone->mWeights[i].mWeight > 1.0f) { + ReportWarning("aiBone::mWeights[%i].mWeight has an invalid value",i); + } + afSum[pBone->mWeights[i].mVertexId] += pBone->mWeights[i].mWeight; + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiAnimation* pAnimation) +{ + Validate(&pAnimation->mName); + + // validate all materials + if (pAnimation->mNumChannels) + { + if (!pAnimation->mChannels) { + ReportError("aiAnimation::mChannels is NULL (aiAnimation::mNumChannels is %i)", + pAnimation->mNumChannels); + } + for (unsigned int i = 0; i < pAnimation->mNumChannels;++i) + { + if (!pAnimation->mChannels[i]) + { + ReportError("aiAnimation::mChannels[%i] is NULL (aiAnimation::mNumChannels is %i)", + i, pAnimation->mNumChannels); + } + Validate(pAnimation, pAnimation->mChannels[i]); + } + } + else { + ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there."); + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial, + aiTextureType type) +{ + const char* szType = TextureTypeToString(type); + + // **************************************************************************** + // Search all keys of the material ... + // textures must be specified with ascending indices + // (e.g. diffuse #2 may not be specified if diffuse #1 is not there ...) + // **************************************************************************** + + int iNumIndices = 0; + int iIndex = -1; + for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) { + aiMaterialProperty* prop = pMaterial->mProperties[ i ]; + ai_assert(nullptr != prop); + if ( !::strcmp(prop->mKey.data,"$tex.file") && prop->mSemantic == static_cast(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 mappings(iNumIndices); + + // Now check whether all UV indices are valid ... + bool bNoSpecified = true; + for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) + { + aiMaterialProperty* prop = pMaterial->mProperties[i]; + if (prop->mSemantic != type)continue; + + if ((int)prop->mIndex >= iNumIndices) + { + ReportError("Found texture property with index %i, although there " + "are only %i textures of type %s", + prop->mIndex, iNumIndices, szType); + } + + if (!::strcmp(prop->mKey.data,"$tex.mapping")) { + if (aiPTI_Integer != prop->mType || prop->mDataLength < sizeof(aiTextureMapping)) + { + ReportError("Material property %s%i is expected to be an integer (size is %i)", + prop->mKey.data,prop->mIndex,prop->mDataLength); + } + mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData); + } + else if (!::strcmp(prop->mKey.data,"$tex.uvtrafo")) { + if (aiPTI_Float != prop->mType || prop->mDataLength < sizeof(aiUVTransform)) + { + ReportError("Material property %s%i is expected to be 5 floats large (size is %i)", + prop->mKey.data,prop->mIndex, prop->mDataLength); + } + mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData); + } + else if (!::strcmp(prop->mKey.data,"$tex.uvwsrc")) { + if (aiPTI_Integer != prop->mType || sizeof(int) > prop->mDataLength) + { + ReportError("Material property %s%i is expected to be an integer (size is %i)", + prop->mKey.data,prop->mIndex,prop->mDataLength); + } + bNoSpecified = false; + + // Ignore UV indices for texture channels that are not there ... + + // Get the value + iIndex = *((unsigned int*)prop->mData); + + // Check whether there is a mesh using this material + // which has not enough UV channels ... + for (unsigned int a = 0; a < mScene->mNumMeshes;++a) + { + aiMesh* mesh = this->mScene->mMeshes[a]; + if(mesh->mMaterialIndex == (unsigned int)i) + { + int iChannels = 0; + while (mesh->HasTextureCoords(iChannels))++iChannels; + if (iIndex >= iChannels) + { + ReportWarning("Invalid UV index: %i (key %s). Mesh %i has only %i UV channels", + iIndex,prop->mKey.data,a,iChannels); + } + } + } + } + } + if (bNoSpecified) + { + // Assume that all textures are using the first UV channel + for (unsigned int a = 0; a < mScene->mNumMeshes;++a) + { + aiMesh* mesh = mScene->mMeshes[a]; + if(mesh->mMaterialIndex == (unsigned int)iIndex && mappings[0] == aiTextureMapping_UV) + { + if (!mesh->mTextureCoords[0]) + { + // This is a special case ... it could be that the + // original mesh format intended the use of a special + // mapping here. + ReportWarning("UV-mapped texture, but there are no UV coords"); + } + } + } + } +} +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiMaterial* pMaterial) +{ + // check whether there are material keys that are obviously not legal + for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) + { + const aiMaterialProperty* prop = pMaterial->mProperties[i]; + if (!prop) { + ReportError("aiMaterial::mProperties[%i] is NULL (aiMaterial::mNumProperties is %i)", + i,pMaterial->mNumProperties); + } + if (!prop->mDataLength || !prop->mData) { + ReportError("aiMaterial::mProperties[%i].mDataLength or " + "aiMaterial::mProperties[%i].mData is 0",i,i); + } + // check all predefined types + if (aiPTI_String == prop->mType) { + // FIX: strings are now stored in a less expensive way, but we can't use the + // validation routine for 'normal' aiStrings + if (prop->mDataLength < 5 || prop->mDataLength < 4 + (*reinterpret_cast(prop->mData)) + 1) { + ReportError("aiMaterial::mProperties[%i].mDataLength is " + "too small to contain a string (%i, needed: %i)", + i,prop->mDataLength,static_cast(sizeof(aiString))); + } + if(prop->mData[prop->mDataLength-1]) { + ReportError("Missing null-terminator in string material property"); + } + // Validate((const aiString*)prop->mData); + } + else if (aiPTI_Float == prop->mType) { + if (prop->mDataLength < sizeof(float)) { + ReportError("aiMaterial::mProperties[%i].mDataLength is " + "too small to contain a float (%i, needed: %i)", + i,prop->mDataLength, static_cast(sizeof(float))); + } + } + else if (aiPTI_Integer == prop->mType) { + if (prop->mDataLength < sizeof(int)) { + ReportError("aiMaterial::mProperties[%i].mDataLength is " + "too small to contain an integer (%i, needed: %i)", + i,prop->mDataLength, static_cast(sizeof(int))); + } + } + // TODO: check whether there is a key with an unknown name ... + } + + // make some more specific tests + ai_real fTemp; + int iShading; + if (AI_SUCCESS == aiGetMaterialInteger( pMaterial,AI_MATKEY_SHADING_MODEL,&iShading)) { + switch ((aiShadingMode)iShading) + { + case aiShadingMode_Blinn: + case aiShadingMode_CookTorrance: + case aiShadingMode_Phong: + + if (AI_SUCCESS != aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS,&fTemp)) { + ReportWarning("A specular shading model is specified but there is no " + "AI_MATKEY_SHININESS key"); + } + if (AI_SUCCESS == aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS_STRENGTH,&fTemp) && !fTemp) { + ReportWarning("A specular shading model is specified but the value of the " + "AI_MATKEY_SHININESS_STRENGTH key is 0.0"); + } + break; + default: + break; + } + } + + if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01)) { + ReportWarning("Invalid opacity value (must be 0 < opacity < 1.0)"); + } + + // Check whether there are invalid texture keys + // TODO: that's a relict of the past, where texture type and index were baked + // into the material string ... we could do that in one single pass. + SearchForInvalidTextures(pMaterial,aiTextureType_DIFFUSE); + SearchForInvalidTextures(pMaterial,aiTextureType_SPECULAR); + SearchForInvalidTextures(pMaterial,aiTextureType_AMBIENT); + SearchForInvalidTextures(pMaterial,aiTextureType_EMISSIVE); + SearchForInvalidTextures(pMaterial,aiTextureType_OPACITY); + SearchForInvalidTextures(pMaterial,aiTextureType_SHININESS); + SearchForInvalidTextures(pMaterial,aiTextureType_HEIGHT); + SearchForInvalidTextures(pMaterial,aiTextureType_NORMALS); + SearchForInvalidTextures(pMaterial,aiTextureType_DISPLACEMENT); + SearchForInvalidTextures(pMaterial,aiTextureType_LIGHTMAP); + SearchForInvalidTextures(pMaterial,aiTextureType_REFLECTION); +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiTexture* pTexture) +{ + // the data section may NEVER be NULL + if (!pTexture->pcData) { + ReportError("aiTexture::pcData is NULL"); + } + if (pTexture->mHeight) + { + if (!pTexture->mWidth){ + ReportError("aiTexture::mWidth is zero (aiTexture::mHeight is %i, uncompressed texture)", + pTexture->mHeight); + } + } + else + { + if (!pTexture->mWidth) { + ReportError("aiTexture::mWidth is zero (compressed texture)"); + } + if ('\0' != pTexture->achFormatHint[3]) { + ReportWarning("aiTexture::achFormatHint must be zero-terminated"); + } + else if ('.' == pTexture->achFormatHint[0]) { + ReportWarning("aiTexture::achFormatHint should contain a file extension " + "without a leading dot (format hint: %s).",pTexture->achFormatHint); + } + } + + const char* sz = pTexture->achFormatHint; + if ((sz[0] >= 'A' && sz[0] <= 'Z') || + (sz[1] >= 'A' && sz[1] <= 'Z') || + (sz[2] >= 'A' && sz[2] <= 'Z') || + (sz[3] >= 'A' && sz[3] <= 'Z')) { + ReportError("aiTexture::achFormatHint contains non-lowercase letters"); + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiAnimation* pAnimation, + const aiNodeAnim* pNodeAnim) +{ + Validate(&pNodeAnim->mNodeName); + + if (!pNodeAnim->mNumPositionKeys && !pNodeAnim->mScalingKeys && !pNodeAnim->mNumRotationKeys) { + ReportError("Empty node animation channel"); + } + // otherwise check whether one of the keys exceeds the total duration of the animation + if (pNodeAnim->mNumPositionKeys) + { + if (!pNodeAnim->mPositionKeys) + { + ReportError("aiNodeAnim::mPositionKeys is NULL (aiNodeAnim::mNumPositionKeys is %i)", + pNodeAnim->mNumPositionKeys); + } + double dLast = -10e10; + for (unsigned int i = 0; i < pNodeAnim->mNumPositionKeys;++i) + { + // ScenePreprocessor will compute the duration if still the default value + // (Aramis) Add small epsilon, comparison tended to fail if max_time == duration, + // seems to be due the compilers register usage/width. + if (pAnimation->mDuration > 0. && pNodeAnim->mPositionKeys[i].mTime > pAnimation->mDuration+0.001) + { + ReportError("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is larger " + "than aiAnimation::mDuration (which is %.5f)",i, + (float)pNodeAnim->mPositionKeys[i].mTime, + (float)pAnimation->mDuration); + } + if (i && pNodeAnim->mPositionKeys[i].mTime <= dLast) + { + ReportWarning("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is smaller " + "than aiAnimation::mPositionKeys[%i] (which is %.5f)",i, + (float)pNodeAnim->mPositionKeys[i].mTime, + i-1, (float)dLast); + } + dLast = pNodeAnim->mPositionKeys[i].mTime; + } + } + // rotation keys + if (pNodeAnim->mNumRotationKeys) + { + if (!pNodeAnim->mRotationKeys) + { + ReportError("aiNodeAnim::mRotationKeys is NULL (aiNodeAnim::mNumRotationKeys is %i)", + pNodeAnim->mNumRotationKeys); + } + double dLast = -10e10; + for (unsigned int i = 0; i < pNodeAnim->mNumRotationKeys;++i) + { + if (pAnimation->mDuration > 0. && pNodeAnim->mRotationKeys[i].mTime > pAnimation->mDuration+0.001) + { + ReportError("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is larger " + "than aiAnimation::mDuration (which is %.5f)",i, + (float)pNodeAnim->mRotationKeys[i].mTime, + (float)pAnimation->mDuration); + } + if (i && pNodeAnim->mRotationKeys[i].mTime <= dLast) + { + ReportWarning("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is smaller " + "than aiAnimation::mRotationKeys[%i] (which is %.5f)",i, + (float)pNodeAnim->mRotationKeys[i].mTime, + i-1, (float)dLast); + } + dLast = pNodeAnim->mRotationKeys[i].mTime; + } + } + // scaling keys + if (pNodeAnim->mNumScalingKeys) + { + if (!pNodeAnim->mScalingKeys) { + ReportError("aiNodeAnim::mScalingKeys is NULL (aiNodeAnim::mNumScalingKeys is %i)", + pNodeAnim->mNumScalingKeys); + } + double dLast = -10e10; + for (unsigned int i = 0; i < pNodeAnim->mNumScalingKeys;++i) + { + if (pAnimation->mDuration > 0. && pNodeAnim->mScalingKeys[i].mTime > pAnimation->mDuration+0.001) + { + ReportError("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is larger " + "than aiAnimation::mDuration (which is %.5f)",i, + (float)pNodeAnim->mScalingKeys[i].mTime, + (float)pAnimation->mDuration); + } + if (i && pNodeAnim->mScalingKeys[i].mTime <= dLast) + { + ReportWarning("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is smaller " + "than aiAnimation::mScalingKeys[%i] (which is %.5f)",i, + (float)pNodeAnim->mScalingKeys[i].mTime, + i-1, (float)dLast); + } + dLast = pNodeAnim->mScalingKeys[i].mTime; + } + } + + if (!pNodeAnim->mNumScalingKeys && !pNodeAnim->mNumRotationKeys && + !pNodeAnim->mNumPositionKeys) + { + ReportError("A node animation channel must have at least one subtrack"); + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiNode* pNode) +{ + if (!pNode) { + ReportError("A node of the scenegraph is NULL"); + } + // Validate node name string first so that it's safe to use in below expressions + this->Validate(&pNode->mName); + const char* nodeName = (&pNode->mName)->C_Str(); + if (pNode != mScene->mRootNode && !pNode->mParent){ + ReportError("Non-root node %s lacks a valid parent (aiNode::mParent is NULL) ", nodeName); + } + + // validate all meshes + if (pNode->mNumMeshes) + { + if (!pNode->mMeshes) + { + ReportError("aiNode::mMeshes is NULL for node %s (aiNode::mNumMeshes is %i)", + nodeName, pNode->mNumMeshes); + } + std::vector abHadMesh; + abHadMesh.resize(mScene->mNumMeshes,false); + for (unsigned int i = 0; i < pNode->mNumMeshes;++i) + { + if (pNode->mMeshes[i] >= mScene->mNumMeshes) + { + ReportError("aiNode::mMeshes[%i] is out of range for node %s (maximum is %i)", + pNode->mMeshes[i], nodeName, mScene->mNumMeshes-1); + } + if (abHadMesh[pNode->mMeshes[i]]) + { + ReportError("aiNode::mMeshes[%i] is already referenced by this node %s (value: %i)", + i, nodeName, pNode->mMeshes[i]); + } + abHadMesh[pNode->mMeshes[i]] = true; + } + } + if (pNode->mNumChildren) + { + if (!pNode->mChildren) { + ReportError("aiNode::mChildren is NULL for node %s (aiNode::mNumChildren is %i)", + nodeName, pNode->mNumChildren); + } + for (unsigned int i = 0; i < pNode->mNumChildren;++i) { + Validate(pNode->mChildren[i]); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiString* pString) +{ + if (pString->length > MAXLEN) + { + ReportError("aiString::length is too large (%lu, maximum is %lu)", + pString->length,MAXLEN); + } + const char* sz = pString->data; + while (true) + { + if ('\0' == *sz) + { + if (pString->length != (unsigned int)(sz-pString->data)) { + ReportError("aiString::data is invalid: the terminal zero is at a wrong offset"); + } + break; + } + else if (sz >= &pString->data[MAXLEN]) { + ReportError("aiString::data is invalid. There is no terminal character"); + } + ++sz; + } +} diff --git a/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.h b/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.h new file mode 100644 index 0000000000..0b891ef414 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.h @@ -0,0 +1,189 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING 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 + +#include "Common/BaseProcess.h" + +struct aiBone; +struct aiMesh; +struct aiAnimation; +struct aiNodeAnim; +struct aiTexture; +struct aiMaterial; +struct aiNode; +struct aiString; +struct aiCamera; +struct aiLight; + +namespace Assimp { + +// -------------------------------------------------------------------------------------- +/** Validates the whole ASSIMP scene data structure for correctness. + * ImportErrorException is thrown of the scene is corrupt.*/ +// -------------------------------------------------------------------------------------- +class ValidateDSProcess : public BaseProcess +{ +public: + + ValidateDSProcess(); + ~ValidateDSProcess(); + +public: + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + +protected: + + // ------------------------------------------------------------------- + /** Report a validation error. This will throw an exception, + * control won't return. + * @param msg Format string for sprintf().*/ + AI_WONT_RETURN void ReportError(const char* msg,...) AI_WONT_RETURN_SUFFIX; + + + // ------------------------------------------------------------------- + /** Report a validation warning. This won't throw an exception, + * control will return to the caller. + * @param msg Format string for sprintf().*/ + void ReportWarning(const char* msg,...); + + + // ------------------------------------------------------------------- + /** Validates a mesh + * @param pMesh Input mesh*/ + void Validate( const aiMesh* pMesh); + + // ------------------------------------------------------------------- + /** Validates a bone + * @param pMesh Input mesh + * @param pBone Input bone*/ + void Validate( const aiMesh* pMesh,const aiBone* pBone,float* afSum); + + // ------------------------------------------------------------------- + /** Validates an animation + * @param pAnimation Input animation*/ + void Validate( const aiAnimation* pAnimation); + + // ------------------------------------------------------------------- + /** Validates a material + * @param pMaterial Input material*/ + void Validate( const aiMaterial* pMaterial); + + // ------------------------------------------------------------------- + /** Search the material data structure for invalid or corrupt + * texture keys. + * @param pMaterial Input material + * @param type Type of the texture*/ + void SearchForInvalidTextures(const aiMaterial* pMaterial, + aiTextureType type); + + // ------------------------------------------------------------------- + /** Validates a texture + * @param pTexture Input texture*/ + void Validate( const aiTexture* pTexture); + + // ------------------------------------------------------------------- + /** Validates a light source + * @param pLight Input light + */ + void Validate( const aiLight* pLight); + + // ------------------------------------------------------------------- + /** Validates a camera + * @param pCamera Input camera*/ + void Validate( const aiCamera* pCamera); + + // ------------------------------------------------------------------- + /** Validates a bone animation channel + * @param pAnimation Animation channel. + * @param pBoneAnim Input bone animation */ + void Validate( const aiAnimation* pAnimation, + const aiNodeAnim* pBoneAnim); + + // ------------------------------------------------------------------- + /** Validates a node and all of its subnodes + * @param Node Input node*/ + void Validate( const aiNode* pNode); + + // ------------------------------------------------------------------- + /** Validates a string + * @param pString Input string*/ + void Validate( const aiString* pString); + +private: + + // template to validate one of the aiScene::mXXX arrays + template + inline void DoValidation(T** array, unsigned int size, + const char* firstName, const char* secondName); + + // extended version: checks whether T::mName occurs twice + template + 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 + inline void DoValidationWithNameCheck(T** array, unsigned int size, + const char* firstName, const char* secondName); + + aiScene* mScene; +}; + + + + +} // end of namespace Assimp + +#endif // AI_VALIDATEPROCESS_H_INC diff --git a/thirdparty/assimp/code/PostStepRegistry.cpp b/thirdparty/assimp/code/PostStepRegistry.cpp deleted file mode 100644 index 15b4a28843..0000000000 --- a/thirdparty/assimp/code/PostStepRegistry.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file ImporterRegistry.cpp - -Central registry for all postprocessing steps available. Do not edit this file -directly (unless you are adding new steps), instead use the -corresponding preprocessor flag to selectively disable steps. -*/ - -#include "ProcessHelper.h" - -#ifndef ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS -# include "CalcTangentsProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS -# include "JoinVerticesProcess.h" -#endif -#if !(defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS && defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS && defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS) -# include "ConvertToLHProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS -# include "TriangulateProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_DROPFACENORMALS_PROCESS -# include "DropFaceNormalsProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS -# include "GenFaceNormalsProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS -# include "GenVertexNormalsProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_REMOVEVC_PROCESS -# include "RemoveVCProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS -# include "SplitLargeMeshes.h" -#endif -#ifndef ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS -# include "PretransformVertices.h" -#endif -#ifndef ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS -# include "LimitBoneWeightsProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS -# include "ValidateDataStructure.h" -#endif -#ifndef ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS -# include "ImproveCacheLocality.h" -#endif -#ifndef ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS -# include "FixNormalsStep.h" -#endif -#ifndef ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS -# include "RemoveRedundantMaterials.h" -#endif -#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS) -# include "EmbedTexturesProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS -# include "FindInvalidDataProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS -# include "FindDegenerates.h" -#endif -#ifndef ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS -# include "SortByPTypeProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS -# include "ComputeUVMappingProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS -# include "TextureTransform.h" -#endif -#ifndef ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS -# include "FindInstancesProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS -# include "OptimizeMeshes.h" -#endif -#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS -# include "OptimizeGraph.h" -#endif -#ifndef ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS -# include "SplitByBoneCountProcess.h" -#endif -#ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS -# include "DeboneProcess.h" -#endif -#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) -# include "ScaleProcess.h" -#endif - -namespace Assimp { - -// ------------------------------------------------------------------------------------------------ -void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out) -{ - // ---------------------------------------------------------------------------- - // Add an instance of each post processing step here in the order - // of sequence it is executed. Steps that are added here are not - // validated - as RegisterPPStep() does - all dependencies must be given. - // ---------------------------------------------------------------------------- - out.reserve(31); -#if (!defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS) - out.push_back( new MakeLeftHandedProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS) - out.push_back( new FlipUVsProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS) - out.push_back( new FlipWindingOrderProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_REMOVEVC_PROCESS) - out.push_back( new RemoveVCProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS) - out.push_back( new RemoveRedundantMatsProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS) - out.push_back( new EmbedTexturesProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS) - out.push_back( new FindInstancesProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS) - out.push_back( new OptimizeGraphProcess()); -#endif -#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS - out.push_back( new ComputeUVMappingProcess()); -#endif -#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS - out.push_back( new TextureTransformStep()); -#endif -#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) - out.push_back( new ScaleProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS) - out.push_back( new PretransformVertices()); -#endif -#if (!defined ASSIMP_BUILD_NO_TRIANGULATE_PROCESS) - out.push_back( new TriangulateProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS) - //find degenerates should run after triangulation (to sort out small - //generated triangles) but before sort by p types (in case there are lines - //and points generated and inserted into a mesh) - out.push_back( new FindDegeneratesProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS) - out.push_back( new SortByPTypeProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS) - out.push_back( new FindInvalidDataProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS) - out.push_back( new OptimizeMeshesProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS) - out.push_back( new FixInfacingNormalsProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS) - out.push_back( new SplitByBoneCountProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS) - out.push_back( new SplitLargeMeshesProcess_Triangle()); -#endif -#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS) - out.push_back( new DropFaceNormalsProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS) - out.push_back( new GenFaceNormalsProcess()); -#endif - // ......................................................................... - // DON'T change the order of these five .. - // XXX this is actually a design weakness that dates back to the time - // when Importer would maintain the postprocessing step list exclusively. - // Now that others access it too, we need a better solution. - out.push_back( new ComputeSpatialSortProcess()); - // ......................................................................... - -#if (!defined ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS) - out.push_back( new GenVertexNormalsProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS) - out.push_back( new CalcTangentsProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_JOINVERTICES_PROCESS) - out.push_back( new JoinVerticesProcess()); -#endif - - // ......................................................................... - out.push_back( new DestroySpatialSortProcess()); - // ......................................................................... - -#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS) - out.push_back( new SplitLargeMeshesProcess_Vertex()); -#endif -#if (!defined ASSIMP_BUILD_NO_DEBONE_PROCESS) - out.push_back( new DeboneProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS) - out.push_back( new LimitBoneWeightsProcess()); -#endif -#if (!defined ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS) - out.push_back( new ImproveCacheLocalityProcess()); -#endif -} - -} diff --git a/thirdparty/assimp/code/PretransformVertices.cpp b/thirdparty/assimp/code/PretransformVertices.cpp deleted file mode 100644 index 52001a0578..0000000000 --- a/thirdparty/assimp/code/PretransformVertices.cpp +++ /dev/null @@ -1,728 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file PretransformVertices.cpp - * @brief Implementation of the "PretransformVertices" post processing step -*/ - - -#include "PretransformVertices.h" -#include "ProcessHelper.h" -#include -#include - -using namespace Assimp; - -// some array offsets -#define AI_PTVS_VERTEX 0x0 -#define AI_PTVS_FACE 0x1 - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -PretransformVertices::PretransformVertices() -: configKeepHierarchy (false) -, configNormalize(false) -, configTransform(false) -, configTransformation() -, mConfigPointCloud( false ) { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -PretransformVertices::~PretransformVertices() { - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool PretransformVertices::IsActive( unsigned int pFlags) const -{ - return (pFlags & aiProcess_PreTransformVertices) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Setup import configuration -void PretransformVertices::SetupProperties(const Importer* pImp) -{ - // Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE, - // AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION - configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY,0)); - configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE,0)); - configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION,0)); - - configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4()); - - mConfigPointCloud = pImp->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); -} - -// ------------------------------------------------------------------------------------------------ -// Count the number of nodes -unsigned int PretransformVertices::CountNodes( aiNode* pcNode ) -{ - unsigned int iRet = 1; - for (unsigned int i = 0;i < pcNode->mNumChildren;++i) - { - iRet += CountNodes(pcNode->mChildren[i]); - } - return iRet; -} - -// ------------------------------------------------------------------------------------------------ -// Get a bitwise combination identifying the vertex format of a mesh -unsigned int PretransformVertices::GetMeshVFormat( aiMesh* pcMesh ) -{ - // the vertex format is stored in aiMesh::mBones for later retrieval. - // there isn't a good reason to compute it a few hundred times - // from scratch. The pointer is unused as animations are lost - // during PretransformVertices. - if (pcMesh->mBones) - return (unsigned int)(uint64_t)pcMesh->mBones; - - - const unsigned int iRet = GetMeshVFormatUnique(pcMesh); - - // store the value for later use - pcMesh->mBones = (aiBone**)(uint64_t)iRet; - return iRet; -} - -// ------------------------------------------------------------------------------------------------ -// Count the number of vertices in the whole scene and a given -// material index -void PretransformVertices::CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, - unsigned int iVFormat, unsigned int* piFaces, unsigned int* piVertices) -{ - for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) - { - aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ]; - if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh)) - { - *piVertices += pcMesh->mNumVertices; - *piFaces += pcMesh->mNumFaces; - } - } - for (unsigned int i = 0;i < pcNode->mNumChildren;++i) - { - CountVerticesAndFaces(pcScene,pcNode->mChildren[i],iMat, - iVFormat,piFaces,piVertices); - } -} - -// ------------------------------------------------------------------------------------------------ -// Collect vertex/face data -void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, - unsigned int iVFormat, aiMesh* pcMeshOut, - unsigned int aiCurrent[2], unsigned int* num_refs) -{ - // No need to multiply if there's no transformation - const bool identity = pcNode->mTransformation.IsIdentity(); - for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) - { - aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ]; - if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh)) - { - // Decrement mesh reference counter - unsigned int& num_ref = num_refs[pcNode->mMeshes[i]]; - ai_assert(0 != num_ref); - --num_ref; - // Save the name of the last mesh - if (num_ref==0) - { - pcMeshOut->mName = pcMesh->mName; - } - - if (identity) { - // copy positions without modifying them - ::memcpy(pcMeshOut->mVertices + aiCurrent[AI_PTVS_VERTEX], - pcMesh->mVertices, - pcMesh->mNumVertices * sizeof(aiVector3D)); - - if (iVFormat & 0x2) { - // copy normals without modifying them - ::memcpy(pcMeshOut->mNormals + aiCurrent[AI_PTVS_VERTEX], - pcMesh->mNormals, - pcMesh->mNumVertices * sizeof(aiVector3D)); - } - if (iVFormat & 0x4) - { - // copy tangents without modifying them - ::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX], - pcMesh->mTangents, - pcMesh->mNumVertices * sizeof(aiVector3D)); - // copy bitangents without modifying them - ::memcpy(pcMeshOut->mBitangents + aiCurrent[AI_PTVS_VERTEX], - pcMesh->mBitangents, - pcMesh->mNumVertices * sizeof(aiVector3D)); - } - } - else - { - // copy positions, transform them to worldspace - for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { - pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n]; - } - aiMatrix4x4 mWorldIT = pcNode->mTransformation; - mWorldIT.Inverse().Transpose(); - - // TODO: implement Inverse() for aiMatrix3x3 - aiMatrix3x3 m = aiMatrix3x3(mWorldIT); - - if (iVFormat & 0x2) - { - // copy normals, transform them to worldspace - for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { - pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] = - (m * pcMesh->mNormals[n]).Normalize(); - } - } - if (iVFormat & 0x4) - { - // copy tangents and bitangents, transform them to worldspace - for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { - pcMeshOut->mTangents [aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mTangents[n]).Normalize(); - pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mBitangents[n]).Normalize(); - } - } - } - unsigned int p = 0; - while (iVFormat & (0x100 << p)) - { - // copy texture coordinates - memcpy(pcMeshOut->mTextureCoords[p] + aiCurrent[AI_PTVS_VERTEX], - pcMesh->mTextureCoords[p], - pcMesh->mNumVertices * sizeof(aiVector3D)); - ++p; - } - p = 0; - while (iVFormat & (0x1000000 << p)) - { - // copy vertex colors - memcpy(pcMeshOut->mColors[p] + aiCurrent[AI_PTVS_VERTEX], - pcMesh->mColors[p], - pcMesh->mNumVertices * sizeof(aiColor4D)); - ++p; - } - // now we need to copy all faces. since we will delete the source mesh afterwards, - // we don't need to reallocate the array of indices except if this mesh is - // referenced multiple times. - for (unsigned int planck = 0;planck < pcMesh->mNumFaces;++planck) - { - aiFace& f_src = pcMesh->mFaces[planck]; - aiFace& f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck]; - - const unsigned int num_idx = f_src.mNumIndices; - - f_dst.mNumIndices = num_idx; - - unsigned int* pi; - if (!num_ref) { /* if last time the mesh is referenced -> no reallocation */ - pi = f_dst.mIndices = f_src.mIndices; - - // offset all vertex indices - for (unsigned int hahn = 0; hahn < num_idx;++hahn){ - pi[hahn] += aiCurrent[AI_PTVS_VERTEX]; - } - } - else { - pi = f_dst.mIndices = new unsigned int[num_idx]; - - // copy and offset all vertex indices - for (unsigned int hahn = 0; hahn < num_idx;++hahn){ - pi[hahn] = f_src.mIndices[hahn] + aiCurrent[AI_PTVS_VERTEX]; - } - } - - // Update the mPrimitiveTypes member of the mesh - switch (pcMesh->mFaces[planck].mNumIndices) - { - case 0x1: - pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POINT; - break; - case 0x2: - pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_LINE; - break; - case 0x3: - pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; - break; - default: - pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POLYGON; - break; - }; - } - aiCurrent[AI_PTVS_VERTEX] += pcMesh->mNumVertices; - aiCurrent[AI_PTVS_FACE] += pcMesh->mNumFaces; - } - } - - // append all children of us - for (unsigned int i = 0;i < pcNode->mNumChildren;++i) { - CollectData(pcScene,pcNode->mChildren[i],iMat, - iVFormat,pcMeshOut,aiCurrent,num_refs); - } -} - -// ------------------------------------------------------------------------------------------------ -// Get a list of all vertex formats that occur for a given material index -// The output list contains duplicate elements -void PretransformVertices::GetVFormatList( aiScene* pcScene, unsigned int iMat, - std::list& aiOut) -{ - for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) - { - aiMesh* pcMesh = pcScene->mMeshes[ i ]; - if (iMat == pcMesh->mMaterialIndex) { - aiOut.push_back(GetMeshVFormat(pcMesh)); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Compute the absolute transformation matrices of each node -void PretransformVertices::ComputeAbsoluteTransform( aiNode* pcNode ) -{ - if (pcNode->mParent) { - pcNode->mTransformation = pcNode->mParent->mTransformation*pcNode->mTransformation; - } - - for (unsigned int i = 0;i < pcNode->mNumChildren;++i) { - ComputeAbsoluteTransform(pcNode->mChildren[i]); - } -} - -// ------------------------------------------------------------------------------------------------ -// Apply the node transformation to a mesh -void PretransformVertices::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat) -{ - // Check whether we need to transform the coordinates at all - if (!mat.IsIdentity()) { - - if (mesh->HasPositions()) { - for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { - mesh->mVertices[i] = mat * mesh->mVertices[i]; - } - } - if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) { - aiMatrix4x4 mWorldIT = mat; - mWorldIT.Inverse().Transpose(); - - // TODO: implement Inverse() for aiMatrix3x3 - aiMatrix3x3 m = aiMatrix3x3(mWorldIT); - - if (mesh->HasNormals()) { - for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { - mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize(); - } - } - if (mesh->HasTangentsAndBitangents()) { - for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { - mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize(); - mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize(); - } - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Simple routine to build meshes in worldspace, no further optimization -void PretransformVertices::BuildWCSMeshes(std::vector& out, aiMesh** in, - unsigned int numIn, aiNode* node) -{ - // NOTE: - // aiMesh::mNumBones store original source mesh, or UINT_MAX if not a copy - // aiMesh::mBones store reference to abs. transform we multiplied with - - // process meshes - for (unsigned int i = 0; i < node->mNumMeshes;++i) { - aiMesh* mesh = in[node->mMeshes[i]]; - - // check whether we can operate on this mesh - if (!mesh->mBones || *reinterpret_cast(mesh->mBones) == node->mTransformation) { - // yes, we can. - mesh->mBones = reinterpret_cast (&node->mTransformation); - mesh->mNumBones = UINT_MAX; - } - else { - - // try to find us in the list of newly created meshes - for (unsigned int n = 0; n < out.size(); ++n) { - aiMesh* ctz = out[n]; - if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast(ctz->mBones) == node->mTransformation) { - - // ok, use this one. Update node mesh index - node->mMeshes[i] = numIn + n; - } - } - if (node->mMeshes[i] < numIn) { - // Worst case. Need to operate on a full copy of the mesh - ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms"); - aiMesh* ntz; - - const unsigned int tmp = mesh->mNumBones; // - mesh->mNumBones = 0; - SceneCombiner::Copy(&ntz,mesh); - mesh->mNumBones = tmp; - - ntz->mNumBones = node->mMeshes[i]; - ntz->mBones = reinterpret_cast (&node->mTransformation); - - out.push_back(ntz); - - node->mMeshes[i] = static_cast(numIn + out.size() - 1); - } - } - } - - // call children - for (unsigned int i = 0; i < node->mNumChildren;++i) - BuildWCSMeshes(out,in,numIn,node->mChildren[i]); -} - -// ------------------------------------------------------------------------------------------------ -// Reset transformation matrices to identity -void PretransformVertices::MakeIdentityTransform(aiNode* nd) -{ - nd->mTransformation = aiMatrix4x4(); - - // call children - for (unsigned int i = 0; i < nd->mNumChildren;++i) - MakeIdentityTransform(nd->mChildren[i]); -} - -// ------------------------------------------------------------------------------------------------ -// Build reference counters for all meshes -void PretransformVertices::BuildMeshRefCountArray(aiNode* nd, unsigned int * refs) -{ - for (unsigned int i = 0; i< nd->mNumMeshes;++i) - refs[nd->mMeshes[i]]++; - - // call children - for (unsigned int i = 0; i < nd->mNumChildren;++i) - BuildMeshRefCountArray(nd->mChildren[i],refs); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void PretransformVertices::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("PretransformVerticesProcess begin"); - - // Return immediately if we have no meshes - if (!pScene->mNumMeshes) - return; - - const unsigned int iOldMeshes = pScene->mNumMeshes; - const unsigned int iOldAnimationChannels = pScene->mNumAnimations; - const unsigned int iOldNodes = CountNodes(pScene->mRootNode); - - if(configTransform) { - pScene->mRootNode->mTransformation = configTransformation; - } - - // first compute absolute transformation matrices for all nodes - ComputeAbsoluteTransform(pScene->mRootNode); - - // Delete aiMesh::mBones for all meshes. The bones are - // removed during this step and we need the pointer as - // temporary storage - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { - aiMesh* mesh = pScene->mMeshes[i]; - - for (unsigned int a = 0; a < mesh->mNumBones;++a) - delete mesh->mBones[a]; - - delete[] mesh->mBones; - mesh->mBones = NULL; - } - - // now build a list of output meshes - std::vector apcOutMeshes; - - // Keep scene hierarchy? It's an easy job in this case ... - // we go on and transform all meshes, if one is referenced by nodes - // with different absolute transformations a depth copy of the mesh - // is required. - if( configKeepHierarchy ) { - - // Hack: store the matrix we're transforming a mesh with in aiMesh::mBones - BuildWCSMeshes(apcOutMeshes,pScene->mMeshes,pScene->mNumMeshes, pScene->mRootNode); - - // ... if new meshes have been generated, append them to the end of the scene - if (apcOutMeshes.size() > 0) { - aiMesh** npp = new aiMesh*[pScene->mNumMeshes + apcOutMeshes.size()]; - - memcpy(npp,pScene->mMeshes,sizeof(aiMesh*)*pScene->mNumMeshes); - memcpy(npp+pScene->mNumMeshes,&apcOutMeshes[0],sizeof(aiMesh*)*apcOutMeshes.size()); - - pScene->mNumMeshes += static_cast(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( 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 aiVFormats; - - std::vector 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::const_iterator j = aiVFormats.begin();j != aiVFormats.end();++j) { - unsigned int iVertices = 0; - unsigned int iFaces = 0; - CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices); - if (0 != iFaces && 0 != iVertices) - { - apcOutMeshes.push_back(new aiMesh()); - aiMesh* pcMesh = apcOutMeshes.back(); - pcMesh->mNumFaces = iFaces; - pcMesh->mNumVertices = iVertices; - pcMesh->mFaces = new aiFace[iFaces]; - pcMesh->mVertices = new aiVector3D[iVertices]; - pcMesh->mMaterialIndex = i; - if ((*j) & 0x2)pcMesh->mNormals = new aiVector3D[iVertices]; - if ((*j) & 0x4) - { - pcMesh->mTangents = new aiVector3D[iVertices]; - pcMesh->mBitangents = new aiVector3D[iVertices]; - } - iFaces = 0; - while ((*j) & (0x100 << iFaces)) - { - pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices]; - if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3; - else pcMesh->mNumUVComponents[iFaces] = 2; - iFaces++; - } - iFaces = 0; - while ((*j) & (0x1000000 << iFaces)) - pcMesh->mColors[iFaces++] = new aiColor4D[iVertices]; - - // fill the mesh ... - unsigned int aiTemp[2] = {0,0}; - CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp,&s[0]); - } - } - } - - // If no meshes are referenced in the node graph it is possible that we get no output meshes. - if (apcOutMeshes.empty()) { - - throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by any nodes"); - } - else - { - // now delete all meshes in the scene and build a new mesh list - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - { - aiMesh* mesh = pScene->mMeshes[i]; - mesh->mNumBones = 0; - mesh->mBones = NULL; - - // we're reusing the face index arrays. avoid destruction - for (unsigned int a = 0; a < mesh->mNumFaces; ++a) { - mesh->mFaces[a].mNumIndices = 0; - mesh->mFaces[a].mIndices = NULL; - } - - delete mesh; - - // Invalidate the contents of the old mesh array. We will most - // likely have less output meshes now, so the last entries of - // the mesh array are not overridden. We set them to NULL to - // make sure the developer gets notified when his application - // attempts to access these fields ... - mesh = NULL; - } - - // It is impossible that we have more output meshes than - // input meshes, so we can easily reuse the old mesh array - pScene->mNumMeshes = (unsigned int)apcOutMeshes.size(); - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { - pScene->mMeshes[i] = apcOutMeshes[i]; - } - } - } - - // remove all animations from the scene - for (unsigned int i = 0; i < pScene->mNumAnimations;++i) - delete pScene->mAnimations[i]; - delete[] pScene->mAnimations; - - pScene->mAnimations = NULL; - pScene->mNumAnimations = 0; - - // --- we need to keep all cameras and lights - for (unsigned int i = 0; i < pScene->mNumCameras;++i) - { - aiCamera* cam = pScene->mCameras[i]; - const aiNode* nd = pScene->mRootNode->FindNode(cam->mName); - ai_assert(NULL != nd); - - // multiply all properties of the camera with the absolute - // transformation of the corresponding node - cam->mPosition = nd->mTransformation * cam->mPosition; - cam->mLookAt = aiMatrix3x3( nd->mTransformation ) * cam->mLookAt; - cam->mUp = aiMatrix3x3( nd->mTransformation ) * cam->mUp; - } - - for (unsigned int i = 0; i < pScene->mNumLights;++i) - { - aiLight* l = pScene->mLights[i]; - const aiNode* nd = pScene->mRootNode->FindNode(l->mName); - ai_assert(NULL != nd); - - // multiply all properties of the camera with the absolute - // transformation of the corresponding node - l->mPosition = nd->mTransformation * l->mPosition; - l->mDirection = aiMatrix3x3( nd->mTransformation ) * l->mDirection; - l->mUp = aiMatrix3x3( nd->mTransformation ) * l->mUp; - } - - if( !configKeepHierarchy ) { - - // now delete all nodes in the scene and build a new - // flat node graph with a root node and some level 1 children - aiNode* newRoot = new aiNode(); - newRoot->mName = pScene->mRootNode->mName; - delete pScene->mRootNode; - pScene->mRootNode = newRoot; - - if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras) - { - pScene->mRootNode->mNumMeshes = 1; - pScene->mRootNode->mMeshes = new unsigned int[1]; - pScene->mRootNode->mMeshes[0] = 0; - } - else - { - pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras; - aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren]; - - // generate mesh nodes - for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes) - { - aiNode* pcNode = new aiNode(); - *nodes = pcNode; - pcNode->mParent = pScene->mRootNode; - pcNode->mName = pScene->mMeshes[i]->mName; - - // setup mesh indices - pcNode->mNumMeshes = 1; - pcNode->mMeshes = new unsigned int[1]; - pcNode->mMeshes[0] = i; - } - // generate light nodes - for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes) - { - aiNode* pcNode = new aiNode(); - *nodes = pcNode; - pcNode->mParent = pScene->mRootNode; - pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u",i); - pScene->mLights[i]->mName = pcNode->mName; - } - // generate camera nodes - for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes) - { - aiNode* pcNode = new aiNode(); - *nodes = pcNode; - pcNode->mParent = pScene->mRootNode; - pcNode->mName.length = ::ai_snprintf(pcNode->mName.data,MAXLEN,"cam_%u",i); - pScene->mCameras[i]->mName = pcNode->mName; - } - } - } - else { - // ... and finally set the transformation matrix of all nodes to identity - MakeIdentityTransform(pScene->mRootNode); - } - - if (configNormalize) { - // compute the boundary of all meshes - aiVector3D min,max; - MinMaxChooser ()(min,max); - - for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { - aiMesh* m = pScene->mMeshes[a]; - for (unsigned int i = 0; i < m->mNumVertices;++i) { - min = std::min(m->mVertices[i],min); - max = std::max(m->mVertices[i],max); - } - } - - // find the dominant axis - aiVector3D d = max-min; - const ai_real div = std::max(d.x,std::max(d.y,d.z))*ai_real( 0.5); - - d = min + d * (ai_real)0.5; - for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { - aiMesh* m = pScene->mMeshes[a]; - for (unsigned int i = 0; i < m->mNumVertices;++i) { - m->mVertices[i] = (m->mVertices[i]-d)/div; - } - } - } - - // print statistics - if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished"); - - ASSIMP_LOG_INFO_F("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (", - CountNodes(pScene->mRootNode) ," output nodes)" ); - ASSIMP_LOG_INFO_F("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras." ); - ASSIMP_LOG_INFO_F("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")"); - } -} diff --git a/thirdparty/assimp/code/PretransformVertices.h b/thirdparty/assimp/code/PretransformVertices.h deleted file mode 100644 index b7329af130..0000000000 --- a/thirdparty/assimp/code/PretransformVertices.h +++ /dev/null @@ -1,163 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file 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 -#include - -struct aiNode; -class PretransformVerticesTest; -namespace Assimp { - -// --------------------------------------------------------------------------- -/** The PretransformVertices pre-transforms all vertices in the node tree - * and removes the whole graph. The output is a list of meshes, one for - * each material. -*/ -class ASSIMP_API PretransformVertices : public BaseProcess { -public: - PretransformVertices (); - ~PretransformVertices (); - - // ------------------------------------------------------------------- - // Check whether step is active - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - // Execute step on a given scene - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - // Setup import settings - void SetupProperties(const Importer* pImp); - - // ------------------------------------------------------------------- - /** @brief Toggle the 'keep hierarchy' option - * @param d hm ... difficult to guess what this means, hu!? - */ - void KeepHierarchy(bool d) { - configKeepHierarchy = d; - } - - // ------------------------------------------------------------------- - /** @brief Check whether 'keep hierarchy' is currently enabled. - * @return ... - */ - bool IsHierarchyKept() const { - return configKeepHierarchy; - } - -private: - // ------------------------------------------------------------------- - // Count the number of nodes - unsigned int CountNodes( aiNode* pcNode ); - - // ------------------------------------------------------------------- - // Get a bitwise combination identifying the vertex format of a mesh - unsigned int GetMeshVFormat(aiMesh* pcMesh); - - // ------------------------------------------------------------------- - // Count the number of vertices in the whole scene and a given - // material index - void CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, - unsigned int iMat, - unsigned int iVFormat, - unsigned int* piFaces, - unsigned int* piVertices); - - // ------------------------------------------------------------------- - // Collect vertex/face data - void CollectData( aiScene* pcScene, aiNode* pcNode, - unsigned int iMat, - unsigned int iVFormat, - aiMesh* pcMeshOut, - unsigned int aiCurrent[2], - unsigned int* num_refs); - - // ------------------------------------------------------------------- - // Get a list of all vertex formats that occur for a given material - // The output list contains duplicate elements - void GetVFormatList( aiScene* pcScene, unsigned int iMat, - std::list& 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& out, aiMesh** in, - unsigned int numIn, aiNode* node); - - // ------------------------------------------------------------------- - // Apply the node transformation to a mesh - void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat); - - // ------------------------------------------------------------------- - // Reset transformation matrices to identity - void MakeIdentityTransform(aiNode* nd); - - // ------------------------------------------------------------------- - // Build reference counters for all meshes - void BuildMeshRefCountArray(aiNode* nd, unsigned int * refs); - - - - //! Configuration option: keep scene hierarchy as long as possible - bool configKeepHierarchy; - bool configNormalize; - bool configTransform; - aiMatrix4x4 configTransformation; - bool mConfigPointCloud; -}; - -} // end of namespace Assimp - -#endif // !!AI_GENFACENORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/ProcessHelper.cpp b/thirdparty/assimp/code/ProcessHelper.cpp deleted file mode 100644 index 59869fdff7..0000000000 --- a/thirdparty/assimp/code/ProcessHelper.cpp +++ /dev/null @@ -1,443 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/// @file ProcessHelper.cpp -/** Implement shared utility functions for postprocessing steps */ - - -#include "ProcessHelper.h" - - -#include - -namespace Assimp { - -// ------------------------------------------------------------------------------- -void ConvertListToStrings(const std::string& in, std::list& out) -{ - const char* s = in.c_str(); - while (*s) { - SkipSpacesAndLineEnd(&s); - if (*s == '\'') { - const char* base = ++s; - while (*s != '\'') { - ++s; - if (*s == '\0') { - ASSIMP_LOG_ERROR("ConvertListToString: String list is ill-formatted"); - return; - } - } - out.push_back(std::string(base,(size_t)(s-base))); - ++s; - } - else { - out.push_back(GetNextToken(s)); - } - } -} - -// ------------------------------------------------------------------------------- -void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max, - const aiMatrix4x4& m) -{ - min = aiVector3D ( ai_real( 10e10 ), ai_real( 10e10 ), ai_real( 10e10 ) ); - max = aiVector3D ( ai_real( -10e10 ), ai_real( -10e10 ), ai_real( -10e10 ) ); - for (unsigned int i = 0;i < mesh->mNumVertices;++i) - { - const aiVector3D v = m * mesh->mVertices[i]; - min = std::min(v,min); - max = std::max(v,max); - } -} - -// ------------------------------------------------------------------------------- -void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max) -{ - ArrayBounds(mesh->mVertices,mesh->mNumVertices, min,max); - out = min + (max-min)*(ai_real)0.5; -} - -// ------------------------------------------------------------------------------- -void FindSceneCenter (aiScene* scene, aiVector3D& out, aiVector3D& min, aiVector3D& max) { - if ( NULL == scene ) { - return; - } - - if ( 0 == scene->mNumMeshes ) { - return; - } - FindMeshCenter(scene->mMeshes[0], out, min, max); - for (unsigned int i = 1; i < scene->mNumMeshes; ++i) { - aiVector3D tout, tmin, tmax; - FindMeshCenter(scene->mMeshes[i], tout, tmin, tmax); - if (min[0] > tmin[0]) min[0] = tmin[0]; - if (min[1] > tmin[1]) min[1] = tmin[1]; - if (min[2] > tmin[2]) min[2] = tmin[2]; - if (max[0] < tmax[0]) max[0] = tmax[0]; - if (max[1] < tmax[1]) max[1] = tmax[1]; - if (max[2] < tmax[2]) max[2] = tmax[2]; - } - out = min + (max-min)*(ai_real)0.5; -} - - -// ------------------------------------------------------------------------------- -void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min, - aiVector3D& max, const aiMatrix4x4& m) -{ - FindAABBTransformed(mesh,min,max,m); - out = min + (max-min)*(ai_real)0.5; -} - -// ------------------------------------------------------------------------------- -void FindMeshCenter (aiMesh* mesh, aiVector3D& out) -{ - aiVector3D min,max; - FindMeshCenter(mesh,out,min,max); -} - -// ------------------------------------------------------------------------------- -void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, - const aiMatrix4x4& m) -{ - aiVector3D min,max; - FindMeshCenterTransformed(mesh,out,min,max,m); -} - -// ------------------------------------------------------------------------------- -ai_real ComputePositionEpsilon(const aiMesh* pMesh) -{ - const ai_real epsilon = ai_real( 1e-4 ); - - // calculate the position bounds so we have a reliable epsilon to check position differences against - aiVector3D minVec, maxVec; - ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,minVec,maxVec); - return (maxVec - minVec).Length() * epsilon; -} - -// ------------------------------------------------------------------------------- -ai_real ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num) -{ - ai_assert( NULL != pMeshes ); - - const ai_real epsilon = ai_real( 1e-4 ); - - // calculate the position bounds so we have a reliable epsilon to check position differences against - aiVector3D minVec, maxVec, mi, ma; - MinMaxChooser()(minVec,maxVec); - - for (size_t a = 0; a < num; ++a) { - const aiMesh* pMesh = pMeshes[a]; - ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,mi,ma); - - minVec = std::min(minVec,mi); - maxVec = std::max(maxVec,ma); - } - return (maxVec - minVec).Length() * epsilon; -} - - -// ------------------------------------------------------------------------------- -unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh) -{ - ai_assert(NULL != pcMesh); - - // FIX: the hash may never be 0. Otherwise a comparison against - // nullptr could be successful - unsigned int iRet = 1; - - // normals - if (pcMesh->HasNormals())iRet |= 0x2; - // tangents and bitangents - if (pcMesh->HasTangentsAndBitangents())iRet |= 0x4; - -#ifdef BOOST_STATIC_ASSERT - BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS); - BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS); -#endif - - // texture coordinates - unsigned int p = 0; - while (pcMesh->HasTextureCoords(p)) - { - iRet |= (0x100 << p); - if (3 == pcMesh->mNumUVComponents[p]) - iRet |= (0x10000 << p); - - ++p; - } - // vertex colors - p = 0; - while (pcMesh->HasVertexColors(p))iRet |= (0x1000000 << p++); - return iRet; -} - -// ------------------------------------------------------------------------------- -VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) -{ - if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) { - return NULL; - } - - VertexWeightTable* avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices]; - for (unsigned int i = 0; i < pMesh->mNumBones;++i) { - - aiBone* bone = pMesh->mBones[i]; - for (unsigned int a = 0; a < bone->mNumWeights;++a) { - const aiVertexWeight& weight = bone->mWeights[a]; - avPerVertexWeights[weight.mVertexId].push_back( std::pair(i,weight.mWeight) ); - } - } - return avPerVertexWeights; -} - - -// ------------------------------------------------------------------------------- -const char* TextureTypeToString(aiTextureType in) -{ - switch (in) - { - case aiTextureType_NONE: - return "n/a"; - case aiTextureType_DIFFUSE: - return "Diffuse"; - case aiTextureType_SPECULAR: - return "Specular"; - case aiTextureType_AMBIENT: - return "Ambient"; - case aiTextureType_EMISSIVE: - return "Emissive"; - case aiTextureType_OPACITY: - return "Opacity"; - case aiTextureType_NORMALS: - return "Normals"; - case aiTextureType_HEIGHT: - return "Height"; - case aiTextureType_SHININESS: - return "Shininess"; - case aiTextureType_DISPLACEMENT: - return "Displacement"; - case aiTextureType_LIGHTMAP: - return "Lightmap"; - case aiTextureType_REFLECTION: - return "Reflection"; - case aiTextureType_UNKNOWN: - return "Unknown"; - default: - break; - } - - ai_assert(false); - return "BUG"; -} - -// ------------------------------------------------------------------------------- -const char* MappingTypeToString(aiTextureMapping in) -{ - switch (in) - { - case aiTextureMapping_UV: - return "UV"; - case aiTextureMapping_BOX: - return "Box"; - case aiTextureMapping_SPHERE: - return "Sphere"; - case aiTextureMapping_CYLINDER: - return "Cylinder"; - case aiTextureMapping_PLANE: - return "Plane"; - case aiTextureMapping_OTHER: - return "Other"; - default: - break; - } - - ai_assert(false); - return "BUG"; -} - - -// ------------------------------------------------------------------------------- -aiMesh* MakeSubmesh(const aiMesh *pMesh, const std::vector &subMeshFaces, unsigned int subFlags) -{ - aiMesh *oMesh = new aiMesh(); - std::vector vMap(pMesh->mNumVertices,UINT_MAX); - - size_t numSubVerts = 0; - size_t numSubFaces = subMeshFaces.size(); - - for(unsigned int i=0;imFaces[subMeshFaces[i]]; - - for(unsigned int j=0;j(numSubVerts++); - } - } - } - - oMesh->mName = pMesh->mName; - - oMesh->mMaterialIndex = pMesh->mMaterialIndex; - oMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes; - - // create all the arrays for this mesh if the old mesh contained them - - oMesh->mNumFaces = static_cast(subMeshFaces.size()); - oMesh->mNumVertices = static_cast(numSubVerts); - oMesh->mVertices = new aiVector3D[numSubVerts]; - if( pMesh->HasNormals() ) { - oMesh->mNormals = new aiVector3D[numSubVerts]; - } - - if( pMesh->HasTangentsAndBitangents() ) { - oMesh->mTangents = new aiVector3D[numSubVerts]; - oMesh->mBitangents = new aiVector3D[numSubVerts]; - } - - for( size_t a = 0; pMesh->HasTextureCoords(static_cast(a)) ; ++a ) { - oMesh->mTextureCoords[a] = new aiVector3D[numSubVerts]; - oMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a]; - } - - for( size_t a = 0; pMesh->HasVertexColors( static_cast(a)); ++a ) { - oMesh->mColors[a] = new aiColor4D[numSubVerts]; - } - - // and copy over the data, generating faces with linear indices along the way - oMesh->mFaces = new aiFace[numSubFaces]; - - for(unsigned int a = 0; a < numSubFaces; ++a ) { - - const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]]; - aiFace& dstFace = oMesh->mFaces[a]; - dstFace.mNumIndices = srcFace.mNumIndices; - dstFace.mIndices = new unsigned int[dstFace.mNumIndices]; - - // accumulate linearly all the vertices of the source face - for( size_t b = 0; b < dstFace.mNumIndices; ++b ) { - dstFace.mIndices[b] = vMap[srcFace.mIndices[b]]; - } - } - - for(unsigned int srcIndex = 0; srcIndex < pMesh->mNumVertices; ++srcIndex ) { - unsigned int nvi = vMap[srcIndex]; - if(nvi==UINT_MAX) { - continue; - } - - oMesh->mVertices[nvi] = pMesh->mVertices[srcIndex]; - if( pMesh->HasNormals() ) { - oMesh->mNormals[nvi] = pMesh->mNormals[srcIndex]; - } - - if( pMesh->HasTangentsAndBitangents() ) { - oMesh->mTangents[nvi] = pMesh->mTangents[srcIndex]; - oMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex]; - } - for( size_t c = 0, cc = pMesh->GetNumUVChannels(); c < cc; ++c ) { - oMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex]; - } - for( size_t c = 0, cc = pMesh->GetNumColorChannels(); c < cc; ++c ) { - oMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex]; - } - } - - if(~subFlags&AI_SUBMESH_FLAGS_SANS_BONES) { - std::vector subBones(pMesh->mNumBones,0); - - for(unsigned int a=0;amNumBones;++a) { - const aiBone* bone = pMesh->mBones[a]; - - for(unsigned int b=0;bmNumWeights;b++) { - unsigned int v = vMap[bone->mWeights[b].mVertexId]; - - if(v!=UINT_MAX) { - subBones[a]++; - } - } - } - - for(unsigned int a=0;amNumBones;++a) { - if(subBones[a]>0) { - oMesh->mNumBones++; - } - } - - if(oMesh->mNumBones) { - oMesh->mBones = new aiBone*[oMesh->mNumBones](); - unsigned int nbParanoia = oMesh->mNumBones; - - oMesh->mNumBones = 0; //rewind - - for(unsigned int a=0;amNumBones;++a) { - if(subBones[a]==0) { - continue; - } - aiBone *newBone = new aiBone; - oMesh->mBones[oMesh->mNumBones++] = newBone; - - const aiBone* bone = pMesh->mBones[a]; - - newBone->mName = bone->mName; - newBone->mOffsetMatrix = bone->mOffsetMatrix; - newBone->mWeights = new aiVertexWeight[subBones[a]]; - - for(unsigned int b=0;bmNumWeights;b++) { - const unsigned int v = vMap[bone->mWeights[b].mVertexId]; - - if(v!=UINT_MAX) { - aiVertexWeight w(v,bone->mWeights[b].mWeight); - newBone->mWeights[newBone->mNumWeights++] = w; - } - } - } - - ai_assert(nbParanoia==oMesh->mNumBones); - (void)nbParanoia; // remove compiler warning on release build - } - } - - return oMesh; -} - -} // namespace Assimp diff --git a/thirdparty/assimp/code/ProcessHelper.h b/thirdparty/assimp/code/ProcessHelper.h deleted file mode 100644 index c59f3217bf..0000000000 --- a/thirdparty/assimp/code/ProcessHelper.h +++ /dev/null @@ -1,386 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -#ifndef AI_PROCESS_HELPER_H_INCLUDED -#define AI_PROCESS_HELPER_H_INCLUDED - -#include -#include -#include -#include -#include -#include - -#include -#include "BaseProcess.h" -#include - -#include - -// ------------------------------------------------------------------------------- -// Some extensions to std namespace. Mainly std::min and std::max for all -// flat data types in the aiScene. They're used to quickly determine the -// min/max bounds of data arrays. -#ifdef __cplusplus -namespace std { - - // std::min for aiVector3D - template - inline ::aiVector3t min (const ::aiVector3t& a, const ::aiVector3t& b) { - return ::aiVector3t (min(a.x,b.x),min(a.y,b.y),min(a.z,b.z)); - } - - // std::max for aiVector3t - template - inline ::aiVector3t max (const ::aiVector3t& a, const ::aiVector3t& b) { - return ::aiVector3t (max(a.x,b.x),max(a.y,b.y),max(a.z,b.z)); - } - - // std::min for aiVector2t - template - inline ::aiVector2t min (const ::aiVector2t& a, const ::aiVector2t& b) { - return ::aiVector2t (min(a.x,b.x),min(a.y,b.y)); - } - - // std::max for aiVector2t - template - inline ::aiVector2t max (const ::aiVector2t& a, const ::aiVector2t& b) { - return ::aiVector2t (max(a.x,b.x),max(a.y,b.y)); - } - - // std::min for aiColor4D - template - inline ::aiColor4t min (const ::aiColor4t& a, const ::aiColor4t& b) { - return ::aiColor4t (min(a.r,b.r),min(a.g,b.g),min(a.b,b.b),min(a.a,b.a)); - } - - // std::max for aiColor4D - template - inline ::aiColor4t max (const ::aiColor4t& a, const ::aiColor4t& b) { - return ::aiColor4t (max(a.r,b.r),max(a.g,b.g),max(a.b,b.b),max(a.a,b.a)); - } - - - // std::min for aiQuaterniont - template - inline ::aiQuaterniont min (const ::aiQuaterniont& a, const ::aiQuaterniont& b) { - return ::aiQuaterniont (min(a.w,b.w),min(a.x,b.x),min(a.y,b.y),min(a.z,b.z)); - } - - // std::max for aiQuaterniont - template - inline ::aiQuaterniont max (const ::aiQuaterniont& a, const ::aiQuaterniont& b) { - return ::aiQuaterniont (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 for all supported Ts -template -struct MinMaxChooser; - -template <> struct MinMaxChooser { - void operator ()(float& min,float& max) { - max = -1e10f; - min = 1e10f; -}}; -template <> struct MinMaxChooser { - void operator ()(double& min,double& max) { - max = -1e10; - min = 1e10; -}}; -template <> struct MinMaxChooser { - void operator ()(unsigned int& min,unsigned int& max) { - max = 0; - min = (1u<<(sizeof(unsigned int)*8-1)); -}}; - -template struct MinMaxChooser< aiVector3t > { - void operator ()(aiVector3t& min,aiVector3t& max) { - max = aiVector3t(-1e10f,-1e10f,-1e10f); - min = aiVector3t( 1e10f, 1e10f, 1e10f); -}}; -template struct MinMaxChooser< aiVector2t > { - void operator ()(aiVector2t& min,aiVector2t& max) { - max = aiVector2t(-1e10f,-1e10f); - min = aiVector2t( 1e10f, 1e10f); - }}; -template struct MinMaxChooser< aiColor4t > { - void operator ()(aiColor4t& min,aiColor4t& max) { - max = aiColor4t(-1e10f,-1e10f,-1e10f,-1e10f); - min = aiColor4t( 1e10f, 1e10f, 1e10f, 1e10f); -}}; - -template struct MinMaxChooser< aiQuaterniont > { - void operator ()(aiQuaterniont& min,aiQuaterniont& max) { - max = aiQuaterniont(-1e10f,-1e10f,-1e10f,-1e10f); - min = aiQuaterniont( 1e10f, 1e10f, 1e10f, 1e10f); -}}; - -template <> struct MinMaxChooser { - void operator ()(aiVectorKey& min,aiVectorKey& max) { - MinMaxChooser()(min.mTime,max.mTime); - MinMaxChooser()(min.mValue,max.mValue); -}}; -template <> struct MinMaxChooser { - void operator ()(aiQuatKey& min,aiQuatKey& max) { - MinMaxChooser()(min.mTime,max.mTime); - MinMaxChooser()(min.mValue,max.mValue); -}}; - -template <> struct MinMaxChooser { - void operator ()(aiVertexWeight& min,aiVertexWeight& max) { - MinMaxChooser()(min.mVertexId,max.mVertexId); - MinMaxChooser()(min.mWeight,max.mWeight); -}}; - -// ------------------------------------------------------------------------------- -/** @brief Find the min/max values of an array of Ts - * @param in Input array - * @param size Number of elements to process - * @param[out] min minimum value - * @param[out] max maximum value - */ -template -inline void ArrayBounds(const T* in, unsigned int size, T& min, T& max) -{ - MinMaxChooser ()(min,max); - for (unsigned int i = 0; i < size;++i) { - min = std::min(in[i],min); - max = std::max(in[i],max); - } -} - - -// ------------------------------------------------------------------------------- -/** Little helper function to calculate the quadratic difference - * of two colours. - * @param pColor1 First color - * @param pColor2 second color - * @return Quadratic color difference */ -inline ai_real GetColorDifference( const aiColor4D& pColor1, const aiColor4D& pColor2) -{ - const aiColor4D c (pColor1.r - pColor2.r, pColor1.g - pColor2.g, pColor1.b - pColor2.b, pColor1.a - pColor2.a); - return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a; -} - - -// ------------------------------------------------------------------------------- -/** @brief Extract single strings from a list of identifiers - * @param in Input string list. - * @param out Receives a list of clean output strings - * @sdee #AI_CONFIG_PP_OG_EXCLUDE_LIST */ -void ConvertListToStrings(const std::string& in, std::list& out); - - -// ------------------------------------------------------------------------------- -/** @brief Compute the AABB of a mesh after applying a given transform - * @param mesh Input mesh - * @param[out] min Receives minimum transformed vertex - * @param[out] max Receives maximum transformed vertex - * @param m Transformation matrix to be applied */ -void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max, const aiMatrix4x4& m); - - -// ------------------------------------------------------------------------------- -/** @brief Helper function to determine the 'real' center of a mesh - * - * That is the center of its axis-aligned bounding box. - * @param mesh Input mesh - * @param[out] min Minimum vertex of the mesh - * @param[out] max maximum vertex of the mesh - * @param[out] out Center point */ -void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max); - -// ------------------------------------------------------------------------------- -/** @brief Helper function to determine the 'real' center of a scene - * - * That is the center of its axis-aligned bounding box. - * @param scene Input scene - * @param[out] min Minimum vertex of the scene - * @param[out] max maximum vertex of the scene - * @param[out] out Center point */ -void FindSceneCenter (aiScene* scene, aiVector3D& out, aiVector3D& min, aiVector3D& max); - - -// ------------------------------------------------------------------------------- -// Helper function to determine the 'real' center of a mesh after applying a given transform -void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,aiVector3D& max, const aiMatrix4x4& m); - - -// ------------------------------------------------------------------------------- -// Helper function to determine the 'real' center of a mesh -void FindMeshCenter (aiMesh* mesh, aiVector3D& out); - - -// ------------------------------------------------------------------------------- -// Helper function to determine the 'real' center of a mesh after applying a given transform -void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,const aiMatrix4x4& m); - - -// ------------------------------------------------------------------------------- -// Compute a good epsilon value for position comparisons on a mesh -ai_real ComputePositionEpsilon(const aiMesh* pMesh); - - -// ------------------------------------------------------------------------------- -// Compute a good epsilon value for position comparisons on a array of meshes -ai_real ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num); - - -// ------------------------------------------------------------------------------- -// Compute an unique value for the vertex format of a mesh -unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh); - - -// defs for ComputeVertexBoneWeightTable() -typedef std::pair PerVertexWeight; -typedef std::vector VertexWeightTable; - -// ------------------------------------------------------------------------------- -// Compute a per-vertex bone weight table -VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh); - - -// ------------------------------------------------------------------------------- -// Get a string for a given aiTextureType -const char* TextureTypeToString(aiTextureType in); - - -// ------------------------------------------------------------------------------- -// Get a string for a given aiTextureMapping -const char* MappingTypeToString(aiTextureMapping in); - - -// flags for MakeSubmesh() -#define AI_SUBMESH_FLAGS_SANS_BONES 0x1 - -// ------------------------------------------------------------------------------- -// Split a mesh given a list of faces to be contained in the sub mesh -aiMesh* MakeSubmesh(const aiMesh *superMesh, const std::vector &subMeshFaces, unsigned int subFlags); - -// ------------------------------------------------------------------------------- -// Utility postprocess step to share the spatial sort tree between -// all steps which use it to speedup its computations. -class ComputeSpatialSortProcess : public BaseProcess -{ - bool IsActive( unsigned int pFlags) const - { - return NULL != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace | - aiProcess_GenNormals | aiProcess_JoinIdenticalVertices)); - } - - void Execute( aiScene* pScene) - { - typedef std::pair _Type; - ASSIMP_LOG_DEBUG("Generate spatially-sorted vertex cache"); - - std::vector<_Type>* p = new std::vector<_Type>(pScene->mNumMeshes); - std::vector<_Type>::iterator it = p->begin(); - - for (unsigned int i = 0; i < pScene->mNumMeshes; ++i, ++it) { - aiMesh* mesh = pScene->mMeshes[i]; - _Type& blubb = *it; - blubb.first.Fill(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D)); - blubb.second = ComputePositionEpsilon(mesh); - } - - shared->AddProperty(AI_SPP_SPATIAL_SORT,p); - } -}; - -// ------------------------------------------------------------------------------- -// ... and the same again to cleanup the whole stuff -class DestroySpatialSortProcess : public BaseProcess -{ - bool IsActive( unsigned int pFlags) const - { - return NULL != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace | - aiProcess_GenNormals | aiProcess_JoinIdenticalVertices)); - } - - void Execute( aiScene* /*pScene*/) - { - shared->RemoveProperty(AI_SPP_SPATIAL_SORT); - } -}; - - - -} // ! namespace Assimp -#endif // !! AI_PROCESS_HELPER_H_INCLUDED diff --git a/thirdparty/assimp/code/RawLoader.cpp b/thirdparty/assimp/code/RawLoader.cpp deleted file mode 100644 index d0da247e47..0000000000 --- a/thirdparty/assimp/code/RawLoader.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file RawLoader.cpp - * @brief Implementation of the RAW importer class - */ - - -#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER - -// internal headers -#include "RawLoader.h" -#include -#include -#include -#include -#include -#include -#include - -using namespace Assimp; - -static const aiImporterDesc desc = { - "Raw Importer", - "", - "", - "", - aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "raw" -}; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -RAWImporter::RAWImporter() -{} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -RAWImporter::~RAWImporter() -{} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. -bool RAWImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const -{ - return SimpleExtensionCheck(pFile,"raw"); -} - -// ------------------------------------------------------------------------------------------------ -const aiImporterDesc* RAWImporter::GetInfo () const -{ - return &desc; -} - -// ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. -void RAWImporter::InternReadFile( const std::string& pFile, - aiScene* pScene, IOSystem* pIOHandler) -{ - std::unique_ptr 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 mBuffer2; - TextFileToBuffer(file.get(),mBuffer2); - const char* buffer = &mBuffer2[0]; - - // list of groups loaded from the file - std::vector< GroupInformation > outGroups(1,GroupInformation("")); - std::vector< GroupInformation >::iterator curGroup = outGroups.begin(); - - // now read all lines - char line[4096]; - while (GetNextLine(buffer,line)) - { - // if the line starts with a non-numeric identifier, it marks - // the beginning of a new group - const char* sz = line;SkipSpaces(&sz); - if (IsLineEnd(*sz))continue; - if (!IsNumeric(*sz)) - { - const char* sz2 = sz; - while (!IsSpaceOrNewLine(*sz2))++sz2; - const unsigned int length = (unsigned int)(sz2-sz); - - // find an existing group with this name - for (std::vector< GroupInformation >::iterator it = outGroups.begin(), end = outGroups.end(); - it != end;++it) - { - if (length == (*it).name.length() && !::strcmp(sz,(*it).name.c_str())) - { - curGroup = it;sz2 = NULL; - break; - } - } - if (sz2) - { - outGroups.push_back(GroupInformation(std::string(sz,length))); - curGroup = outGroups.end()-1; - } - } - else - { - // there can be maximally 12 floats plus an extra texture file name - float data[12]; - unsigned int num; - for (num = 0; num < 12;++num) - { - if(!SkipSpaces(&sz) || !IsNumeric(*sz))break; - sz = fast_atoreal_move(sz,data[num]); - } - if (num != 12 && num != 9) - { - ASSIMP_LOG_ERROR("A line may have either 9 or 12 floats and an optional texture"); - continue; - } - - MeshInformation* output = NULL; - - const char* sz2 = sz; - unsigned int length; - if (!IsLineEnd(*sz)) - { - while (!IsSpaceOrNewLine(*sz2))++sz2; - length = (unsigned int)(sz2-sz); - } - else if (9 == num) - { - sz = "%default%"; - length = 9; - } - else - { - sz = ""; - length = 0; - } - - // search in the list of meshes whether we have one with this texture - for (auto &mesh : (*curGroup).meshes) - { - if (length == mesh.name.length() && (length ? !::strcmp(sz, mesh.name.c_str()) : true)) - { - output = &mesh; - break; - } - } - // if we don't have the mesh, create it - if (!output) - { - (*curGroup).meshes.push_back(MeshInformation(std::string(sz,length))); - output = &((*curGroup).meshes.back()); - } - if (12 == num) - { - aiColor4D v(data[0],data[1],data[2],1.0f); - output->colors.push_back(v); - output->colors.push_back(v); - output->colors.push_back(v); - - output->vertices.push_back(aiVector3D(data[3],data[4],data[5])); - output->vertices.push_back(aiVector3D(data[6],data[7],data[8])); - output->vertices.push_back(aiVector3D(data[9],data[10],data[11])); - } - else - { - output->vertices.push_back(aiVector3D(data[0],data[1],data[2])); - output->vertices.push_back(aiVector3D(data[3],data[4],data[5])); - output->vertices.push_back(aiVector3D(data[6],data[7],data[8])); - } - } - } - - pScene->mRootNode = new aiNode(); - pScene->mRootNode->mName.Set(""); - - // count the number of valid groups - // (meshes can't be empty) - for (auto & outGroup : outGroups) - { - if (!outGroup.meshes.empty()) - { - ++pScene->mRootNode->mNumChildren; - pScene->mNumMeshes += (unsigned int) outGroup.meshes.size(); - } - } - - if (!pScene->mNumMeshes) - { - throw DeadlyImportError("RAW: No meshes loaded. The file seems to be corrupt or empty."); - } - - pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; - aiNode** cc; - if (1 == pScene->mRootNode->mNumChildren) - { - cc = &pScene->mRootNode; - pScene->mRootNode->mNumChildren = 0; - } else { - cc = new aiNode*[pScene->mRootNode->mNumChildren]; - memset(cc, 0, sizeof(aiNode*) * pScene->mRootNode->mNumChildren); - pScene->mRootNode->mChildren = cc; - } - - pScene->mNumMaterials = pScene->mNumMeshes; - aiMaterial** mats = pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; - - unsigned int meshIdx = 0; - for (auto & outGroup : outGroups) - { - if (outGroup.meshes.empty())continue; - - aiNode* node; - if (pScene->mRootNode->mNumChildren) - { - node = *cc = new aiNode(); - node->mParent = pScene->mRootNode; - } - else node = *cc; - node->mName.Set(outGroup.name); - - // add all meshes - node->mNumMeshes = (unsigned int) outGroup.meshes.size(); - unsigned int* pi = node->mMeshes = new unsigned int[ node->mNumMeshes ]; - for (std::vector< MeshInformation >::iterator it2 = outGroup.meshes.begin(), - end2 = outGroup.meshes.end(); it2 != end2; ++it2) - { - ai_assert(!(*it2).vertices.empty()); - - // allocate the mesh - *pi++ = meshIdx; - aiMesh* mesh = pScene->mMeshes[meshIdx] = new aiMesh(); - mesh->mMaterialIndex = meshIdx++; - - mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - - // allocate storage for the vertex components and copy them - mesh->mNumVertices = (unsigned int)(*it2).vertices.size(); - mesh->mVertices = new aiVector3D[ mesh->mNumVertices ]; - ::memcpy(mesh->mVertices,&(*it2).vertices[0],sizeof(aiVector3D)*mesh->mNumVertices); - - if ((*it2).colors.size()) - { - ai_assert((*it2).colors.size() == mesh->mNumVertices); - - mesh->mColors[0] = new aiColor4D[ mesh->mNumVertices ]; - ::memcpy(mesh->mColors[0],&(*it2).colors[0],sizeof(aiColor4D)*mesh->mNumVertices); - } - - // generate triangles - ai_assert(0 == mesh->mNumVertices % 3); - aiFace* fc = mesh->mFaces = new aiFace[ mesh->mNumFaces = mesh->mNumVertices/3 ]; - aiFace* const fcEnd = fc + mesh->mNumFaces; - unsigned int n = 0; - while (fc != fcEnd) - { - aiFace& f = *fc++; - f.mIndices = new unsigned int[f.mNumIndices = 3]; - for (unsigned int m = 0; m < 3;++m) - f.mIndices[m] = n++; - } - - // generate a material for the mesh - aiMaterial* mat = new aiMaterial(); - - aiColor4D clr(1.0f,1.0f,1.0f,1.0f); - if ("%default%" == (*it2).name) // a gray default material - { - clr.r = clr.g = clr.b = 0.6f; - } - else if ((*it2).name.length() > 0) // a texture - { - aiString s; - s.Set((*it2).name); - mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0)); - } - mat->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE); - *mats++ = mat; - } - } -} - -#endif // !! ASSIMP_BUILD_NO_RAW_IMPORTER diff --git a/thirdparty/assimp/code/RemoveComments.cpp b/thirdparty/assimp/code/RemoveComments.cpp deleted file mode 100644 index 91700a7699..0000000000 --- a/thirdparty/assimp/code/RemoveComments.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file RemoveComments.cpp - * @brief Defines the CommentRemover utility class - */ - -#include -#include - -namespace Assimp { - -// ------------------------------------------------------------------------------------------------ -// Remove line comments from a file -void CommentRemover::RemoveLineComments(const char* szComment, - char* szBuffer, char chReplacement /* = ' ' */) -{ - // validate parameters - ai_assert(NULL != szComment && NULL != szBuffer && *szComment); - - const size_t len = strlen(szComment); - while (*szBuffer) { - - // skip over quotes - if (*szBuffer == '\"' || *szBuffer == '\'') - while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\''); - - if (!strncmp(szBuffer,szComment,len)) { - while (!IsLineEnd(*szBuffer)) - *szBuffer++ = chReplacement; - - if (!*szBuffer) { - break; - } - } - ++szBuffer; - } -} - -// ------------------------------------------------------------------------------------------------ -// Remove multi-line comments from a file -void CommentRemover::RemoveMultiLineComments(const char* szCommentStart, - const char* szCommentEnd,char* szBuffer, - char chReplacement) -{ - // validate parameters - ai_assert(NULL != szCommentStart && NULL != szCommentEnd && - NULL != szBuffer && *szCommentStart && *szCommentEnd); - - const size_t len = strlen(szCommentEnd); - const size_t len2 = strlen(szCommentStart); - - while (*szBuffer) { - // skip over quotes - if (*szBuffer == '\"' || *szBuffer == '\'') - while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\''); - - if (!strncmp(szBuffer,szCommentStart,len2)) { - while (*szBuffer) { - if (!::strncmp(szBuffer,szCommentEnd,len)) { - for (unsigned int i = 0; i < len;++i) - *szBuffer++ = chReplacement; - - break; - } - *szBuffer++ = chReplacement; - } - continue; - } - ++szBuffer; - } -} - -} // !! Assimp diff --git a/thirdparty/assimp/code/RemoveRedundantMaterials.cpp b/thirdparty/assimp/code/RemoveRedundantMaterials.cpp deleted file mode 100644 index 632bdca3fe..0000000000 --- a/thirdparty/assimp/code/RemoveRedundantMaterials.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ -/** @file RemoveRedundantMaterials.cpp - * @brief Implementation of the "RemoveRedundantMaterials" post processing step -*/ - -// internal headers - -#include "RemoveRedundantMaterials.h" -#include -#include "ProcessHelper.h" -#include "MaterialSystem.h" -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -RemoveRedundantMatsProcess::RemoveRedundantMatsProcess() -: configFixedMaterials() { - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -RemoveRedundantMatsProcess::~RemoveRedundantMatsProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const -{ - return (pFlags & aiProcess_RemoveRedundantMaterials) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Setup import properties -void RemoveRedundantMatsProcess::SetupProperties(const Importer* pImp) -{ - // Get value of AI_CONFIG_PP_RRM_EXCLUDE_LIST - configFixedMaterials = pImp->GetPropertyString(AI_CONFIG_PP_RRM_EXCLUDE_LIST,""); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void RemoveRedundantMatsProcess::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess begin"); - - unsigned int redundantRemoved = 0, unreferencedRemoved = 0; - if (pScene->mNumMaterials) - { - // Find out which materials are referenced by meshes - std::vector 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 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::const_iterator it = std::find(strings.begin(), strings.end(), name.data); - if (it != strings.end()) { - - // Our brilliant 'salt': A single material property with ~ as first - // character to mark it as internal and temporary. - const int dummy = 1; - ((aiMaterial*)mat)->AddProperty(&dummy,1,"~RRM.UniqueMaterial",0,0); - - // Keep this material even if no mesh references it - abReferenced[i] = true; - ASSIMP_LOG_DEBUG_F( "Found positive match in exclusion list: \'", name.data, "\'"); - } - } - } - } - - // TODO: re-implement this algorithm to work in-place - unsigned int *aiMappingTable = new unsigned int[pScene->mNumMaterials]; - for ( unsigned int i=0; imNumMaterials; i++ ) { - aiMappingTable[ i ] = 0; - } - unsigned int iNewNum = 0; - - // Iterate through all materials and calculate a hash for them - // store all hashes in a list and so a quick search whether - // we do already have a specific hash. This allows us to - // determine which materials are identical. - uint32_t *aiHashes = new uint32_t[ pScene->mNumMaterials ];; - for (unsigned int i = 0; i < pScene->mNumMaterials;++i) - { - // No mesh is referencing this material, remove it. - if (!abReferenced[i]) { - ++unreferencedRemoved; - delete pScene->mMaterials[i]; - pScene->mMaterials[i] = nullptr; - continue; - } - - // Check all previously mapped materials for a matching hash. - // On a match we can delete this material and just make it ref to the same index. - uint32_t me = aiHashes[i] = ComputeMaterialHash(pScene->mMaterials[i]); - for (unsigned int a = 0; a < i;++a) - { - if (abReferenced[a] && me == aiHashes[a]) { - ++redundantRemoved; - me = 0; - aiMappingTable[i] = aiMappingTable[a]; - delete pScene->mMaterials[i]; - pScene->mMaterials[i] = nullptr; - break; - } - } - // This is a new material that is referenced, add to the map. - if (me) { - aiMappingTable[i] = iNewNum++; - } - } - // If the new material count differs from the original, - // we need to rebuild the material list and remap mesh material indexes. - if (iNewNum != pScene->mNumMaterials) { - ai_assert(iNewNum > 0); - aiMaterial** ppcMaterials = new aiMaterial*[iNewNum]; - ::memset(ppcMaterials,0,sizeof(void*)*iNewNum); - for (unsigned int p = 0; p < pScene->mNumMaterials;++p) - { - // if the material is not referenced ... remove it - if (!abReferenced[p]) { - continue; - } - - // generate new names for modified materials that had no names - const unsigned int idx = aiMappingTable[p]; - if (ppcMaterials[idx]) { - aiString sz; - if( ppcMaterials[idx]->Get(AI_MATKEY_NAME, sz) != AI_SUCCESS ) { - sz.length = ::ai_snprintf(sz.data,MAXLEN,"JoinedMaterial_#%u",p); - ((aiMaterial*)ppcMaterials[idx])->AddProperty(&sz,AI_MATKEY_NAME); - } - } else { - ppcMaterials[idx] = pScene->mMaterials[p]; - } - } - // update all material indices - for (unsigned int p = 0; p < pScene->mNumMeshes;++p) { - aiMesh* mesh = pScene->mMeshes[p]; - ai_assert( NULL!=mesh ); - mesh->mMaterialIndex = aiMappingTable[mesh->mMaterialIndex]; - } - // delete the old material list - delete[] pScene->mMaterials; - pScene->mMaterials = ppcMaterials; - pScene->mNumMaterials = iNewNum; - } - // delete temporary storage - delete[] aiHashes; - delete[] aiMappingTable; - } - if (redundantRemoved == 0 && unreferencedRemoved == 0) - { - ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess finished "); - } - else - { - ASSIMP_LOG_INFO_F("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", - unreferencedRemoved, " unused materials."); - } -} diff --git a/thirdparty/assimp/code/RemoveRedundantMaterials.h b/thirdparty/assimp/code/RemoveRedundantMaterials.h deleted file mode 100644 index dbd4d44cc0..0000000000 --- a/thirdparty/assimp/code/RemoveRedundantMaterials.h +++ /dev/null @@ -1,107 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file 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 - -class RemoveRedundantMatsTest; - -namespace Assimp { - -// --------------------------------------------------------------------------- -/** RemoveRedundantMatsProcess: Post-processing step to remove redundant - * materials from the imported scene. - */ -class ASSIMP_API RemoveRedundantMatsProcess : public BaseProcess -{ -public: - /// The default class constructor. - RemoveRedundantMatsProcess(); - - /// The class destructor. - ~RemoveRedundantMatsProcess(); - -public: - // ------------------------------------------------------------------- - // Check whether step is active - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - // Execute step on a given scene - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - // Setup import settings - void SetupProperties(const Importer* pImp); - - - // ------------------------------------------------------------------- - /** @brief Set list of fixed (unmutable) materials - * @param fixed See #AI_CONFIG_PP_RRM_EXCLUDE_LIST - */ - void SetFixedMaterialsString(const std::string& fixed = "") { - configFixedMaterials = fixed; - } - - // ------------------------------------------------------------------- - /** @brief Get list of fixed (unmutable) materials - * @return See #AI_CONFIG_PP_RRM_EXCLUDE_LIST - */ - const std::string& GetFixedMaterialsString() const { - return configFixedMaterials; - } - -private: - - //! Configuration option: list of all fixed materials - std::string configFixedMaterials; -}; - -} // end of namespace Assimp - -#endif // !!AI_REMOVEREDUNDANTMATERIALS_H_INC diff --git a/thirdparty/assimp/code/RemoveVCProcess.cpp b/thirdparty/assimp/code/RemoveVCProcess.cpp deleted file mode 100644 index 99fd47a3aa..0000000000 --- a/thirdparty/assimp/code/RemoveVCProcess.cpp +++ /dev/null @@ -1,337 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ -/** @file Implementation of the post processing step to remove - * any parts of the mesh structure from the imported data. -*/ - - -#include "RemoveVCProcess.h" -#include -#include -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -RemoveVCProcess::RemoveVCProcess() : - configDeleteFlags() - , mScene() -{} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -RemoveVCProcess::~RemoveVCProcess() -{} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool RemoveVCProcess::IsActive( unsigned int pFlags) const -{ - return (pFlags & aiProcess_RemoveComponent) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Small helper function to delete all elements in a T** aray using delete -template -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& childsOfParent,bool root) -{ - bool b = false; - - std::list 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::iterator it = mine.begin(), end = mine.end(); - it != end; ++it) - { - *ptr++ = *it; - } - delete[] node->mChildren; - node->mChildren = children; - return false; - } - } - return b; -} -#endif - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void RemoveVCProcess::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("RemoveVCProcess begin"); - bool bHas = false; //,bMasked = false; - - mScene = pScene; - - // handle animations - if ( configDeleteFlags & aiComponent_ANIMATIONS) - { - - bHas = true; - ArrayDelete(pScene->mAnimations,pScene->mNumAnimations); - } - - // handle textures - if ( configDeleteFlags & aiComponent_TEXTURES) - { - bHas = true; - ArrayDelete(pScene->mTextures,pScene->mNumTextures); - } - - // handle materials - if ( configDeleteFlags & aiComponent_MATERIALS && pScene->mNumMaterials) - { - bHas = true; - for (unsigned int i = 1;i < pScene->mNumMaterials;++i) - delete pScene->mMaterials[i]; - - pScene->mNumMaterials = 1; - aiMaterial* helper = (aiMaterial*) pScene->mMaterials[0]; - ai_assert(NULL != helper); - helper->Clear(); - - // gray - aiColor3D clr(0.6f,0.6f,0.6f); - helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE); - - // add a small ambient color value - clr = aiColor3D(0.05f,0.05f,0.05f); - helper->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT); - - aiString s; - s.Set("Dummy_MaterialsRemoved"); - helper->AddProperty(&s,AI_MATKEY_NAME); - } - - // handle light sources - if ( configDeleteFlags & aiComponent_LIGHTS) - { - bHas = true; - ArrayDelete(pScene->mLights,pScene->mNumLights); - } - - // handle camneras - if ( configDeleteFlags & aiComponent_CAMERAS) - { - bHas = true; - ArrayDelete(pScene->mCameras,pScene->mNumCameras); - } - - // handle meshes - if (configDeleteFlags & aiComponent_MESHES) - { - bHas = true; - ArrayDelete(pScene->mMeshes,pScene->mNumMeshes); - } - else - { - for( unsigned int a = 0; a < pScene->mNumMeshes; a++) - { - if( ProcessMesh( pScene->mMeshes[a])) - bHas = true; - } - } - - - // now check whether the result is still a full scene - if (!pScene->mNumMeshes || !pScene->mNumMaterials) - { - pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; - ASSIMP_LOG_DEBUG("Setting AI_SCENE_FLAGS_INCOMPLETE flag"); - - // If we have no meshes anymore we should also clear another flag ... - if (!pScene->mNumMeshes) - pScene->mFlags &= ~AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; - } - - if (bHas) { - ASSIMP_LOG_INFO("RemoveVCProcess finished. Data structure cleanup has been done."); - } else { - ASSIMP_LOG_DEBUG("RemoveVCProcess finished. Nothing to be done ..."); - } -} - -// ------------------------------------------------------------------------------------------------ -// Setup configuration properties for the step -void RemoveVCProcess::SetupProperties(const Importer* pImp) -{ - configDeleteFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS,0x0); - if (!configDeleteFlags) - { - ASSIMP_LOG_WARN("RemoveVCProcess: AI_CONFIG_PP_RVC_FLAGS is zero."); - } -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh) -{ - bool ret = false; - - // if all materials have been deleted let the material - // index of the mesh point to the created default material - if ( configDeleteFlags & aiComponent_MATERIALS) - pMesh->mMaterialIndex = 0; - - // handle normals - if (configDeleteFlags & aiComponent_NORMALS && pMesh->mNormals) - { - delete[] pMesh->mNormals; - pMesh->mNormals = NULL; - ret = true; - } - - // handle tangents and bitangents - if (configDeleteFlags & aiComponent_TANGENTS_AND_BITANGENTS && pMesh->mTangents) - { - delete[] pMesh->mTangents; - pMesh->mTangents = NULL; - - delete[] pMesh->mBitangents; - pMesh->mBitangents = NULL; - ret = true; - } - - // handle texture coordinates - bool b = (0 != (configDeleteFlags & aiComponent_TEXCOORDS)); - for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++real) - { - if (!pMesh->mTextureCoords[i])break; - if (configDeleteFlags & aiComponent_TEXCOORDSn(real) || b) - { - delete [] pMesh->mTextureCoords[i]; - pMesh->mTextureCoords[i] = NULL; - ret = true; - - if (!b) - { - // collapse the rest of the array - for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) - pMesh->mTextureCoords[a-1] = pMesh->mTextureCoords[a]; - - pMesh->mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS-1] = NULL; - continue; - } - } - ++i; - } - - // handle vertex colors - b = (0 != (configDeleteFlags & aiComponent_COLORS)); - for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_COLOR_SETS; ++real) - { - if (!pMesh->mColors[i])break; - if (configDeleteFlags & aiComponent_COLORSn(i) || b) - { - delete [] pMesh->mColors[i]; - pMesh->mColors[i] = NULL; - ret = true; - - if (!b) - { - // collapse the rest of the array - for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) - pMesh->mColors[a-1] = pMesh->mColors[a]; - - pMesh->mColors[AI_MAX_NUMBER_OF_COLOR_SETS-1] = NULL; - continue; - } - } - ++i; - } - - // handle bones - if (configDeleteFlags & aiComponent_BONEWEIGHTS && pMesh->mBones) - { - ArrayDelete(pMesh->mBones,pMesh->mNumBones); - ret = true; - } - return ret; -} diff --git a/thirdparty/assimp/code/RemoveVCProcess.h b/thirdparty/assimp/code/RemoveVCProcess.h deleted file mode 100644 index 617d7b9b20..0000000000 --- a/thirdparty/assimp/code/RemoveVCProcess.h +++ /dev/null @@ -1,123 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a post processing step to remove specific parts of the scene */ -#ifndef AI_REMOVEVCPROCESS_H_INCLUDED -#define AI_REMOVEVCPROCESS_H_INCLUDED - -#include "BaseProcess.h" -#include - -class RemoveVCProcessTest; - -namespace Assimp { - -// --------------------------------------------------------------------------- -/** RemoveVCProcess: Class to exclude specific parts of the data structure - * from further processing by removing them, -*/ -class ASSIMP_API RemoveVCProcess : public BaseProcess { -public: - /// The default class constructor. - RemoveVCProcess(); - - /// The class destructor. - ~RemoveVCProcess(); - - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag field. - * @param pFlags The processing flags the importer was called with. A bitwise - * combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, false if not. - */ - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - /** Called prior to ExecuteOnScene(). - * The function is a request to the process to update its configuration - * basing on the Importer's configuration property list. - */ - virtual void SetupProperties(const Importer* pImp); - - // ------------------------------------------------------------------- - /** Manually setup the configuration flags for the step - * - * @param Bitwise combination of the #aiComponent enumerated values. - */ - void SetDeleteFlags(unsigned int f) - { - configDeleteFlags = f; - } - - // ------------------------------------------------------------------- - /** Query the current configuration. - */ - unsigned int GetDeleteFlags() const - { - return configDeleteFlags; - } - -private: - - bool ProcessMesh (aiMesh* pcMesh); - - /** Configuration flag - */ - unsigned int configDeleteFlags; - - /** The scene we're working with - */ - aiScene* mScene; -}; - -// --------------------------------------------------------------------------- - -} // end of namespace Assimp - -#endif // !!AI_REMOVEVCPROCESS_H_INCLUDED diff --git a/thirdparty/assimp/code/SGSpatialSort.cpp b/thirdparty/assimp/code/SGSpatialSort.cpp deleted file mode 100644 index 120070b0aa..0000000000 --- a/thirdparty/assimp/code/SGSpatialSort.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of the helper class to quickly find -vertices close to a given position. Special implementation for -the 3ds loader handling smooth groups correctly */ - -#include - -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& poResults, - bool exactMatch /*= false*/) const -{ - float dist = pPosition * mPlaneNormal; - float minDist = dist - pRadius, maxDist = dist + pRadius; - - // clear the array - poResults.clear(); - - // quick check for positions outside the range - if( mPositions.empty() ) - return; - if( maxDist < mPositions.front().mDistance) - return; - if( minDist > mPositions.back().mDistance) - return; - - // do a binary search for the minimal distance to start the iteration there - unsigned int index = (unsigned int)mPositions.size() / 2; - unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; - while( binaryStepSize > 1) - { - if( mPositions[index].mDistance < minDist) - index += binaryStepSize; - else - index -= binaryStepSize; - - binaryStepSize /= 2; - } - - // depending on the direction of the last step we need to single step a bit back or forth - // to find the actual beginning element of the range - while( index > 0 && mPositions[index].mDistance > minDist) - index--; - while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist) - index++; - - // Mow start iterating from there until the first position lays outside of the distance range. - // Add all positions inside the distance range within the given radius to the result aray - - float squareEpsilon = pRadius * pRadius; - std::vector::const_iterator it = mPositions.begin() + index; - std::vector::const_iterator end = mPositions.end(); - - if (exactMatch) - { - while( it->mDistance < maxDist) - { - if((it->mPosition - pPosition).SquareLength() < squareEpsilon && it->mSmoothGroups == pSG) - { - poResults.push_back( it->mIndex); - } - ++it; - if( end == it )break; - } - } - else - { - // if the given smoothing group is 0, we'll return all surrounding vertices - if (!pSG) - { - while( it->mDistance < maxDist) - { - if((it->mPosition - pPosition).SquareLength() < squareEpsilon) - poResults.push_back( it->mIndex); - ++it; - if( end == it)break; - } - } - else while( it->mDistance < maxDist) - { - if((it->mPosition - pPosition).SquareLength() < squareEpsilon && - (it->mSmoothGroups & pSG || !it->mSmoothGroups)) - { - poResults.push_back( it->mIndex); - } - ++it; - if( end == it)break; - } - } -} - - diff --git a/thirdparty/assimp/code/ScaleProcess.cpp b/thirdparty/assimp/code/ScaleProcess.cpp deleted file mode 100644 index 6d458c4b11..0000000000 --- a/thirdparty/assimp/code/ScaleProcess.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -#ifndef ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS - -#include "ScaleProcess.h" - -#include -#include - -namespace Assimp { - -ScaleProcess::ScaleProcess() -: BaseProcess() -, mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) { - // empty -} - -ScaleProcess::~ScaleProcess() { - // empty -} - -void ScaleProcess::setScale( ai_real scale ) { - mScale = scale; -} - -ai_real ScaleProcess::getScale() const { - return mScale; -} - -bool ScaleProcess::IsActive( unsigned int pFlags ) const { - return ( pFlags & aiProcess_GlobalScale ) != 0; -} - -void ScaleProcess::SetupProperties( const Importer* pImp ) { - mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 0 ); -} - -void ScaleProcess::Execute( aiScene* pScene ) { - if ( nullptr == pScene ) { - return; - } - - if ( nullptr == pScene->mRootNode ) { - return; - } - - traverseNodes( pScene->mRootNode ); -} - -void ScaleProcess::traverseNodes( aiNode *node ) { - applyScaling( node ); -} - -void ScaleProcess::applyScaling( aiNode *currentNode ) { - if ( nullptr != currentNode ) { - currentNode->mTransformation.a1 = currentNode->mTransformation.a1 * mScale; - currentNode->mTransformation.b2 = currentNode->mTransformation.b2 * mScale; - currentNode->mTransformation.c3 = currentNode->mTransformation.c3 * mScale; - } -} - -} // Namespace Assimp - -#endif // !! ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS diff --git a/thirdparty/assimp/code/ScaleProcess.h b/thirdparty/assimp/code/ScaleProcess.h deleted file mode 100644 index 55146ae064..0000000000 --- a/thirdparty/assimp/code/ScaleProcess.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -#pragma once - -#include "BaseProcess.h" - -struct aiNode; - -#if (!defined AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT) -# define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f -#endif // !! AI_DEBONE_THRESHOLD - -namespace Assimp { - -// --------------------------------------------------------------------------- -/** ScaleProcess: Class to rescale the whole model. -*/ -class ASSIMP_API ScaleProcess : public BaseProcess { -public: - /// The default class constructor. - ScaleProcess(); - - /// The class destructor. - virtual ~ScaleProcess(); - - /// Will set the scale manually. - void setScale( ai_real scale ); - - /// Returns the current scaling value. - ai_real getScale() const; - - /// Overwritten, @see BaseProcess - virtual bool IsActive( unsigned int pFlags ) const; - - /// Overwritten, @see BaseProcess - virtual void SetupProperties( const Importer* pImp ); - - /// Overwritten, @see BaseProcess - virtual void Execute( aiScene* pScene ); - -private: - void traverseNodes( aiNode *currentNode ); - void applyScaling( aiNode *currentNode ); - -private: - ai_real mScale; -}; - -} // Namespace Assimp diff --git a/thirdparty/assimp/code/SceneCombiner.cpp b/thirdparty/assimp/code/SceneCombiner.cpp deleted file mode 100644 index e445bd7434..0000000000 --- a/thirdparty/assimp/code/SceneCombiner.cpp +++ /dev/null @@ -1,1300 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -// TODO: refactor entire file to get rid of the "flat-copy" first approach -// to copying structures. This easily breaks in the most unintuitive way -// possible as new fields are added to assimp structures. - -// ---------------------------------------------------------------------------- -/** - * @file Implements Assimp::SceneCombiner. This is a smart utility - * class that combines multiple scenes, meshes, ... into one. Currently - * these utilities are used by the IRR and LWS loaders and the - * OptimizeGraph step. - */ -// ---------------------------------------------------------------------------- -#include -#include -#include -#include -#include -#include "time.h" -#include -#include -#include -#include -#include "ScenePrivate.h" - -namespace Assimp { - -// ------------------------------------------------------------------------------------------------ -// Add a prefix to a string -inline -void PrefixString(aiString& string,const char* prefix, unsigned int len) { - // If the string is already prefixed, we won't prefix it a second time - if (string.length >= 1 && string.data[0] == '$') - return; - - if (len+string.length>=MAXLEN-1) { - ASSIMP_LOG_DEBUG("Can't add an unique prefix because the string is too long"); - ai_assert(false); - return; - } - - // Add the prefix - ::memmove(string.data+len,string.data,string.length+1); - ::memcpy (string.data, prefix, len); - - // And update the string's length - string.length += len; -} - -// ------------------------------------------------------------------------------------------------ -// Add node identifiers to a hashing set -void SceneCombiner::AddNodeHashes(aiNode* node, std::set& hashes) { - // Add node name to hashing set if it is non-empty - empty nodes are allowed - // and they can't have any anims assigned so its absolutely safe to duplicate them. - if (node->mName.length) { - hashes.insert( SuperFastHash(node->mName.data, static_cast(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& input, unsigned int cur) { - const unsigned int hash = SuperFastHash(name.data, static_cast(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& input, unsigned int cur) { - ai_assert(NULL != prefix); - const unsigned int hash = SuperFastHash(node->mName.data, static_cast(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& src, unsigned int flags) { - if ( nullptr == _dest ) { - return; - } - - // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it - if (src.empty()) { - if (*_dest) { - (*_dest)->~aiScene(); - SceneCombiner::CopySceneFlat(_dest,src[0]); - } - else *_dest = src[0]; - return; - } - if (*_dest)(*_dest)->~aiScene(); - else *_dest = new aiScene(); - - // Create a dummy scene to serve as master for the others - aiScene* master = new aiScene(); - master->mRootNode = new aiNode(); - master->mRootNode->mName.Set(""); - - std::vector 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& srcList) { - unsigned int cnt; - for ( cnt = 0; cnt < attach->mNumChildren; ++cnt ) { - AttachToGraph( attach->mChildren[ cnt ], srcList ); - } - - cnt = 0; - for (std::vector::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& src) { - ai_assert(NULL != master); - AttachToGraph(master->mRootNode,src); -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master, std::vector& srcList, unsigned int flags) { - if ( nullptr == _dest ) { - return; - } - - // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it - if (srcList.empty()) { - if (*_dest) { - SceneCombiner::CopySceneFlat(_dest,master); - } - else *_dest = master; - return; - } - if (*_dest) { - (*_dest)->~aiScene(); - new (*_dest) aiScene(); - } - else *_dest = new aiScene(); - - aiScene* dest = *_dest; - - std::vector 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 duplicates(src.size(),UINT_MAX); - - // this helper array is used as lookup table several times - std::vector offset(src.size()); - - // Find duplicate scenes - for (unsigned int i = 0; i < src.size();++i) { - if (duplicates[i] != i && duplicates[i] != UINT_MAX) { - continue; - } - - duplicates[i] = i; - for ( unsigned int a = i+1; a < src.size(); ++a) { - if (src[i].scene == src[a].scene) { - duplicates[a] = i; - } - } - } - - // Generate unique names for all named stuff? - if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) - { -#if 0 - // Construct a proper random number generator - boost::mt19937 rng( ); - boost::uniform_int<> dist(1u,1 << 24u); - boost::variate_generator > rndGen(rng, dist); -#endif - for (unsigned int i = 1; i < src.size();++i) - { - //if (i != duplicates[i]) - //{ - // // duplicate scenes share the same UID - // ::strcpy( src[i].id, src[duplicates[i]].id ); - // src[i].idlen = src[duplicates[i]].idlen; - - // continue; - //} - - src[i].idlen = ai_snprintf(src[i].id, 32, "$%.6X$_",i); - - if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { - - // Compute hashes for all identifiers in this scene and store them - // in a sorted table (for convenience I'm using std::set). We hash - // just the node and animation channel names, all identifiers except - // the material names should be caught by doing this. - AddNodeHashes(src[i]->mRootNode,src[i].hashes); - - for (unsigned int a = 0; a < src[i]->mNumAnimations;++a) { - aiAnimation* anim = src[i]->mAnimations[a]; - src[i].hashes.insert(SuperFastHash(anim->mName.data,static_cast(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: *, - // where n is the index of the texture. - aiString& s = *((aiString*)prop->mData); - if ('*' == s.data[0]) { - // Offset the index and write it back .. - const unsigned int idx = strtoul10(&s.data[1]) + offset[n]; - ASSIMP_itoa10(&s.data[1],sizeof(s.data)-1,idx); - } - } - - // Need to generate new, unique material names? - else if (!::strcmp( prop->mKey.data,"$mat.name" ) && flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) - { - aiString* pcSrc = (aiString*) prop->mData; - PrefixString(*pcSrc, (*cur).id, (*cur).idlen); - } - } - } - ++pip; - } - - offset[n] = cnt; - cnt = (unsigned int)(pip - dest->mMaterials); - } - } - - // generate the output mesh list + again an offset table for all mesh indices - if (dest->mNumMeshes) - { - aiMesh** pip = dest->mMeshes = new aiMesh*[dest->mNumMeshes]; - cnt = 0; - for ( unsigned int n = 0; n < src.size();++n ) - { - SceneHelper* cur = &src[n]; - for (unsigned int i = 0; i < (*cur)->mNumMeshes;++i) - { - if (n != duplicates[n]) { - if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) - Copy(pip, (*cur)->mMeshes[i]); - - else continue; - } - else *pip = (*cur)->mMeshes[i]; - - // update the material index of the mesh - (*pip)->mMaterialIndex += offset[n]; - ++pip; - } - - // reuse the offset array - store now the mesh offset in it - offset[n] = cnt; - cnt = (unsigned int)(pip - dest->mMeshes); - } - } - - std::vector 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 ... $_ - // We could also use a counter, but using a random value allows us to - // use just one prefix if we are joining multiple scene hierarchies recursively. - // Chances are quite good we don't collide, so we try that ... - // ---------------------------------------------------------------------------- - - // Allocate space for light sources, cameras and animations - aiLight** ppLights = dest->mLights = (dest->mNumLights - ? new aiLight*[dest->mNumLights] : NULL); - - aiCamera** ppCameras = dest->mCameras = (dest->mNumCameras - ? new aiCamera*[dest->mNumCameras] : NULL); - - aiAnimation** ppAnims = dest->mAnimations = (dest->mNumAnimations - ? new aiAnimation*[dest->mNumAnimations] : NULL); - - for ( int n = static_cast(src.size()-1); n >= 0 ;--n ) /* !!! important !!! */ - { - SceneHelper* cur = &src[n]; - aiNode* node; - - // To offset or not to offset, this is the question - if (n != (int)duplicates[n]) - { - // Get full scene-graph copy - Copy( &node, (*cur)->mRootNode ); - OffsetNodeMeshIndices(node,offset[duplicates[n]]); - - if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) { - // (note:) they are already 'offseted' by offset[duplicates[n]] - OffsetNodeMeshIndices(node,offset[n] - offset[duplicates[n]]); - } - } - else // if (n == duplicates[n]) - { - node = (*cur)->mRootNode; - OffsetNodeMeshIndices(node,offset[n]); - } - if (n) // src[0] is the master node - nodes.push_back(NodeAttachmentInfo( node,srcList[n-1].attachToNode,n )); - - // add name prefixes? - if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { - - // or the whole scenegraph - if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { - AddNodePrefixesChecked(node,(*cur).id,(*cur).idlen,src,n); - } - else AddNodePrefixes(node,(*cur).id,(*cur).idlen); - - // meshes - for (unsigned int i = 0; i < (*cur)->mNumMeshes;++i) { - aiMesh* mesh = (*cur)->mMeshes[i]; - - // rename all bones - for (unsigned int a = 0; a < mesh->mNumBones;++a) { - if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { - if (!FindNameMatch(mesh->mBones[a]->mName,src,n)) - continue; - } - PrefixString(mesh->mBones[a]->mName,(*cur).id,(*cur).idlen); - } - } - } - - // -------------------------------------------------------------------- - // Copy light sources - for (unsigned int i = 0; i < (*cur)->mNumLights;++i,++ppLights) - { - if (n != (int)duplicates[n]) // duplicate scene? - { - Copy(ppLights, (*cur)->mLights[i]); - } - else *ppLights = (*cur)->mLights[i]; - - - // Add name prefixes? - if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { - if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { - if (!FindNameMatch((*ppLights)->mName,src,n)) - continue; - } - - PrefixString((*ppLights)->mName,(*cur).id,(*cur).idlen); - } - } - - // -------------------------------------------------------------------- - // Copy cameras - for (unsigned int i = 0; i < (*cur)->mNumCameras;++i,++ppCameras) { - if (n != (int)duplicates[n]) // duplicate scene? - { - Copy(ppCameras, (*cur)->mCameras[i]); - } - else *ppCameras = (*cur)->mCameras[i]; - - // Add name prefixes? - if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { - if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { - if (!FindNameMatch((*ppCameras)->mName,src,n)) - continue; - } - - PrefixString((*ppCameras)->mName,(*cur).id,(*cur).idlen); - } - } - - // -------------------------------------------------------------------- - // Copy animations - for (unsigned int i = 0; i < (*cur)->mNumAnimations;++i,++ppAnims) { - if (n != (int)duplicates[n]) // duplicate scene? - { - Copy(ppAnims, (*cur)->mAnimations[i]); - } - else *ppAnims = (*cur)->mAnimations[i]; - - // Add name prefixes? - if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { - if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { - if (!FindNameMatch((*ppAnims)->mName,src,n)) - continue; - } - - PrefixString((*ppAnims)->mName,(*cur).id,(*cur).idlen); - - // don't forget to update all node animation channels - for (unsigned int a = 0; a < (*ppAnims)->mNumChannels;++a) { - if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { - if (!FindNameMatch((*ppAnims)->mChannels[a]->mNodeName,src,n)) - continue; - } - - PrefixString((*ppAnims)->mChannels[a]->mNodeName,(*cur).id,(*cur).idlen); - } - } - } - } - - // Now build the output graph - AttachToGraph ( master, nodes); - dest->mRootNode = master->mRootNode; - - // Check whether we succeeded at building the output graph - for (std::vector ::iterator it = nodes.begin(); - it != nodes.end(); ++it) - { - if (!(*it).resolved) { - if (flags & AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS) { - // search for this attachment point in all other imported scenes, too. - for ( unsigned int n = 0; n < src.size();++n ) { - if (n != (*it).src_idx) { - AttachToGraph(src[n].scene,nodes); - if ((*it).resolved) - break; - } - } - } - if (!(*it).resolved) { - ASSIMP_LOG_ERROR_F( "SceneCombiner: Failed to resolve attachment ", (*it).node->mName.data, - " ", (*it).attachToNode->mName.data ); - } - } - } - - // now delete all input scenes. Make sure duplicate scenes aren't - // deleted more than one time - for ( unsigned int n = 0; n < src.size();++n ) { - if (n != duplicates[n]) // duplicate scene? - continue; - - aiScene* deleteMe = src[n].scene; - - // We need to delete the arrays before the destructor is called - - // we are reusing the array members - delete[] deleteMe->mMeshes; deleteMe->mMeshes = NULL; - delete[] deleteMe->mCameras; deleteMe->mCameras = NULL; - delete[] deleteMe->mLights; deleteMe->mLights = NULL; - delete[] deleteMe->mMaterials; deleteMe->mMaterials = NULL; - delete[] deleteMe->mAnimations; deleteMe->mAnimations = NULL; - - deleteMe->mRootNode = NULL; - - // Now we can safely delete the scene - delete deleteMe; - } - - // Check flags - if (!dest->mNumMeshes || !dest->mNumMaterials) { - dest->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; - } - - // We're finished -} - -// ------------------------------------------------------------------------------------------------ -// Build a list of unique bones -void SceneCombiner::BuildUniqueBoneList(std::list& asBones, - std::vector::const_iterator it, - std::vector::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::iterator it2 = asBones.begin(); - std::list::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::const_iterator it, - std::vector::const_iterator end) -{ - if ( nullptr == out || out->mNumBones == 0 ) { - return; - } - - // find we need to build an unique list of all bones. - // we work with hashes to make the comparisons MUCH faster, - // at least if we have many bones. - std::list asBones; - BuildUniqueBoneList( asBones, it, end ); - - // now create the output bones - out->mNumBones = 0; - out->mBones = new aiBone*[asBones.size()]; - - for (std::list::const_iterator boneIt = asBones.begin(),boneEnd = asBones.end(); boneIt != boneEnd; ++boneIt ) { - // Allocate a bone and setup it's name - aiBone* pc = out->mBones[out->mNumBones++] = new aiBone(); - pc->mName = aiString( *( boneIt->second )); - - std::vector< BoneSrcIndex >::const_iterator wend = boneIt->pSrcBones.end(); - - // Loop through all bones to be joined for this bone - for (std::vector< BoneSrcIndex >::const_iterator wmit = boneIt->pSrcBones.begin(); wmit != wend; ++wmit) { - pc->mNumWeights += (*wmit).first->mNumWeights; - - // NOTE: different offset matrices for bones with equal names - // are - at the moment - not handled correctly. - if (wmit != boneIt->pSrcBones.begin() && pc->mOffsetMatrix != wmit->first->mOffsetMatrix) { - ASSIMP_LOG_WARN("Bones with equal names but different offset matrices can't be joined at the moment"); - continue; - } - pc->mOffsetMatrix = wmit->first->mOffsetMatrix; - } - - // Allocate the vertex weight array - aiVertexWeight* avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights]; - - // And copy the final weights - adjust the vertex IDs by the - // face index offset of the corresponding mesh. - for (std::vector< BoneSrcIndex >::const_iterator wmit = (*boneIt).pSrcBones.begin(); wmit != (*boneIt).pSrcBones.end(); ++wmit) { - if (wmit == wend) { - break; - } - - aiBone* pip = (*wmit).first; - for (unsigned int mp = 0; mp < pip->mNumWeights;++mp,++avw) { - const aiVertexWeight& vfi = pip->mWeights[mp]; - avw->mWeight = vfi.mWeight; - avw->mVertexId = vfi.mVertexId + (*wmit).second; - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Merge a list of meshes -void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/, - std::vector::const_iterator begin, - std::vector::const_iterator end) -{ - if ( nullptr == _out ) { - return; - } - - if (begin == end) { - *_out = NULL; // no meshes ... - return; - } - - // Allocate the output mesh - aiMesh* out = *_out = new aiMesh(); - out->mMaterialIndex = (*begin)->mMaterialIndex; - - std::string name; - // Find out how much output storage we'll need - for (std::vector::const_iterator it = begin; it != end; ++it) { - const char *meshName( (*it)->mName.C_Str() ); - name += std::string( meshName ); - if ( it != end - 1 ) { - name += "."; - } - out->mNumVertices += (*it)->mNumVertices; - out->mNumFaces += (*it)->mNumFaces; - out->mNumBones += (*it)->mNumBones; - - // combine primitive type flags - out->mPrimitiveTypes |= (*it)->mPrimitiveTypes; - } - out->mName.Set( name.c_str() ); - - if (out->mNumVertices) { - aiVector3D* pv2; - - // copy vertex positions - if ((**begin).HasPositions()) { - - pv2 = out->mVertices = new aiVector3D[out->mNumVertices]; - for (std::vector::const_iterator it = begin; it != end; ++it) { - if ((*it)->mVertices) { - ::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D)); - } - else ASSIMP_LOG_WARN("JoinMeshes: Positions expected but input mesh contains no positions"); - pv2 += (*it)->mNumVertices; - } - } - // copy normals - if ((**begin).HasNormals()) { - - pv2 = out->mNormals = new aiVector3D[out->mNumVertices]; - for (std::vector::const_iterator it = begin; it != end;++it) { - if ((*it)->mNormals) { - ::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D)); - } else { - ASSIMP_LOG_WARN( "JoinMeshes: Normals expected but input mesh contains no normals" ); - } - pv2 += (*it)->mNumVertices; - } - } - // copy tangents and bi-tangents - if ((**begin).HasTangentsAndBitangents()) { - - pv2 = out->mTangents = new aiVector3D[out->mNumVertices]; - aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices]; - - for (std::vector::const_iterator it = begin; it != end;++it) { - if ((*it)->mTangents) { - ::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices*sizeof(aiVector3D)); - ::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D)); - } else { - ASSIMP_LOG_WARN( "JoinMeshes: Tangents expected but input mesh contains no tangents" ); - } - pv2 += (*it)->mNumVertices; - pv2b += (*it)->mNumVertices; - } - } - // copy texture coordinates - unsigned int n = 0; - while ((**begin).HasTextureCoords(n)) { - out->mNumUVComponents[n] = (*begin)->mNumUVComponents[n]; - - pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices]; - for (std::vector::const_iterator it = begin; it != end;++it) { - if ((*it)->mTextureCoords[n]) { - ::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D)); - } else { - ASSIMP_LOG_WARN( "JoinMeshes: UVs expected but input mesh contains no UVs" ); - } - pv2 += (*it)->mNumVertices; - } - ++n; - } - // copy vertex colors - n = 0; - while ((**begin).HasVertexColors(n)) { - aiColor4D *pVec2 = out->mColors[n] = new aiColor4D[out->mNumVertices]; - for ( std::vector::const_iterator it = begin; it != end; ++it ) { - if ((*it)->mColors[n]) { - ::memcpy( pVec2, (*it)->mColors[ n ], (*it)->mNumVertices * sizeof( aiColor4D ) ) ; - } else { - ASSIMP_LOG_WARN( "JoinMeshes: VCs expected but input mesh contains no VCs" ); - } - pVec2 += (*it)->mNumVertices; - } - ++n; - } - } - - if (out->mNumFaces) // just for safety - { - // copy faces - out->mFaces = new aiFace[out->mNumFaces]; - aiFace* pf2 = out->mFaces; - - unsigned int ofs = 0; - for (std::vector::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::const_iterator it = begin; it != end;++it) - delete *it; -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::MergeMaterials(aiMaterial** dest, - std::vector::const_iterator begin, - std::vector::const_iterator end) -{ - if ( nullptr == dest ) { - return; - } - - if (begin == end) { - *dest = NULL; // no materials ... - return; - } - - // Allocate the output material - aiMaterial* out = *dest = new aiMaterial(); - - // Get the maximal number of properties - unsigned int size = 0; - for (std::vector::const_iterator it = begin; it != end; ++it) { - size += (*it)->mNumProperties; - } - - out->Clear(); - delete[] out->mProperties; - - out->mNumAllocated = size; - out->mNumProperties = 0; - out->mProperties = new aiMaterialProperty*[out->mNumAllocated]; - - for (std::vector::const_iterator it = begin; it != end; ++it) { - for(unsigned int i = 0; i < (*it)->mNumProperties; ++i) { - aiMaterialProperty* sprop = (*it)->mProperties[i]; - - // Test if we already have a matching property - const aiMaterialProperty* prop_exist; - if(aiGetMaterialProperty(out, sprop->mKey.C_Str(), sprop->mSemantic, sprop->mIndex, &prop_exist) != AI_SUCCESS) { - // If not, we add it to the new material - aiMaterialProperty* prop = out->mProperties[out->mNumProperties] = new aiMaterialProperty(); - - prop->mDataLength = sprop->mDataLength; - prop->mData = new char[prop->mDataLength]; - ::memcpy(prop->mData, sprop->mData, prop->mDataLength); - - prop->mIndex = sprop->mIndex; - prop->mSemantic = sprop->mSemantic; - prop->mKey = sprop->mKey; - prop->mType = sprop->mType; - - out->mNumProperties++; - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -template -inline -void CopyPtrArray (Type**& dest, const Type* const * src, ai_uint num) { - if (!num) { - dest = NULL; - return; - } - dest = new Type*[num]; - for (ai_uint i = 0; i < num;++i) { - SceneCombiner::Copy(&dest[i],src[i]); - } -} - -// ------------------------------------------------------------------------------------------------ -template -inline -void GetArrayCopy(Type*& dest, ai_uint num ) { - if ( !dest ) { - return; - } - Type* old = dest; - - dest = new Type[num]; - ::memcpy(dest, old, sizeof(Type) * num); -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::CopySceneFlat(aiScene** _dest,const aiScene* src) { - if ( nullptr == _dest || nullptr == src ) { - return; - } - - // reuse the old scene or allocate a new? - if (*_dest) { - (*_dest)->~aiScene(); - new (*_dest) aiScene(); - } else { - *_dest = new aiScene(); - } - - ::memcpy(*_dest,src,sizeof(aiScene)); -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src,bool allocate) { - if ( nullptr == _dest || nullptr == src ) { - return; - } - - if (allocate) { - *_dest = new aiScene(); - } - aiScene* dest = *_dest; - ai_assert(nullptr != dest); - - // copy metadata - if ( nullptr != src->mMetaData ) { - dest->mMetaData = new aiMetadata( *src->mMetaData ); - } - - // copy animations - dest->mNumAnimations = src->mNumAnimations; - CopyPtrArray(dest->mAnimations,src->mAnimations, - dest->mNumAnimations); - - // copy textures - dest->mNumTextures = src->mNumTextures; - CopyPtrArray(dest->mTextures,src->mTextures, - dest->mNumTextures); - - // copy materials - dest->mNumMaterials = src->mNumMaterials; - CopyPtrArray(dest->mMaterials,src->mMaterials, - dest->mNumMaterials); - - // copy lights - dest->mNumLights = src->mNumLights; - CopyPtrArray(dest->mLights,src->mLights, - dest->mNumLights); - - // copy cameras - dest->mNumCameras = src->mNumCameras; - CopyPtrArray(dest->mCameras,src->mCameras, - dest->mNumCameras); - - // copy meshes - dest->mNumMeshes = src->mNumMeshes; - CopyPtrArray(dest->mMeshes,src->mMeshes, - dest->mNumMeshes); - - // now - copy the root node of the scene (deep copy, too) - Copy( &dest->mRootNode, src->mRootNode); - - // and keep the flags ... - dest->mFlags = src->mFlags; - - // source private data might be NULL if the scene is user-allocated (i.e. for use with the export API) - if (dest->mPrivate != NULL) { - ScenePriv(dest)->mPPStepsApplied = ScenePriv(src) ? ScenePriv(src)->mPPStepsApplied : 0; - } -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy( aiMesh** _dest, const aiMesh* src ) { - if ( nullptr == _dest || nullptr == src ) { - return; - } - - aiMesh* dest = *_dest = new aiMesh(); - - // get a flat copy - ::memcpy(dest,src,sizeof(aiMesh)); - - // and reallocate all arrays - GetArrayCopy( dest->mVertices, dest->mNumVertices ); - GetArrayCopy( dest->mNormals , dest->mNumVertices ); - GetArrayCopy( dest->mTangents, dest->mNumVertices ); - GetArrayCopy( dest->mBitangents, dest->mNumVertices ); - - unsigned int n = 0; - while (dest->HasTextureCoords(n)) - GetArrayCopy( dest->mTextureCoords[n++], dest->mNumVertices ); - - n = 0; - while (dest->HasVertexColors(n)) - GetArrayCopy( dest->mColors[n++], dest->mNumVertices ); - - // make a deep copy of all bones - CopyPtrArray(dest->mBones,dest->mBones,dest->mNumBones); - - // make a deep copy of all faces - GetArrayCopy(dest->mFaces,dest->mNumFaces); - for (unsigned int i = 0; i < dest->mNumFaces;++i) { - aiFace& f = dest->mFaces[i]; - GetArrayCopy(f.mIndices,f.mNumIndices); - } -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy (aiMaterial** _dest, const aiMaterial* src) { - if ( nullptr == _dest || nullptr == src ) { - return; - } - - aiMaterial* dest = (aiMaterial*) ( *_dest = new aiMaterial() ); - - dest->Clear(); - delete[] dest->mProperties; - - dest->mNumAllocated = src->mNumAllocated; - dest->mNumProperties = src->mNumProperties; - dest->mProperties = new aiMaterialProperty* [dest->mNumAllocated]; - - for (unsigned int i = 0; i < dest->mNumProperties;++i) - { - aiMaterialProperty* prop = dest->mProperties[i] = new aiMaterialProperty(); - aiMaterialProperty* sprop = src->mProperties[i]; - - prop->mDataLength = sprop->mDataLength; - prop->mData = new char[prop->mDataLength]; - ::memcpy(prop->mData,sprop->mData,prop->mDataLength); - - prop->mIndex = sprop->mIndex; - prop->mSemantic = sprop->mSemantic; - prop->mKey = sprop->mKey; - prop->mType = sprop->mType; - } -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy(aiTexture** _dest, const aiTexture* src) { - if ( nullptr == _dest || nullptr == src ) { - return; - } - - aiTexture* dest = *_dest = new aiTexture(); - - // get a flat copy - ::memcpy(dest,src,sizeof(aiTexture)); - - // and reallocate all arrays. We must do it manually here - const char* old = (const char*)dest->pcData; - if (old) - { - unsigned int cpy; - if (!dest->mHeight)cpy = dest->mWidth; - else cpy = dest->mHeight * dest->mWidth * sizeof(aiTexel); - - if (!cpy) - { - dest->pcData = NULL; - return; - } - // the cast is legal, the aiTexel c'tor does nothing important - dest->pcData = (aiTexel*) new char[cpy]; - ::memcpy(dest->pcData, old, cpy); - } -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy( aiAnimation** _dest, const aiAnimation* src ) { - if ( nullptr == _dest || nullptr == src ) { - return; - } - - aiAnimation* dest = *_dest = new aiAnimation(); - - // get a flat copy - ::memcpy(dest,src,sizeof(aiAnimation)); - - // and reallocate all arrays - CopyPtrArray( dest->mChannels, src->mChannels, dest->mNumChannels ); -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy(aiNodeAnim** _dest, const aiNodeAnim* src) { - if ( nullptr == _dest || nullptr == src ) { - return; - } - - aiNodeAnim* dest = *_dest = new aiNodeAnim(); - - // get a flat copy - ::memcpy(dest,src,sizeof(aiNodeAnim)); - - // and reallocate all arrays - GetArrayCopy( dest->mPositionKeys, dest->mNumPositionKeys ); - GetArrayCopy( dest->mScalingKeys, dest->mNumScalingKeys ); - GetArrayCopy( dest->mRotationKeys, dest->mNumRotationKeys ); -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy( aiCamera** _dest,const aiCamera* src) { - if ( nullptr == _dest || nullptr == src ) { - return; - } - - aiCamera* dest = *_dest = new aiCamera(); - - // get a flat copy, that's already OK - ::memcpy(dest,src,sizeof(aiCamera)); -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy(aiLight** _dest, const aiLight* src) { - if ( nullptr == _dest || nullptr == src ) { - return; - } - - aiLight* dest = *_dest = new aiLight(); - - // get a flat copy, that's already OK - ::memcpy(dest,src,sizeof(aiLight)); -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy(aiBone** _dest, const aiBone* src) { - if ( nullptr == _dest || nullptr == src ) { - return; - } - - aiBone* dest = *_dest = new aiBone(); - - // get a flat copy - ::memcpy(dest,src,sizeof(aiBone)); - - // and reallocate all arrays - GetArrayCopy( dest->mWeights, dest->mNumWeights ); -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy (aiNode** _dest, const aiNode* src) -{ - ai_assert(NULL != _dest && NULL != src); - - aiNode* dest = *_dest = new aiNode(); - - // get a flat copy - ::memcpy(dest,src,sizeof(aiNode)); - - if (src->mMetaData) { - Copy(&dest->mMetaData, src->mMetaData); - } - - // and reallocate all arrays - GetArrayCopy( dest->mMeshes, dest->mNumMeshes ); - CopyPtrArray( dest->mChildren, src->mChildren,dest->mNumChildren); - - // need to set the mParent fields to the created aiNode. - for( unsigned int i = 0; i < dest->mNumChildren; i ++ ) { - dest->mChildren[i]->mParent = dest; - } -} - -// ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy(aiMetadata** _dest, const aiMetadata* src) { - if ( nullptr == _dest || nullptr == src ) { - return; - } - - if ( 0 == src->mNumProperties ) { - return; - } - - aiMetadata* dest = *_dest = aiMetadata::Alloc( src->mNumProperties ); - std::copy(src->mKeys, src->mKeys + src->mNumProperties, dest->mKeys); - - dest->mValues = new aiMetadataEntry[src->mNumProperties]; - for (unsigned int i = 0; i < src->mNumProperties; ++i) { - aiMetadataEntry& in = src->mValues[i]; - aiMetadataEntry& out = dest->mValues[i]; - out.mType = in.mType; - switch (dest->mValues[i].mType) { - case AI_BOOL: - out.mData = new bool(*static_cast(in.mData)); - break; - case AI_INT32: - out.mData = new int32_t(*static_cast(in.mData)); - break; - case AI_UINT64: - out.mData = new uint64_t(*static_cast(in.mData)); - break; - case AI_FLOAT: - out.mData = new float(*static_cast(in.mData)); - break; - case AI_DOUBLE: - out.mData = new double(*static_cast(in.mData)); - break; - case AI_AISTRING: - out.mData = new aiString(*static_cast(in.mData)); - break; - case AI_AIVECTOR3D: - out.mData = new aiVector3D(*static_cast(in.mData)); - break; - default: - ai_assert(false); - break; - } - } -} - -} // Namespace Assimp - diff --git a/thirdparty/assimp/code/ScenePreprocessor.cpp b/thirdparty/assimp/code/ScenePreprocessor.cpp deleted file mode 100644 index 432a3d7666..0000000000 --- a/thirdparty/assimp/code/ScenePreprocessor.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -#include "ScenePreprocessor.h" -#include -#include -#include - - -using namespace Assimp; - -// --------------------------------------------------------------------------------------------- -void ScenePreprocessor::ProcessScene () -{ - ai_assert(scene != NULL); - - // Process all meshes - for (unsigned int i = 0; i < scene->mNumMeshes;++i) - ProcessMesh(scene->mMeshes[i]); - - // - nothing to do for nodes for the moment - // - nothing to do for textures for the moment - // - nothing to do for lights for the moment - // - nothing to do for cameras for the moment - - // Process all animations - for (unsigned int i = 0; i < scene->mNumAnimations;++i) - ProcessAnimation(scene->mAnimations[i]); - - // Generate a default material if none was specified - if (!scene->mNumMaterials && scene->mNumMeshes) { - scene->mMaterials = new aiMaterial*[2]; - aiMaterial* helper; - - aiString name; - - scene->mMaterials[scene->mNumMaterials] = helper = new aiMaterial(); - aiColor3D clr(0.6f,0.6f,0.6f); - helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE); - - // setup the default name to make this material identifiable - name.Set(AI_DEFAULT_MATERIAL_NAME); - helper->AddProperty(&name,AI_MATKEY_NAME); - - ASSIMP_LOG_DEBUG("ScenePreprocessor: Adding default material \'" AI_DEFAULT_MATERIAL_NAME "\'"); - - for (unsigned int i = 0; i < scene->mNumMeshes;++i) { - scene->mMeshes[i]->mMaterialIndex = scene->mNumMaterials; - } - - scene->mNumMaterials++; - } -} - -// --------------------------------------------------------------------------------------------- -void ScenePreprocessor::ProcessMesh (aiMesh* mesh) -{ - // If aiMesh::mNumUVComponents is *not* set assign the default value of 2 - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { - if (!mesh->mTextureCoords[i]) { - mesh->mNumUVComponents[i] = 0; - } else { - if (!mesh->mNumUVComponents[i]) - mesh->mNumUVComponents[i] = 2; - - aiVector3D* p = mesh->mTextureCoords[i], *end = p+mesh->mNumVertices; - - // Ensure unused components are zeroed. This will make 1D texture channels work - // as if they were 2D channels .. just in case an application doesn't handle - // this case - if (2 == mesh->mNumUVComponents[i]) { - for (; p != end; ++p) - p->z = 0.f; - } - else if (1 == mesh->mNumUVComponents[i]) { - for (; p != end; ++p) - p->z = p->y = 0.f; - } - else if (3 == mesh->mNumUVComponents[i]) { - // Really 3D coordinates? Check whether the third coordinate is != 0 for at least one element - for (; p != end; ++p) { - if (p->z != 0) - break; - } - if (p == end) { - ASSIMP_LOG_WARN("ScenePreprocessor: UVs are declared to be 3D but they're obviously not. Reverting to 2D."); - mesh->mNumUVComponents[i] = 2; - } - } - } - } - - // If the information which primitive types are there in the - // mesh is currently not available, compute it. - if (!mesh->mPrimitiveTypes) { - for (unsigned int a = 0; a < mesh->mNumFaces; ++a) { - aiFace& face = mesh->mFaces[a]; - switch (face.mNumIndices) - { - case 3u: - mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; - break; - - case 2u: - mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; - break; - - case 1u: - mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; - break; - - default: - mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; - break; - } - } - } - - // If tangents and normals are given but no bitangents compute them - if (mesh->mTangents && mesh->mNormals && !mesh->mBitangents) { - - mesh->mBitangents = new aiVector3D[mesh->mNumVertices]; - for (unsigned int i = 0; i < mesh->mNumVertices;++i) { - mesh->mBitangents[i] = mesh->mNormals[i] ^ mesh->mTangents[i]; - } - } -} - -// --------------------------------------------------------------------------------------------- -void ScenePreprocessor::ProcessAnimation (aiAnimation* anim) -{ - double first = 10e10, last = -10e10; - for (unsigned int i = 0; i < anim->mNumChannels;++i) { - aiNodeAnim* channel = anim->mChannels[i]; - - /* If the exact duration of the animation is not given - * compute it now. - */ - if (anim->mDuration == -1.) { - - // Position keys - for (unsigned int j = 0; j < channel->mNumPositionKeys;++j) { - aiVectorKey& key = channel->mPositionKeys[j]; - first = std::min (first, key.mTime); - last = std::max (last, key.mTime); - } - - // Scaling keys - for (unsigned int j = 0; j < channel->mNumScalingKeys;++j ) { - aiVectorKey& key = channel->mScalingKeys[j]; - first = std::min (first, key.mTime); - last = std::max (last, key.mTime); - } - - // Rotation keys - for (unsigned int j = 0; j < channel->mNumRotationKeys;++j ) { - aiQuatKey& key = channel->mRotationKeys[ j ]; - first = std::min (first, key.mTime); - last = std::max (last, key.mTime); - } - } - - /* Check whether the animation channel has no rotation - * or position tracks. In this case we generate a dummy - * track from the information we have in the transformation - * matrix of the corresponding node. - */ - if (!channel->mNumRotationKeys || !channel->mNumPositionKeys || !channel->mNumScalingKeys) { - // Find the node that belongs to this animation - aiNode* node = scene->mRootNode->FindNode(channel->mNodeName); - if (node) // ValidateDS will complain later if 'node' is NULL - { - // Decompose the transformation matrix of the node - aiVector3D scaling, position; - aiQuaternion rotation; - - node->mTransformation.Decompose(scaling, rotation,position); - - // No rotation keys? Generate a dummy track - if (!channel->mNumRotationKeys) { - channel->mNumRotationKeys = 1; - channel->mRotationKeys = new aiQuatKey[1]; - aiQuatKey& q = channel->mRotationKeys[0]; - - q.mTime = 0.; - q.mValue = rotation; - - ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy rotation track has been generated"); - } - - // No scaling keys? Generate a dummy track - if (!channel->mNumScalingKeys) { - channel->mNumScalingKeys = 1; - channel->mScalingKeys = new aiVectorKey[1]; - aiVectorKey& q = channel->mScalingKeys[0]; - - q.mTime = 0.; - q.mValue = scaling; - - ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy scaling track has been generated"); - } - - // No position keys? Generate a dummy track - if (!channel->mNumPositionKeys) { - channel->mNumPositionKeys = 1; - channel->mPositionKeys = new aiVectorKey[1]; - aiVectorKey& q = channel->mPositionKeys[0]; - - q.mTime = 0.; - q.mValue = position; - - ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy position track has been generated"); - } - } - } - } - - if (anim->mDuration == -1.) { - ASSIMP_LOG_DEBUG("ScenePreprocessor: Setting animation duration"); - anim->mDuration = last - std::min( first, 0. ); - } -} diff --git a/thirdparty/assimp/code/ScenePreprocessor.h b/thirdparty/assimp/code/ScenePreprocessor.h deleted file mode 100644 index 3f4c8d7c3f..0000000000 --- a/thirdparty/assimp/code/ScenePreprocessor.h +++ /dev/null @@ -1,125 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a post processing step to search all meshes for - degenerated faces */ -#ifndef AI_SCENE_PREPROCESSOR_H_INC -#define AI_SCENE_PREPROCESSOR_H_INC - -#include -#include - -struct aiScene; -struct aiAnimation; -struct aiMesh; - -class ScenePreprocessorTest; -namespace Assimp { - -// ---------------------------------------------------------------------------------- -/** ScenePreprocessor: Preprocess a scene before any post-processing - * steps are executed. - * - * The step computes data that needn't necessarily be provided by the - * importer, such as aiMesh::mPrimitiveTypes. -*/ -// ---------------------------------------------------------------------------------- -class ASSIMP_API ScenePreprocessor -{ - // Make ourselves a friend of the corresponding test unit. - friend class ::ScenePreprocessorTest; -public: - - // ---------------------------------------------------------------- - /** Default c'tpr. Use SetScene() to assign a scene to the object. - */ - ScenePreprocessor() - : scene (NULL) - {} - - /** Constructs the object and assigns a specific scene to it - */ - ScenePreprocessor(aiScene* _scene) - : scene (_scene) - {} - - // ---------------------------------------------------------------- - /** Assign a (new) scene to the object. - * - * One 'SceneProcessor' can be used for multiple scenes. - * Call ProcessScene to have the scene preprocessed. - * @param sc Scene to be processed. - */ - void SetScene (aiScene* sc) { - scene = sc; - } - - // ---------------------------------------------------------------- - /** Preprocess the current scene - */ - void ProcessScene (); - -protected: - - // ---------------------------------------------------------------- - /** Preprocess an animation in the scene - * @param anim Anim to be preprocessed. - */ - void ProcessAnimation (aiAnimation* anim); - - - // ---------------------------------------------------------------- - /** Preprocess a mesh in the scene - * @param mesh Mesh to be preprocessed. - */ - void ProcessMesh (aiMesh* mesh); - -protected: - - //! Scene we're currently working on - aiScene* scene; -}; - - -} // ! end namespace Assimp - -#endif // include guard diff --git a/thirdparty/assimp/code/ScenePrivate.h b/thirdparty/assimp/code/ScenePrivate.h deleted file mode 100644 index f336aafc9a..0000000000 --- a/thirdparty/assimp/code/ScenePrivate.h +++ /dev/null @@ -1,105 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Stuff to deal with aiScene::mPrivate - */ -#pragma once -#ifndef AI_SCENEPRIVATE_H_INCLUDED -#define AI_SCENEPRIVATE_H_INCLUDED - -#include -#include - -namespace Assimp { - -// Forward declarations -class Importer; - -struct ScenePrivateData { - // The struct constructor. - ScenePrivateData() AI_NO_EXCEPT; - - // Importer that originally loaded the scene though the C-API - // If set, this object is owned by this private data instance. - Assimp::Importer* mOrigImporter; - - // List of post-processing steps already applied to the scene. - unsigned int mPPStepsApplied; - - // true if the scene is a copy made with aiCopyScene() - // or the corresponding C++ API. This means that user code - // may have made modifications to it, so mPPStepsApplied - // and mOrigImporter are no longer safe to rely on and only - // serve informative purposes. - bool mIsCopy; -}; - -inline -ScenePrivateData::ScenePrivateData() AI_NO_EXCEPT -: mOrigImporter( nullptr ) -, mPPStepsApplied( 0 ) -, mIsCopy( false ) { - // empty -} - -// Access private data stored in the scene -inline -ScenePrivateData* ScenePriv(aiScene* in) { - ai_assert( nullptr != in ); - if ( nullptr == in ) { - return nullptr; - } - return static_cast(in->mPrivate); -} - -inline -const ScenePrivateData* ScenePriv(const aiScene* in) { - ai_assert( nullptr != in ); - if ( nullptr == in ) { - return nullptr; - } - return static_cast(in->mPrivate); -} - -} // Namespace Assimp - -#endif // AI_SCENEPRIVATE_H_INCLUDED diff --git a/thirdparty/assimp/code/SkeletonMeshBuilder.cpp b/thirdparty/assimp/code/SkeletonMeshBuilder.cpp deleted file mode 100644 index 06cfe034e9..0000000000 --- a/thirdparty/assimp/code/SkeletonMeshBuilder.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file SkeletonMeshBuilder.cpp - * @brief Implementation of a little class to construct a dummy mesh for a skeleton - */ - -#include -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// The constructor processes the given scene and adds a mesh there. -SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root, bool bKnobsOnly) -{ - // nothing to do if there's mesh data already present at the scene - if( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL) - return; - - if (!root) - root = pScene->mRootNode; - - mKnobsOnly = bKnobsOnly; - - // build some faces around each node - CreateGeometry( root ); - - // create a mesh to hold all the generated faces - pScene->mNumMeshes = 1; - pScene->mMeshes = new aiMesh*[1]; - pScene->mMeshes[0] = CreateMesh(); - // and install it at the root node - root->mNumMeshes = 1; - root->mMeshes = new unsigned int[1]; - root->mMeshes[0] = 0; - - // create a dummy material for the mesh - if(pScene->mNumMaterials==0){ - pScene->mNumMaterials = 1; - pScene->mMaterials = new aiMaterial*[1]; - pScene->mMaterials[0] = CreateMaterial(); - } -} - -// ------------------------------------------------------------------------------------------------ -// Recursively builds a simple mesh representation for the given node -void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode) -{ - // add a joint entry for the node. - const unsigned int vertexStartIndex = static_cast(mVertices.size()); - - // now build the geometry. - if( pNode->mNumChildren > 0 && !mKnobsOnly) - { - // If the node has children, we build little pointers to each of them - for( unsigned int a = 0; a < pNode->mNumChildren; a++) - { - // find a suitable coordinate system - const aiMatrix4x4& childTransform = pNode->mChildren[a]->mTransformation; - aiVector3D childpos( childTransform.a4, childTransform.b4, childTransform.c4); - ai_real distanceToChild = childpos.Length(); - if( distanceToChild < 0.0001) - continue; - aiVector3D up = aiVector3D( childpos).Normalize(); - - aiVector3D orth( 1.0, 0.0, 0.0); - if( std::fabs( orth * up) > 0.99) - orth.Set( 0.0, 1.0, 0.0); - - aiVector3D front = (up ^ orth).Normalize(); - aiVector3D side = (front ^ up).Normalize(); - - unsigned int localVertexStart = static_cast(mVertices.size()); - mVertices.push_back( -front * distanceToChild * (ai_real)0.1); - mVertices.push_back( childpos); - mVertices.push_back( -side * distanceToChild * (ai_real)0.1); - mVertices.push_back( -side * distanceToChild * (ai_real)0.1); - mVertices.push_back( childpos); - mVertices.push_back( front * distanceToChild * (ai_real)0.1); - mVertices.push_back( front * distanceToChild * (ai_real)0.1); - mVertices.push_back( childpos); - mVertices.push_back( side * distanceToChild * (ai_real)0.1); - mVertices.push_back( side * distanceToChild * (ai_real)0.1); - mVertices.push_back( childpos); - mVertices.push_back( -front * distanceToChild * (ai_real)0.1); - - mFaces.push_back( Face( localVertexStart + 0, localVertexStart + 1, localVertexStart + 2)); - mFaces.push_back( Face( localVertexStart + 3, localVertexStart + 4, localVertexStart + 5)); - mFaces.push_back( Face( localVertexStart + 6, localVertexStart + 7, localVertexStart + 8)); - mFaces.push_back( Face( localVertexStart + 9, localVertexStart + 10, localVertexStart + 11)); - } - } - else - { - // if the node has no children, it's an end node. Put a little knob there instead - aiVector3D ownpos( pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4); - ai_real sizeEstimate = ownpos.Length() * ai_real( 0.18 ); - - mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); - mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); - mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); - mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); - mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); - mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); - mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); - mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); - mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); - mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); - mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); - mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); - - mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); - mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); - mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); - mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); - mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); - mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); - mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); - mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); - mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); - mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); - mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); - mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); - - mFaces.push_back( Face( vertexStartIndex + 0, vertexStartIndex + 1, vertexStartIndex + 2)); - mFaces.push_back( Face( vertexStartIndex + 3, vertexStartIndex + 4, vertexStartIndex + 5)); - mFaces.push_back( Face( vertexStartIndex + 6, vertexStartIndex + 7, vertexStartIndex + 8)); - mFaces.push_back( Face( vertexStartIndex + 9, vertexStartIndex + 10, vertexStartIndex + 11)); - mFaces.push_back( Face( vertexStartIndex + 12, vertexStartIndex + 13, vertexStartIndex + 14)); - mFaces.push_back( Face( vertexStartIndex + 15, vertexStartIndex + 16, vertexStartIndex + 17)); - mFaces.push_back( Face( vertexStartIndex + 18, vertexStartIndex + 19, vertexStartIndex + 20)); - mFaces.push_back( Face( vertexStartIndex + 21, vertexStartIndex + 22, vertexStartIndex + 23)); - } - - unsigned int numVertices = static_cast(mVertices.size() - vertexStartIndex); - if( numVertices > 0) - { - // create a bone affecting all the newly created vertices - aiBone* bone = new aiBone; - mBones.push_back( bone); - bone->mName = pNode->mName; - - // calculate the bone offset matrix by concatenating the inverse transformations of all parents - bone->mOffsetMatrix = aiMatrix4x4( pNode->mTransformation).Inverse(); - for( aiNode* parent = pNode->mParent; parent != NULL; parent = parent->mParent) - bone->mOffsetMatrix = aiMatrix4x4( parent->mTransformation).Inverse() * bone->mOffsetMatrix; - - // add all the vertices to the bone's influences - bone->mNumWeights = numVertices; - bone->mWeights = new aiVertexWeight[numVertices]; - for( unsigned int a = 0; a < numVertices; a++) - bone->mWeights[a] = aiVertexWeight( vertexStartIndex + a, 1.0); - - // HACK: (thom) transform all vertices to the bone's local space. Should be done before adding - // them to the array, but I'm tired now and I'm annoyed. - aiMatrix4x4 boneToMeshTransform = aiMatrix4x4( bone->mOffsetMatrix).Inverse(); - for( unsigned int a = vertexStartIndex; a < mVertices.size(); a++) - mVertices[a] = boneToMeshTransform * mVertices[a]; - } - - // and finally recurse into the children list - for( unsigned int a = 0; a < pNode->mNumChildren; a++) - CreateGeometry( pNode->mChildren[a]); -} - -// ------------------------------------------------------------------------------------------------ -// Creates the mesh from the internally accumulated stuff and returns it. -aiMesh* SkeletonMeshBuilder::CreateMesh() -{ - aiMesh* mesh = new aiMesh(); - - // add points - mesh->mNumVertices = static_cast(mVertices.size()); - mesh->mVertices = new aiVector3D[mesh->mNumVertices]; - std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices); - - mesh->mNormals = new aiVector3D[mesh->mNumVertices]; - - // add faces - mesh->mNumFaces = static_cast(mFaces.size()); - mesh->mFaces = new aiFace[mesh->mNumFaces]; - for( unsigned int a = 0; a < mesh->mNumFaces; a++) - { - const Face& inface = mFaces[a]; - aiFace& outface = mesh->mFaces[a]; - outface.mNumIndices = 3; - outface.mIndices = new unsigned int[3]; - outface.mIndices[0] = inface.mIndices[0]; - outface.mIndices[1] = inface.mIndices[1]; - outface.mIndices[2] = inface.mIndices[2]; - - // Compute per-face normals ... we don't want the bones to be smoothed ... they're built to visualize - // the skeleton, so it's good if there's a visual difference to the rest of the geometry - aiVector3D nor = ((mVertices[inface.mIndices[2]] - mVertices[inface.mIndices[0]]) ^ - (mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]])); - - if (nor.Length() < 1e-5) /* ensure that FindInvalidData won't remove us ...*/ - nor = aiVector3D(1.0,0.0,0.0); - - for (unsigned int n = 0; n < 3; ++n) - mesh->mNormals[inface.mIndices[n]] = nor; - } - - // add the bones - mesh->mNumBones = static_cast(mBones.size()); - mesh->mBones = new aiBone*[mesh->mNumBones]; - std::copy( mBones.begin(), mBones.end(), mesh->mBones); - - // default - mesh->mMaterialIndex = 0; - - return mesh; -} - -// ------------------------------------------------------------------------------------------------ -// Creates a dummy material and returns it. -aiMaterial* SkeletonMeshBuilder::CreateMaterial() -{ - aiMaterial* matHelper = new aiMaterial; - - // Name - aiString matName( std::string( "SkeletonMaterial")); - matHelper->AddProperty( &matName, AI_MATKEY_NAME); - - // Prevent backface culling - const int no_cull = 1; - matHelper->AddProperty(&no_cull,1,AI_MATKEY_TWOSIDED); - - return matHelper; -} diff --git a/thirdparty/assimp/code/SortByPTypeProcess.cpp b/thirdparty/assimp/code/SortByPTypeProcess.cpp deleted file mode 100644 index 2e0cc54004..0000000000 --- a/thirdparty/assimp/code/SortByPTypeProcess.cpp +++ /dev/null @@ -1,403 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of the DeterminePTypeHelperProcess and - * SortByPTypeProcess post-process steps. -*/ - - - -// internal headers -#include "ProcessHelper.h" -#include "SortByPTypeProcess.h" -#include - -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& replaceMeshIndex, aiNode* node) -{ - if (node->mNumMeshes) - { - unsigned int newSize = 0; - for (unsigned int m = 0; m< node->mNumMeshes; ++m) - { - unsigned int add = node->mMeshes[m]<<2; - for (unsigned int i = 0; i < 4;++i) - { - if (UINT_MAX != replaceMeshIndex[add+i])++newSize; - } - } - if (!newSize) - { - delete[] node->mMeshes; - node->mNumMeshes = 0; - node->mMeshes = NULL; - } - else - { - // Try to reuse the old array if possible - unsigned int* newMeshes = (newSize > node->mNumMeshes - ? new unsigned int[newSize] : node->mMeshes); - - for (unsigned int m = 0; m< node->mNumMeshes; ++m) - { - unsigned int add = node->mMeshes[m]<<2; - for (unsigned int i = 0; i < 4;++i) - { - if (UINT_MAX != replaceMeshIndex[add+i]) - *newMeshes++ = replaceMeshIndex[add+i]; - } - } - if (newSize > node->mNumMeshes) - delete[] node->mMeshes; - - node->mMeshes = newMeshes-(node->mNumMeshes = newSize); - } - } - - // call all subnodes recursively - for (unsigned int m = 0; m < node->mNumChildren; ++m) - UpdateNodes(replaceMeshIndex,node->mChildren[m]); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void SortByPTypeProcess::Execute( aiScene* pScene) { - if ( 0 == pScene->mNumMeshes) { - ASSIMP_LOG_DEBUG("SortByPTypeProcess skipped, there are no meshes"); - return; - } - - ASSIMP_LOG_DEBUG("SortByPTypeProcess begin"); - - unsigned int aiNumMeshesPerPType[4] = {0,0,0,0}; - - std::vector outMeshes; - outMeshes.reserve(pScene->mNumMeshes<<1u); - - bool bAnyChanges = false; - - std::vector replaceMeshIndex(pScene->mNumMeshes*4,UINT_MAX); - std::vector::iterator meshIdx = replaceMeshIndex.begin(); - for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { - aiMesh* const mesh = pScene->mMeshes[i]; - ai_assert(0 != mesh->mPrimitiveTypes); - - // if there's just one primitive type in the mesh there's nothing to do for us - unsigned int num = 0; - if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT) { - ++aiNumMeshesPerPType[0]; - ++num; - } - if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE) { - ++aiNumMeshesPerPType[1]; - ++num; - } - if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) { - ++aiNumMeshesPerPType[2]; - ++num; - } - if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON) { - ++aiNumMeshesPerPType[3]; - ++num; - } - - if (1 == num) { - if (!(configRemoveMeshes & mesh->mPrimitiveTypes)) { - *meshIdx = static_cast( outMeshes.size() ); - outMeshes.push_back(mesh); - } else { - delete mesh; - pScene->mMeshes[ i ] = nullptr; - bAnyChanges = true; - } - - meshIdx += 4; - continue; - } - bAnyChanges = true; - - // reuse our current mesh arrays for the submesh - // with the largest number of primitives - unsigned int aiNumPerPType[4] = {0,0,0,0}; - aiFace* pFirstFace = mesh->mFaces; - aiFace* const pLastFace = pFirstFace + mesh->mNumFaces; - - unsigned int numPolyVerts = 0; - for (;pFirstFace != pLastFace; ++pFirstFace) { - if (pFirstFace->mNumIndices <= 3) - ++aiNumPerPType[pFirstFace->mNumIndices-1]; - else - { - ++aiNumPerPType[3]; - numPolyVerts += pFirstFace-> mNumIndices; - } - } - - VertexWeightTable* avw = ComputeVertexBoneWeightTable(mesh); - for (unsigned int real = 0; real < 4; ++real,++meshIdx) - { - if ( !aiNumPerPType[real] || configRemoveMeshes & (1u << real)) - { - continue; - } - - *meshIdx = (unsigned int) outMeshes.size(); - outMeshes.push_back(new aiMesh()); - aiMesh* out = outMeshes.back(); - - // the name carries the adjacency information between the meshes - out->mName = mesh->mName; - - // copy data members - out->mPrimitiveTypes = 1u << real; - out->mMaterialIndex = mesh->mMaterialIndex; - - // allocate output storage - out->mNumFaces = aiNumPerPType[real]; - aiFace* outFaces = out->mFaces = new aiFace[out->mNumFaces]; - - out->mNumVertices = (3 == real ? numPolyVerts : out->mNumFaces * (real+1)); - - aiVector3D *vert(nullptr), *nor(nullptr), *tan(nullptr), *bit(nullptr); - aiVector3D *uv [AI_MAX_NUMBER_OF_TEXTURECOORDS]; - aiColor4D *cols [AI_MAX_NUMBER_OF_COLOR_SETS]; - - if (mesh->mVertices) { - vert = out->mVertices = new aiVector3D[out->mNumVertices]; - } - - if (mesh->mNormals) { - nor = out->mNormals = new aiVector3D[out->mNumVertices]; - } - - if (mesh->mTangents) { - tan = out->mTangents = new aiVector3D[out->mNumVertices]; - bit = out->mBitangents = new aiVector3D[out->mNumVertices]; - } - - for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_TEXTURECOORDS;++j) { - uv[j] = nullptr; - if (mesh->mTextureCoords[j]) { - uv[j] = out->mTextureCoords[j] = new aiVector3D[out->mNumVertices]; - } - - out->mNumUVComponents[j] = mesh->mNumUVComponents[j]; - } - - for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_COLOR_SETS;++j) { - cols[j] = nullptr; - if (mesh->mColors[j]) { - cols[j] = out->mColors[j] = new aiColor4D[out->mNumVertices]; - } - } - - typedef std::vector< aiVertexWeight > TempBoneInfo; - std::vector< TempBoneInfo > tempBones(mesh->mNumBones); - - // try to guess how much storage we'll need - for (unsigned int q = 0; q < mesh->mNumBones;++q) - { - tempBones[q].reserve(mesh->mBones[q]->mNumWeights / (num-1)); - } - - unsigned int outIdx = 0; - for (unsigned int m = 0; m < mesh->mNumFaces; ++m) - { - aiFace& in = mesh->mFaces[m]; - if ((real == 3 && in.mNumIndices <= 3) || (real != 3 && in.mNumIndices != real+1)) - { - continue; - } - - outFaces->mNumIndices = in.mNumIndices; - outFaces->mIndices = in.mIndices; - - for (unsigned int q = 0; q < in.mNumIndices; ++q) - { - unsigned int idx = in.mIndices[q]; - - // process all bones of this index - if (avw) - { - VertexWeightTable& tbl = avw[idx]; - for (VertexWeightTable::const_iterator it = tbl.begin(), end = tbl.end(); - it != end; ++it) - { - tempBones[ (*it).first ].push_back( aiVertexWeight(outIdx, (*it).second) ); - } - } - - if (vert) - { - *vert++ = mesh->mVertices[idx]; - //mesh->mVertices[idx].x = get_qnan(); - } - if (nor )*nor++ = mesh->mNormals[idx]; - if (tan ) - { - *tan++ = mesh->mTangents[idx]; - *bit++ = mesh->mBitangents[idx]; - } - - for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++pp) - { - if (!uv[pp])break; - *uv[pp]++ = mesh->mTextureCoords[pp][idx]; - } - - for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_COLOR_SETS; ++pp) - { - if (!cols[pp])break; - *cols[pp]++ = mesh->mColors[pp][idx]; - } - - in.mIndices[q] = outIdx++; - } - - in.mIndices = nullptr; - ++outFaces; - } - ai_assert(outFaces == out->mFaces + out->mNumFaces); - - // now generate output bones - for (unsigned int q = 0; q < mesh->mNumBones;++q) - if (!tempBones[q].empty())++out->mNumBones; - - if (out->mNumBones) - { - out->mBones = new aiBone*[out->mNumBones]; - for (unsigned int q = 0, real = 0; q < mesh->mNumBones;++q) - { - TempBoneInfo& in = tempBones[q]; - if (in.empty())continue; - - aiBone* srcBone = mesh->mBones[q]; - aiBone* bone = out->mBones[real] = new aiBone(); - - bone->mName = srcBone->mName; - bone->mOffsetMatrix = srcBone->mOffsetMatrix; - - bone->mNumWeights = (unsigned int)in.size(); - bone->mWeights = new aiVertexWeight[bone->mNumWeights]; - - ::memcpy(bone->mWeights,&in[0],bone->mNumWeights*sizeof(aiVertexWeight)); - - ++real; - } - } - } - - // delete the per-vertex bone weights table - delete[] avw; - - // delete the input mesh - delete mesh; - - // avoid invalid pointer - pScene->mMeshes[i] = NULL; - } - - if (outMeshes.empty()) - { - // This should not occur - throw DeadlyImportError("No meshes remaining"); - } - - // If we added at least one mesh process all nodes in the node - // graph and update their respective mesh indices. - if (bAnyChanges) - { - UpdateNodes(replaceMeshIndex,pScene->mRootNode); - } - - if (outMeshes.size() != pScene->mNumMeshes) - { - delete[] pScene->mMeshes; - pScene->mNumMeshes = (unsigned int)outMeshes.size(); - pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; - } - ::memcpy(pScene->mMeshes,&outMeshes[0],pScene->mNumMeshes*sizeof(void*)); - - if (!DefaultLogger::isNullLogger()) - { - char buffer[1024]; - ::ai_snprintf(buffer,1024,"Points: %u%s, Lines: %u%s, Triangles: %u%s, Polygons: %u%s (Meshes, X = removed)", - aiNumMeshesPerPType[0], ((configRemoveMeshes & aiPrimitiveType_POINT) ? "X" : ""), - aiNumMeshesPerPType[1], ((configRemoveMeshes & aiPrimitiveType_LINE) ? "X" : ""), - aiNumMeshesPerPType[2], ((configRemoveMeshes & aiPrimitiveType_TRIANGLE) ? "X" : ""), - aiNumMeshesPerPType[3], ((configRemoveMeshes & aiPrimitiveType_POLYGON) ? "X" : "")); - ASSIMP_LOG_INFO(buffer); - ASSIMP_LOG_DEBUG("SortByPTypeProcess finished"); - } -} - diff --git a/thirdparty/assimp/code/SortByPTypeProcess.h b/thirdparty/assimp/code/SortByPTypeProcess.h deleted file mode 100644 index c9d9924d8f..0000000000 --- a/thirdparty/assimp/code/SortByPTypeProcess.h +++ /dev/null @@ -1,85 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a post processing step to sort meshes by the types - of primitives they contain */ -#ifndef AI_SORTBYPTYPEPROCESS_H_INC -#define AI_SORTBYPTYPEPROCESS_H_INC - -#include "BaseProcess.h" -#include - -class SortByPTypeProcessTest; -namespace Assimp { - - -// --------------------------------------------------------------------------- -/** SortByPTypeProcess: Sorts meshes by the types of primitives they contain. - * A mesh with 5 lines, 3 points and 145 triangles would be split in 3 - * submeshes. -*/ -class ASSIMP_API SortByPTypeProcess : public BaseProcess -{ -public: - - SortByPTypeProcess(); - ~SortByPTypeProcess(); - -public: - // ------------------------------------------------------------------- - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - void SetupProperties(const Importer* pImp); - -private: - - int configRemoveMeshes; -}; - - -} // end of namespace Assimp - -#endif // !!AI_SORTBYPTYPEPROCESS_H_INC diff --git a/thirdparty/assimp/code/SpatialSort.cpp b/thirdparty/assimp/code/SpatialSort.cpp deleted file mode 100644 index a4f3a4e4b8..0000000000 --- a/thirdparty/assimp/code/SpatialSort.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of the helper class to quickly find vertices close to a given position */ - -#include -#include - -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 (pPositions); - const aiVector3D* vec = reinterpret_cast (tempPointer + a * pElementOffset); - - // store position by index and distance - ai_real distance = *vec * mPlaneNormal; - mPositions.push_back( Entry( static_cast(a+initial), *vec, distance)); - } - - if (pFinalize) { - // now sort the array ascending by distance. - Finalize(); - } -} - -// ------------------------------------------------------------------------------------------------ -// Returns an iterator for all positions close to the given position. -void SpatialSort::FindPositions( const aiVector3D& pPosition, - ai_real pRadius, std::vector& poResults) const -{ - const ai_real dist = pPosition * mPlaneNormal; - const ai_real minDist = dist - pRadius, maxDist = dist + pRadius; - - // clear the array - poResults.clear(); - - // quick check for positions outside the range - if( mPositions.size() == 0) - return; - if( maxDist < mPositions.front().mDistance) - return; - if( minDist > mPositions.back().mDistance) - return; - - // do a binary search for the minimal distance to start the iteration there - unsigned int index = (unsigned int)mPositions.size() / 2; - unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; - while( binaryStepSize > 1) - { - if( mPositions[index].mDistance < minDist) - index += binaryStepSize; - else - index -= binaryStepSize; - - binaryStepSize /= 2; - } - - // depending on the direction of the last step we need to single step a bit back or forth - // to find the actual beginning element of the range - while( index > 0 && mPositions[index].mDistance > minDist) - index--; - while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist) - index++; - - // Mow start iterating from there until the first position lays outside of the distance range. - // Add all positions inside the distance range within the given radius to the result aray - std::vector::const_iterator it = mPositions.begin() + index; - const ai_real pSquared = pRadius*pRadius; - while( it->mDistance < maxDist) - { - if( (it->mPosition - pPosition).SquareLength() < pSquared) - poResults.push_back( it->mIndex); - ++it; - if( it == mPositions.end()) - break; - } - - // that's it -} - -namespace { - - // Binary, signed-integer representation of a single-precision floating-point value. - // IEEE 754 says: "If two floating-point numbers in the same format are ordered then they are - // ordered the same way when their bits are reinterpreted as sign-magnitude integers." - // This allows us to convert all floating-point numbers to signed integers of arbitrary size - // and then use them to work with ULPs (Units in the Last Place, for high-precision - // computations) or to compare them (integer comparisons are faster than floating-point - // comparisons on many platforms). - typedef ai_int BinFloat; - - // -------------------------------------------------------------------------------------------- - // Converts the bit pattern of a floating-point number to its signed integer representation. - BinFloat ToBinary( const ai_real & pValue) { - - // If this assertion fails, signed int is not big enough to store a float on your platform. - // Please correct the declaration of BinFloat a few lines above - but do it in a portable, - // #ifdef'd manner! - static_assert( sizeof(BinFloat) >= sizeof(ai_real), "sizeof(BinFloat) >= sizeof(ai_real)"); - - #if defined( _MSC_VER) - // If this assertion fails, Visual C++ has finally moved to ILP64. This means that this - // code has just become legacy code! Find out the current value of _MSC_VER and modify - // the #if above so it evaluates false on the current and all upcoming VC versions (or - // on the current platform, if LP64 or LLP64 are still used on other platforms). - static_assert( sizeof(BinFloat) == sizeof(ai_real), "sizeof(BinFloat) == sizeof(ai_real)"); - - // This works best on Visual C++, but other compilers have their problems with it. - const BinFloat binValue = reinterpret_cast(pValue); - #else - // On many compilers, reinterpreting a float address as an integer causes aliasing - // problems. This is an ugly but more or less safe way of doing it. - union { - ai_real asFloat; - BinFloat asBin; - } conversion; - conversion.asBin = 0; // zero empty space in case sizeof(BinFloat) > sizeof(float) - conversion.asFloat = pValue; - const BinFloat binValue = conversion.asBin; - #endif - - // floating-point numbers are of sign-magnitude format, so find out what signed number - // representation we must convert negative values to. - // See http://en.wikipedia.org/wiki/Signed_number_representations. - - // Two's complement? - if( (-42 == (~42 + 1)) && (binValue & 0x80000000)) - return BinFloat(1 << (CHAR_BIT * sizeof(BinFloat) - 1)) - binValue; - // One's complement? - else if ( (-42 == ~42) && (binValue & 0x80000000)) - return BinFloat(-0) - binValue; - // Sign-magnitude? - else if( (-42 == (42 | (-0))) && (binValue & 0x80000000)) // -0 = 1000... binary - return binValue; - else - return binValue; - } - -} // namespace - -// ------------------------------------------------------------------------------------------------ -// Fills an array with indices of all positions identical to the given position. In opposite to -// FindPositions(), not an epsilon is used but a (very low) tolerance of four floating-point units. -void SpatialSort::FindIdenticalPositions( const aiVector3D& pPosition, - std::vector& poResults) const -{ - // Epsilons have a huge disadvantage: they are of constant precision, while floating-point - // values are of log2 precision. If you apply e=0.01 to 100, the epsilon is rather small, but - // if you apply it to 0.001, it is enormous. - - // The best way to overcome this is the unit in the last place (ULP). A precision of 2 ULPs - // tells us that a float does not differ more than 2 bits from the "real" value. ULPs are of - // logarithmic precision - around 1, they are 1*(2^24) and around 10000, they are 0.00125. - - // For standard C math, we can assume a precision of 0.5 ULPs according to IEEE 754. The - // incoming vertex positions might have already been transformed, probably using rather - // inaccurate SSE instructions, so we assume a tolerance of 4 ULPs to safely identify - // identical vertex positions. - static const int toleranceInULPs = 4; - // An interesting point is that the inaccuracy grows linear with the number of operations: - // multiplying to numbers, each inaccurate to four ULPs, results in an inaccuracy of four ULPs - // plus 0.5 ULPs for the multiplication. - // To compute the distance to the plane, a dot product is needed - that is a multiplication and - // an addition on each number. - static const int distanceToleranceInULPs = toleranceInULPs + 1; - // The squared distance between two 3D vectors is computed the same way, but with an additional - // subtraction. - static const int distance3DToleranceInULPs = distanceToleranceInULPs + 1; - - // Convert the plane distance to its signed integer representation so the ULPs tolerance can be - // applied. For some reason, VC won't optimize two calls of the bit pattern conversion. - const BinFloat minDistBinary = ToBinary( pPosition * mPlaneNormal) - distanceToleranceInULPs; - const BinFloat maxDistBinary = minDistBinary + 2 * distanceToleranceInULPs; - - // clear the array in this strange fashion because a simple clear() would also deallocate - // the array which we want to avoid - poResults.resize( 0 ); - - // do a binary search for the minimal distance to start the iteration there - unsigned int index = (unsigned int)mPositions.size() / 2; - unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; - while( binaryStepSize > 1) - { - // Ugly, but conditional jumps are faster with integers than with floats - if( minDistBinary > ToBinary(mPositions[index].mDistance)) - index += binaryStepSize; - else - index -= binaryStepSize; - - binaryStepSize /= 2; - } - - // depending on the direction of the last step we need to single step a bit back or forth - // to find the actual beginning element of the range - while( index > 0 && minDistBinary < ToBinary(mPositions[index].mDistance) ) - index--; - while( index < (mPositions.size() - 1) && minDistBinary > ToBinary(mPositions[index].mDistance)) - index++; - - // Now start iterating from there until the first position lays outside of the distance range. - // Add all positions inside the distance range within the tolerance to the result array - std::vector::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& fill, ai_real pRadius) const -{ - fill.resize(mPositions.size(),UINT_MAX); - ai_real dist, maxDist; - - unsigned int t=0; - const ai_real pSquared = pRadius*pRadius; - for (size_t i = 0; i < mPositions.size();) { - dist = mPositions[i].mPosition * mPlaneNormal; - maxDist = dist + pRadius; - - fill[mPositions[i].mIndex] = t; - const aiVector3D& oldpos = mPositions[i].mPosition; - for (++i; i < fill.size() && mPositions[i].mDistance < maxDist - && (mPositions[i].mPosition - oldpos).SquareLength() < pSquared; ++i) - { - fill[mPositions[i].mIndex] = t; - } - ++t; - } - -#ifdef ASSIMP_BUILD_DEBUG - - // debug invariant: mPositions[i].mIndex values must range from 0 to mPositions.size()-1 - for (size_t i = 0; i < fill.size(); ++i) { - ai_assert(fill[i] -#include - -#include -#include - -using namespace Assimp; -using namespace Assimp::Formatter; - -// ------------------------------------------------------------------------------------------------ -// Constructor -SplitByBoneCountProcess::SplitByBoneCountProcess() -{ - // set default, might be overridden by importer config - mMaxBoneCount = AI_SBBC_DEFAULT_MAX_BONES; -} - -// ------------------------------------------------------------------------------------------------ -// Destructor -SplitByBoneCountProcess::~SplitByBoneCountProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag. -bool SplitByBoneCountProcess::IsActive( unsigned int pFlags) const -{ - return !!(pFlags & aiProcess_SplitByBoneCount); -} - -// ------------------------------------------------------------------------------------------------ -// Updates internal properties -void SplitByBoneCountProcess::SetupProperties(const Importer* pImp) -{ - mMaxBoneCount = pImp->GetPropertyInteger(AI_CONFIG_PP_SBBC_MAX_BONES,AI_SBBC_DEFAULT_MAX_BONES); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void SplitByBoneCountProcess::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("SplitByBoneCountProcess begin"); - - // early out - bool isNecessary = false; - for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) - if( pScene->mMeshes[a]->mNumBones > mMaxBoneCount ) - isNecessary = true; - - if( !isNecessary ) - { - ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess early-out: no meshes with more than " << mMaxBoneCount << " bones." ); - return; - } - - // we need to do something. Let's go. - mSubMeshIndices.clear(); - mSubMeshIndices.resize( pScene->mNumMeshes); - - // build a new array of meshes for the scene - std::vector meshes; - - for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) - { - aiMesh* srcMesh = pScene->mMeshes[a]; - - std::vector newMeshes; - SplitMesh( pScene->mMeshes[a], newMeshes); - - // mesh was split - if( !newMeshes.empty() ) - { - // store new meshes and indices of the new meshes - for( unsigned int b = 0; b < newMeshes.size(); ++b) - { - mSubMeshIndices[a].push_back( static_cast(meshes.size())); - meshes.push_back( newMeshes[b]); - } - - // and destroy the source mesh. It should be completely contained inside the new submeshes - delete srcMesh; - } - else - { - // Mesh is kept unchanged - store it's new place in the mesh array - mSubMeshIndices[a].push_back( static_cast(meshes.size())); - meshes.push_back( srcMesh); - } - } - - // rebuild the scene's mesh array - pScene->mNumMeshes = static_cast(meshes.size()); - delete [] pScene->mMeshes; - pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; - std::copy( meshes.begin(), meshes.end(), pScene->mMeshes); - - // recurse through all nodes and translate the node's mesh indices to fit the new mesh array - UpdateNode( pScene->mRootNode); - - ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess end: split " << mSubMeshIndices.size() << " meshes into " << meshes.size() << " submeshes." ); -} - -// ------------------------------------------------------------------------------------------------ -// Splits the given mesh by bone count. -void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector& poNewMeshes) const -{ - // skip if not necessary - if( pMesh->mNumBones <= mMaxBoneCount ) - return; - - // necessary optimisation: build a list of all affecting bones for each vertex - // TODO: (thom) maybe add a custom allocator here to avoid allocating tens of thousands of small arrays - typedef std::pair BoneWeight; - std::vector< std::vector > vertexBones( pMesh->mNumVertices); - for( unsigned int a = 0; a < pMesh->mNumBones; ++a) - { - const aiBone* bone = pMesh->mBones[a]; - for( unsigned int b = 0; b < bone->mNumWeights; ++b) - vertexBones[ bone->mWeights[b].mVertexId ].push_back( BoneWeight( a, bone->mWeights[b].mWeight)); - } - - unsigned int numFacesHandled = 0; - std::vector isFaceHandled( pMesh->mNumFaces, false); - while( numFacesHandled < pMesh->mNumFaces ) - { - // which bones are used in the current submesh - unsigned int numBones = 0; - std::vector isBoneUsed( pMesh->mNumBones, false); - // indices of the faces which are going to go into this submesh - std::vector subMeshFaces; - subMeshFaces.reserve( pMesh->mNumFaces); - // accumulated vertex count of all the faces in this submesh - unsigned int numSubMeshVertices = 0; - // a small local array of new bones for the current face. State of all used bones for that face - // can only be updated AFTER the face is completely analysed. Thanks to imre for the fix. - std::vector newBonesAtCurrentFace; - - // add faces to the new submesh as long as all bones affecting the faces' vertices fit in the limit - for( unsigned int a = 0; a < pMesh->mNumFaces; ++a) - { - // skip if the face is already stored in a submesh - if( isFaceHandled[a] ) - continue; - - const aiFace& face = pMesh->mFaces[a]; - // check every vertex if its bones would still fit into the current submesh - for( unsigned int b = 0; b < face.mNumIndices; ++b ) - { - const std::vector& vb = vertexBones[face.mIndices[b]]; - for( unsigned int c = 0; c < vb.size(); ++c) - { - unsigned int boneIndex = vb[c].first; - // if the bone is already used in this submesh, it's ok - if( isBoneUsed[boneIndex] ) - continue; - - // if it's not used, yet, we would need to add it. Store its bone index - if( std::find( newBonesAtCurrentFace.begin(), newBonesAtCurrentFace.end(), boneIndex) == newBonesAtCurrentFace.end() ) - newBonesAtCurrentFace.push_back( boneIndex); - } - } - - // leave out the face if the new bones required for this face don't fit the bone count limit anymore - if( numBones + newBonesAtCurrentFace.size() > mMaxBoneCount ) - continue; - - // mark all new bones as necessary - while( !newBonesAtCurrentFace.empty() ) - { - unsigned int newIndex = newBonesAtCurrentFace.back(); - newBonesAtCurrentFace.pop_back(); // this also avoids the deallocation which comes with a clear() - if( isBoneUsed[newIndex] ) - continue; - - isBoneUsed[newIndex] = true; - numBones++; - } - - // store the face index and the vertex count - subMeshFaces.push_back( a); - numSubMeshVertices += face.mNumIndices; - - // remember that this face is handled - isFaceHandled[a] = true; - numFacesHandled++; - } - - // create a new mesh to hold this subset of the source mesh - aiMesh* newMesh = new aiMesh; - if( pMesh->mName.length > 0 ) - newMesh->mName.Set( format() << pMesh->mName.data << "_sub" << poNewMeshes.size()); - newMesh->mMaterialIndex = pMesh->mMaterialIndex; - newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes; - poNewMeshes.push_back( newMesh); - - // create all the arrays for this mesh if the old mesh contained them - newMesh->mNumVertices = numSubMeshVertices; - newMesh->mNumFaces = static_cast(subMeshFaces.size()); - newMesh->mVertices = new aiVector3D[newMesh->mNumVertices]; - if( pMesh->HasNormals() ) - newMesh->mNormals = new aiVector3D[newMesh->mNumVertices]; - if( pMesh->HasTangentsAndBitangents() ) - { - newMesh->mTangents = new aiVector3D[newMesh->mNumVertices]; - newMesh->mBitangents = new aiVector3D[newMesh->mNumVertices]; - } - for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) - { - if( pMesh->HasTextureCoords( a) ) - newMesh->mTextureCoords[a] = new aiVector3D[newMesh->mNumVertices]; - newMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a]; - } - for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a ) - { - if( pMesh->HasVertexColors( a) ) - newMesh->mColors[a] = new aiColor4D[newMesh->mNumVertices]; - } - - // and copy over the data, generating faces with linear indices along the way - newMesh->mFaces = new aiFace[subMeshFaces.size()]; - unsigned int nvi = 0; // next vertex index - std::vector previousVertexIndices( numSubMeshVertices, std::numeric_limits::max()); // per new vertex: its index in the source mesh - for( unsigned int a = 0; a < subMeshFaces.size(); ++a ) - { - const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]]; - aiFace& dstFace = newMesh->mFaces[a]; - dstFace.mNumIndices = srcFace.mNumIndices; - dstFace.mIndices = new unsigned int[dstFace.mNumIndices]; - - // accumulate linearly all the vertices of the source face - for( unsigned int b = 0; b < dstFace.mNumIndices; ++b ) - { - unsigned int srcIndex = srcFace.mIndices[b]; - dstFace.mIndices[b] = nvi; - previousVertexIndices[nvi] = srcIndex; - - newMesh->mVertices[nvi] = pMesh->mVertices[srcIndex]; - if( pMesh->HasNormals() ) - newMesh->mNormals[nvi] = pMesh->mNormals[srcIndex]; - if( pMesh->HasTangentsAndBitangents() ) - { - newMesh->mTangents[nvi] = pMesh->mTangents[srcIndex]; - newMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex]; - } - for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c ) - { - if( pMesh->HasTextureCoords( c) ) - newMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex]; - } - for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c ) - { - if( pMesh->HasVertexColors( c) ) - newMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex]; - } - - nvi++; - } - } - - ai_assert( nvi == numSubMeshVertices ); - - // Create the bones for the new submesh: first create the bone array - newMesh->mNumBones = 0; - newMesh->mBones = new aiBone*[numBones]; - - std::vector mappedBoneIndex( pMesh->mNumBones, std::numeric_limits::max()); - for( unsigned int a = 0; a < pMesh->mNumBones; ++a ) - { - if( !isBoneUsed[a] ) - continue; - - // create the new bone - const aiBone* srcBone = pMesh->mBones[a]; - aiBone* dstBone = new aiBone; - mappedBoneIndex[a] = newMesh->mNumBones; - newMesh->mBones[newMesh->mNumBones++] = dstBone; - dstBone->mName = srcBone->mName; - dstBone->mOffsetMatrix = srcBone->mOffsetMatrix; - dstBone->mNumWeights = 0; - } - - ai_assert( newMesh->mNumBones == numBones ); - - // iterate over all new vertices and count which bones affected its old vertex in the source mesh - for( unsigned int a = 0; a < numSubMeshVertices; ++a ) - { - unsigned int oldIndex = previousVertexIndices[a]; - const std::vector& bonesOnThisVertex = vertexBones[oldIndex]; - - for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b ) - { - unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ]; - if( newBoneIndex != std::numeric_limits::max() ) - newMesh->mBones[newBoneIndex]->mNumWeights++; - } - } - - // allocate all bone weight arrays accordingly - for( unsigned int a = 0; a < newMesh->mNumBones; ++a ) - { - aiBone* bone = newMesh->mBones[a]; - ai_assert( bone->mNumWeights > 0 ); - bone->mWeights = new aiVertexWeight[bone->mNumWeights]; - bone->mNumWeights = 0; // for counting up in the next step - } - - // now copy all the bone vertex weights for all the vertices which made it into the new submesh - for( unsigned int a = 0; a < numSubMeshVertices; ++a) - { - // find the source vertex for it in the source mesh - unsigned int previousIndex = previousVertexIndices[a]; - // these bones were affecting it - const std::vector& bonesOnThisVertex = vertexBones[previousIndex]; - // all of the bones affecting it should be present in the new submesh, or else - // the face it comprises shouldn't be present - for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b) - { - unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ]; - ai_assert( newBoneIndex != std::numeric_limits::max() ); - aiVertexWeight* dstWeight = newMesh->mBones[newBoneIndex]->mWeights + newMesh->mBones[newBoneIndex]->mNumWeights; - newMesh->mBones[newBoneIndex]->mNumWeights++; - - dstWeight->mVertexId = a; - dstWeight->mWeight = bonesOnThisVertex[b].second; - } - } - - // I have the strange feeling that this will break apart at some point in time... - } -} - -// ------------------------------------------------------------------------------------------------ -// Recursively updates the node's mesh list to account for the changed mesh list -void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const -{ - // rebuild the node's mesh index list - if( pNode->mNumMeshes > 0 ) - { - std::vector newMeshList; - for( unsigned int a = 0; a < pNode->mNumMeshes; ++a) - { - unsigned int srcIndex = pNode->mMeshes[a]; - const std::vector& replaceMeshes = mSubMeshIndices[srcIndex]; - newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end()); - } - - delete [] pNode->mMeshes; - pNode->mNumMeshes = static_cast(newMeshList.size()); - pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; - std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes); - } - - // do that also recursively for all children - for( unsigned int a = 0; a < pNode->mNumChildren; ++a ) - { - UpdateNode( pNode->mChildren[a]); - } -} diff --git a/thirdparty/assimp/code/SplitByBoneCountProcess.h b/thirdparty/assimp/code/SplitByBoneCountProcess.h deleted file mode 100644 index 6c904a9df4..0000000000 --- a/thirdparty/assimp/code/SplitByBoneCountProcess.h +++ /dev/null @@ -1,111 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/// @file SplitByBoneCountProcess.h -/// Defines a post processing step to split meshes with many bones into submeshes -#ifndef AI_SPLITBYBONECOUNTPROCESS_H_INC -#define AI_SPLITBYBONECOUNTPROCESS_H_INC - -#include -#include "BaseProcess.h" - -#include -#include - -namespace Assimp -{ - - -/** Postprocessing filter to split meshes with many bones into submeshes - * so that each submesh has a certain max bone count. - * - * Applied BEFORE the JoinVertices-Step occurs. - * Returns NON-UNIQUE vertices, splits by bone count. -*/ -class SplitByBoneCountProcess : public BaseProcess -{ -public: - - SplitByBoneCountProcess(); - ~SplitByBoneCountProcess(); - -public: - /** Returns whether the processing step is present in the given flag. - * @param pFlags The processing flags the importer was called with. A - * bitwise combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, - * false if not. - */ - bool IsActive( unsigned int pFlags) const; - - /** Called prior to ExecuteOnScene(). - * The function is a request to the process to update its configuration - * basing on the Importer's configuration property list. - */ - virtual void SetupProperties(const Importer* pImp); - -protected: - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - - /// Splits the given mesh by bone count. - /// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split. - /// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary. - void SplitMesh( const aiMesh* pMesh, std::vector& poNewMeshes) const; - - /// Recursively updates the node's mesh list to account for the changed mesh list - void UpdateNode( aiNode* pNode) const; - -public: - /// Max bone count. Splitting occurs if a mesh has more than that number of bones. - size_t mMaxBoneCount; - - /// Per mesh index: Array of indices of the new submeshes. - std::vector< std::vector > mSubMeshIndices; -}; - -} // end of namespace Assimp - -#endif // !!AI_SPLITBYBONECOUNTPROCESS_H_INC diff --git a/thirdparty/assimp/code/SplitLargeMeshes.cpp b/thirdparty/assimp/code/SplitLargeMeshes.cpp deleted file mode 100644 index 1797b28d5a..0000000000 --- a/thirdparty/assimp/code/SplitLargeMeshes.cpp +++ /dev/null @@ -1,623 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** - * @file Implementation of the SplitLargeMeshes postprocessing step - */ - -// internal headers of the post-processing framework -#include "SplitLargeMeshes.h" -#include "ProcessHelper.h" - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -SplitLargeMeshesProcess_Triangle::SplitLargeMeshesProcess_Triangle() { - LIMIT = AI_SLM_DEFAULT_MAX_TRIANGLES; -} - -// ------------------------------------------------------------------------------------------------ -SplitLargeMeshesProcess_Triangle::~SplitLargeMeshesProcess_Triangle() { - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool SplitLargeMeshesProcess_Triangle::IsActive( unsigned int pFlags) const { - return (pFlags & aiProcess_SplitLargeMeshes) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void SplitLargeMeshesProcess_Triangle::Execute( aiScene* pScene) { - if (0xffffffff == this->LIMIT || nullptr == pScene ) { - return; - } - - ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle begin"); - std::vector > avList; - - for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) { - this->SplitMesh(a, pScene->mMeshes[a],avList); - } - - if (avList.size() != pScene->mNumMeshes) { - // it seems something has been split. rebuild the mesh list - delete[] pScene->mMeshes; - pScene->mNumMeshes = (unsigned int)avList.size(); - pScene->mMeshes = new aiMesh*[avList.size()]; - - for (unsigned int i = 0; i < avList.size();++i) { - pScene->mMeshes[i] = avList[i].first; - } - - // now we need to update all nodes - this->UpdateNode(pScene->mRootNode,avList); - ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Triangle finished. Meshes have been split"); - } else { - ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle finished. There was nothing to do"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Setup properties -void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp) { - // get the current value of the split property - this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES); -} - -// ------------------------------------------------------------------------------------------------ -// Update a node after some meshes have been split -void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode, - const std::vector >& avList) { - // for every index in out list build a new entry - std::vector aiEntries; - aiEntries.reserve(pcNode->mNumMeshes + 1); - for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) { - for (unsigned int a = 0; a < avList.size();++a) { - if (avList[a].second == pcNode->mMeshes[i]) { - aiEntries.push_back(a); - } - } - } - - // now build the new list - delete[] pcNode->mMeshes; - pcNode->mNumMeshes = (unsigned int)aiEntries.size(); - pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; - - for (unsigned int b = 0; b < pcNode->mNumMeshes;++b) { - pcNode->mMeshes[b] = aiEntries[b]; - } - - // recusively update all other nodes - for (unsigned int i = 0; i < pcNode->mNumChildren;++i) { - UpdateNode ( pcNode->mChildren[i], avList ); - } -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void SplitLargeMeshesProcess_Triangle::SplitMesh( - unsigned int a, - aiMesh* pMesh, - std::vector >& avList) { - if (pMesh->mNumFaces > SplitLargeMeshesProcess_Triangle::LIMIT) { - ASSIMP_LOG_INFO("Mesh exceeds the triangle limit. It will be split ..."); - - // we need to split this mesh into sub meshes - // determine the size of a submesh - const unsigned int iSubMeshes = (pMesh->mNumFaces / LIMIT) + 1; - - const unsigned int iOutFaceNum = pMesh->mNumFaces / iSubMeshes; - const unsigned int iOutVertexNum = iOutFaceNum * 3; - - // now generate all submeshes - for (unsigned int i = 0; i < iSubMeshes;++i) { - aiMesh* pcMesh = new aiMesh; - pcMesh->mNumFaces = iOutFaceNum; - pcMesh->mMaterialIndex = pMesh->mMaterialIndex; - - // the name carries the adjacency information between the meshes - pcMesh->mName = pMesh->mName; - - if (i == iSubMeshes-1) { - pcMesh->mNumFaces = iOutFaceNum + ( - pMesh->mNumFaces - iOutFaceNum * iSubMeshes); - } - // copy the list of faces - pcMesh->mFaces = new aiFace[pcMesh->mNumFaces]; - - const unsigned int iBase = iOutFaceNum * i; - - // get the total number of indices - unsigned int iCnt = 0; - for (unsigned int p = iBase; p < pcMesh->mNumFaces + iBase;++p) { - iCnt += pMesh->mFaces[p].mNumIndices; - } - pcMesh->mNumVertices = iCnt; - - // allocate storage - if (pMesh->mVertices != nullptr) { - pcMesh->mVertices = new aiVector3D[iCnt]; - } - - if (pMesh->HasNormals()) { - pcMesh->mNormals = new aiVector3D[iCnt]; - } - - if (pMesh->HasTangentsAndBitangents()) { - pcMesh->mTangents = new aiVector3D[iCnt]; - pcMesh->mBitangents = new aiVector3D[iCnt]; - } - - // texture coordinates - for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { - pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c]; - if (pMesh->HasTextureCoords( c)) { - pcMesh->mTextureCoords[c] = new aiVector3D[iCnt]; - } - } - - // vertex colors - for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { - if (pMesh->HasVertexColors( c)) { - pcMesh->mColors[c] = new aiColor4D[iCnt]; - } - } - - if (pMesh->HasBones()) { - // assume the number of bones won't change in most cases - pcMesh->mBones = new aiBone*[pMesh->mNumBones]; - - // iterate through all bones of the mesh and find those which - // need to be copied to the split mesh - std::vector avTempWeights; - for (unsigned int p = 0; p < pcMesh->mNumBones;++p) { - aiBone* const bone = pcMesh->mBones[p]; - avTempWeights.clear(); - avTempWeights.reserve(bone->mNumWeights / iSubMeshes); - - for (unsigned int q = 0; q < bone->mNumWeights;++q) { - aiVertexWeight& weight = bone->mWeights[q]; - if(weight.mVertexId >= iBase && weight.mVertexId < iBase + iOutVertexNum) { - avTempWeights.push_back(weight); - weight = avTempWeights.back(); - weight.mVertexId -= iBase; - } - } - - if (!avTempWeights.empty()) { - // we'll need this bone. Copy it ... - aiBone* pc = new aiBone(); - pcMesh->mBones[pcMesh->mNumBones++] = pc; - pc->mName = aiString(bone->mName); - pc->mNumWeights = (unsigned int)avTempWeights.size(); - pc->mOffsetMatrix = bone->mOffsetMatrix; - - // no need to reallocate the array for the last submesh. - // Here we can reuse the (large) source array, although - // we'll waste some memory - if (iSubMeshes-1 == i) { - pc->mWeights = bone->mWeights; - bone->mWeights = nullptr; - } else { - pc->mWeights = new aiVertexWeight[pc->mNumWeights]; - } - - // copy the weights - ::memcpy(pc->mWeights,&avTempWeights[0],sizeof(aiVertexWeight)*pc->mNumWeights); - } - } - } - - // (we will also need to copy the array of indices) - unsigned int iCurrent = 0; - for (unsigned int p = 0; p < pcMesh->mNumFaces;++p) { - pcMesh->mFaces[p].mNumIndices = 3; - // allocate a new array - const unsigned int iTemp = p + iBase; - const unsigned int iNumIndices = pMesh->mFaces[iTemp].mNumIndices; - - // setup face type and number of indices - pcMesh->mFaces[p].mNumIndices = iNumIndices; - unsigned int* pi = pMesh->mFaces[iTemp].mIndices; - unsigned int* piOut = pcMesh->mFaces[p].mIndices = new unsigned int[iNumIndices]; - - // need to update the output primitive types - switch (iNumIndices) { - case 1: - pcMesh->mPrimitiveTypes |= aiPrimitiveType_POINT; - break; - case 2: - pcMesh->mPrimitiveTypes |= aiPrimitiveType_LINE; - break; - case 3: - pcMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; - break; - default: - pcMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; - } - - // and copy the contents of the old array, offset by current base - for (unsigned int v = 0; v < iNumIndices;++v) { - unsigned int iIndex = pi[v]; - unsigned int iIndexOut = iCurrent++; - piOut[v] = iIndexOut; - - // copy positions - if (pMesh->mVertices != nullptr) { - pcMesh->mVertices[iIndexOut] = pMesh->mVertices[iIndex]; - } - - // copy normals - if (pMesh->HasNormals()) { - pcMesh->mNormals[iIndexOut] = pMesh->mNormals[iIndex]; - } - - // copy tangents/bitangents - if (pMesh->HasTangentsAndBitangents()) { - pcMesh->mTangents[iIndexOut] = pMesh->mTangents[iIndex]; - pcMesh->mBitangents[iIndexOut] = pMesh->mBitangents[iIndex]; - } - - // texture coordinates - for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { - if (pMesh->HasTextureCoords( c ) ) { - pcMesh->mTextureCoords[c][iIndexOut] = pMesh->mTextureCoords[c][iIndex]; - } - } - // vertex colors - for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { - if (pMesh->HasVertexColors( c)) { - pcMesh->mColors[c][iIndexOut] = pMesh->mColors[c][iIndex]; - } - } - } - } - - // add the newly created mesh to the list - avList.push_back(std::pair(pcMesh,a)); - } - - // now delete the old mesh data - delete pMesh; - } else { - avList.push_back(std::pair(pMesh,a)); - } -} - -// ------------------------------------------------------------------------------------------------ -SplitLargeMeshesProcess_Vertex::SplitLargeMeshesProcess_Vertex() { - LIMIT = AI_SLM_DEFAULT_MAX_VERTICES; -} - -// ------------------------------------------------------------------------------------------------ -SplitLargeMeshesProcess_Vertex::~SplitLargeMeshesProcess_Vertex() { - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool SplitLargeMeshesProcess_Vertex::IsActive( unsigned int pFlags) const { - return (pFlags & aiProcess_SplitLargeMeshes) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene) { - if (0xffffffff == this->LIMIT || nullptr == pScene ) { - return; - } - - ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Vertex begin"); - - std::vector > avList; - - //Check for point cloud first, - //Do not process point cloud, splitMesh works only with faces data - for (unsigned int a = 0; a < pScene->mNumMeshes; a++) { - if ( pScene->mMeshes[a]->mPrimitiveTypes == aiPrimitiveType_POINT ) { - return; - } - } - - for( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) { - this->SplitMesh(a, pScene->mMeshes[a], avList); - } - - if (avList.size() != pScene->mNumMeshes) { - // it seems something has been split. rebuild the mesh list - delete[] pScene->mMeshes; - pScene->mNumMeshes = (unsigned int)avList.size(); - pScene->mMeshes = new aiMesh*[avList.size()]; - - for (unsigned int i = 0; i < avList.size();++i) { - pScene->mMeshes[i] = avList[i].first; - } - - // now we need to update all nodes - SplitLargeMeshesProcess_Triangle::UpdateNode(pScene->mRootNode,avList); - ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Vertex finished. Meshes have been split"); - } else { - ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Vertex finished. There was nothing to do"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Setup properties -void SplitLargeMeshesProcess_Vertex::SetupProperties( const Importer* pImp) { - this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES); -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void SplitLargeMeshesProcess_Vertex::SplitMesh( - unsigned int a, - aiMesh* pMesh, - std::vector >& avList) { - if (pMesh->mNumVertices > SplitLargeMeshesProcess_Vertex::LIMIT) { - typedef std::vector< std::pair > VertexWeightTable; - - // build a per-vertex weight list if necessary - VertexWeightTable* avPerVertexWeights = ComputeVertexBoneWeightTable(pMesh); - - // we need to split this mesh into sub meshes - // determine the estimated size of a submesh - // (this could be too large. Max waste is a single digit percentage) - const unsigned int iSubMeshes = (pMesh->mNumVertices / SplitLargeMeshesProcess_Vertex::LIMIT) + 1; - - // create a std::vector to indicate which vertices - // have already been copied - std::vector 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 BoneWeightList; - if (pMesh->HasBones()) { - pcMesh->mBones = new aiBone*[pMesh->mNumBones]; - ::memset(pcMesh->mBones,0,sizeof(void*)*pMesh->mNumBones); - } - - // clear the temporary helper array - if (iBase) { - // we can't use memset here we unsigned int needn' be 32 bits - for (auto &elem : avWasCopied) { - elem = 0xffffffff; - } - } - - // output vectors - std::vector vFaces; - - // reserve enough storage for most cases - if (pMesh->HasPositions()) { - pcMesh->mVertices = new aiVector3D[iOutVertexNum]; - } - if (pMesh->HasNormals()) { - pcMesh->mNormals = new aiVector3D[iOutVertexNum]; - } - if (pMesh->HasTangentsAndBitangents()) { - pcMesh->mTangents = new aiVector3D[iOutVertexNum]; - pcMesh->mBitangents = new aiVector3D[iOutVertexNum]; - } - for (unsigned int c = 0; pMesh->HasVertexColors(c);++c) { - pcMesh->mColors[c] = new aiColor4D[iOutVertexNum]; - } - for (unsigned int c = 0; pMesh->HasTextureCoords(c);++c) { - pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c]; - pcMesh->mTextureCoords[c] = new aiVector3D[iOutVertexNum]; - } - vFaces.reserve(iEstimatedSize); - - // (we will also need to copy the array of indices) - while (iBase < pMesh->mNumFaces) { - // allocate a new array - const unsigned int iNumIndices = pMesh->mFaces[iBase].mNumIndices; - - // doesn't catch degenerates but is quite fast - unsigned int iNeed = 0; - for (unsigned int v = 0; v < iNumIndices;++v) { - unsigned int iIndex = pMesh->mFaces[iBase].mIndices[v]; - - // check whether we do already have this vertex - if (0xFFFFFFFF == avWasCopied[iIndex]) { - iNeed++; - } - } - if (pcMesh->mNumVertices + iNeed > iOutVertexNum) { - // don't use this face - break; - } - - vFaces.push_back(aiFace()); - aiFace& rFace = vFaces.back(); - - // setup face type and number of indices - rFace.mNumIndices = iNumIndices; - rFace.mIndices = new unsigned int[iNumIndices]; - - // need to update the output primitive types - switch (rFace.mNumIndices) { - case 1: - pcMesh->mPrimitiveTypes |= aiPrimitiveType_POINT; - break; - case 2: - pcMesh->mPrimitiveTypes |= aiPrimitiveType_LINE; - break; - case 3: - pcMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; - break; - default: - pcMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; - } - - // and copy the contents of the old array, offset by current base - for (unsigned int v = 0; v < iNumIndices;++v) { - unsigned int iIndex = pMesh->mFaces[iBase].mIndices[v]; - - // check whether we do already have this vertex - if (0xFFFFFFFF != avWasCopied[iIndex]) { - rFace.mIndices[v] = avWasCopied[iIndex]; - continue; - } - - // copy positions - pcMesh->mVertices[pcMesh->mNumVertices] = (pMesh->mVertices[iIndex]); - - // copy normals - if (pMesh->HasNormals()) { - pcMesh->mNormals[pcMesh->mNumVertices] = (pMesh->mNormals[iIndex]); - } - - // copy tangents/bitangents - if (pMesh->HasTangentsAndBitangents()) { - pcMesh->mTangents[pcMesh->mNumVertices] = (pMesh->mTangents[iIndex]); - pcMesh->mBitangents[pcMesh->mNumVertices] = (pMesh->mBitangents[iIndex]); - } - - // texture coordinates - for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { - if (pMesh->HasTextureCoords( c)) { - pcMesh->mTextureCoords[c][pcMesh->mNumVertices] = pMesh->mTextureCoords[c][iIndex]; - } - } - // vertex colors - for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { - if (pMesh->HasVertexColors( c)) { - pcMesh->mColors[c][pcMesh->mNumVertices] = pMesh->mColors[c][iIndex]; - } - } - // check whether we have bone weights assigned to this vertex - rFace.mIndices[v] = pcMesh->mNumVertices; - if (avPerVertexWeights) { - VertexWeightTable& table = avPerVertexWeights[ pcMesh->mNumVertices ]; - if( !table.empty() ) { - for (VertexWeightTable::const_iterator iter = table.begin(); - iter != table.end();++iter) { - // allocate the bone weight array if necessary - BoneWeightList* pcWeightList = (BoneWeightList*)pcMesh->mBones[(*iter).first]; - if (nullptr == pcWeightList) { - pcMesh->mBones[(*iter).first] = (aiBone*)(pcWeightList = new BoneWeightList()); - } - pcWeightList->push_back(aiVertexWeight(pcMesh->mNumVertices,(*iter).second)); - } - } - } - - avWasCopied[iIndex] = pcMesh->mNumVertices; - pcMesh->mNumVertices++; - } - ++iBase; - if(pcMesh->mNumVertices == iOutVertexNum) { - // break here. The face is only added if it was complete - break; - } - } - - // check which bones we'll need to create for this submesh - if (pMesh->HasBones()) { - aiBone** ppCurrent = pcMesh->mBones; - for (unsigned int k = 0; k < pMesh->mNumBones;++k) { - // check whether the bone is existing - BoneWeightList* pcWeightList; - if ((pcWeightList = (BoneWeightList*)pcMesh->mBones[k])) { - aiBone* pcOldBone = pMesh->mBones[k]; - aiBone* pcOut( nullptr ); - *ppCurrent++ = pcOut = new aiBone(); - pcOut->mName = aiString(pcOldBone->mName); - pcOut->mOffsetMatrix = pcOldBone->mOffsetMatrix; - pcOut->mNumWeights = (unsigned int)pcWeightList->size(); - pcOut->mWeights = new aiVertexWeight[pcOut->mNumWeights]; - - // copy the vertex weights - ::memcpy(pcOut->mWeights,&pcWeightList->operator[](0), - pcOut->mNumWeights * sizeof(aiVertexWeight)); - - // delete the temporary bone weight list - delete pcWeightList; - pcMesh->mNumBones++; - } - } - } - - // copy the face list to the mesh - pcMesh->mFaces = new aiFace[vFaces.size()]; - pcMesh->mNumFaces = (unsigned int)vFaces.size(); - - for (unsigned int p = 0; p < pcMesh->mNumFaces;++p) { - pcMesh->mFaces[p] = vFaces[p]; - } - - // add the newly created mesh to the list - avList.push_back(std::pair(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(pMesh,a)); -} diff --git a/thirdparty/assimp/code/SplitLargeMeshes.h b/thirdparty/assimp/code/SplitLargeMeshes.h deleted file mode 100644 index 77f089ce7e..0000000000 --- a/thirdparty/assimp/code/SplitLargeMeshes.h +++ /dev/null @@ -1,210 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a post processing step to split large meshes into submeshes - */ -#ifndef AI_SPLITLARGEMESHES_H_INC -#define AI_SPLITLARGEMESHES_H_INC - -#include -#include "BaseProcess.h" - -#include -#include - -class SplitLargeMeshesTest; - -namespace Assimp -{ - -class SplitLargeMeshesProcess_Triangle; -class SplitLargeMeshesProcess_Vertex; - -// NOTE: If you change these limits, don't forget to change the -// corresponding values in all Assimp ports - -// ********************************************************** -// Java: ConfigProperty.java, -// ConfigProperty.DEFAULT_VERTEX_SPLIT_LIMIT -// ConfigProperty.DEFAULT_TRIANGLE_SPLIT_LIMIT -// ********************************************************** - -// default limit for vertices -#if (!defined AI_SLM_DEFAULT_MAX_VERTICES) -# define AI_SLM_DEFAULT_MAX_VERTICES 1000000 -#endif - -// default limit for triangles -#if (!defined AI_SLM_DEFAULT_MAX_TRIANGLES) -# define AI_SLM_DEFAULT_MAX_TRIANGLES 1000000 -#endif - -// --------------------------------------------------------------------------- -/** Post-processing filter to split large meshes into sub-meshes - * - * Applied BEFORE the JoinVertices-Step occurs. - * Returns NON-UNIQUE vertices, splits by triangle number. -*/ -class ASSIMP_API SplitLargeMeshesProcess_Triangle : public BaseProcess -{ - friend class SplitLargeMeshesProcess_Vertex; - -public: - - SplitLargeMeshesProcess_Triangle(); - ~SplitLargeMeshesProcess_Triangle(); - -public: - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag. - * @param pFlags The processing flags the importer was called with. A - * bitwise combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, - * false if not. - */ - bool IsActive( unsigned int pFlags) const; - - - // ------------------------------------------------------------------- - /** Called prior to ExecuteOnScene(). - * The function is a request to the process to update its configuration - * basing on the Importer's configuration property list. - */ - virtual void SetupProperties(const Importer* pImp); - - - //! Set the split limit - needed for unit testing - inline void SetLimit(unsigned int l) - {LIMIT = l;} - - //! Get the split limit - inline unsigned int GetLimit() const - {return LIMIT;} - -public: - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - //! Apply the algorithm to a given mesh - void SplitMesh (unsigned int a, aiMesh* pcMesh, - std::vector >& avList); - - // ------------------------------------------------------------------- - //! Update a node in the asset after a few of its meshes - //! have been split - static void UpdateNode(aiNode* pcNode, - const std::vector >& avList); - -public: - //! Triangle limit - unsigned int LIMIT; -}; - - -// --------------------------------------------------------------------------- -/** Post-processing filter to split large meshes into sub-meshes - * - * Applied AFTER the JoinVertices-Step occurs. - * Returns UNIQUE vertices, splits by vertex number. -*/ -class ASSIMP_API SplitLargeMeshesProcess_Vertex : public BaseProcess -{ -public: - - SplitLargeMeshesProcess_Vertex(); - ~SplitLargeMeshesProcess_Vertex(); - -public: - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag field. - * @param pFlags The processing flags the importer was called with. A bitwise - * combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, false if not. - */ - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - /** Called prior to ExecuteOnScene(). - * The function is a request to the process to update its configuration - * basing on the Importer's configuration property list. - */ - virtual void SetupProperties(const Importer* pImp); - - - //! Set the split limit - needed for unit testing - inline void SetLimit(unsigned int l) - {LIMIT = l;} - - //! Get the split limit - inline unsigned int GetLimit() const - {return LIMIT;} - -public: - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - //! Apply the algorithm to a given mesh - void SplitMesh (unsigned int a, aiMesh* pcMesh, - std::vector >& avList); - - // NOTE: Reuse SplitLargeMeshesProcess_Triangle::UpdateNode() - -public: - //! Triangle limit - unsigned int LIMIT; -}; - -} // end of namespace Assimp - -#endif // !!AI_SPLITLARGEMESHES_H_INC diff --git a/thirdparty/assimp/code/StandardShapes.cpp b/thirdparty/assimp/code/StandardShapes.cpp deleted file mode 100644 index 2e5100130f..0000000000 --- a/thirdparty/assimp/code/StandardShapes.cpp +++ /dev/null @@ -1,507 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file StandardShapes.cpp - * @brief Implementation of the StandardShapes class - * - * The primitive geometry data comes from - * http://geometrictools.com/Documentation/PlatonicSolids.pdf. - */ - -#include -#include -#include -#include -#include - -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& positions) -{ - // assume this to be constant - (fixme: must be 1.0? I think so) - const ai_real fl1 = positions[0].Length(); - - unsigned int origSize = (unsigned int)positions.size(); - for (unsigned int i = 0 ; i < origSize ; i+=3) - { - aiVector3D& tv0 = positions[i]; - aiVector3D& tv1 = positions[i+1]; - aiVector3D& tv2 = positions[i+2]; - - aiVector3D a = tv0, b = tv1, c = tv2; - aiVector3D v1 = aiVector3D(a.x+b.x, a.y+b.y, a.z+b.z).Normalize()*fl1; - aiVector3D v2 = aiVector3D(a.x+c.x, a.y+c.y, a.z+c.z).Normalize()*fl1; - aiVector3D v3 = aiVector3D(b.x+c.x, b.y+c.y, b.z+c.z).Normalize()*fl1; - - tv0 = v1; tv1 = v3; tv2 = v2; // overwrite the original - ADD_TRIANGLE(v1, v2, a); - ADD_TRIANGLE(v2, v3, c); - ADD_TRIANGLE(v3, v1, b); - } -} - -// ------------------------------------------------------------------------------------------------ -// Construct a mesh from given vertex positions -aiMesh* StandardShapes::MakeMesh(const std::vector& positions, - unsigned int numIndices) -{ - if (positions.empty() || !numIndices) return NULL; - - // Determine which kinds of primitives the mesh consists of - aiMesh* out = new aiMesh(); - switch (numIndices) { - case 1: - out->mPrimitiveTypes = aiPrimitiveType_POINT; - break; - case 2: - out->mPrimitiveTypes = aiPrimitiveType_LINE; - break; - case 3: - out->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - break; - default: - out->mPrimitiveTypes = aiPrimitiveType_POLYGON; - break; - }; - - out->mNumFaces = (unsigned int)positions.size() / numIndices; - out->mFaces = new aiFace[out->mNumFaces]; - for (unsigned int i = 0, a = 0; i < out->mNumFaces;++i) { - aiFace& f = out->mFaces[i]; - f.mNumIndices = numIndices; - f.mIndices = new unsigned int[numIndices]; - for (unsigned int j = 0; i < numIndices; ++i, ++a) { - f.mIndices[j] = a; - } - } - out->mNumVertices = (unsigned int)positions.size(); - out->mVertices = new aiVector3D[out->mNumVertices]; - ::memcpy(out->mVertices,&positions[0],out->mNumVertices*sizeof(aiVector3D)); - - return out; -} - -// ------------------------------------------------------------------------------------------------ -// Construct a mesh with a specific shape (callback) -aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)( - std::vector&)) -{ - std::vector 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&, bool)) -{ - std::vector 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&)) -{ - std::vector temp; - (*GenerateFunc)(num,temp); - return MakeMesh(temp,3); -} - -// ------------------------------------------------------------------------------------------------ -// Build an incosahedron with points.magnitude == 1 -unsigned int StandardShapes::MakeIcosahedron(std::vector& positions) -{ - positions.reserve(positions.size()+60); - - const ai_real t = ( ai_real( 1.0 )+ ai_real( 2.236067977 ) ) / ai_real( 2.0 ); - const ai_real s = std::sqrt(ai_real(1.0) + t*t); - - const aiVector3D v0 = aiVector3D(t,1.0, 0.0)/s; - const aiVector3D v1 = aiVector3D(-t,1.0, 0.0)/s; - const aiVector3D v2 = aiVector3D(t,-1.0, 0.0)/s; - const aiVector3D v3 = aiVector3D(-t,-1.0, 0.0)/s; - const aiVector3D v4 = aiVector3D(1.0, 0.0, t)/s; - const aiVector3D v5 = aiVector3D(1.0, 0.0,-t)/s; - const aiVector3D v6 = aiVector3D(-1.0, 0.0,t)/s; - const aiVector3D v7 = aiVector3D(-1.0, 0.0,-t)/s; - const aiVector3D v8 = aiVector3D(0.0, t, 1.0)/s; - const aiVector3D v9 = aiVector3D(0.0,-t, 1.0)/s; - const aiVector3D v10 = aiVector3D(0.0, t,-1.0)/s; - const aiVector3D v11 = aiVector3D(0.0,-t,-1.0)/s; - - ADD_TRIANGLE(v0,v8,v4); - ADD_TRIANGLE(v0,v5,v10); - ADD_TRIANGLE(v2,v4,v9); - ADD_TRIANGLE(v2,v11,v5); - - ADD_TRIANGLE(v1,v6,v8); - ADD_TRIANGLE(v1,v10,v7); - ADD_TRIANGLE(v3,v9,v6); - ADD_TRIANGLE(v3,v7,v11); - - ADD_TRIANGLE(v0,v10,v8); - ADD_TRIANGLE(v1,v8,v10); - ADD_TRIANGLE(v2,v9,v11); - ADD_TRIANGLE(v3,v11,v9); - - ADD_TRIANGLE(v4,v2,v0); - ADD_TRIANGLE(v5,v0,v2); - ADD_TRIANGLE(v6,v1,v3); - ADD_TRIANGLE(v7,v3,v1); - - ADD_TRIANGLE(v8,v6,v4); - ADD_TRIANGLE(v9,v4,v6); - ADD_TRIANGLE(v10,v5,v7); - ADD_TRIANGLE(v11,v7,v5); - return 3; -} - -// ------------------------------------------------------------------------------------------------ -// Build a dodecahedron with points.magnitude == 1 -unsigned int StandardShapes::MakeDodecahedron(std::vector& positions, - bool polygons /*= false*/) -{ - positions.reserve(positions.size()+108); - - const ai_real a = ai_real( 1.0 ) / ai_real(1.7320508); - const ai_real b = std::sqrt(( ai_real( 3.0 )- ai_real( 2.23606797))/ ai_real( 6.0) ); - const ai_real c = std::sqrt(( ai_real( 3.0 )+ ai_real( 2.23606797f))/ ai_real( 6.0) ); - - const aiVector3D v0 = aiVector3D(a,a,a); - const aiVector3D v1 = aiVector3D(a,a,-a); - const aiVector3D v2 = aiVector3D(a,-a,a); - const aiVector3D v3 = aiVector3D(a,-a,-a); - const aiVector3D v4 = aiVector3D(-a,a,a); - const aiVector3D v5 = aiVector3D(-a,a,-a); - const aiVector3D v6 = aiVector3D(-a,-a,a); - const aiVector3D v7 = aiVector3D(-a,-a,-a); - const aiVector3D v8 = aiVector3D(b,c,0.0); - const aiVector3D v9 = aiVector3D(-b,c,0.0); - const aiVector3D v10 = aiVector3D(b,-c,0.0); - const aiVector3D v11 = aiVector3D(-b,-c,0.0); - const aiVector3D v12 = aiVector3D(c, 0.0, b); - const aiVector3D v13 = aiVector3D(c, 0.0, -b); - const aiVector3D v14 = aiVector3D(-c, 0.0, b); - const aiVector3D v15 = aiVector3D(-c, 0.0, -b); - const aiVector3D v16 = aiVector3D(0.0, b, c); - const aiVector3D v17 = aiVector3D(0.0, -b, c); - const aiVector3D v18 = aiVector3D(0.0, b, -c); - const aiVector3D v19 = aiVector3D(0.0, -b, -c); - - ADD_PENTAGON(v0, v8, v9, v4, v16); - ADD_PENTAGON(v0, v12, v13, v1, v8); - ADD_PENTAGON(v0, v16, v17, v2, v12); - ADD_PENTAGON(v8, v1, v18, v5, v9); - ADD_PENTAGON(v12, v2, v10, v3, v13); - ADD_PENTAGON(v16, v4, v14, v6, v17); - ADD_PENTAGON(v9, v5, v15, v14, v4); - - ADD_PENTAGON(v6, v11, v10, v2, v17); - ADD_PENTAGON(v3, v19, v18, v1, v13); - ADD_PENTAGON(v7, v15, v5, v18, v19); - ADD_PENTAGON(v7, v11, v6, v14, v15); - ADD_PENTAGON(v7, v19, v3, v10, v11); - return (polygons ? 5 : 3); -} - -// ------------------------------------------------------------------------------------------------ -// Build an octahedron with points.magnitude == 1 -unsigned int StandardShapes::MakeOctahedron(std::vector& positions) -{ - positions.reserve(positions.size()+24); - - const aiVector3D v0 = aiVector3D(1.0, 0.0, 0.0) ; - const aiVector3D v1 = aiVector3D(-1.0, 0.0, 0.0); - const aiVector3D v2 = aiVector3D(0.0, 1.0, 0.0); - const aiVector3D v3 = aiVector3D(0.0, -1.0, 0.0); - const aiVector3D v4 = aiVector3D(0.0, 0.0, 1.0); - const aiVector3D v5 = aiVector3D(0.0, 0.0, -1.0); - - ADD_TRIANGLE(v4,v0,v2); - ADD_TRIANGLE(v4,v2,v1); - ADD_TRIANGLE(v4,v1,v3); - ADD_TRIANGLE(v4,v3,v0); - - ADD_TRIANGLE(v5,v2,v0); - ADD_TRIANGLE(v5,v1,v2); - ADD_TRIANGLE(v5,v3,v1); - ADD_TRIANGLE(v5,v0,v3); - return 3; -} - -// ------------------------------------------------------------------------------------------------ -// Build a tetrahedron with points.magnitude == 1 -unsigned int StandardShapes::MakeTetrahedron(std::vector& positions) -{ - positions.reserve(positions.size()+9); - - const ai_real invThree = ai_real( 1.0 ) / ai_real( 3.0 ); - const ai_real a = ai_real( 1.41421 ) * invThree; - const ai_real b = ai_real( 2.4494 ) * invThree; - - const aiVector3D v0 = aiVector3D(0.0,0.0,1.0); - const aiVector3D v1 = aiVector3D(2*a,0,-invThree ); - const aiVector3D v2 = aiVector3D(-a,b,-invThree ); - const aiVector3D v3 = aiVector3D(-a,-b,-invThree ); - - ADD_TRIANGLE(v0,v1,v2); - ADD_TRIANGLE(v0,v2,v3); - ADD_TRIANGLE(v0,v3,v1); - ADD_TRIANGLE(v1,v3,v2); - return 3; -} - -// ------------------------------------------------------------------------------------------------ -// Build a hexahedron with points.magnitude == 1 -unsigned int StandardShapes::MakeHexahedron(std::vector& positions, - bool polygons /*= false*/) -{ - positions.reserve(positions.size()+36); - const ai_real length = ai_real(1.0)/ai_real(1.73205080); - - const aiVector3D v0 = aiVector3D(-1.0,-1.0,-1.0)*length; - const aiVector3D v1 = aiVector3D(1.0,-1.0,-1.0)*length; - const aiVector3D v2 = aiVector3D(1.0,1.0,-1.0)*length; - const aiVector3D v3 = aiVector3D(-1.0,1.0,-1.0)*length; - const aiVector3D v4 = aiVector3D(-1.0,-1.0,1.0)*length; - const aiVector3D v5 = aiVector3D(1.0,-1.0,1.0)*length; - const aiVector3D v6 = aiVector3D(1.0,1.0,1.0)*length; - const aiVector3D v7 = aiVector3D(-1.0,1.0,1.0)*length; - - ADD_QUAD(v0,v3,v2,v1); - ADD_QUAD(v0,v1,v5,v4); - ADD_QUAD(v0,v4,v7,v3); - ADD_QUAD(v6,v5,v1,v2); - ADD_QUAD(v6,v2,v3,v7); - ADD_QUAD(v6,v7,v4,v5); - return (polygons ? 4 : 3); -} - -// Cleanup ... -#undef ADD_TRIANGLE -#undef ADD_QUAD -#undef ADD_PENTAGON - -// ------------------------------------------------------------------------------------------------ -// Create a subdivision sphere -void StandardShapes::MakeSphere(unsigned int tess, - std::vector& positions) -{ - // Reserve enough storage. Every subdivision - // splits each triangle in 4, the icosahedron consists of 60 verts - positions.reserve(positions.size()+60 * integer_pow(4, tess)); - - // Construct an icosahedron to start with - MakeIcosahedron(positions); - - // ... and subdivide it until the requested output - // tessellation is reached - for (unsigned int i = 0; i& positions,bool bOpen /*= false */) -{ - // Sorry, a cone with less than 3 segments makes ABSOLUTELY NO SENSE - if (tess < 3 || !height) - return; - - size_t old = positions.size(); - - // No negative radii - radius1 = std::fabs(radius1); - radius2 = std::fabs(radius2); - - ai_real halfHeight = height / ai_real(2.0); - - // radius1 is always the smaller one - if (radius2 > radius1) - { - std::swap(radius2,radius1); - halfHeight = -halfHeight; - } - else old = SIZE_MAX; - - // Use a large epsilon to check whether the cone is pointy - if (radius1 < (radius2-radius1)*10e-3)radius1 = 0.0; - - // We will need 3*2 verts per segment + 3*2 verts per segment - // if the cone is closed - const unsigned int mem = tess*6 + (!bOpen ? tess*3 * (radius1 ? 2 : 1) : 0); - positions.reserve(positions.size () + mem); - - // Now construct all segments - const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess; - const ai_real angle_max = (ai_real)AI_MATH_TWO_PI; - - ai_real s = 1.0; // std::cos(angle == 0); - ai_real t = 0.0; // std::sin(angle == 0); - - for (ai_real angle = 0.0; angle < angle_max; ) - { - const aiVector3D v1 = aiVector3D (s * radius1, -halfHeight, t * radius1 ); - const aiVector3D v2 = aiVector3D (s * radius2, halfHeight, t * radius2 ); - - const ai_real next = angle + angle_delta; - ai_real s2 = std::cos(next); - ai_real t2 = std::sin(next); - - const aiVector3D v3 = aiVector3D (s2 * radius2, halfHeight, t2 * radius2 ); - const aiVector3D v4 = aiVector3D (s2 * radius1, -halfHeight, t2 * radius1 ); - - positions.push_back(v1); - positions.push_back(v2); - positions.push_back(v3); - positions.push_back(v4); - positions.push_back(v1); - positions.push_back(v3); - - if (!bOpen) - { - // generate the end 'cap' - positions.push_back(aiVector3D(s * radius2, halfHeight, t * radius2 )); - positions.push_back(aiVector3D(s2 * radius2, halfHeight, t2 * radius2 )); - positions.push_back(aiVector3D(0.0, halfHeight, 0.0)); - - - if (radius1) - { - // generate the other end 'cap' - positions.push_back(aiVector3D(s * radius1, -halfHeight, t * radius1 )); - positions.push_back(aiVector3D(s2 * radius1, -halfHeight, t2 * radius1 )); - positions.push_back(aiVector3D(0.0, -halfHeight, 0.0)); - - } - } - s = s2; - t = t2; - angle = next; - } - - // Need to flip face order? - if ( SIZE_MAX != old ) { - for (size_t p = old; p < positions.size();p += 3) { - std::swap(positions[p],positions[p+1]); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Build a circle -void StandardShapes::MakeCircle(ai_real radius, unsigned int tess, - std::vector& positions) -{ - // Sorry, a circle with less than 3 segments makes ABSOLUTELY NO SENSE - if (tess < 3 || !radius) - return; - - radius = std::fabs(radius); - - // We will need 3 vertices per segment - positions.reserve(positions.size()+tess*3); - - const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess; - const ai_real angle_max = (ai_real)AI_MATH_TWO_PI; - - ai_real s = 1.0; // std::cos(angle == 0); - ai_real t = 0.0; // std::sin(angle == 0); - - for (ai_real angle = 0.0; angle < angle_max; ) - { - positions.push_back(aiVector3D(s * radius,0.0,t * radius)); - angle += angle_delta; - s = std::cos(angle); - t = std::sin(angle); - positions.push_back(aiVector3D(s * radius,0.0,t * radius)); - - positions.push_back(aiVector3D(0.0,0.0,0.0)); - } -} - -} // ! Assimp diff --git a/thirdparty/assimp/code/StdOStreamLogStream.h b/thirdparty/assimp/code/StdOStreamLogStream.h deleted file mode 100644 index 893e261a2b..0000000000 --- a/thirdparty/assimp/code/StdOStreamLogStream.h +++ /dev/null @@ -1,101 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file StdOStreamLogStream.h -* @brief Implementation of StdOStreamLogStream -*/ - -#ifndef AI_STROSTREAMLOGSTREAM_H_INC -#define AI_STROSTREAMLOGSTREAM_H_INC - -#include -#include - -namespace Assimp { - -// --------------------------------------------------------------------------- -/** @class StdOStreamLogStream - * @brief Logs into a std::ostream - */ -class StdOStreamLogStream : public LogStream { -public: - /** @brief Construction from an existing std::ostream - * @param _ostream Output stream to be used - */ - explicit StdOStreamLogStream(std::ostream& _ostream); - - /** @brief Destructor */ - ~StdOStreamLogStream(); - - /** @brief Writer */ - void write(const char* message); - -private: - std::ostream& mOstream; -}; - -// --------------------------------------------------------------------------- -// Default constructor -inline StdOStreamLogStream::StdOStreamLogStream(std::ostream& _ostream) -: mOstream (_ostream){ - // empty -} - -// --------------------------------------------------------------------------- -// Default constructor -inline StdOStreamLogStream::~StdOStreamLogStream() { - // empty -} - -// --------------------------------------------------------------------------- -// Write method -inline void StdOStreamLogStream::write(const char* message) { - mOstream << message; - mOstream.flush(); -} - -// --------------------------------------------------------------------------- - -} // Namespace Assimp - -#endif // guard diff --git a/thirdparty/assimp/code/Subdivision.cpp b/thirdparty/assimp/code/Subdivision.cpp deleted file mode 100644 index 19db223a55..0000000000 --- a/thirdparty/assimp/code/Subdivision.cpp +++ /dev/null @@ -1,588 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -#include -#include -#include -#include "ProcessHelper.h" -#include -#include -#include - -using namespace Assimp; -void mydummy() {} - -// ------------------------------------------------------------------------------------------------ -/** Subdivider stub class to implement the Catmull-Clarke subdivision algorithm. The - * implementation is basing on recursive refinement. Directly evaluating the result is also - * possible and much quicker, but it depends on lengthy matrix lookup tables. */ -// ------------------------------------------------------------------------------------------------ -class CatmullClarkSubdivider : public Subdivider -{ -public: - void Subdivide (aiMesh* mesh, aiMesh*& out, unsigned int num, bool discard_input); - void Subdivide (aiMesh** smesh, size_t nmesh, - aiMesh** out, unsigned int num, bool discard_input); - - // --------------------------------------------------------------------------- - /** Intermediate description of an edge between two corners of a polygon*/ - // --------------------------------------------------------------------------- - struct Edge - { - Edge() - : ref(0) - {} - Vertex edge_point, midpoint; - unsigned int ref; - }; - - typedef std::vector UIntVector; - typedef std::map 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 id0out+nmesh); - if (!num) { - // No subdivision at all. Need to copy all the meshes .. argh. - if (discard_input) { - for (size_t s = 0; s < nmesh; ++s) { - out[s] = smesh[s]; - smesh[s] = NULL; - } - } - else { - for (size_t s = 0; s < nmesh; ++s) { - SceneCombiner::Copy(out+s,smesh[s]); - } - } - return; - } - - std::vector inmeshes; - std::vector outmeshes; - std::vector maptbl; - - inmeshes.reserve(nmesh); - outmeshes.reserve(nmesh); - maptbl.reserve(nmesh); - - // Remove pure line and point meshes from the working set to reduce the - // number of edge cases the subdivider is forced to deal with. Line and - // point meshes are simply passed through. - for (size_t s = 0; s < nmesh; ++s) { - aiMesh* i = smesh[s]; - // FIX - mPrimitiveTypes might not yet be initialized - if (i->mPrimitiveTypes && (i->mPrimitiveTypes & (aiPrimitiveType_LINE|aiPrimitiveType_POINT))==i->mPrimitiveTypes) { - ASSIMP_LOG_DEBUG("Catmull-Clark Subdivider: Skipping pure line/point mesh"); - - if (discard_input) { - out[s] = i; - smesh[s] = NULL; - } - else { - SceneCombiner::Copy(out+s,i); - } - continue; - } - - outmeshes.push_back(NULL);inmeshes.push_back(i); - maptbl.push_back(static_cast(s)); - } - - // Do the actual subdivision on the preallocated storage. InternSubdivide - // *always* assumes that enough storage is available, it does not bother - // checking any ranges. - ai_assert(inmeshes.size()==outmeshes.size()&&inmeshes.size()==maptbl.size()); - if (inmeshes.empty()) { - ASSIMP_LOG_WARN("Catmull-Clark Subdivider: Pure point/line scene, I can't do anything"); - return; - } - InternSubdivide(&inmeshes.front(),inmeshes.size(),&outmeshes.front(),num); - for (unsigned int i = 0; i < maptbl.size(); ++i) { - ai_assert(nullptr != outmeshes[i]); - out[maptbl[i]] = outmeshes[i]; - } - - if (discard_input) { - for (size_t s = 0; s < nmesh; ++s) { - delete smesh[s]; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Note - this is an implementation of the standard (recursive) Cm-Cl algorithm without further -// optimizations (except we're using some nice LUTs). A description of the algorithm can be found -// here: http://en.wikipedia.org/wiki/Catmull-Clark_subdivision_surface -// -// The code is mostly O(n), however parts are O(nlogn) which is therefore the algorithm's -// expected total runtime complexity. The implementation is able to work in-place on the same -// mesh arrays. Calling #InternSubdivide() directly is not encouraged. The code can operate -// in-place unless 'smesh' and 'out' are equal (no strange overlaps or reorderings). -// Previous data is replaced/deleted then. -// ------------------------------------------------------------------------------------------------ -void CatmullClarkSubdivider::InternSubdivide ( - const aiMesh* const * smesh, - size_t nmesh, - aiMesh** out, - unsigned int num - ) -{ - ai_assert(NULL != smesh && NULL != out); - INIT_EDGE_HASH_TEMPORARIES(); - - // no subdivision requested or end of recursive refinement - if (!num) { - return; - } - - UIntVector maptbl; - SpatialSort spatial; - - // --------------------------------------------------------------------- - // 0. Offset table to index all meshes continuously, generate a spatially - // sorted representation of all vertices in all meshes. - // --------------------------------------------------------------------- - typedef std::pair IntPair; - std::vector 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 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(face.mNumIndices); - nfacesout += face.mNumIndices; - } - } - - { - // we want edges to go away before the recursive calls so begin a new scope - EdgeMap edges; - - // --------------------------------------------------------------------- - // 2. Set each edge point to be the average of all neighbouring - // face points and original points. Every edge exists twice - // if there is a neighboring face. - // --------------------------------------------------------------------- - for (size_t t = 0; t < nmesh; ++t) { - const aiMesh* mesh = smesh[t]; - - for (unsigned int i = 0; i < mesh->mNumFaces;++i) { - const aiFace& face = mesh->mFaces[i]; - - for (unsigned int p =0; p< face.mNumIndices; ++p) { - const unsigned int id[] = { - face.mIndices[p], - face.mIndices[p==face.mNumIndices-1?0:p+1] - }; - const unsigned int mp[] = { - maptbl[FLATTEN_VERTEX_IDX(t,id[0])], - maptbl[FLATTEN_VERTEX_IDX(t,id[1])] - }; - - Edge& e = edges[MAKE_EDGE_HASH(mp[0],mp[1])]; - e.ref++; - if (e.ref<=2) { - if (e.ref==1) { // original points (end points) - add only once - e.edge_point = e.midpoint = Vertex(mesh,id[0])+Vertex(mesh,id[1]); - e.midpoint *= 0.5f; - } - e.edge_point += centroids[FLATTEN_FACE_IDX(t,i)]; - } - } - } - } - - // --------------------------------------------------------------------- - // 3. Normalize edge points - // --------------------------------------------------------------------- - {unsigned int bad_cnt = 0; - for (EdgeMap::iterator it = edges.begin(); it != edges.end(); ++it) { - if ((*it).second.ref < 2) { - ai_assert((*it).second.ref); - ++bad_cnt; - } - (*it).second.edge_point *= 1.f/((*it).second.ref+2.f); - } - - if (bad_cnt) { - // Report the number of bad edges. bad edges are referenced by less than two - // faces in the mesh. They occur at outer model boundaries in non-closed - // shapes. - ASSIMP_LOG_DEBUG_F("Catmull-Clark Subdivider: got ", bad_cnt, " bad edges touching only one face (totally ", - static_cast(edges.size()), " edges). "); - }} - - // --------------------------------------------------------------------- - // 4. Compute a vertex-face adjacency table. We can't reuse the code - // from VertexTriangleAdjacency because we need the table for multiple - // meshes and out vertex indices need to be mapped to distinct values - // first. - // --------------------------------------------------------------------- - UIntVector faceadjac(nfacesout), cntadjfac(maptbl.size(),0), ofsadjvec(maptbl.size()+1,0); { - for (size_t t = 0; t < nmesh; ++t) { - const aiMesh* const minp = smesh[t]; - for (unsigned int i = 0; i < minp->mNumFaces; ++i) { - - const aiFace& f = minp->mFaces[i]; - for (unsigned int n = 0; n < f.mNumIndices; ++n) { - ++cntadjfac[maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]]; - } - } - } - unsigned int cur = 0; - for (size_t i = 0; i < cntadjfac.size(); ++i) { - ofsadjvec[i+1] = cur; - cur += cntadjfac[i]; - } - for (size_t t = 0; t < nmesh; ++t) { - const aiMesh* const minp = smesh[t]; - for (unsigned int i = 0; i < minp->mNumFaces; ++i) { - - const aiFace& f = minp->mFaces[i]; - for (unsigned int n = 0; n < f.mNumIndices; ++n) { - faceadjac[ofsadjvec[1+maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]]++] = FLATTEN_FACE_IDX(t,i); - } - } - } - - // check the other way round for consistency -#ifdef ASSIMP_BUILD_DEBUG - - for (size_t t = 0; t < ofsadjvec.size()-1; ++t) { - for (unsigned int m = 0; m < cntadjfac[t]; ++m) { - const unsigned int fidx = faceadjac[ofsadjvec[t]+m]; - ai_assert(fidx < totfaces); - for (size_t n = 1; n < nmesh; ++n) { - - if (moffsets[n].first > fidx) { - const aiMesh* msh = smesh[--n]; - const aiFace& f = msh->mFaces[fidx-moffsets[n].first]; - - bool haveit = false; - for (unsigned int i = 0; i < f.mNumIndices; ++i) { - if (maptbl[FLATTEN_VERTEX_IDX(n,f.mIndices[i])]==(unsigned int)t) { - haveit = true; - break; - } - } - ai_assert(haveit); - if (!haveit) { - ASSIMP_LOG_DEBUG("Catmull-Clark Subdivider: Index not used"); - } - break; - } - } - } - } - -#endif - } - -#define GET_ADJACENT_FACES_AND_CNT(vidx,fstartout,numout) \ - fstartout = &faceadjac[ofsadjvec[vidx]], numout = cntadjfac[vidx] - - typedef std::pair TouchedOVertex; - std::vector new_points(num_unique,TouchedOVertex(false,Vertex())); - // --------------------------------------------------------------------- - // 5. Spawn a quad from each face point to the corresponding edge points - // the original points being the fourth quad points. - // --------------------------------------------------------------------- - for (size_t t = 0; t < nmesh; ++t) { - const aiMesh* const minp = smesh[t]; - aiMesh* const mout = out[t] = new aiMesh(); - - for (unsigned int a = 0; a < minp->mNumFaces; ++a) { - mout->mNumFaces += minp->mFaces[a].mNumIndices; - } - - // We need random access to the old face buffer, so reuse is not possible. - mout->mFaces = new aiFace[mout->mNumFaces]; - - mout->mNumVertices = mout->mNumFaces*4; - mout->mVertices = new aiVector3D[mout->mNumVertices]; - - // quads only, keep material index - mout->mPrimitiveTypes = aiPrimitiveType_POLYGON; - mout->mMaterialIndex = minp->mMaterialIndex; - - if (minp->HasNormals()) { - mout->mNormals = new aiVector3D[mout->mNumVertices]; - } - - if (minp->HasTangentsAndBitangents()) { - mout->mTangents = new aiVector3D[mout->mNumVertices]; - mout->mBitangents = new aiVector3D[mout->mNumVertices]; - } - - for(unsigned int i = 0; minp->HasTextureCoords(i); ++i) { - mout->mTextureCoords[i] = new aiVector3D[mout->mNumVertices]; - mout->mNumUVComponents[i] = minp->mNumUVComponents[i]; - } - - for(unsigned int i = 0; minp->HasVertexColors(i); ++i) { - mout->mColors[i] = new aiColor4D[mout->mNumVertices]; - } - - mout->mNumVertices = mout->mNumFaces<<2u; - for (unsigned int i = 0, v = 0, n = 0; i < minp->mNumFaces;++i) { - - const aiFace& face = minp->mFaces[i]; - for (unsigned int a = 0; a < face.mNumIndices;++a) { - - // Get a clean new face. - aiFace& faceOut = mout->mFaces[n++]; - faceOut.mIndices = new unsigned int [faceOut.mNumIndices = 4]; - - // Spawn a new quadrilateral (ccw winding) for this original point between: - // a) face centroid - centroids[FLATTEN_FACE_IDX(t,i)].SortBack(mout,faceOut.mIndices[0]=v++); - - // b) adjacent edge on the left, seen from the centroid - const Edge& e0 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])], - maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a==face.mNumIndices-1?0:a+1]) - ])]; // fixme: replace with mod face.mNumIndices? - - // c) adjacent edge on the right, seen from the centroid - const Edge& e1 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])], - maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[!a?face.mNumIndices-1:a-1]) - ])]; // fixme: replace with mod face.mNumIndices? - - e0.edge_point.SortBack(mout,faceOut.mIndices[3]=v++); - e1.edge_point.SortBack(mout,faceOut.mIndices[1]=v++); - - // d= original point P with distinct index i - // F := 0 - // R := 0 - // n := 0 - // for each face f containing i - // F := F+ centroid of f - // R := R+ midpoint of edge of f from i to i+1 - // n := n+1 - // - // (F+2R+(n-3)P)/n - const unsigned int org = maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])]; - TouchedOVertex& ov = new_points[org]; - - if (!ov.first) { - ov.first = true; - - const unsigned int* adj; unsigned int cnt; - GET_ADJACENT_FACES_AND_CNT(org,adj,cnt); - - if (cnt < 3) { - ov.second = Vertex(minp,face.mIndices[a]); - } - else { - - Vertex F,R; - for (unsigned int o = 0; o < cnt; ++o) { - ai_assert(adj[o] < totfaces); - F += centroids[adj[o]]; - - // adj[0] is a global face index - search the face in the mesh list - const aiMesh* mp = NULL; - size_t nidx; - - if (adj[o] < moffsets[0].first) { - mp = smesh[nidx=0]; - } - else { - for (nidx = 1; nidx<= nmesh; ++nidx) { - if (nidx == nmesh ||moffsets[nidx].first > adj[o]) { - mp = smesh[--nidx]; - break; - } - } - } - - ai_assert(adj[o]-moffsets[nidx].first < mp->mNumFaces); - const aiFace& f = mp->mFaces[adj[o]-moffsets[nidx].first]; - bool haveit = false; - - // find our original point in the face - for (unsigned int m = 0; m < f.mNumIndices; ++m) { - if (maptbl[FLATTEN_VERTEX_IDX(nidx,f.mIndices[m])] == org) { - - // add *both* edges. this way, we can be sure that we add - // *all* adjacent edges to R. In a closed shape, every - // edge is added twice - so we simply leave out the - // factor 2.f in the amove formula and get the right - // result. - - const Edge& c0 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX( - nidx,f.mIndices[!m?f.mNumIndices-1:m-1])])]; - // fixme: replace with mod face.mNumIndices? - - const Edge& c1 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX( - nidx,f.mIndices[m==f.mNumIndices-1?0:m+1])])]; - // fixme: replace with mod face.mNumIndices? - R += c0.midpoint+c1.midpoint; - - haveit = true; - break; - } - } - - // this invariant *must* hold if the vertex-to-face adjacency table is valid - ai_assert(haveit); - if ( !haveit ) { - ASSIMP_LOG_WARN( "OBJ: no name for material library specified." ); - } - } - - const float div = static_cast(cnt), divsq = 1.f/(div*div); - ov.second = Vertex(minp,face.mIndices[a])*((div-3.f) / div) + R*divsq + F*divsq; - } - } - ov.second.SortBack(mout,faceOut.mIndices[2]=v++); - } - } - } - } // end of scope for edges, freeing its memory - - // --------------------------------------------------------------------- - // 7. Apply the next subdivision step. - // --------------------------------------------------------------------- - if (num != 1) { - std::vector tmp(nmesh); - InternSubdivide (out,nmesh,&tmp.front(),num-1); - for (size_t i = 0; i < nmesh; ++i) { - delete out[i]; - out[i] = tmp[i]; - } - } -} diff --git a/thirdparty/assimp/code/TargetAnimation.cpp b/thirdparty/assimp/code/TargetAnimation.cpp deleted file mode 100644 index b8062499ff..0000000000 --- a/thirdparty/assimp/code/TargetAnimation.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -#include "TargetAnimation.h" -#include -#include - -using namespace Assimp; - - -// ------------------------------------------------------------------------------------------------ -KeyIterator::KeyIterator(const std::vector* _objPos, - const std::vector* _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 -inline T Interpolate(const T& one, const T& two, ai_real val) -{ - return one + (two-one)*val; -} - -// ------------------------------------------------------------------------------------------------ -void KeyIterator::operator ++() -{ - // If we are already at the end of all keyframes, return - if (reachedEnd) { - return; - } - - // Now search in all arrays for the time value closest - // to our current position on the time line - double d0,d1; - - d0 = objPos->at ( std::min ( nextObjPos, static_cast(objPos->size()-1)) ).mTime; - d1 = targetObjPos->at( std::min ( nextTargetObjPos, static_cast(targetObjPos->size()-1)) ).mTime; - - // Easiest case - all are identical. In this - // case we don't need to interpolate so we can - // return earlier - if ( d0 == d1 ) - { - curTime = d0; - curPosition = objPos->at(nextObjPos).mValue; - curTargetPosition = targetObjPos->at(nextTargetObjPos).mValue; - - // increment counters - if (objPos->size() != nextObjPos-1) - ++nextObjPos; - - if (targetObjPos->size() != nextTargetObjPos-1) - ++nextTargetObjPos; - } - - // An object position key is closest to us - else if (d0 < d1) - { - curTime = d0; - - // interpolate the other - if (1 == targetObjPos->size() || !nextTargetObjPos) { - curTargetPosition = targetObjPos->at(0).mValue; - } - else - { - const aiVectorKey& last = targetObjPos->at(nextTargetObjPos); - const aiVectorKey& first = targetObjPos->at(nextTargetObjPos-1); - - curTargetPosition = Interpolate(first.mValue, last.mValue, (ai_real) ( - (curTime-first.mTime) / (last.mTime-first.mTime) )); - } - - if (objPos->size() != nextObjPos-1) - ++nextObjPos; - } - // A target position key is closest to us - else - { - curTime = d1; - - // interpolate the other - if (1 == objPos->size() || !nextObjPos) { - curPosition = objPos->at(0).mValue; - } - else - { - const aiVectorKey& last = objPos->at(nextObjPos); - const aiVectorKey& first = objPos->at(nextObjPos-1); - - curPosition = Interpolate(first.mValue, last.mValue, (ai_real) ( - (curTime-first.mTime) / (last.mTime-first.mTime))); - } - - if (targetObjPos->size() != nextTargetObjPos-1) - ++nextTargetObjPos; - } - - if (nextObjPos >= objPos->size()-1 && - nextTargetObjPos >= targetObjPos->size()-1) - { - // We reached the very last keyframe - reachedEnd = true; - } -} - -// ------------------------------------------------------------------------------------------------ -void TargetAnimationHelper::SetTargetAnimationChannel ( - const std::vector* _targetPositions) -{ - ai_assert(NULL != _targetPositions); - targetPositions = _targetPositions; -} - -// ------------------------------------------------------------------------------------------------ -void TargetAnimationHelper::SetMainAnimationChannel ( - const std::vector* _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* distanceTrack) -{ - ai_assert(NULL != targetPositions && NULL != distanceTrack); - - // TODO: in most cases we won't need the extra array - std::vector real; - - std::vector* fill = (distanceTrack == objectPositions ? &real : distanceTrack); - fill->reserve(std::max( objectPositions->size(), targetPositions->size() )); - - // Iterate through all object keys and interpolate their values if necessary. - // Then get the corresponding target position, compute the difference - // vector between object and target position. Then compute a rotation matrix - // that rotates the base vector of the object coordinate system at that time - // to match the diff vector. - - KeyIterator iter(objectPositions,targetPositions,&fixedMain); - for (;!iter.Finished();++iter) - { - const aiVector3D& position = iter.GetCurPosition(); - const aiVector3D& tposition = iter.GetCurTargetPosition(); - - // diff vector - aiVector3D diff = tposition - position; - ai_real f = diff.Length(); - - // output distance vector - if (f) - { - fill->push_back(aiVectorKey()); - aiVectorKey& v = fill->back(); - v.mTime = iter.GetCurTime(); - v.mValue = diff; - - diff /= f; - } - else - { - // FIXME: handle this - } - - // diff is now the vector in which our camera is pointing - } - - if (real.size()) { - *distanceTrack = real; - } -} diff --git a/thirdparty/assimp/code/TargetAnimation.h b/thirdparty/assimp/code/TargetAnimation.h deleted file mode 100644 index 91634ab5aa..0000000000 --- a/thirdparty/assimp/code/TargetAnimation.h +++ /dev/null @@ -1,183 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a helper class for the ASE and 3DS loaders to - help them compute camera and spot light animation channels */ -#ifndef AI_TARGET_ANIMATION_H_INC -#define AI_TARGET_ANIMATION_H_INC - -#include -#include - -namespace Assimp { - - - -// --------------------------------------------------------------------------- -/** Helper class to iterate through all keys in an animation channel. - * - * Missing tracks are interpolated. This is a helper class for - * TargetAnimationHelper, but it can be freely used for other purposes. -*/ -class KeyIterator -{ -public: - - - // ------------------------------------------------------------------ - /** Constructs a new key iterator - * - * @param _objPos Object position track. May be NULL. - * @param _targetObjPos Target object position track. May be NULL. - * @param defaultObjectPos Default object position to be used if - * no animated track is available. May be NULL. - * @param defaultTargetPos Default target position to be used if - * no animated track is available. May be NULL. - */ - KeyIterator(const std::vector* _objPos, - const std::vector* _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* objPos,*targetObjPos; - - unsigned int nextObjPos, nextTargetObjPos; - std::vector defaultObjPos,defaultTargetObjPos; -}; - -// --------------------------------------------------------------------------- -/** Helper class for the 3DS and ASE loaders to compute camera and spot light - * animations. - * - * 3DS and ASE store the differently to Assimp - there is an animation - * channel for the camera/spot light itself and a separate position - * animation channels specifying the position of the camera/spot light - * look-at target */ -class TargetAnimationHelper -{ -public: - - TargetAnimationHelper() - : targetPositions (NULL) - , objectPositions (NULL) - {} - - - // ------------------------------------------------------------------ - /** Sets the target animation channel - * - * This channel specifies the position of the camera/spot light - * target at a specific position. - * - * @param targetPositions Translation channel*/ - void SetTargetAnimationChannel (const - std::vector* targetPositions); - - - // ------------------------------------------------------------------ - /** Sets the main animation channel - * - * @param objectPositions Translation channel */ - void SetMainAnimationChannel ( const - std::vector* 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* distanceTrack ); - - -private: - - const std::vector* targetPositions,*objectPositions; - aiVector3D fixedMain; -}; - - -} // ! end namespace Assimp - -#endif // include guard diff --git a/thirdparty/assimp/code/TextureTransform.cpp b/thirdparty/assimp/code/TextureTransform.cpp deleted file mode 100644 index 8ae2ba7218..0000000000 --- a/thirdparty/assimp/code/TextureTransform.cpp +++ /dev/null @@ -1,566 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file A helper class that processes texture transformations */ - - - -#include -#include -#include -#include - -#include "TextureTransform.h" -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -TextureTransformStep::TextureTransformStep() : - configFlags() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -TextureTransformStep::~TextureTransformStep() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool TextureTransformStep::IsActive( unsigned int pFlags) const -{ - return (pFlags & aiProcess_TransformUVCoords) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Setup properties -void TextureTransformStep::SetupProperties(const Importer* pImp) -{ - configFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_TUV_EVALUATE,AI_UVTRAFO_ALL); -} - -// ------------------------------------------------------------------------------------------------ -void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info) -{ - /* This function tries to simplify the input UV transformation. - * That's very important as it allows us to reduce the number - * of output UV channels. The order in which the transformations - * are applied is - as always - scaling, rotation, translation. - */ - - char szTemp[512]; - int rounded = 0; - - - /* Optimize the rotation angle. That's slightly difficult as - * we have an inprecise floating-point number (when comparing - * UV transformations we'll take that into account by using - * an epsilon of 5 degrees). If there is a rotation value, we can't - * perform any further optimizations. - */ - if (info.mRotation) - { - float out = info.mRotation; - if ((rounded = static_cast((info.mRotation / static_cast(AI_MATH_TWO_PI))))) - { - out -= rounded * static_cast(AI_MATH_PI); - ASSIMP_LOG_INFO_F("Texture coordinate rotation ", info.mRotation, " can be simplified to ", out); - } - - // Next step - convert negative rotation angles to positives - if (out < 0.f) - out = (float)AI_MATH_TWO_PI * 2 + out; - - info.mRotation = out; - return; - } - - - /* Optimize UV translation in the U direction. To determine whether - * or not we can optimize we need to look at the requested mapping - * type (e.g. if mirroring is active there IS a difference between - * offset 2 and 3) - */ - if ((rounded = (int)info.mTranslation.x)) { - float out = 0.0f; - szTemp[0] = 0; - if (aiTextureMapMode_Wrap == info.mapU) { - // Wrap - simple take the fraction of the field - out = info.mTranslation.x-(float)rounded; - ai_snprintf(szTemp, 512, "[w] UV U offset %f can be simplified to %f", info.mTranslation.x, out); - } - else if (aiTextureMapMode_Mirror == info.mapU && 1 != rounded) { - // Mirror - if (rounded % 2) - rounded--; - out = info.mTranslation.x-(float)rounded; - - ai_snprintf(szTemp,512,"[m/d] UV U offset %f can be simplified to %f",info.mTranslation.x,out); - } - else if (aiTextureMapMode_Clamp == info.mapU || aiTextureMapMode_Decal == info.mapU) { - // Clamp - translations beyond 1,1 are senseless - ai_snprintf(szTemp,512,"[c] UV U offset %f can be clamped to 1.0f",info.mTranslation.x); - - out = 1.f; - } - if (szTemp[0]) { - ASSIMP_LOG_INFO(szTemp); - info.mTranslation.x = out; - } - } - - /* Optimize UV translation in the V direction. To determine whether - * or not we can optimize we need to look at the requested mapping - * type (e.g. if mirroring is active there IS a difference between - * offset 2 and 3) - */ - if ((rounded = (int)info.mTranslation.y)) { - float out = 0.0f; - szTemp[0] = 0; - if (aiTextureMapMode_Wrap == info.mapV) { - // Wrap - simple take the fraction of the field - out = info.mTranslation.y-(float)rounded; - ::ai_snprintf(szTemp,512,"[w] UV V offset %f can be simplified to %f",info.mTranslation.y,out); - } - else if (aiTextureMapMode_Mirror == info.mapV && 1 != rounded) { - // Mirror - if (rounded % 2) - rounded--; - out = info.mTranslation.x-(float)rounded; - - ::ai_snprintf(szTemp,512,"[m/d] UV V offset %f can be simplified to %f",info.mTranslation.y,out); - } - else if (aiTextureMapMode_Clamp == info.mapV || aiTextureMapMode_Decal == info.mapV) { - // Clamp - translations beyond 1,1 are senseless - ::ai_snprintf(szTemp,512,"[c] UV V offset %f canbe clamped to 1.0f",info.mTranslation.y); - - out = 1.f; - } - if (szTemp[0]) { - ASSIMP_LOG_INFO(szTemp); - info.mTranslation.y = out; - } - } - return; -} - -// ------------------------------------------------------------------------------------------------ -void UpdateUVIndex(const std::list& l, unsigned int n) -{ - // Don't set if == 0 && wasn't set before - for (std::list::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*)&n,1,AI_MATKEY_UVWSRC(info.semantic,info.index)); - } - } -} - -// ------------------------------------------------------------------------------------------------ -inline const char* MappingModeToChar(aiTextureMapMode map) -{ - if (aiTextureMapMode_Wrap == map) - return "-w"; - - if (aiTextureMapMode_Mirror == map) - return "-m"; - - return "-c"; -} - -// ------------------------------------------------------------------------------------------------ -void TextureTransformStep::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("TransformUVCoordsProcess begin"); - - - /* We build a per-mesh list of texture transformations we'll need - * to apply. To achieve this, we iterate through all materials, - * find all textures and get their transformations and UV indices. - * Then we search for all meshes using this material. - */ - typedef std::list MeshTrafoList; - std::vector meshLists(pScene->mNumMeshes); - - for (unsigned int i = 0; i < pScene->mNumMaterials;++i) { - - aiMaterial* mat = pScene->mMaterials[i]; - for (unsigned int a = 0; a < mat->mNumProperties;++a) { - - aiMaterialProperty* prop = mat->mProperties[a]; - if (!::strcmp( prop->mKey.data, "$tex.file")) { - STransformVecInfo info; - - // Setup a shortcut structure to allow for a fast updating - // of the UV index later - TTUpdateInfo update; - update.mat = (aiMaterial*) mat; - update.semantic = prop->mSemantic; - update.index = prop->mIndex; - - // Get textured properties and transform - for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2) { - aiMaterialProperty* prop2 = mat->mProperties[a2]; - if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex) { - continue; - } - - if ( !::strcmp( prop2->mKey.data, "$tex.uvwsrc")) { - info.uvIndex = *((int*)prop2->mData); - - // Store a direct pointer for later use - update.directShortcut = (unsigned int*) prop2->mData; - } - - else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodeu")) { - info.mapU = *((aiTextureMapMode*)prop2->mData); - } - else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodev")) { - info.mapV = *((aiTextureMapMode*)prop2->mData); - } - else if ( !::strcmp( prop2->mKey.data, "$tex.uvtrafo")) { - // ValidateDS should check this - ai_assert(prop2->mDataLength >= 20); - ::memcpy(&info.mTranslation.x,prop2->mData,sizeof(float)*5); - - // Directly remove this property from the list - mat->mNumProperties--; - for (unsigned int a3 = a2; a3 < mat->mNumProperties;++a3) { - mat->mProperties[a3] = mat->mProperties[a3+1]; - } - - delete prop2; - - // Warn: could be an underflow, but this does not invoke undefined behaviour - --a2; - } - } - - // Find out which transformations are to be evaluated - if (!(configFlags & AI_UVTRAFO_ROTATION)) { - info.mRotation = 0.f; - } - if (!(configFlags & AI_UVTRAFO_SCALING)) { - info.mScaling = aiVector2D(1.f,1.f); - } - if (!(configFlags & AI_UVTRAFO_TRANSLATION)) { - info.mTranslation = aiVector2D(0.f,0.f); - } - - // Do some preprocessing - PreProcessUVTransform(info); - info.uvIndex = std::min(info.uvIndex,AI_MAX_NUMBER_OF_TEXTURECOORDS -1u); - - // Find out whether this material is used by more than - // one mesh. This will make our task much, much more difficult! - unsigned int cnt = 0; - for (unsigned int n = 0; n < pScene->mNumMeshes;++n) { - if (pScene->mMeshes[n]->mMaterialIndex == i) - ++cnt; - } - - if (!cnt) - continue; - else if (1 != cnt) { - // This material is referenced by more than one mesh! - // So we need to make sure the UV index for the texture - // is identical for each of it ... - info.lockedPos = AI_TT_UV_IDX_LOCK_TBD; - } - - // Get all corresponding meshes - for (unsigned int n = 0; n < pScene->mNumMeshes;++n) { - aiMesh* mesh = pScene->mMeshes[n]; - if (mesh->mMaterialIndex != i || !mesh->mTextureCoords[0]) - continue; - - unsigned int uv = info.uvIndex; - if (!mesh->mTextureCoords[uv]) { - // If the requested UV index is not available, take the first one instead. - uv = 0; - } - - if (mesh->mNumUVComponents[info.uvIndex] >= 3){ - ASSIMP_LOG_WARN("UV transformations on 3D mapping channels are not supported"); - continue; - } - - MeshTrafoList::iterator it; - - // Check whether we have this transform setup already - for (it = meshLists[n].begin();it != meshLists[n].end(); ++it) { - - if ((*it) == info && (*it).uvIndex == uv) { - (*it).updateList.push_back(update); - break; - } - } - - if (it == meshLists[n].end()) { - meshLists[n].push_back(info); - meshLists[n].back().uvIndex = uv; - meshLists[n].back().updateList.push_back(update); - } - } - } - } - } - - char buffer[1024]; // should be sufficiently large - unsigned int outChannels = 0, inChannels = 0, transformedChannels = 0; - - // Now process all meshes. Important: we don't remove unreferenced UV channels. - // This is a job for the RemoveUnreferencedData-Step. - for (unsigned int q = 0; q < pScene->mNumMeshes;++q) { - - aiMesh* mesh = pScene->mMeshes[q]; - MeshTrafoList& trafo = meshLists[q]; - - inChannels += mesh->GetNumUVChannels(); - - if (!mesh->mTextureCoords[0] || trafo.empty() || (trafo.size() == 1 && trafo.begin()->IsUntransformed())) { - outChannels += mesh->GetNumUVChannels(); - continue; - } - - // Move untransformed UV channels to the first position in the list .... - // except if we need a new locked index which should be as small as possible - bool veto = false, need = false; - unsigned int cnt = 0; - unsigned int untransformed = 0; - - MeshTrafoList::iterator it,it2; - for (it = trafo.begin();it != trafo.end(); ++it,++cnt) { - - if (!(*it).IsUntransformed()) { - need = true; - } - - if ((*it).lockedPos == AI_TT_UV_IDX_LOCK_TBD) { - // Lock this index and make sure it won't be changed - (*it).lockedPos = cnt; - veto = true; - continue; - } - - if (!veto && it != trafo.begin() && (*it).IsUntransformed()) { - for (it2 = trafo.begin();it2 != it; ++it2) { - if (!(*it2).IsUntransformed()) - break; - } - trafo.insert(it2,*it); - trafo.erase(it); - break; - } - } - if (!need) - continue; - - // Find all that are not at their 'locked' position and move them to it. - // Conflicts are possible but quite unlikely. - cnt = 0; - for (it = trafo.begin();it != trafo.end(); ++it,++cnt) { - if ((*it).lockedPos != AI_TT_UV_IDX_LOCK_NONE && (*it).lockedPos != cnt) { - it2 = trafo.begin();unsigned int t = 0; - while (t != (*it).lockedPos) - ++it2; - - if ((*it2).lockedPos != AI_TT_UV_IDX_LOCK_NONE) { - ASSIMP_LOG_ERROR("Channel mismatch, can't compute all transformations properly [design bug]"); - continue; - } - - std::swap(*it2,*it); - if ((*it).lockedPos == untransformed) - untransformed = cnt; - } - } - - // ... and add dummies for all unreferenced channels - // at the end of the list - bool ref[AI_MAX_NUMBER_OF_TEXTURECOORDS]; - for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) - ref[n] = (!mesh->mTextureCoords[n] ? true : false); - - for (it = trafo.begin();it != trafo.end(); ++it) - ref[(*it).uvIndex] = true; - - for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) { - if (ref[n]) - continue; - trafo.push_back(STransformVecInfo()); - trafo.back().uvIndex = n; - } - - // Then check whether this list breaks the channel limit. - // The unimportant ones are at the end of the list, so - // it shouldn't be too worse if we remove them. - unsigned int size = (unsigned int)trafo.size(); - if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) { - - if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_ERROR_F(static_cast(trafo.size()), " UV channels required but just ", - AI_MAX_NUMBER_OF_TEXTURECOORDS, " available"); - } - size = AI_MAX_NUMBER_OF_TEXTURECOORDS; - } - - - aiVector3D* old[AI_MAX_NUMBER_OF_TEXTURECOORDS]; - for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) - old[n] = mesh->mTextureCoords[n]; - - // Now continue and generate the output channels. Channels - // that we're not going to need later can be overridden. - it = trafo.begin(); - for (unsigned int n = 0; n < trafo.size();++n,++it) { - - if (n >= size) { - // Try to use an untransformed channel for all channels we threw over board - UpdateUVIndex((*it).updateList,untransformed); - continue; - } - - outChannels++; - - // Write to the log - if (!DefaultLogger::isNullLogger()) { - ::ai_snprintf(buffer,1024,"Mesh %u, channel %u: t(%.3f,%.3f), s(%.3f,%.3f), r(%.3f), %s%s", - q,n, - (*it).mTranslation.x, - (*it).mTranslation.y, - (*it).mScaling.x, - (*it).mScaling.y, - AI_RAD_TO_DEG( (*it).mRotation), - MappingModeToChar ((*it).mapU), - MappingModeToChar ((*it).mapV)); - - ASSIMP_LOG_INFO(buffer); - } - - // Check whether we need a new buffer here - if (mesh->mTextureCoords[n]) { - - it2 = it;++it2; - for (unsigned int m = n+1; m < size;++m, ++it2) { - - if ((*it2).uvIndex == n){ - it2 = trafo.begin(); - break; - } - } - if (it2 == trafo.begin()){ - mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices]; - } - } - else mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices]; - - aiVector3D* src = old[(*it).uvIndex]; - aiVector3D* dest, *end; - dest = mesh->mTextureCoords[n]; - - ai_assert(NULL != src); - - // Copy the data to the destination array - if (dest != src) - ::memcpy(dest,src,sizeof(aiVector3D)*mesh->mNumVertices); - - end = dest + mesh->mNumVertices; - - // Build a transformation matrix and transform all UV coords with it - if (!(*it).IsUntransformed()) { - const aiVector2D& trl = (*it).mTranslation; - const aiVector2D& scl = (*it).mScaling; - - // fixme: simplify .. - ++transformedChannels; - aiMatrix3x3 matrix; - - aiMatrix3x3 m2,m3,m4,m5; - - m4.a1 = scl.x; - m4.b2 = scl.y; - - m2.a3 = m2.b3 = 0.5f; - m3.a3 = m3.b3 = -0.5f; - - if ((*it).mRotation > AI_TT_ROTATION_EPSILON ) - aiMatrix3x3::RotationZ((*it).mRotation,matrix); - - m5.a3 += trl.x; m5.b3 += trl.y; - matrix = m2 * m4 * matrix * m3 * m5; - - for (src = dest; src != end; ++src) { /* manual homogenious divide */ - src->z = 1.f; - *src = matrix * *src; - src->x /= src->z; - src->y /= src->z; - src->z = 0.f; - } - } - - // Update all UV indices - UpdateUVIndex((*it).updateList,n); - } - } - - // Print some detailed statistics into the log - if (!DefaultLogger::isNullLogger()) { - - if (transformedChannels) { - ASSIMP_LOG_INFO_F("TransformUVCoordsProcess end: ", outChannels, " output channels (in: ", inChannels, ", modified: ", transformedChannels,")"); - } else { - ASSIMP_LOG_DEBUG("TransformUVCoordsProcess finished"); - } - } -} - - diff --git a/thirdparty/assimp/code/TextureTransform.h b/thirdparty/assimp/code/TextureTransform.h deleted file mode 100644 index c556ff5d8c..0000000000 --- a/thirdparty/assimp/code/TextureTransform.h +++ /dev/null @@ -1,232 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Definition of a helper step that processes texture transformations */ -#ifndef AI_TEXTURE_TRANSFORM_H_INCLUDED -#define AI_TEXTURE_TRANSFORM_H_INCLUDED - -#include -#include "BaseProcess.h" - -#include -#include - -struct aiNode; -struct aiMaterial; - -namespace Assimp { - -#define AI_TT_UV_IDX_LOCK_TBD 0xffffffff -#define AI_TT_UV_IDX_LOCK_NONE 0xeeeeeeee - - -#define AI_TT_ROTATION_EPSILON ((float)AI_DEG_TO_RAD(0.5)) - -// --------------------------------------------------------------------------- -/** Small helper structure representing a shortcut into the material list - * to be able to update some values quickly. -*/ -struct TTUpdateInfo { - TTUpdateInfo() AI_NO_EXCEPT - : directShortcut(nullptr) - , mat(nullptr) - , semantic(0) - , index(0) { - // empty - } - - //! Direct shortcut, if available - unsigned int* directShortcut; - - //! Material - aiMaterial *mat; - - //! Texture type and index - unsigned int semantic, index; -}; - - -// --------------------------------------------------------------------------- -/** Helper class representing texture coordinate transformations -*/ -struct STransformVecInfo : public aiUVTransform { - STransformVecInfo() AI_NO_EXCEPT - : uvIndex(0) - , mapU(aiTextureMapMode_Wrap) - , mapV(aiTextureMapMode_Wrap) - , lockedPos(AI_TT_UV_IDX_LOCK_NONE) { - // empty - } - - //! Source texture coordinate index - unsigned int uvIndex; - - //! Texture mapping mode in the u, v direction - aiTextureMapMode mapU,mapV; - - //! Locked destination UV index - //! AI_TT_UV_IDX_LOCK_TBD - to be determined - //! AI_TT_UV_IDX_LOCK_NONE - none (default) - unsigned int lockedPos; - - //! Update info - shortcuts into all materials - //! that are referencing this transform setup - std::list updateList; - - - // ------------------------------------------------------------------- - /** Compare two transform setups - */ - inline bool operator== (const STransformVecInfo& other) const - { - // We use a small epsilon here - const static float epsilon = 0.05f; - - if (std::fabs( mTranslation.x - other.mTranslation.x ) > epsilon || - std::fabs( mTranslation.y - other.mTranslation.y ) > epsilon) - { - return false; - } - - if (std::fabs( mScaling.x - other.mScaling.x ) > epsilon || - std::fabs( mScaling.y - other.mScaling.y ) > epsilon) - { - return false; - } - - if (std::fabs( mRotation - other.mRotation) > epsilon) - { - return false; - } - return true; - } - - inline bool operator!= (const STransformVecInfo& other) const - { - return !(*this == other); - } - - - // ------------------------------------------------------------------- - /** Returns whether this is an untransformed texture coordinate set - */ - inline bool IsUntransformed() const - { - return (1.0f == mScaling.x && 1.f == mScaling.y && - !mTranslation.x && !mTranslation.y && - mRotation < AI_TT_ROTATION_EPSILON); - } - - // ------------------------------------------------------------------- - /** Build a 3x3 matrix from the transformations - */ - inline void GetMatrix(aiMatrix3x3& mOut) - { - mOut = aiMatrix3x3(); - - if (1.0f != mScaling.x || 1.0f != mScaling.y) - { - aiMatrix3x3 mScale; - mScale.a1 = mScaling.x; - mScale.b2 = mScaling.y; - mOut = mScale; - } - if (mRotation) - { - aiMatrix3x3 mRot; - mRot.a1 = mRot.b2 = std::cos(mRotation); - mRot.a2 = mRot.b1 = std::sin(mRotation); - mRot.a2 = -mRot.a2; - mOut *= mRot; - } - if (mTranslation.x || mTranslation.y) - { - aiMatrix3x3 mTrans; - mTrans.a3 = mTranslation.x; - mTrans.b3 = mTranslation.y; - mOut *= mTrans; - } - } -}; - - -// --------------------------------------------------------------------------- -/** Helper step to compute final UV coordinate sets if there are scalings - * or rotations in the original data read from the file. -*/ -class TextureTransformStep : public BaseProcess -{ -public: - - TextureTransformStep(); - ~TextureTransformStep(); - -public: - - // ------------------------------------------------------------------- - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - void Execute( aiScene* pScene); - - // ------------------------------------------------------------------- - void SetupProperties(const Importer* pImp); - - -protected: - - - // ------------------------------------------------------------------- - /** Preprocess a specific UV transformation setup - * - * @param info Transformation setup to be preprocessed. - */ - void PreProcessUVTransform(STransformVecInfo& info); - -private: - - unsigned int configFlags; -}; - -} - -#endif //! AI_TEXTURE_TRANSFORM_H_INCLUDED diff --git a/thirdparty/assimp/code/TriangulateProcess.cpp b/thirdparty/assimp/code/TriangulateProcess.cpp deleted file mode 100644 index 0f68f47ddb..0000000000 --- a/thirdparty/assimp/code/TriangulateProcess.cpp +++ /dev/null @@ -1,530 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file TriangulateProcess.cpp - * @brief Implementation of the post processing step to split up - * all faces with more than three indices into triangles. - * - * - * The triangulation algorithm will handle concave or convex polygons. - * Self-intersecting or non-planar polygons are not rejected, but - * they're probably not triangulated correctly. - * - * DEBUG SWITCHES - do not enable any of them in release builds: - * - * AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING - * - generates vertex colors to represent the face winding order. - * the first vertex of a polygon becomes red, the last blue. - * AI_BUILD_TRIANGULATE_DEBUG_POLYS - * - dump all polygons and their triangulation sequences to - * a file - */ -#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS -#include "TriangulateProcess.h" -#include "ProcessHelper.h" -#include "PolyTools.h" -#include - -//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING -//#define AI_BUILD_TRIANGULATE_DEBUG_POLYS - -#define POLY_GRID_Y 40 -#define POLY_GRID_X 70 -#define POLY_GRID_XPAD 20 -#define POLY_OUTPUT_FILE "assimp_polygons_debug.txt" - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -TriangulateProcess::TriangulateProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -TriangulateProcess::~TriangulateProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool TriangulateProcess::IsActive( unsigned int pFlags) const -{ - return (pFlags & aiProcess_Triangulate) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void TriangulateProcess::Execute( aiScene* pScene) -{ - ASSIMP_LOG_DEBUG("TriangulateProcess begin"); - - bool bHas = false; - for( unsigned int a = 0; a < pScene->mNumMeshes; a++) - { - if (pScene->mMeshes[ a ]) { - if ( TriangulateMesh( pScene->mMeshes[ a ] ) ) { - bHas = true; - } - } - } - if ( bHas ) { - ASSIMP_LOG_INFO( "TriangulateProcess finished. All polygons have been triangulated." ); - } else { - ASSIMP_LOG_DEBUG( "TriangulateProcess finished. There was nothing to be done." ); - } -} - -// ------------------------------------------------------------------------------------------------ -// Triangulates the given mesh. -bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) -{ - // Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases - if (!pMesh->mPrimitiveTypes) { - bool bNeed = false; - - for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { - const aiFace& face = pMesh->mFaces[a]; - - if( face.mNumIndices != 3) { - bNeed = true; - } - } - if (!bNeed) - return false; - } - else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) { - return false; - } - - // Find out how many output faces we'll get - unsigned int numOut = 0, max_out = 0; - bool get_normals = true; - for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { - aiFace& face = pMesh->mFaces[a]; - if (face.mNumIndices <= 4) { - get_normals = false; - } - if( face.mNumIndices <= 3) { - numOut++; - - } - else { - numOut += face.mNumIndices-2; - max_out = std::max(max_out,face.mNumIndices); - } - } - - // Just another check whether aiMesh::mPrimitiveTypes is correct - ai_assert(numOut != pMesh->mNumFaces); - - aiVector3D* nor_out = NULL; - - // if we don't have normals yet, but expect them to be a cheap side - // product of triangulation anyway, allocate storage for them. - if (!pMesh->mNormals && get_normals) { - // XXX need a mechanism to inform the GenVertexNormals process to treat these normals as preprocessed per-face normals - // nor_out = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; - } - - // the output mesh will contain triangles, but no polys anymore - pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; - pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON; - - aiFace* out = new aiFace[numOut](), *curOut = out; - std::vector temp_verts3d(max_out+2); /* temporary storage for vertices */ - std::vector temp_verts(max_out+2); - - // Apply vertex colors to represent the face winding? -#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING - if (!pMesh->mColors[0]) - pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices]; - else - new(pMesh->mColors[0]) aiColor4D[pMesh->mNumVertices]; - - aiColor4D* clr = pMesh->mColors[0]; -#endif - -#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS - FILE* fout = fopen(POLY_OUTPUT_FILE,"a"); -#endif - - const aiVector3D* verts = pMesh->mVertices; - - // use std::unique_ptr to avoid slow std::vector specialiations - std::unique_ptr done(new bool[max_out]); - for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { - aiFace& face = pMesh->mFaces[a]; - - unsigned int* idx = face.mIndices; - int num = (int)face.mNumIndices, ear = 0, tmp, prev = num-1, next = 0, max = num; - - // Apply vertex colors to represent the face winding? -#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING - for (unsigned int i = 0; i < face.mNumIndices; ++i) { - aiColor4D& c = clr[idx[i]]; - c.r = (i+1) / (float)max; - c.b = 1.f - c.r; - } -#endif - - aiFace* const last_face = curOut; - - // if it's a simple point,line or triangle: just copy it - if( face.mNumIndices <= 3) - { - aiFace& nface = *curOut++; - nface.mNumIndices = face.mNumIndices; - nface.mIndices = face.mIndices; - - face.mIndices = NULL; - continue; - } - // optimized code for quadrilaterals - else if ( face.mNumIndices == 4) { - - // quads can have at maximum one concave vertex. Determine - // this vertex (if it exists) and start tri-fanning from - // it. - unsigned int start_vertex = 0; - for (unsigned int i = 0; i < 4; ++i) { - const aiVector3D& v0 = verts[face.mIndices[(i+3) % 4]]; - const aiVector3D& v1 = verts[face.mIndices[(i+2) % 4]]; - const aiVector3D& v2 = verts[face.mIndices[(i+1) % 4]]; - - const aiVector3D& v = verts[face.mIndices[i]]; - - aiVector3D left = (v0-v); - aiVector3D diag = (v1-v); - aiVector3D right = (v2-v); - - left.Normalize(); - diag.Normalize(); - right.Normalize(); - - const float angle = std::acos(left*diag) + std::acos(right*diag); - if (angle > AI_MATH_PI_F) { - // this is the concave point - start_vertex = i; - break; - } - } - - const unsigned int temp[] = {face.mIndices[0], face.mIndices[1], face.mIndices[2], face.mIndices[3]}; - - aiFace& nface = *curOut++; - nface.mNumIndices = 3; - nface.mIndices = face.mIndices; - - nface.mIndices[0] = temp[start_vertex]; - nface.mIndices[1] = temp[(start_vertex + 1) % 4]; - nface.mIndices[2] = temp[(start_vertex + 2) % 4]; - - aiFace& sface = *curOut++; - sface.mNumIndices = 3; - sface.mIndices = new unsigned int[3]; - - sface.mIndices[0] = temp[start_vertex]; - sface.mIndices[1] = temp[(start_vertex + 2) % 4]; - sface.mIndices[2] = temp[(start_vertex + 3) % 4]; - - // prevent double deletion of the indices field - face.mIndices = NULL; - continue; - } - else - { - // A polygon with more than 3 vertices can be either concave or convex. - // Usually everything we're getting is convex and we could easily - // triangulate by tri-fanning. However, LightWave is probably the only - // modeling suite to make extensive use of highly concave, monster polygons ... - // so we need to apply the full 'ear cutting' algorithm to get it right. - - // RERQUIREMENT: polygon is expected to be simple and *nearly* planar. - // We project it onto a plane to get a 2d triangle. - - // Collect all vertices of of the polygon. - for (tmp = 0; tmp < max; ++tmp) { - temp_verts3d[tmp] = verts[idx[tmp]]; - } - - // Get newell normal of the polygon. Store it for future use if it's a polygon-only mesh - aiVector3D n; - NewellNormal<3,3,3>(n,max,&temp_verts3d.front().x,&temp_verts3d.front().y,&temp_verts3d.front().z); - if (nor_out) { - for (tmp = 0; tmp < max; ++tmp) - nor_out[idx[tmp]] = n; - } - - // Select largest normal coordinate to ignore for projection - const float ax = (n.x>0 ? n.x : -n.x); - const float ay = (n.y>0 ? n.y : -n.y); - const float az = (n.z>0 ? n.z : -n.z); - - unsigned int ac = 0, bc = 1; /* no z coord. projection to xy */ - float inv = n.z; - if (ax > ay) { - if (ax > az) { /* no x coord. projection to yz */ - ac = 1; bc = 2; - inv = n.x; - } - } - else if (ay > az) { /* no y coord. projection to zy */ - ac = 2; bc = 0; - inv = n.y; - } - - // Swap projection axes to take the negated projection vector into account - if (inv < 0.f) { - std::swap(ac,bc); - } - - for (tmp =0; tmp < max; ++tmp) { - temp_verts[tmp].x = verts[idx[tmp]][ac]; - temp_verts[tmp].y = verts[idx[tmp]][bc]; - done[tmp] = false; - } - -#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS - // plot the plane onto which we mapped the polygon to a 2D ASCII pic - aiVector2D bmin,bmax; - ArrayBounds(&temp_verts[0],max,bmin,bmax); - - char grid[POLY_GRID_Y][POLY_GRID_X+POLY_GRID_XPAD]; - std::fill_n((char*)grid,POLY_GRID_Y*(POLY_GRID_X+POLY_GRID_XPAD),' '); - - for (int i =0; i < max; ++i) { - const aiVector2D& v = (temp_verts[i] - bmin) / (bmax-bmin); - const size_t x = static_cast(v.x*(POLY_GRID_X-1)), y = static_cast(v.y*(POLY_GRID_Y-1)); - char* loc = grid[y]+x; - if (grid[y][x] != ' ') { - for(;*loc != ' '; ++loc); - *loc++ = '_'; - } - *(loc+::ai_snprintf(loc, POLY_GRID_XPAD,"%i",i)) = ' '; - } - - - for(size_t y = 0; y < POLY_GRID_Y; ++y) { - grid[y][POLY_GRID_X+POLY_GRID_XPAD-1] = '\0'; - fprintf(fout,"%s\n",grid[y]); - } - - fprintf(fout,"\ntriangulation sequence: "); -#endif - - // - // FIXME: currently this is the slow O(kn) variant with a worst case - // complexity of O(n^2) (I think). Can be done in O(n). - while (num > 3) { - - // Find the next ear of the polygon - int num_found = 0; - for (ear = next;;prev = ear,ear = next) { - - // break after we looped two times without a positive match - for (next=ear+1;done[(next>=max?next=0:next)];++next); - if (next < ear) { - if (++num_found == 2) { - break; - } - } - const aiVector2D* pnt1 = &temp_verts[ear], - *pnt0 = &temp_verts[prev], - *pnt2 = &temp_verts[next]; - - // Must be a convex point. Assuming ccw winding, it must be on the right of the line between p-1 and p+1. - if (OnLeftSideOfLine2D(*pnt0,*pnt2,*pnt1)) { - continue; - } - - // and no other point may be contained in this triangle - for ( tmp = 0; tmp < max; ++tmp) { - - // We need to compare the actual values because it's possible that multiple indexes in - // the polygon are referring to the same position. concave_polygon.obj is a sample - // - // FIXME: Use 'epsiloned' comparisons instead? Due to numeric inaccuracies in - // PointInTriangle() I'm guessing that it's actually possible to construct - // input data that would cause us to end up with no ears. The problem is, - // which epsilon? If we chose a too large value, we'd get wrong results - const aiVector2D& vtmp = temp_verts[tmp]; - if ( vtmp != *pnt1 && vtmp != *pnt2 && vtmp != *pnt0 && PointInTriangle2D(*pnt0,*pnt1,*pnt2,vtmp)) { - break; - } - } - if (tmp != max) { - continue; - } - - // this vertex is an ear - break; - } - if (num_found == 2) { - - // Due to the 'two ear theorem', every simple polygon with more than three points must - // have 2 'ears'. Here's definitely something wrong ... but we don't give up yet. - // - - // Instead we're continuing with the standard tri-fanning algorithm which we'd - // use if we had only convex polygons. That's life. - ASSIMP_LOG_ERROR("Failed to triangulate polygon (no ear found). Probably not a simple polygon?"); - -#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS - fprintf(fout,"critical error here, no ear found! "); -#endif - num = 0; - break; - - curOut -= (max-num); /* undo all previous work */ - for (tmp = 0; tmp < max-2; ++tmp) { - aiFace& nface = *curOut++; - - nface.mNumIndices = 3; - if (!nface.mIndices) - nface.mIndices = new unsigned int[3]; - - nface.mIndices[0] = 0; - nface.mIndices[1] = tmp+1; - nface.mIndices[2] = tmp+2; - - } - num = 0; - break; - } - - aiFace& nface = *curOut++; - nface.mNumIndices = 3; - - if (!nface.mIndices) { - nface.mIndices = new unsigned int[3]; - } - - // setup indices for the new triangle ... - nface.mIndices[0] = prev; - nface.mIndices[1] = ear; - nface.mIndices[2] = next; - - // exclude the ear from most further processing - done[ear] = true; - --num; - } - if (num > 0) { - // We have three indices forming the last 'ear' remaining. Collect them. - aiFace& nface = *curOut++; - nface.mNumIndices = 3; - if (!nface.mIndices) { - nface.mIndices = new unsigned int[3]; - } - - for (tmp = 0; done[tmp]; ++tmp); - nface.mIndices[0] = tmp; - - for (++tmp; done[tmp]; ++tmp); - nface.mIndices[1] = tmp; - - for (++tmp; done[tmp]; ++tmp); - nface.mIndices[2] = tmp; - - } - } - -#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS - - for(aiFace* f = last_face; f != curOut; ++f) { - unsigned int* i = f->mIndices; - fprintf(fout," (%i %i %i)",i[0],i[1],i[2]); - } - - fprintf(fout,"\n*********************************************************************\n"); - fflush(fout); - -#endif - - for(aiFace* f = last_face; f != curOut; ) { - unsigned int* i = f->mIndices; - - // drop dumb 0-area triangles - deactivated for now: - //FindDegenerates post processing step can do the same thing - //if (std::fabs(GetArea2D(temp_verts[i[0]],temp_verts[i[1]],temp_verts[i[2]])) < 1e-5f) { - // ASSIMP_LOG_DEBUG("Dropping triangle with area 0"); - // --curOut; - - // delete[] f->mIndices; - // f->mIndices = nullptr; - - // for(aiFace* ff = f; ff != curOut; ++ff) { - // ff->mNumIndices = (ff+1)->mNumIndices; - // ff->mIndices = (ff+1)->mIndices; - // (ff+1)->mIndices = nullptr; - // } - // continue; - //} - - i[0] = idx[i[0]]; - i[1] = idx[i[1]]; - i[2] = idx[i[2]]; - ++f; - } - - delete[] face.mIndices; - face.mIndices = NULL; - } - -#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS - fclose(fout); -#endif - - // kill the old faces - delete [] pMesh->mFaces; - - // ... and store the new ones - pMesh->mFaces = out; - pMesh->mNumFaces = (unsigned int)(curOut-out); /* not necessarily equal to numOut */ - return true; -} - -#endif // !! ASSIMP_BUILD_NO_TRIANGULATE_PROCESS diff --git a/thirdparty/assimp/code/TriangulateProcess.h b/thirdparty/assimp/code/TriangulateProcess.h deleted file mode 100644 index 47bd2115ad..0000000000 --- a/thirdparty/assimp/code/TriangulateProcess.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a post processing step to triangulate all faces - with more than three vertices. - */ -#ifndef AI_TRIANGULATEPROCESS_H_INC -#define AI_TRIANGULATEPROCESS_H_INC - -#include "BaseProcess.h" - -struct aiMesh; - -class TriangulateProcessTest; - -namespace Assimp { - -// --------------------------------------------------------------------------- -/** The TriangulateProcess splits up all faces with more than three indices - * into triangles. You usually want this to happen because the graphics cards - * need their data as triangles. - */ -class ASSIMP_API TriangulateProcess : public BaseProcess -{ -public: - - TriangulateProcess(); - ~TriangulateProcess(); - -public: - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag field. - * @param pFlags The processing flags the importer was called with. A bitwise - * combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, false if not. - */ - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * At the moment a process is not supposed to fail. - * @param pScene The imported data to work at. - */ - void Execute( aiScene* pScene); - -public: - // ------------------------------------------------------------------- - /** Triangulates the given mesh. - * @param pMesh The mesh to triangulate. - */ - bool TriangulateMesh( aiMesh* pMesh); -}; - -} // end of namespace Assimp - -#endif // AI_TRIANGULATEPROCESS_H_INC diff --git a/thirdparty/assimp/code/ValidateDataStructure.cpp b/thirdparty/assimp/code/ValidateDataStructure.cpp deleted file mode 100644 index 657b0361b7..0000000000 --- a/thirdparty/assimp/code/ValidateDataStructure.cpp +++ /dev/null @@ -1,986 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file ValidateDataStructure.cpp - * @brief Implementation of the post processing step to validate - * the data structure returned by Assimp. - */ - - - -// internal headers -#include "ValidateDataStructure.h" -#include -#include -#include "ProcessHelper.h" -#include - -// CRT headers -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -ValidateDSProcess::ValidateDSProcess() : - mScene() -{} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -ValidateDSProcess::~ValidateDSProcess() -{} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool ValidateDSProcess::IsActive( unsigned int pFlags) const -{ - return (pFlags & aiProcess_ValidateDataStructure) != 0; -} -// ------------------------------------------------------------------------------------------------ -AI_WONT_RETURN void ValidateDSProcess::ReportError(const char* msg,...) -{ - ai_assert(NULL != msg); - - va_list args; - va_start(args,msg); - - char szBuffer[3000]; - const int iLen = vsprintf(szBuffer,msg,args); - ai_assert(iLen > 0); - - va_end(args); - - throw DeadlyImportError("Validation failed: " + std::string(szBuffer,iLen)); -} -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::ReportWarning(const char* msg,...) -{ - ai_assert(NULL != msg); - - va_list args; - va_start(args,msg); - - char szBuffer[3000]; - const int iLen = vsprintf(szBuffer,msg,args); - ai_assert(iLen > 0); - - va_end(args); - ASSIMP_LOG_WARN("Validation warning: " + std::string(szBuffer,iLen)); -} - -// ------------------------------------------------------------------------------------------------ -inline int HasNameMatch(const aiString& in, aiNode* node) -{ - int result = (node->mName == in ? 1 : 0 ); - for (unsigned int i = 0; i < node->mNumChildren;++i) { - result += HasNameMatch(in,node->mChildren[i]); - } - return result; -} - -// ------------------------------------------------------------------------------------------------ -template -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 -inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size, - const char* firstName, const char* secondName) -{ - // validate all entries - if (size) - { - if (!parray) { - ReportError("aiScene::%s is NULL (aiScene::%s is %i)", - firstName, secondName, size); - } - for (unsigned int i = 0; i < size;++i) - { - if (!parray[i]) - { - ReportError("aiScene::%s[%u] is NULL (aiScene::%s is %u)", - firstName,i,secondName,size); - } - Validate(parray[i]); - - // check whether there are duplicate names - for (unsigned int a = i+1; a < size;++a) - { - if (parray[i]->mName == parray[a]->mName) - { - ReportError("aiScene::%s[%u] has the same name as " - "aiScene::%s[%u]",firstName, i,secondName, a); - } - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -template -inline -void ValidateDSProcess::DoValidationWithNameCheck(T** array, unsigned int size, const char* firstName, const char* secondName) { - // validate all entries - DoValidationEx(array,size,firstName,secondName); - - for (unsigned int i = 0; i < size;++i) { - int res = HasNameMatch(array[i]->mName,mScene->mRootNode); - if (0 == res) { - const std::string name = static_cast(array[i]->mName.data); - ReportError("aiScene::%s[%i] has no corresponding node in the scene graph (%s)", - firstName,i, name.c_str()); - } else if (1 != res) { - const std::string name = static_cast(array[i]->mName.data); - ReportError("aiScene::%s[%i]: there are more than one nodes with %s as name", - firstName,i, name.c_str()); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void ValidateDSProcess::Execute( aiScene* pScene) -{ - this->mScene = pScene; - ASSIMP_LOG_DEBUG("ValidateDataStructureProcess begin"); - - // validate the node graph of the scene - Validate(pScene->mRootNode); - - // validate all meshes - if (pScene->mNumMeshes) { - DoValidation(pScene->mMeshes,pScene->mNumMeshes,"mMeshes","mNumMeshes"); - } - else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { - ReportError("aiScene::mNumMeshes is 0. At least one mesh must be there"); - } - else if (pScene->mMeshes) { - ReportError("aiScene::mMeshes is non-null although there are no meshes"); - } - - // validate all animations - if (pScene->mNumAnimations) { - DoValidation(pScene->mAnimations,pScene->mNumAnimations, - "mAnimations","mNumAnimations"); - } - else if (pScene->mAnimations) { - ReportError("aiScene::mAnimations is non-null although there are no animations"); - } - - // validate all cameras - if (pScene->mNumCameras) { - DoValidationWithNameCheck(pScene->mCameras,pScene->mNumCameras, - "mCameras","mNumCameras"); - } - else if (pScene->mCameras) { - ReportError("aiScene::mCameras is non-null although there are no cameras"); - } - - // validate all lights - if (pScene->mNumLights) { - DoValidationWithNameCheck(pScene->mLights,pScene->mNumLights, - "mLights","mNumLights"); - } - else if (pScene->mLights) { - ReportError("aiScene::mLights is non-null although there are no lights"); - } - - // validate all textures - if (pScene->mNumTextures) { - DoValidation(pScene->mTextures,pScene->mNumTextures, - "mTextures","mNumTextures"); - } - else if (pScene->mTextures) { - ReportError("aiScene::mTextures is non-null although there are no textures"); - } - - // validate all materials - if (pScene->mNumMaterials) { - DoValidation(pScene->mMaterials,pScene->mNumMaterials,"mMaterials","mNumMaterials"); - } -#if 0 - // NOTE: ScenePreprocessor generates a default material if none is there - else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { - ReportError("aiScene::mNumMaterials is 0. At least one material must be there"); - } -#endif - else if (pScene->mMaterials) { - ReportError("aiScene::mMaterials is non-null although there are no materials"); - } - -// if (!has)ReportError("The aiScene data structure is empty"); - ASSIMP_LOG_DEBUG("ValidateDataStructureProcess end"); -} - -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate( const aiLight* pLight) -{ - if (pLight->mType == aiLightSource_UNDEFINED) - ReportWarning("aiLight::mType is aiLightSource_UNDEFINED"); - - if (!pLight->mAttenuationConstant && - !pLight->mAttenuationLinear && - !pLight->mAttenuationQuadratic) { - ReportWarning("aiLight::mAttenuationXXX - all are zero"); - } - - if (pLight->mAngleInnerCone > pLight->mAngleOuterCone) - ReportError("aiLight::mAngleInnerCone is larger than aiLight::mAngleOuterCone"); - - if (pLight->mColorDiffuse.IsBlack() && pLight->mColorAmbient.IsBlack() - && pLight->mColorSpecular.IsBlack()) - { - ReportWarning("aiLight::mColorXXX - all are black and won't have any influence"); - } -} - -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate( const aiCamera* pCamera) -{ - if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear) - ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear"); - - // FIX: there are many 3ds files with invalid FOVs. No reason to - // reject them at all ... a warning is appropriate. - if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI) - ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV",pCamera->mHorizontalFOV); -} - -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate( const aiMesh* pMesh) -{ - // validate the material index of the mesh - if (mScene->mNumMaterials && pMesh->mMaterialIndex >= mScene->mNumMaterials) - { - ReportError("aiMesh::mMaterialIndex is invalid (value: %i maximum: %i)", - pMesh->mMaterialIndex,mScene->mNumMaterials-1); - } - - Validate(&pMesh->mName); - - for (unsigned int i = 0; i < pMesh->mNumFaces; ++i) - { - aiFace& face = pMesh->mFaces[i]; - - if (pMesh->mPrimitiveTypes) - { - switch (face.mNumIndices) - { - case 0: - ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i); - break; - case 1: - if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT)) - { - ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimitiveTypes " - "does not report the POINT flag",i); - } - break; - case 2: - if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_LINE)) - { - ReportError("aiMesh::mFaces[%i] is a LINE but aiMesh::mPrimitiveTypes " - "does not report the LINE flag",i); - } - break; - case 3: - if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE)) - { - ReportError("aiMesh::mFaces[%i] is a TRIANGLE but aiMesh::mPrimitiveTypes " - "does not report the TRIANGLE flag",i); - } - break; - default: - if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) - { - this->ReportError("aiMesh::mFaces[%i] is a POLYGON but aiMesh::mPrimitiveTypes " - "does not report the POLYGON flag",i); - } - break; - }; - } - - if (!face.mIndices) - ReportError("aiMesh::mFaces[%i].mIndices is NULL",i); - } - - // positions must always be there ... - if (!pMesh->mNumVertices || (!pMesh->mVertices && !mScene->mFlags)) { - ReportError("The mesh %s contains no vertices", pMesh->mName.C_Str()); - } - - if (pMesh->mNumVertices > AI_MAX_VERTICES) { - ReportError("Mesh has too many vertices: %u, but the limit is %u",pMesh->mNumVertices,AI_MAX_VERTICES); - } - if (pMesh->mNumFaces > AI_MAX_FACES) { - ReportError("Mesh has too many faces: %u, but the limit is %u",pMesh->mNumFaces,AI_MAX_FACES); - } - - // if tangents are there there must also be bitangent vectors ... - if ((pMesh->mTangents != NULL) != (pMesh->mBitangents != NULL)) { - ReportError("If there are tangents, bitangent vectors must be present as well"); - } - - // faces, too - if (!pMesh->mNumFaces || (!pMesh->mFaces && !mScene->mFlags)) { - ReportError("Mesh %s contains no faces", pMesh->mName.C_Str()); - } - - // now check whether the face indexing layout is correct: - // unique vertices, pseudo-indexed. - std::vector abRefList; - abRefList.resize(pMesh->mNumVertices,false); - for (unsigned int i = 0; i < pMesh->mNumFaces;++i) - { - aiFace& face = pMesh->mFaces[i]; - if (face.mNumIndices > AI_MAX_FACE_INDICES) { - ReportError("Face %u has too many faces: %u, but the limit is %u",i,face.mNumIndices,AI_MAX_FACE_INDICES); - } - - for (unsigned int a = 0; a < face.mNumIndices;++a) - { - if (face.mIndices[a] >= pMesh->mNumVertices) { - ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a); - } - // the MSB flag is temporarily used by the extra verbose - // mode to tell us that the JoinVerticesProcess might have - // been executed already. - /*if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && !(this->mScene->mFlags & AI_SCENE_FLAGS_ALLOW_SHARED) && - abRefList[face.mIndices[a]]) - { - ReportError("aiMesh::mVertices[%i] is referenced twice - second " - "time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a); - }*/ - abRefList[face.mIndices[a]] = true; - } - } - - // check whether there are vertices that aren't referenced by a face - bool b = false; - for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { - if (!abRefList[i])b = true; - } - abRefList.clear(); - if (b) { - ReportWarning("There are unreferenced vertices"); - } - - // texture channel 2 may not be set if channel 1 is zero ... - { - unsigned int i = 0; - for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) - { - if (!pMesh->HasTextureCoords(i))break; - } - for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) - if (pMesh->HasTextureCoords(i)) - { - ReportError("Texture coordinate channel %i exists " - "although the previous channel was NULL.",i); - } - } - // the same for the vertex colors - { - unsigned int i = 0; - for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i) - { - if (!pMesh->HasVertexColors(i))break; - } - for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i) - if (pMesh->HasVertexColors(i)) - { - ReportError("Vertex color channel %i is exists " - "although the previous channel was NULL.",i); - } - } - - - // now validate all bones - if (pMesh->mNumBones) - { - if (!pMesh->mBones) - { - ReportError("aiMesh::mBones is NULL (aiMesh::mNumBones is %i)", - pMesh->mNumBones); - } - std::unique_ptr afSum(nullptr); - if (pMesh->mNumVertices) - { - afSum.reset(new float[pMesh->mNumVertices]); - for (unsigned int i = 0; i < pMesh->mNumVertices;++i) - afSum[i] = 0.0f; - } - - // check whether there are duplicate bone names - for (unsigned int i = 0; i < pMesh->mNumBones;++i) - { - const aiBone* bone = pMesh->mBones[i]; - if (bone->mNumWeights > AI_MAX_BONE_WEIGHTS) { - ReportError("Bone %u has too many weights: %u, but the limit is %u",i,bone->mNumWeights,AI_MAX_BONE_WEIGHTS); - } - - if (!pMesh->mBones[i]) - { - ReportError("aiMesh::mBones[%i] is NULL (aiMesh::mNumBones is %i)", - i,pMesh->mNumBones); - } - Validate(pMesh,pMesh->mBones[i],afSum.get()); - - for (unsigned int a = i+1; a < pMesh->mNumBones;++a) - { - if (pMesh->mBones[i]->mName == pMesh->mBones[a]->mName) - { - const char *name = "unknown"; - if (nullptr != pMesh->mBones[ i ]->mName.C_Str()) { - name = pMesh->mBones[ i ]->mName.C_Str(); - } - ReportError("aiMesh::mBones[%i], name = \"%s\" has the same name as " - "aiMesh::mBones[%i]", i, name, a ); - } - } - } - // check whether all bone weights for a vertex sum to 1.0 ... - for (unsigned int i = 0; i < pMesh->mNumVertices;++i) - { - if (afSum[i] && (afSum[i] <= 0.94 || afSum[i] >= 1.05)) { - ReportWarning("aiMesh::mVertices[%i]: bone weight sum != 1.0 (sum is %f)",i,afSum[i]); - } - } - } - else if (pMesh->mBones) - { - ReportError("aiMesh::mBones is non-null although there are no bones"); - } -} - -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate( const aiMesh* pMesh, - const aiBone* pBone,float* afSum) -{ - this->Validate(&pBone->mName); - - if (!pBone->mNumWeights) { - ReportError("aiBone::mNumWeights is zero"); - } - - // check whether all vertices affected by this bone are valid - for (unsigned int i = 0; i < pBone->mNumWeights;++i) - { - if (pBone->mWeights[i].mVertexId >= pMesh->mNumVertices) { - ReportError("aiBone::mWeights[%i].mVertexId is out of range",i); - } - else if (!pBone->mWeights[i].mWeight || pBone->mWeights[i].mWeight > 1.0f) { - ReportWarning("aiBone::mWeights[%i].mWeight has an invalid value",i); - } - afSum[pBone->mWeights[i].mVertexId] += pBone->mWeights[i].mWeight; - } -} - -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate( const aiAnimation* pAnimation) -{ - Validate(&pAnimation->mName); - - // validate all materials - if (pAnimation->mNumChannels) - { - if (!pAnimation->mChannels) { - ReportError("aiAnimation::mChannels is NULL (aiAnimation::mNumChannels is %i)", - pAnimation->mNumChannels); - } - for (unsigned int i = 0; i < pAnimation->mNumChannels;++i) - { - if (!pAnimation->mChannels[i]) - { - ReportError("aiAnimation::mChannels[%i] is NULL (aiAnimation::mNumChannels is %i)", - i, pAnimation->mNumChannels); - } - Validate(pAnimation, pAnimation->mChannels[i]); - } - } - else { - ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there."); - } - - // Animation duration is allowed to be zero in cases where the anim contains only a single key frame. - // if (!pAnimation->mDuration)this->ReportError("aiAnimation::mDuration is zero"); -} - -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial, - aiTextureType type) -{ - const char* szType = TextureTypeToString(type); - - // **************************************************************************** - // Search all keys of the material ... - // textures must be specified with ascending indices - // (e.g. diffuse #2 may not be specified if diffuse #1 is not there ...) - // **************************************************************************** - - int iNumIndices = 0; - int iIndex = -1; - for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) { - aiMaterialProperty* prop = pMaterial->mProperties[ i ]; - ai_assert(nullptr != prop); - if ( !::strcmp(prop->mKey.data,"$tex.file") && prop->mSemantic == static_cast(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 mappings(iNumIndices); - - // Now check whether all UV indices are valid ... - bool bNoSpecified = true; - for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) - { - aiMaterialProperty* prop = pMaterial->mProperties[i]; - if (prop->mSemantic != type)continue; - - if ((int)prop->mIndex >= iNumIndices) - { - ReportError("Found texture property with index %i, although there " - "are only %i textures of type %s", - prop->mIndex, iNumIndices, szType); - } - - if (!::strcmp(prop->mKey.data,"$tex.mapping")) { - if (aiPTI_Integer != prop->mType || prop->mDataLength < sizeof(aiTextureMapping)) - { - ReportError("Material property %s%i is expected to be an integer (size is %i)", - prop->mKey.data,prop->mIndex,prop->mDataLength); - } - mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData); - } - else if (!::strcmp(prop->mKey.data,"$tex.uvtrafo")) { - if (aiPTI_Float != prop->mType || prop->mDataLength < sizeof(aiUVTransform)) - { - ReportError("Material property %s%i is expected to be 5 floats large (size is %i)", - prop->mKey.data,prop->mIndex, prop->mDataLength); - } - mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData); - } - else if (!::strcmp(prop->mKey.data,"$tex.uvwsrc")) { - if (aiPTI_Integer != prop->mType || sizeof(int) > prop->mDataLength) - { - ReportError("Material property %s%i is expected to be an integer (size is %i)", - prop->mKey.data,prop->mIndex,prop->mDataLength); - } - bNoSpecified = false; - - // Ignore UV indices for texture channels that are not there ... - - // Get the value - iIndex = *((unsigned int*)prop->mData); - - // Check whether there is a mesh using this material - // which has not enough UV channels ... - for (unsigned int a = 0; a < mScene->mNumMeshes;++a) - { - aiMesh* mesh = this->mScene->mMeshes[a]; - if(mesh->mMaterialIndex == (unsigned int)i) - { - int iChannels = 0; - while (mesh->HasTextureCoords(iChannels))++iChannels; - if (iIndex >= iChannels) - { - ReportWarning("Invalid UV index: %i (key %s). Mesh %i has only %i UV channels", - iIndex,prop->mKey.data,a,iChannels); - } - } - } - } - } - if (bNoSpecified) - { - // Assume that all textures are using the first UV channel - for (unsigned int a = 0; a < mScene->mNumMeshes;++a) - { - aiMesh* mesh = mScene->mMeshes[a]; - if(mesh->mMaterialIndex == (unsigned int)iIndex && mappings[0] == aiTextureMapping_UV) - { - if (!mesh->mTextureCoords[0]) - { - // This is a special case ... it could be that the - // original mesh format intended the use of a special - // mapping here. - ReportWarning("UV-mapped texture, but there are no UV coords"); - } - } - } - } -} -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate( const aiMaterial* pMaterial) -{ - // check whether there are material keys that are obviously not legal - for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) - { - const aiMaterialProperty* prop = pMaterial->mProperties[i]; - if (!prop) { - ReportError("aiMaterial::mProperties[%i] is NULL (aiMaterial::mNumProperties is %i)", - i,pMaterial->mNumProperties); - } - if (!prop->mDataLength || !prop->mData) { - ReportError("aiMaterial::mProperties[%i].mDataLength or " - "aiMaterial::mProperties[%i].mData is 0",i,i); - } - // check all predefined types - if (aiPTI_String == prop->mType) { - // FIX: strings are now stored in a less expensive way, but we can't use the - // validation routine for 'normal' aiStrings - if (prop->mDataLength < 5 || prop->mDataLength < 4 + (*reinterpret_cast(prop->mData)) + 1) { - ReportError("aiMaterial::mProperties[%i].mDataLength is " - "too small to contain a string (%i, needed: %i)", - i,prop->mDataLength,static_cast(sizeof(aiString))); - } - if(prop->mData[prop->mDataLength-1]) { - ReportError("Missing null-terminator in string material property"); - } - // Validate((const aiString*)prop->mData); - } - else if (aiPTI_Float == prop->mType) { - if (prop->mDataLength < sizeof(float)) { - ReportError("aiMaterial::mProperties[%i].mDataLength is " - "too small to contain a float (%i, needed: %i)", - i,prop->mDataLength, static_cast(sizeof(float))); - } - } - else if (aiPTI_Integer == prop->mType) { - if (prop->mDataLength < sizeof(int)) { - ReportError("aiMaterial::mProperties[%i].mDataLength is " - "too small to contain an integer (%i, needed: %i)", - i,prop->mDataLength, static_cast(sizeof(int))); - } - } - // TODO: check whether there is a key with an unknown name ... - } - - // make some more specific tests - ai_real fTemp; - int iShading; - if (AI_SUCCESS == aiGetMaterialInteger( pMaterial,AI_MATKEY_SHADING_MODEL,&iShading)) { - switch ((aiShadingMode)iShading) - { - case aiShadingMode_Blinn: - case aiShadingMode_CookTorrance: - case aiShadingMode_Phong: - - if (AI_SUCCESS != aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS,&fTemp)) { - ReportWarning("A specular shading model is specified but there is no " - "AI_MATKEY_SHININESS key"); - } - if (AI_SUCCESS == aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS_STRENGTH,&fTemp) && !fTemp) { - ReportWarning("A specular shading model is specified but the value of the " - "AI_MATKEY_SHININESS_STRENGTH key is 0.0"); - } - break; - default: ; - }; - } - - if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01)) { - ReportWarning("Invalid opacity value (must be 0 < opacity < 1.0)"); - } - - // Check whether there are invalid texture keys - // TODO: that's a relict of the past, where texture type and index were baked - // into the material string ... we could do that in one single pass. - SearchForInvalidTextures(pMaterial,aiTextureType_DIFFUSE); - SearchForInvalidTextures(pMaterial,aiTextureType_SPECULAR); - SearchForInvalidTextures(pMaterial,aiTextureType_AMBIENT); - SearchForInvalidTextures(pMaterial,aiTextureType_EMISSIVE); - SearchForInvalidTextures(pMaterial,aiTextureType_OPACITY); - SearchForInvalidTextures(pMaterial,aiTextureType_SHININESS); - SearchForInvalidTextures(pMaterial,aiTextureType_HEIGHT); - SearchForInvalidTextures(pMaterial,aiTextureType_NORMALS); - SearchForInvalidTextures(pMaterial,aiTextureType_DISPLACEMENT); - SearchForInvalidTextures(pMaterial,aiTextureType_LIGHTMAP); - SearchForInvalidTextures(pMaterial,aiTextureType_REFLECTION); -} - -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate( const aiTexture* pTexture) -{ - // the data section may NEVER be NULL - if (!pTexture->pcData) { - ReportError("aiTexture::pcData is NULL"); - } - if (pTexture->mHeight) - { - if (!pTexture->mWidth){ - ReportError("aiTexture::mWidth is zero (aiTexture::mHeight is %i, uncompressed texture)", - pTexture->mHeight); - } - } - else - { - if (!pTexture->mWidth) { - ReportError("aiTexture::mWidth is zero (compressed texture)"); - } - if ('\0' != pTexture->achFormatHint[3]) { - ReportWarning("aiTexture::achFormatHint must be zero-terminated"); - } - else if ('.' == pTexture->achFormatHint[0]) { - ReportWarning("aiTexture::achFormatHint should contain a file extension " - "without a leading dot (format hint: %s).",pTexture->achFormatHint); - } - } - - const char* sz = pTexture->achFormatHint; - if ((sz[0] >= 'A' && sz[0] <= 'Z') || - (sz[1] >= 'A' && sz[1] <= 'Z') || - (sz[2] >= 'A' && sz[2] <= 'Z') || - (sz[3] >= 'A' && sz[3] <= 'Z')) { - ReportError("aiTexture::achFormatHint contains non-lowercase letters"); - } -} - -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate( const aiAnimation* pAnimation, - const aiNodeAnim* pNodeAnim) -{ - Validate(&pNodeAnim->mNodeName); - - if (!pNodeAnim->mNumPositionKeys && !pNodeAnim->mScalingKeys && !pNodeAnim->mNumRotationKeys) { - ReportError("Empty node animation channel"); - } - // otherwise check whether one of the keys exceeds the total duration of the animation - if (pNodeAnim->mNumPositionKeys) - { - if (!pNodeAnim->mPositionKeys) - { - ReportError("aiNodeAnim::mPositionKeys is NULL (aiNodeAnim::mNumPositionKeys is %i)", - pNodeAnim->mNumPositionKeys); - } - double dLast = -10e10; - for (unsigned int i = 0; i < pNodeAnim->mNumPositionKeys;++i) - { - // ScenePreprocessor will compute the duration if still the default value - // (Aramis) Add small epsilon, comparison tended to fail if max_time == duration, - // seems to be due the compilers register usage/width. - if (pAnimation->mDuration > 0. && pNodeAnim->mPositionKeys[i].mTime > pAnimation->mDuration+0.001) - { - ReportError("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is larger " - "than aiAnimation::mDuration (which is %.5f)",i, - (float)pNodeAnim->mPositionKeys[i].mTime, - (float)pAnimation->mDuration); - } - if (i && pNodeAnim->mPositionKeys[i].mTime <= dLast) - { - ReportWarning("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is smaller " - "than aiAnimation::mPositionKeys[%i] (which is %.5f)",i, - (float)pNodeAnim->mPositionKeys[i].mTime, - i-1, (float)dLast); - } - dLast = pNodeAnim->mPositionKeys[i].mTime; - } - } - // rotation keys - if (pNodeAnim->mNumRotationKeys) - { - if (!pNodeAnim->mRotationKeys) - { - ReportError("aiNodeAnim::mRotationKeys is NULL (aiNodeAnim::mNumRotationKeys is %i)", - pNodeAnim->mNumRotationKeys); - } - double dLast = -10e10; - for (unsigned int i = 0; i < pNodeAnim->mNumRotationKeys;++i) - { - if (pAnimation->mDuration > 0. && pNodeAnim->mRotationKeys[i].mTime > pAnimation->mDuration+0.001) - { - ReportError("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is larger " - "than aiAnimation::mDuration (which is %.5f)",i, - (float)pNodeAnim->mRotationKeys[i].mTime, - (float)pAnimation->mDuration); - } - if (i && pNodeAnim->mRotationKeys[i].mTime <= dLast) - { - ReportWarning("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is smaller " - "than aiAnimation::mRotationKeys[%i] (which is %.5f)",i, - (float)pNodeAnim->mRotationKeys[i].mTime, - i-1, (float)dLast); - } - dLast = pNodeAnim->mRotationKeys[i].mTime; - } - } - // scaling keys - if (pNodeAnim->mNumScalingKeys) - { - if (!pNodeAnim->mScalingKeys) { - ReportError("aiNodeAnim::mScalingKeys is NULL (aiNodeAnim::mNumScalingKeys is %i)", - pNodeAnim->mNumScalingKeys); - } - double dLast = -10e10; - for (unsigned int i = 0; i < pNodeAnim->mNumScalingKeys;++i) - { - if (pAnimation->mDuration > 0. && pNodeAnim->mScalingKeys[i].mTime > pAnimation->mDuration+0.001) - { - ReportError("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is larger " - "than aiAnimation::mDuration (which is %.5f)",i, - (float)pNodeAnim->mScalingKeys[i].mTime, - (float)pAnimation->mDuration); - } - if (i && pNodeAnim->mScalingKeys[i].mTime <= dLast) - { - ReportWarning("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is smaller " - "than aiAnimation::mScalingKeys[%i] (which is %.5f)",i, - (float)pNodeAnim->mScalingKeys[i].mTime, - i-1, (float)dLast); - } - dLast = pNodeAnim->mScalingKeys[i].mTime; - } - } - - if (!pNodeAnim->mNumScalingKeys && !pNodeAnim->mNumRotationKeys && - !pNodeAnim->mNumPositionKeys) - { - ReportError("A node animation channel must have at least one subtrack"); - } -} - -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate( const aiNode* pNode) -{ - if (!pNode) { - ReportError("A node of the scenegraph is NULL"); - } - // Validate node name string first so that it's safe to use in below expressions - this->Validate(&pNode->mName); - const char* nodeName = (&pNode->mName)->C_Str(); - if (pNode != mScene->mRootNode && !pNode->mParent){ - ReportError("Non-root node %s lacks a valid parent (aiNode::mParent is NULL) ", nodeName); - } - - // validate all meshes - if (pNode->mNumMeshes) - { - if (!pNode->mMeshes) - { - ReportError("aiNode::mMeshes is NULL for node %s (aiNode::mNumMeshes is %i)", - nodeName, pNode->mNumMeshes); - } - std::vector abHadMesh; - abHadMesh.resize(mScene->mNumMeshes,false); - for (unsigned int i = 0; i < pNode->mNumMeshes;++i) - { - if (pNode->mMeshes[i] >= mScene->mNumMeshes) - { - ReportError("aiNode::mMeshes[%i] is out of range for node %s (maximum is %i)", - pNode->mMeshes[i], nodeName, mScene->mNumMeshes-1); - } - if (abHadMesh[pNode->mMeshes[i]]) - { - ReportError("aiNode::mMeshes[%i] is already referenced by this node %s (value: %i)", - i, nodeName, pNode->mMeshes[i]); - } - abHadMesh[pNode->mMeshes[i]] = true; - } - } - if (pNode->mNumChildren) - { - if (!pNode->mChildren) { - ReportError("aiNode::mChildren is NULL for node %s (aiNode::mNumChildren is %i)", - nodeName, pNode->mNumChildren); - } - for (unsigned int i = 0; i < pNode->mNumChildren;++i) { - Validate(pNode->mChildren[i]); - } - } -} - -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate( const aiString* pString) -{ - if (pString->length > MAXLEN) - { - ReportError("aiString::length is too large (%lu, maximum is %lu)", - pString->length,MAXLEN); - } - const char* sz = pString->data; - while (true) - { - if ('\0' == *sz) - { - if (pString->length != (unsigned int)(sz-pString->data)) { - ReportError("aiString::data is invalid: the terminal zero is at a wrong offset"); - } - break; - } - else if (sz >= &pString->data[MAXLEN]) { - ReportError("aiString::data is invalid. There is no terminal character"); - } - ++sz; - } -} diff --git a/thirdparty/assimp/code/ValidateDataStructure.h b/thirdparty/assimp/code/ValidateDataStructure.h deleted file mode 100644 index bd21e88545..0000000000 --- a/thirdparty/assimp/code/ValidateDataStructure.h +++ /dev/null @@ -1,188 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a (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 -#include "BaseProcess.h" - -struct aiBone; -struct aiMesh; -struct aiAnimation; -struct aiNodeAnim; -struct aiTexture; -struct aiMaterial; -struct aiNode; -struct aiString; -struct aiCamera; -struct aiLight; - -namespace Assimp { - -// -------------------------------------------------------------------------------------- -/** Validates the whole ASSIMP scene data structure for correctness. - * ImportErrorException is thrown of the scene is corrupt.*/ -// -------------------------------------------------------------------------------------- -class ValidateDSProcess : public BaseProcess -{ -public: - - ValidateDSProcess(); - ~ValidateDSProcess(); - -public: - // ------------------------------------------------------------------- - bool IsActive( unsigned int pFlags) const; - - // ------------------------------------------------------------------- - void Execute( aiScene* pScene); - -protected: - - // ------------------------------------------------------------------- - /** Report a validation error. This will throw an exception, - * control won't return. - * @param msg Format string for sprintf().*/ - AI_WONT_RETURN void ReportError(const char* msg,...) AI_WONT_RETURN_SUFFIX; - - - // ------------------------------------------------------------------- - /** Report a validation warning. This won't throw an exception, - * control will return to the caller. - * @param msg Format string for sprintf().*/ - void ReportWarning(const char* msg,...); - - - // ------------------------------------------------------------------- - /** Validates a mesh - * @param pMesh Input mesh*/ - void Validate( const aiMesh* pMesh); - - // ------------------------------------------------------------------- - /** Validates a bone - * @param pMesh Input mesh - * @param pBone Input bone*/ - void Validate( const aiMesh* pMesh,const aiBone* pBone,float* afSum); - - // ------------------------------------------------------------------- - /** Validates an animation - * @param pAnimation Input animation*/ - void Validate( const aiAnimation* pAnimation); - - // ------------------------------------------------------------------- - /** Validates a material - * @param pMaterial Input material*/ - void Validate( const aiMaterial* pMaterial); - - // ------------------------------------------------------------------- - /** Search the material data structure for invalid or corrupt - * texture keys. - * @param pMaterial Input material - * @param type Type of the texture*/ - void SearchForInvalidTextures(const aiMaterial* pMaterial, - aiTextureType type); - - // ------------------------------------------------------------------- - /** Validates a texture - * @param pTexture Input texture*/ - void Validate( const aiTexture* pTexture); - - // ------------------------------------------------------------------- - /** Validates a light source - * @param pLight Input light - */ - void Validate( const aiLight* pLight); - - // ------------------------------------------------------------------- - /** Validates a camera - * @param pCamera Input camera*/ - void Validate( const aiCamera* pCamera); - - // ------------------------------------------------------------------- - /** Validates a bone animation channel - * @param pAnimation Animation channel. - * @param pBoneAnim Input bone animation */ - void Validate( const aiAnimation* pAnimation, - const aiNodeAnim* pBoneAnim); - - // ------------------------------------------------------------------- - /** Validates a node and all of its subnodes - * @param Node Input node*/ - void Validate( const aiNode* pNode); - - // ------------------------------------------------------------------- - /** Validates a string - * @param pString Input string*/ - void Validate( const aiString* pString); - -private: - - // template to validate one of the aiScene::mXXX arrays - template - inline void DoValidation(T** array, unsigned int size, - const char* firstName, const char* secondName); - - // extended version: checks whether T::mName occurs twice - template - 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 - inline void DoValidationWithNameCheck(T** array, unsigned int size, - const char* firstName, const char* secondName); - - aiScene* mScene; -}; - - - - -} // end of namespace Assimp - -#endif // AI_VALIDATEPROCESS_H_INC diff --git a/thirdparty/assimp/code/Version.cpp b/thirdparty/assimp/code/Version.cpp deleted file mode 100644 index 0381037ff1..0000000000 --- a/thirdparty/assimp/code/Version.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -// Actually just a dummy, used by the compiler to build the precompiled header. - -#include -#include -#include "ScenePrivate.h" - -static const unsigned int MajorVersion = 4; -static const unsigned int MinorVersion = 1; - -// -------------------------------------------------------------------------------- -// Legal information string - don't remove this. -static const char* LEGAL_INFORMATION = - -"Open Asset Import Library (Assimp).\n" -"A free C/C++ library to import various 3D file formats into applications\n\n" - -"(c) 2008-2017, assimp team\n" -"License under the terms and conditions of the 3-clause BSD license\n" -"http://assimp.sourceforge.net\n" -; - -// ------------------------------------------------------------------------------------------------ -// Get legal string -ASSIMP_API const char* aiGetLegalString () { - return LEGAL_INFORMATION; -} - -// ------------------------------------------------------------------------------------------------ -// Get Assimp minor version -ASSIMP_API unsigned int aiGetVersionMinor () { - return MinorVersion; -} - -// ------------------------------------------------------------------------------------------------ -// Get Assimp major version -ASSIMP_API unsigned int aiGetVersionMajor () { - return MajorVersion; -} - -// ------------------------------------------------------------------------------------------------ -// Get flags used for compilation -ASSIMP_API unsigned int aiGetCompileFlags () { - - unsigned int flags = 0; - -#ifdef ASSIMP_BUILD_BOOST_WORKAROUND - flags |= ASSIMP_CFLAGS_NOBOOST; -#endif -#ifdef ASSIMP_BUILD_SINGLETHREADED - flags |= ASSIMP_CFLAGS_SINGLETHREADED; -#endif -#ifdef ASSIMP_BUILD_DEBUG - flags |= ASSIMP_CFLAGS_DEBUG; -#endif -#ifdef ASSIMP_BUILD_DLL_EXPORT - flags |= ASSIMP_CFLAGS_SHARED; -#endif -#ifdef _STLPORT_VERSION - flags |= ASSIMP_CFLAGS_STLPORT; -#endif - - return flags; -} - -// include current build revision, which is even updated from time to time -- :-) -#include "revision.h" - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API unsigned int aiGetVersionRevision() { - return GitVersion; -} - -ASSIMP_API const char *aiGetBranchName() { - return GitBranch; -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API aiScene::aiScene() -: mFlags(0) -, mRootNode(nullptr) -, mNumMeshes(0) -, mMeshes(nullptr) -, mNumMaterials(0) -, mMaterials(nullptr) -, mNumAnimations(0) -, mAnimations(nullptr) -, mNumTextures(0) -, mTextures(nullptr) -, mNumLights(0) -, mLights(nullptr) -, mNumCameras(0) -, mCameras(nullptr) -, mMetaData(nullptr) -, mPrivate(new Assimp::ScenePrivateData()) { - // empty -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API aiScene::~aiScene() { - // delete all sub-objects recursively - delete mRootNode; - - // To make sure we won't crash if the data is invalid it's - // much better to check whether both mNumXXX and mXXX are - // valid instead of relying on just one of them. - if (mNumMeshes && mMeshes) - for( unsigned int a = 0; a < mNumMeshes; a++) - delete mMeshes[a]; - delete [] mMeshes; - - if (mNumMaterials && mMaterials) { - for (unsigned int a = 0; a < mNumMaterials; ++a ) { - delete mMaterials[ a ]; - } - } - delete [] mMaterials; - - if (mNumAnimations && mAnimations) - for( unsigned int a = 0; a < mNumAnimations; a++) - delete mAnimations[a]; - delete [] mAnimations; - - if (mNumTextures && mTextures) - for( unsigned int a = 0; a < mNumTextures; a++) - delete mTextures[a]; - delete [] mTextures; - - if (mNumLights && mLights) - for( unsigned int a = 0; a < mNumLights; a++) - delete mLights[a]; - delete [] mLights; - - if (mNumCameras && mCameras) - for( unsigned int a = 0; a < mNumCameras; a++) - delete mCameras[a]; - delete [] mCameras; - - aiMetadata::Dealloc(mMetaData); - mMetaData = nullptr; - - delete static_cast( mPrivate ); -} - diff --git a/thirdparty/assimp/code/VertexTriangleAdjacency.cpp b/thirdparty/assimp/code/VertexTriangleAdjacency.cpp deleted file mode 100644 index 7cfd1a3505..0000000000 --- a/thirdparty/assimp/code/VertexTriangleAdjacency.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of the VertexTriangleAdjacency helper class - */ - -// internal headers -#include "VertexTriangleAdjacency.h" -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces, - unsigned int iNumFaces, - unsigned int iNumVertices /*= 0*/, - bool bComputeNumTriangles /*= false*/) -{ - // compute the number of referenced vertices if it wasn't specified by the caller - const aiFace* const pcFaceEnd = pcFaces + iNumFaces; - if (!iNumVertices) { - for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) { - ai_assert( nullptr != pcFace ); - ai_assert(3 == pcFace->mNumIndices); - iNumVertices = std::max(iNumVertices,pcFace->mIndices[0]); - iNumVertices = std::max(iNumVertices,pcFace->mIndices[1]); - iNumVertices = std::max(iNumVertices,pcFace->mIndices[2]); - } - } - - mNumVertices = iNumVertices; - - unsigned int* pi; - - // allocate storage - if (bComputeNumTriangles) { - pi = mLiveTriangles = new unsigned int[iNumVertices+1]; - ::memset(mLiveTriangles,0,sizeof(unsigned int)*(iNumVertices+1)); - mOffsetTable = new unsigned int[iNumVertices+2]+1; - } else { - pi = mOffsetTable = new unsigned int[iNumVertices+2]+1; - ::memset(mOffsetTable,0,sizeof(unsigned int)*(iNumVertices+1)); - mLiveTriangles = NULL; // important, otherwise the d'tor would crash - } - - // get a pointer to the end of the buffer - unsigned int* piEnd = pi+iNumVertices; - *piEnd++ = 0u; - - // first pass: compute the number of faces referencing each vertex - for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) - { - unsigned nind = pcFace->mNumIndices; - unsigned * ind = pcFace->mIndices; - if (nind > 0) pi[ind[0]]++; - if (nind > 1) pi[ind[1]]++; - if (nind > 2) pi[ind[2]]++; - } - - // second pass: compute the final offset table - unsigned int iSum = 0; - unsigned int* piCurOut = this->mOffsetTable; - for (unsigned int* piCur = pi; piCur != piEnd;++piCur,++piCurOut) { - - unsigned int iLastSum = iSum; - iSum += *piCur; - *piCurOut = iLastSum; - } - pi = this->mOffsetTable; - - // third pass: compute the final table - this->mAdjacencyTable = new unsigned int[iSum]; - iSum = 0; - for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace,++iSum) { - unsigned nind = pcFace->mNumIndices; - unsigned * ind = pcFace->mIndices; - - if (nind > 0) mAdjacencyTable[pi[ind[0]]++] = iSum; - if (nind > 1) mAdjacencyTable[pi[ind[1]]++] = iSum; - if (nind > 2) mAdjacencyTable[pi[ind[2]]++] = iSum; - } - // fourth pass: undo the offset computations made during the third pass - // We could do this in a separate buffer, but this would be TIMES slower. - --mOffsetTable; - *mOffsetTable = 0u; -} -// ------------------------------------------------------------------------------------------------ -VertexTriangleAdjacency::~VertexTriangleAdjacency() -{ - // delete allocated storage - delete[] mOffsetTable; - delete[] mAdjacencyTable; - delete[] mLiveTriangles; -} diff --git a/thirdparty/assimp/code/VertexTriangleAdjacency.h b/thirdparty/assimp/code/VertexTriangleAdjacency.h deleted file mode 100644 index f3be47612d..0000000000 --- a/thirdparty/assimp/code/VertexTriangleAdjacency.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines a helper class to compute a vertex-triangle adjacency map */ -#ifndef AI_VTADJACENCY_H_INC -#define AI_VTADJACENCY_H_INC - -#include "BaseProcess.h" -#include -#include - -struct aiMesh; -struct aiFace; - -namespace Assimp { - -// -------------------------------------------------------------------------------------------- -/** @brief The VertexTriangleAdjacency class computes a vertex-triangle - * adjacency map from a given index buffer. - * - * @note Although it is called #VertexTriangleAdjacency, the current version does also - * support arbitrary polygons. */ -// -------------------------------------------------------------------------------------------- -class ASSIMP_API VertexTriangleAdjacency { -public: - // ---------------------------------------------------------------------------- - /** @brief Construction from an existing index buffer - * @param pcFaces Index buffer - * @param iNumFaces Number of faces in the buffer - * @param iNumVertices Number of referenced vertices. This value - * is computed automatically if 0 is specified. - * @param bComputeNumTriangles If you want the class to compute - * a list containing the number of referenced triangles per vertex - * per vertex - pass true. */ - VertexTriangleAdjacency(aiFace* pcFaces,unsigned int iNumFaces, - unsigned int iNumVertices = 0, - bool bComputeNumTriangles = true); - - // ---------------------------------------------------------------------------- - /** @brief Destructor */ - ~VertexTriangleAdjacency(); - - // ---------------------------------------------------------------------------- - /** @brief Get all triangles adjacent to a vertex - * @param iVertIndex Index of the vertex - * @return A pointer to the adjacency list. */ - unsigned int* GetAdjacentTriangles(unsigned int iVertIndex) const { - ai_assert(iVertIndex < mNumVertices); - return &mAdjacencyTable[ mOffsetTable[iVertIndex]]; - } - - // ---------------------------------------------------------------------------- - /** @brief Get the number of triangles that are referenced by - * a vertex. This function returns a reference that can be modified - * @param iVertIndex Index of the vertex - * @return Number of referenced triangles */ - unsigned int& GetNumTrianglesPtr(unsigned int iVertIndex) { - ai_assert( iVertIndex < mNumVertices ); - ai_assert( nullptr != mLiveTriangles ); - return mLiveTriangles[iVertIndex]; - } - - //! Offset table - unsigned int* mOffsetTable; - - //! Adjacency table - unsigned int* mAdjacencyTable; - - //! Table containing the number of referenced triangles per vertex - unsigned int* mLiveTriangles; - - //! Debug: Number of referenced vertices - unsigned int mNumVertices; -}; - -} //! ns Assimp - -#endif // !! AI_VTADJACENCY_H_INC diff --git a/thirdparty/assimp/code/Win32DebugLogStream.h b/thirdparty/assimp/code/Win32DebugLogStream.h deleted file mode 100644 index a6063a261e..0000000000 --- a/thirdparty/assimp/code/Win32DebugLogStream.h +++ /dev/null @@ -1,95 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Win32DebugLogStream.h -* @brief Implementation of Win32DebugLogStream -*/ -#ifndef AI_WIN32DEBUGLOGSTREAM_H_INC -#define AI_WIN32DEBUGLOGSTREAM_H_INC - -#ifdef _WIN32 - -#include -#include "windows.h" - -namespace Assimp { - -// --------------------------------------------------------------------------- -/** @class Win32DebugLogStream - * @brief Logs into the debug stream from win32. - */ -class Win32DebugLogStream : public LogStream { -public: - /** @brief Default constructor */ - Win32DebugLogStream(); - - /** @brief Destructor */ - ~Win32DebugLogStream(); - - /** @brief Writer */ - void write(const char* messgae); -}; - -// --------------------------------------------------------------------------- -inline -Win32DebugLogStream::Win32DebugLogStream(){ - // empty -} - -// --------------------------------------------------------------------------- -inline -Win32DebugLogStream::~Win32DebugLogStream(){ - // empty -} - -// --------------------------------------------------------------------------- -inline -void Win32DebugLogStream::write(const char* message) { - ::OutputDebugStringA( message); -} - -// --------------------------------------------------------------------------- -} // Namespace Assimp - -#endif // ! _WIN32 -#endif // guard diff --git a/thirdparty/assimp/code/scene.cpp b/thirdparty/assimp/code/scene.cpp deleted file mode 100644 index 2acb348d81..0000000000 --- a/thirdparty/assimp/code/scene.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ -#include - -aiNode::aiNode() -: mName("") -, mParent(NULL) -, mNumChildren(0) -, mChildren(NULL) -, mNumMeshes(0) -, mMeshes(NULL) -, mMetaData(NULL) { - // empty -} - -aiNode::aiNode(const std::string& name) -: mName(name) -, mParent(NULL) -, mNumChildren(0) -, mChildren(NULL) -, mNumMeshes(0) -, mMeshes(NULL) -, mMetaData(NULL) { - // empty -} - -/** Destructor */ -aiNode::~aiNode() { - // delete all children recursively - // to make sure we won't crash if the data is invalid ... - if (mChildren && mNumChildren) - { - for (unsigned int a = 0; a < mNumChildren; a++) - delete mChildren[a]; - } - delete[] mChildren; - delete[] mMeshes; - delete mMetaData; -} - -const aiNode *aiNode::FindNode(const char* name) const { - if (nullptr == name) { - return nullptr; - } - if (!::strcmp(mName.data, name)) { - return this; - } - for (unsigned int i = 0; i < mNumChildren; ++i) { - const aiNode* const p = mChildren[i]->FindNode(name); - if (p) { - return p; - } - } - // there is definitely no sub-node with this name - return nullptr; -} - -aiNode *aiNode::FindNode(const char* name) { - if (!::strcmp(mName.data, name))return this; - for (unsigned int i = 0; i < mNumChildren; ++i) - { - aiNode* const p = mChildren[i]->FindNode(name); - if (p) { - return p; - } - } - // there is definitely no sub-node with this name - return nullptr; -} - -void aiNode::addChildren(unsigned int numChildren, aiNode **children) { - if (nullptr == children || 0 == numChildren) { - return; - } - - for (unsigned int i = 0; i < numChildren; i++) { - aiNode *child = children[i]; - if (nullptr != child) { - child->mParent = this; - } - } - - if (mNumChildren > 0) { - aiNode **tmp = new aiNode*[mNumChildren]; - ::memcpy(tmp, mChildren, sizeof(aiNode*) * mNumChildren); - delete[] mChildren; - mChildren = new aiNode*[mNumChildren + numChildren]; - ::memcpy(mChildren, tmp, sizeof(aiNode*) * mNumChildren); - ::memcpy(&mChildren[mNumChildren], children, sizeof(aiNode*)* numChildren); - mNumChildren += numChildren; - delete[] tmp; - } - else { - mChildren = new aiNode*[numChildren]; - for (unsigned int i = 0; i < numChildren; i++) { - mChildren[i] = children[i]; - } - mNumChildren = numChildren; - } -} diff --git a/thirdparty/assimp/code/simd.cpp b/thirdparty/assimp/code/simd.cpp deleted file mode 100644 index 04615f408e..0000000000 --- a/thirdparty/assimp/code/simd.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ -#include "simd.h" - -namespace Assimp { - -bool CPUSupportsSSE2() { -#if defined(__x86_64__) || defined(_M_X64) - //* x86_64 always has SSE2 instructions */ - return true; -#elif defined(__GNUC__) && defined(i386) - // for GCC x86 we check cpuid - unsigned int d; - __asm__( - "pushl %%ebx\n\t" - "cpuid\n\t" - "popl %%ebx\n\t" - : "=d" ( d ) - :"a" ( 1 ) ); - return ( d & 0x04000000 ) != 0; -#elif (defined(_MSC_VER) && defined(_M_IX86)) - // also check cpuid for MSVC x86 - unsigned int d; - __asm { - xor eax, eax - inc eax - push ebx - cpuid - pop ebx - mov d, edx - } - return ( d & 0x04000000 ) != 0; -#else - return false; -#endif -} - - -} // Namespace Assimp diff --git a/thirdparty/assimp/code/simd.h b/thirdparty/assimp/code/simd.h deleted file mode 100644 index 3eecdd4581..0000000000 --- a/thirdparty/assimp/code/simd.h +++ /dev/null @@ -1,53 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ -#pragma once - -#include - -namespace Assimp { - -/// @brief Checks if the platform supports SSE2 optimization -/// @return true, if SSE2 is supported. false if SSE2 is not supported. -bool ASSIMP_API CPUSupportsSSE2(); - -} // Namespace Assimp diff --git a/thirdparty/assimp/include/assimp/Exporter.hpp b/thirdparty/assimp/include/assimp/Exporter.hpp index bf0096e7e9..ea0303e804 100644 --- a/thirdparty/assimp/include/assimp/Exporter.hpp +++ b/thirdparty/assimp/include/assimp/Exporter.hpp @@ -190,7 +190,7 @@ public: * @note Use aiCopyScene() to get a modifiable copy of a previously * imported scene. */ const aiExportDataBlob* ExportToBlob(const aiScene* pScene, const char* pFormatId, - unsigned int pPreprocessing = 0u, const ExportProperties* = nullptr); + unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr); const aiExportDataBlob* ExportToBlob( const aiScene* pScene, const std::string& pFormatId, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr); diff --git a/thirdparty/assimp/include/assimp/ParsingUtils.h b/thirdparty/assimp/include/assimp/ParsingUtils.h index ca30ce13b0..6b9574fc67 100644 --- a/thirdparty/assimp/include/assimp/ParsingUtils.h +++ b/thirdparty/assimp/include/assimp/ParsingUtils.h @@ -196,8 +196,7 @@ bool GetNextLine( const char_t*& buffer, char_t out[ BufferSize ] ) { // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool IsNumeric( char_t in) -{ +AI_FORCE_INLINE bool IsNumeric( char_t in) { return ( in >= '0' && in <= '9' ) || '-' == in || '+' == in; } diff --git a/thirdparty/assimp/include/assimp/aabb.h b/thirdparty/assimp/include/assimp/aabb.h new file mode 100644 index 0000000000..a20f317424 --- /dev/null +++ b/thirdparty/assimp/include/assimp/aabb.h @@ -0,0 +1,76 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#pragma once +#ifndef AI_AABB_H_INC +#define AI_AABB_H_INC + +#include + +struct aiAABB { + C_STRUCT aiVector3D mMin; + C_STRUCT aiVector3D mMax; + +#ifdef __cplusplus + + aiAABB() + : mMin() + , mMax() { + // empty + } + + aiAABB(const aiVector3D &min, const aiVector3D &max ) + : mMin(min) + , mMax(max) { + // empty + } + + ~aiAABB() { + // empty + } + +#endif +}; + + +#endif diff --git a/thirdparty/assimp/include/assimp/camera.h b/thirdparty/assimp/include/assimp/camera.h index 99daf69934..e573eea5d1 100644 --- a/thirdparty/assimp/include/assimp/camera.h +++ b/thirdparty/assimp/include/assimp/camera.h @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2019, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -60,7 +58,7 @@ extern "C" { * * 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 + * scene-graph. This means, any values such as the look-at vector are not * *absolute*, they're relative 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 @@ -162,7 +160,6 @@ struct aiCamera */ float mClipPlaneFar; - /** Screen aspect ratio. * * This is the ration between the width and the height of the diff --git a/thirdparty/assimp/include/assimp/color4.inl b/thirdparty/assimp/include/assimp/color4.inl index 3192d55f39..afa53dcb5b 100644 --- a/thirdparty/assimp/include/assimp/color4.inl +++ b/thirdparty/assimp/include/assimp/color4.inl @@ -85,6 +85,8 @@ AI_FORCE_INLINE TReal aiColor4t::operator[](unsigned int i) const { return g; case 2: return b; + case 3: + return a; default: break; } @@ -100,6 +102,8 @@ AI_FORCE_INLINE TReal& aiColor4t::operator[](unsigned int i) { return g; case 2: return b; + case 3: + return a; default: break; } diff --git a/thirdparty/assimp/include/assimp/config.h.in b/thirdparty/assimp/include/assimp/config.h.in index a37ff0b8c8..d08b929a10 100644 --- a/thirdparty/assimp/include/assimp/config.h.in +++ b/thirdparty/assimp/include/assimp/config.h.in @@ -142,7 +142,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @brief Specifies the maximum angle that may be between two vertex tangents * that their tangents and bi-tangents are smoothed. * - * This applies to the CalcTangentSpace-Step. The angle is specified + * This applies to the CalcTangentSpace-Step. TFvhe angle is specified * in degrees. The maximum value is 175. * Property type: float. Default value: 45 degrees */ @@ -651,13 +651,28 @@ enum aiComponent // --------------------------------------------------------------------------- /** @brief Set whether the fbx importer will use the legacy embedded texture naming. -* -* The default value is false (0) -* Property type: bool -*/ + * + * The default value is false (0) + * Property type: bool + */ #define AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING \ "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING" - + +// --------------------------------------------------------------------------- +/** @brief Set wether the importer shall not remove empty bones. + * + * Empty bone are often used to define connections for other models. + */ +#define AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES \ + "AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES" + + +// --------------------------------------------------------------------------- +/** @brief Set wether the FBX importer shall convert the unit from cm to m. + */ +#define AI_CONFIG_FBX_CONVERT_TO_M \ + "AI_CONFIG_FBX_CONVERT_TO_M" + // --------------------------------------------------------------------------- /** @brief Set the vertex animation keyframe to be imported * @@ -966,8 +981,12 @@ enum aiComponent #define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT" -/** - * +/** @brief Specifies whether the assimp export shall be able to export point clouds + * + * When this flag is not defined the render data has to contain valid faces. + * Point clouds are only a collection of vertices which have nor spatial organization + * by a face and the validation process will remove them. Enabling this feature will + * switch off the flag and enable the functionality to export pure point clouds. */ #define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS" diff --git a/thirdparty/assimp/include/assimp/defs.h b/thirdparty/assimp/include/assimp/defs.h index 4a177e3c3e..05a5e3fd4b 100644 --- a/thirdparty/assimp/include/assimp/defs.h +++ b/thirdparty/assimp/include/assimp/defs.h @@ -122,7 +122,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OPTIMIZEANIMS * OPTIMIZEGRAPH * GENENTITYMESHES - * FIXTEXTUREPATHS */ + * FIXTEXTUREPATHS + * GENBOUNDINGBOXES */ ////////////////////////////////////////////////////////////////////////// #ifdef _MSC_VER @@ -214,10 +215,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endif #if (defined(__BORLANDC__) || defined (__BCPLUSPLUS__)) -#error Currently, Borland is unsupported. Feel free to port Assimp. - -// "W8059 Packgröße der Struktur geändert" - +# error Currently, Borland is unsupported. Feel free to port Assimp. #endif @@ -243,10 +241,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. typedef double ai_real; typedef signed long long int ai_int; typedef unsigned long long int ai_uint; +#ifndef ASSIMP_AI_REAL_TEXT_PRECISION +#define ASSIMP_AI_REAL_TEXT_PRECISION 16 +#endif // ASSIMP_AI_REAL_TEXT_PRECISION #else // ASSIMP_DOUBLE_PRECISION typedef float ai_real; typedef signed int ai_int; typedef unsigned int ai_uint; +#ifndef ASSIMP_AI_REAL_TEXT_PRECISION +#define ASSIMP_AI_REAL_TEXT_PRECISION 8 +#endif // ASSIMP_AI_REAL_TEXT_PRECISION #endif // ASSIMP_DOUBLE_PRECISION ////////////////////////////////////////////////////////////////////////// @@ -267,6 +271,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_DEG_TO_RAD(x) ((x)*(ai_real)0.0174532925) #define AI_RAD_TO_DEG(x) ((x)*(ai_real)57.2957795) +/* Numerical limits */ +static const ai_real ai_epsilon = (ai_real) 0.00001; + /* Support for big-endian builds */ #if defined(__BYTE_ORDER__) # if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) @@ -293,11 +300,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef _MSC_VER # define AI_NO_EXCEPT noexcept #else -# if (_MSC_VER == 1915 ) +# if (_MSC_VER >= 1915 ) # define AI_NO_EXCEPT noexcept # else # define AI_NO_EXCEPT # endif -#endif +#endif // _MSC_VER #endif // !! AI_DEFINES_H_INC diff --git a/thirdparty/assimp/include/assimp/mesh.h b/thirdparty/assimp/include/assimp/mesh.h index 36f3ed2afd..f1628f1f54 100644 --- a/thirdparty/assimp/include/assimp/mesh.h +++ b/thirdparty/assimp/include/assimp/mesh.h @@ -48,7 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_MESH_H_INC #define AI_MESH_H_INC -#include "types.h" +#include +#include #ifdef __cplusplus extern "C" { @@ -714,6 +715,11 @@ struct aiMesh * Method of morphing when animeshes are specified. */ unsigned int mMethod; + + /** + * + */ + C_STRUCT aiAABB mAABB; #ifdef __cplusplus @@ -735,7 +741,8 @@ struct aiMesh , mMaterialIndex( 0 ) , mNumAnimMeshes( 0 ) , mAnimMeshes(nullptr) - , mMethod( 0 ) { + , mMethod( 0 ) + , mAABB() { for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) { mNumUVComponents[a] = 0; mTextureCoords[a] = nullptr; diff --git a/thirdparty/assimp/include/assimp/postprocess.h b/thirdparty/assimp/include/assimp/postprocess.h index c23a5490a5..2a74414216 100644 --- a/thirdparty/assimp/include/assimp/postprocess.h +++ b/thirdparty/assimp/include/assimp/postprocess.h @@ -438,7 +438,7 @@ enum aiPostProcessSteps aiProcess_FindInstances = 0x100000, // ------------------------------------------------------------------------- - /**
A postprocessing step to reduce the number of meshes. + /**
A post-processing step to reduce the number of meshes. * * This will, in fact, reduce the number of draw calls. * @@ -450,7 +450,7 @@ enum aiPostProcessSteps // ------------------------------------------------------------------------- - /**
A postprocessing step to optimize the scene hierarchy. + /**
A post-processing step to optimize the scene hierarchy. * * Nodes without animations, bones, lights or cameras assigned are * collapsed and joined. @@ -514,7 +514,7 @@ enum aiPostProcessSteps // ------------------------------------------------------------------------- /**
This step splits meshes with many bones into sub-meshes so that each - * su-bmesh has fewer or as many bones as a given limit. + * sub-mesh has fewer or as many bones as a given limit. */ aiProcess_SplitByBoneCount = 0x2000000, @@ -541,7 +541,7 @@ enum aiPostProcessSteps * global scaling from your importer settings like in FBX. Use the flag * AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY from the global property table to configure this. * - * Use #AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY to setup the global scaing factor. + * Use #AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY to setup the global scaling factor. */ aiProcess_GlobalScale = 0x8000000, @@ -574,6 +574,11 @@ enum aiPostProcessSteps * This process gives sense back to aiProcess_JoinIdenticalVertices */ aiProcess_DropNormals = 0x40000000, + + // ------------------------------------------------------------------------- + /** + */ + aiProcess_GenBoundingBoxes = 0x80000000 }; diff --git a/thirdparty/assimp/include/assimp/scene.h b/thirdparty/assimp/include/assimp/scene.h index de0239702d..e973f6c5b9 100644 --- a/thirdparty/assimp/include/assimp/scene.h +++ b/thirdparty/assimp/include/assimp/scene.h @@ -389,6 +389,14 @@ struct aiScene //! Returns an embedded texture const aiTexture* GetEmbeddedTexture(const char* filename) const { + // lookup using texture ID (if referenced like: "*1", "*2", etc.) + if ('*' == *filename) { + int index = std::atoi(filename + 1); + if (0 > index || mNumTextures <= static_cast(index)) + return nullptr; + return mTextures[index]; + } + // lookup using filename const char* shortFilename = GetShortFilename(filename); for (unsigned int i = 0; i < mNumTextures; i++) { const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str()); -- cgit v1.2.3