summaryrefslogtreecommitdiff
path: root/thirdparty
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty')
-rw-r--r--thirdparty/README.md32
-rw-r--r--thirdparty/assimp/assimp/config.h23
-rw-r--r--thirdparty/assimp/code/CApi/AssimpCExport.cpp156
-rw-r--r--thirdparty/assimp/code/CApi/CInterfaceIOWrapper.cpp (renamed from thirdparty/assimp/code/CInterfaceIOWrapper.cpp)0
-rw-r--r--thirdparty/assimp/code/CApi/CInterfaceIOWrapper.h (renamed from thirdparty/assimp/code/CInterfaceIOWrapper.h)0
-rw-r--r--thirdparty/assimp/code/Common/Assimp.cpp695
-rw-r--r--thirdparty/assimp/code/Common/BaseImporter.cpp (renamed from thirdparty/assimp/code/BaseImporter.cpp)33
-rw-r--r--thirdparty/assimp/code/Common/BaseProcess.cpp (renamed from thirdparty/assimp/code/BaseProcess.cpp)2
-rw-r--r--thirdparty/assimp/code/Common/BaseProcess.h (renamed from thirdparty/assimp/code/BaseProcess.h)0
-rw-r--r--thirdparty/assimp/code/Common/Bitmap.cpp (renamed from thirdparty/assimp/code/Bitmap.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/CreateAnimMesh.cpp (renamed from thirdparty/assimp/code/CreateAnimMesh.cpp)4
-rw-r--r--thirdparty/assimp/code/Common/DefaultIOStream.cpp (renamed from thirdparty/assimp/code/DefaultIOStream.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/DefaultIOSystem.cpp (renamed from thirdparty/assimp/code/DefaultIOSystem.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/DefaultLogger.cpp (renamed from thirdparty/assimp/code/DefaultLogger.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/DefaultProgressHandler.h (renamed from thirdparty/assimp/code/DefaultProgressHandler.h)0
-rw-r--r--thirdparty/assimp/code/Common/Exporter.cpp (renamed from thirdparty/assimp/code/Exporter.cpp)32
-rw-r--r--thirdparty/assimp/code/Common/FileLogStream.h (renamed from thirdparty/assimp/code/FileLogStream.h)0
-rw-r--r--thirdparty/assimp/code/Common/FileSystemFilter.h (renamed from thirdparty/assimp/code/FileSystemFilter.h)0
-rw-r--r--thirdparty/assimp/code/Common/IFF.h102
-rw-r--r--thirdparty/assimp/code/Common/Importer.cpp (renamed from thirdparty/assimp/code/Importer.cpp)19
-rw-r--r--thirdparty/assimp/code/Common/Importer.h (renamed from thirdparty/assimp/code/Importer.h)0
-rw-r--r--thirdparty/assimp/code/Common/ImporterRegistry.cpp (renamed from thirdparty/assimp/code/ImporterRegistry.cpp)96
-rw-r--r--thirdparty/assimp/code/Common/PolyTools.h (renamed from thirdparty/assimp/code/PolyTools.h)0
-rw-r--r--thirdparty/assimp/code/Common/PostStepRegistry.cpp (renamed from thirdparty/assimp/code/PostStepRegistry.cpp)63
-rw-r--r--thirdparty/assimp/code/Common/RemoveComments.cpp (renamed from thirdparty/assimp/code/RemoveComments.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/SGSpatialSort.cpp (renamed from thirdparty/assimp/code/SGSpatialSort.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/SceneCombiner.cpp (renamed from thirdparty/assimp/code/SceneCombiner.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/ScenePreprocessor.cpp (renamed from thirdparty/assimp/code/ScenePreprocessor.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/ScenePreprocessor.h (renamed from thirdparty/assimp/code/ScenePreprocessor.h)0
-rw-r--r--thirdparty/assimp/code/Common/ScenePrivate.h (renamed from thirdparty/assimp/code/ScenePrivate.h)0
-rw-r--r--thirdparty/assimp/code/Common/SkeletonMeshBuilder.cpp (renamed from thirdparty/assimp/code/SkeletonMeshBuilder.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/SpatialSort.cpp (renamed from thirdparty/assimp/code/SpatialSort.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/SplitByBoneCountProcess.cpp (renamed from thirdparty/assimp/code/SplitByBoneCountProcess.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/SplitByBoneCountProcess.h (renamed from thirdparty/assimp/code/SplitByBoneCountProcess.h)0
-rw-r--r--thirdparty/assimp/code/Common/StandardShapes.cpp (renamed from thirdparty/assimp/code/StandardShapes.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/StdOStreamLogStream.h (renamed from thirdparty/assimp/code/StdOStreamLogStream.h)0
-rw-r--r--thirdparty/assimp/code/Common/Subdivision.cpp (renamed from thirdparty/assimp/code/Subdivision.cpp)7
-rw-r--r--thirdparty/assimp/code/Common/TargetAnimation.cpp (renamed from thirdparty/assimp/code/TargetAnimation.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/TargetAnimation.h (renamed from thirdparty/assimp/code/TargetAnimation.h)0
-rw-r--r--thirdparty/assimp/code/Common/Version.cpp (renamed from thirdparty/assimp/code/Version.cpp)2
-rw-r--r--thirdparty/assimp/code/Common/VertexTriangleAdjacency.cpp (renamed from thirdparty/assimp/code/VertexTriangleAdjacency.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/VertexTriangleAdjacency.h (renamed from thirdparty/assimp/code/VertexTriangleAdjacency.h)0
-rw-r--r--thirdparty/assimp/code/Common/Win32DebugLogStream.h (renamed from thirdparty/assimp/code/Win32DebugLogStream.h)0
-rw-r--r--thirdparty/assimp/code/Common/assbin_chunks.h196
-rw-r--r--thirdparty/assimp/code/Common/scene.cpp (renamed from thirdparty/assimp/code/scene.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/simd.cpp (renamed from thirdparty/assimp/code/simd.cpp)0
-rw-r--r--thirdparty/assimp/code/Common/simd.h (renamed from thirdparty/assimp/code/simd.h)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXAnimation.cpp (renamed from thirdparty/assimp/code/FBXAnimation.cpp)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXBinaryTokenizer.cpp (renamed from thirdparty/assimp/code/FBXBinaryTokenizer.cpp)12
-rw-r--r--thirdparty/assimp/code/FBX/FBXCommon.h (renamed from thirdparty/assimp/code/FBXCommon.h)4
-rw-r--r--thirdparty/assimp/code/FBX/FBXCompileConfig.h (renamed from thirdparty/assimp/code/FBXCompileConfig.h)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXConverter.cpp (renamed from thirdparty/assimp/code/FBXConverter.cpp)276
-rw-r--r--thirdparty/assimp/code/FBX/FBXConverter.h (renamed from thirdparty/assimp/code/FBXConverter.h)30
-rw-r--r--thirdparty/assimp/code/FBX/FBXDeformer.cpp (renamed from thirdparty/assimp/code/FBXDeformer.cpp)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXDocument.cpp (renamed from thirdparty/assimp/code/FBXDocument.cpp)8
-rw-r--r--thirdparty/assimp/code/FBX/FBXDocument.h (renamed from thirdparty/assimp/code/FBXDocument.h)4
-rw-r--r--thirdparty/assimp/code/FBX/FBXDocumentUtil.cpp (renamed from thirdparty/assimp/code/FBXDocumentUtil.cpp)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXDocumentUtil.h (renamed from thirdparty/assimp/code/FBXDocumentUtil.h)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXExportNode.cpp (renamed from thirdparty/assimp/code/FBXExportNode.cpp)11
-rw-r--r--thirdparty/assimp/code/FBX/FBXExportNode.h (renamed from thirdparty/assimp/code/FBXExportNode.h)12
-rw-r--r--thirdparty/assimp/code/FBX/FBXExportProperty.cpp (renamed from thirdparty/assimp/code/FBXExportProperty.cpp)257
-rw-r--r--thirdparty/assimp/code/FBX/FBXExportProperty.h (renamed from thirdparty/assimp/code/FBXExportProperty.h)48
-rw-r--r--thirdparty/assimp/code/FBX/FBXExporter.cpp (renamed from thirdparty/assimp/code/FBXExporter.cpp)150
-rw-r--r--thirdparty/assimp/code/FBX/FBXExporter.h (renamed from thirdparty/assimp/code/FBXExporter.h)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXImportSettings.h (renamed from thirdparty/assimp/code/FBXImportSettings.h)39
-rw-r--r--thirdparty/assimp/code/FBX/FBXImporter.cpp (renamed from thirdparty/assimp/code/FBXImporter.cpp)18
-rw-r--r--thirdparty/assimp/code/FBX/FBXImporter.h (renamed from thirdparty/assimp/code/FBXImporter.h)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXMaterial.cpp (renamed from thirdparty/assimp/code/FBXMaterial.cpp)44
-rw-r--r--thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp (renamed from thirdparty/assimp/code/FBXMeshGeometry.cpp)18
-rw-r--r--thirdparty/assimp/code/FBX/FBXMeshGeometry.h (renamed from thirdparty/assimp/code/FBXMeshGeometry.h)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXModel.cpp (renamed from thirdparty/assimp/code/FBXModel.cpp)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXNodeAttribute.cpp (renamed from thirdparty/assimp/code/FBXNodeAttribute.cpp)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXParser.cpp (renamed from thirdparty/assimp/code/FBXParser.cpp)12
-rw-r--r--thirdparty/assimp/code/FBX/FBXParser.h (renamed from thirdparty/assimp/code/FBXParser.h)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXProperties.cpp (renamed from thirdparty/assimp/code/FBXProperties.cpp)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXProperties.h (renamed from thirdparty/assimp/code/FBXProperties.h)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXTokenizer.cpp (renamed from thirdparty/assimp/code/FBXTokenizer.cpp)0
-rw-r--r--thirdparty/assimp/code/FBX/FBXTokenizer.h (renamed from thirdparty/assimp/code/FBXTokenizer.h)12
-rw-r--r--thirdparty/assimp/code/FBX/FBXUtil.cpp (renamed from thirdparty/assimp/code/FBXUtil.cpp)135
-rw-r--r--thirdparty/assimp/code/FBX/FBXUtil.h (renamed from thirdparty/assimp/code/FBXUtil.h)23
-rw-r--r--thirdparty/assimp/code/FIReader.cpp1834
-rw-r--r--thirdparty/assimp/code/MMD/MMDCpp14.h (renamed from thirdparty/assimp/code/MMDCpp14.h)0
-rw-r--r--thirdparty/assimp/code/MMD/MMDImporter.cpp (renamed from thirdparty/assimp/code/MMDImporter.cpp)12
-rw-r--r--thirdparty/assimp/code/MMD/MMDImporter.h (renamed from thirdparty/assimp/code/MMDImporter.h)0
-rw-r--r--thirdparty/assimp/code/MMD/MMDPmdParser.h (renamed from thirdparty/assimp/code/MMDPmdParser.h)0
-rw-r--r--thirdparty/assimp/code/MMD/MMDPmxParser.cpp (renamed from thirdparty/assimp/code/MMDPmxParser.cpp)6
-rw-r--r--thirdparty/assimp/code/MMD/MMDPmxParser.h (renamed from thirdparty/assimp/code/MMDPmxParser.h)0
-rw-r--r--thirdparty/assimp/code/MMD/MMDVmdParser.h (renamed from thirdparty/assimp/code/MMDVmdParser.h)0
-rw-r--r--thirdparty/assimp/code/Material/MaterialSystem.cpp (renamed from thirdparty/assimp/code/MaterialSystem.cpp)12
-rw-r--r--thirdparty/assimp/code/Material/MaterialSystem.h (renamed from thirdparty/assimp/code/MaterialSystem.h)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.cpp (renamed from thirdparty/assimp/code/CalcTangentsProcess.cpp)6
-rw-r--r--thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.h (renamed from thirdparty/assimp/code/CalcTangentsProcess.h)4
-rw-r--r--thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.cpp (renamed from thirdparty/assimp/code/ComputeUVMappingProcess.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.h (renamed from thirdparty/assimp/code/ComputeUVMappingProcess.h)3
-rw-r--r--thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.cpp (renamed from thirdparty/assimp/code/ConvertToLHProcess.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.h (renamed from thirdparty/assimp/code/ConvertToLHProcess.h)3
-rw-r--r--thirdparty/assimp/code/PostProcessing/DeboneProcess.cpp (renamed from thirdparty/assimp/code/DeboneProcess.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/DeboneProcess.h (renamed from thirdparty/assimp/code/DeboneProcess.h)17
-rw-r--r--thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.cpp (renamed from thirdparty/assimp/code/DropFaceNormalsProcess.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.h (renamed from thirdparty/assimp/code/DropFaceNormalsProcess.h)11
-rw-r--r--thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.cpp (renamed from thirdparty/assimp/code/EmbedTexturesProcess.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.h (renamed from thirdparty/assimp/code/EmbedTexturesProcess.h)2
-rw-r--r--thirdparty/assimp/code/PostProcessing/FindDegenerates.cpp (renamed from thirdparty/assimp/code/FindDegenerates.cpp)1
-rw-r--r--thirdparty/assimp/code/PostProcessing/FindDegenerates.h (renamed from thirdparty/assimp/code/FindDegenerates.h)3
-rw-r--r--thirdparty/assimp/code/PostProcessing/FindInstancesProcess.cpp (renamed from thirdparty/assimp/code/FindInstancesProcess.cpp)12
-rw-r--r--thirdparty/assimp/code/PostProcessing/FindInstancesProcess.h (renamed from thirdparty/assimp/code/FindInstancesProcess.h)4
-rw-r--r--thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.cpp (renamed from thirdparty/assimp/code/FindInvalidDataProcess.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.h (renamed from thirdparty/assimp/code/FindInvalidDataProcess.h)3
-rw-r--r--thirdparty/assimp/code/PostProcessing/FixNormalsStep.cpp (renamed from thirdparty/assimp/code/FixNormalsStep.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/FixNormalsStep.h (renamed from thirdparty/assimp/code/FixNormalsStep.h)2
-rw-r--r--thirdparty/assimp/code/PostProcessing/GenBoundingBoxesProcess.cpp115
-rw-r--r--thirdparty/assimp/code/PostProcessing/GenBoundingBoxesProcess.h76
-rw-r--r--thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.cpp (renamed from thirdparty/assimp/code/GenFaceNormalsProcess.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.h (renamed from thirdparty/assimp/code/GenFaceNormalsProcess.h)2
-rw-r--r--thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.cpp (renamed from thirdparty/assimp/code/GenVertexNormalsProcess.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.h (renamed from thirdparty/assimp/code/GenVertexNormalsProcess.h)18
-rw-r--r--thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.cpp (renamed from thirdparty/assimp/code/ImproveCacheLocality.cpp)65
-rw-r--r--thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.h (renamed from thirdparty/assimp/code/ImproveCacheLocality.h)7
-rw-r--r--thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp (renamed from thirdparty/assimp/code/JoinVerticesProcess.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.h (renamed from thirdparty/assimp/code/JoinVerticesProcess.h)10
-rw-r--r--thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.cpp (renamed from thirdparty/assimp/code/LimitBoneWeightsProcess.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.h (renamed from thirdparty/assimp/code/LimitBoneWeightsProcess.h)34
-rw-r--r--thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp (renamed from thirdparty/assimp/code/MakeVerboseFormat.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h (renamed from thirdparty/assimp/code/MakeVerboseFormat.h)3
-rw-r--r--thirdparty/assimp/code/PostProcessing/OptimizeGraph.cpp (renamed from thirdparty/assimp/code/OptimizeGraph.cpp)2
-rw-r--r--thirdparty/assimp/code/PostProcessing/OptimizeGraph.h (renamed from thirdparty/assimp/code/OptimizeGraph.h)27
-rw-r--r--thirdparty/assimp/code/PostProcessing/OptimizeMeshes.cpp (renamed from thirdparty/assimp/code/OptimizeMeshes.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/OptimizeMeshes.h (renamed from thirdparty/assimp/code/OptimizeMeshes.h)10
-rw-r--r--thirdparty/assimp/code/PostProcessing/PretransformVertices.cpp (renamed from thirdparty/assimp/code/PretransformVertices.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/PretransformVertices.h (renamed from thirdparty/assimp/code/PretransformVertices.h)15
-rw-r--r--thirdparty/assimp/code/PostProcessing/ProcessHelper.cpp (renamed from thirdparty/assimp/code/ProcessHelper.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/ProcessHelper.h (renamed from thirdparty/assimp/code/ProcessHelper.h)2
-rw-r--r--thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.cpp (renamed from thirdparty/assimp/code/RemoveRedundantMaterials.cpp)10
-rw-r--r--thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.h (renamed from thirdparty/assimp/code/RemoveRedundantMaterials.h)18
-rw-r--r--thirdparty/assimp/code/PostProcessing/RemoveVCProcess.cpp (renamed from thirdparty/assimp/code/RemoveVCProcess.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/RemoveVCProcess.h (renamed from thirdparty/assimp/code/RemoveVCProcess.h)3
-rw-r--r--thirdparty/assimp/code/PostProcessing/ScaleProcess.cpp208
-rw-r--r--thirdparty/assimp/code/PostProcessing/ScaleProcess.h (renamed from thirdparty/assimp/code/ScaleProcess.h)15
-rw-r--r--thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.cpp (renamed from thirdparty/assimp/code/SortByPTypeProcess.cpp)18
-rw-r--r--thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.h (renamed from thirdparty/assimp/code/SortByPTypeProcess.h)11
-rw-r--r--thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.cpp (renamed from thirdparty/assimp/code/SplitLargeMeshes.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.h (renamed from thirdparty/assimp/code/SplitLargeMeshes.h)9
-rw-r--r--thirdparty/assimp/code/PostProcessing/TextureTransform.cpp (renamed from thirdparty/assimp/code/TextureTransform.cpp)0
-rw-r--r--thirdparty/assimp/code/PostProcessing/TextureTransform.h (renamed from thirdparty/assimp/code/TextureTransform.h)2
-rw-r--r--thirdparty/assimp/code/PostProcessing/TriangulateProcess.cpp (renamed from thirdparty/assimp/code/TriangulateProcess.cpp)10
-rw-r--r--thirdparty/assimp/code/PostProcessing/TriangulateProcess.h (renamed from thirdparty/assimp/code/TriangulateProcess.h)8
-rw-r--r--thirdparty/assimp/code/PostProcessing/ValidateDataStructure.cpp (renamed from thirdparty/assimp/code/ValidateDataStructure.cpp)33
-rw-r--r--thirdparty/assimp/code/PostProcessing/ValidateDataStructure.h (renamed from thirdparty/assimp/code/ValidateDataStructure.h)3
-rw-r--r--thirdparty/assimp/code/RawLoader.cpp331
-rw-r--r--thirdparty/assimp/code/ScaleProcess.cpp103
-rw-r--r--thirdparty/assimp/contrib/utf8cpp/doc/ReleaseNotes12
-rw-r--r--thirdparty/assimp/contrib/utf8cpp/doc/utf8cpp.html1789
-rw-r--r--thirdparty/assimp/include/assimp/.editorconfig8
-rw-r--r--thirdparty/assimp/include/assimp/BaseImporter.h61
-rw-r--r--thirdparty/assimp/include/assimp/Exporter.hpp2
-rw-r--r--thirdparty/assimp/include/assimp/ParsingUtils.h3
-rw-r--r--thirdparty/assimp/include/assimp/aabb.h76
-rw-r--r--thirdparty/assimp/include/assimp/camera.h5
-rw-r--r--thirdparty/assimp/include/assimp/color4.inl4
-rw-r--r--thirdparty/assimp/include/assimp/config.h.in40
-rw-r--r--thirdparty/assimp/include/assimp/defs.h21
-rw-r--r--thirdparty/assimp/include/assimp/irrXMLWrapper.h144
-rw-r--r--thirdparty/assimp/include/assimp/mesh.h11
-rw-r--r--thirdparty/assimp/include/assimp/postprocess.h13
-rw-r--r--thirdparty/assimp/include/assimp/scene.h9
-rw-r--r--thirdparty/jpeg-compressor/jpgd.cpp55
-rw-r--r--thirdparty/xatlas/avoid-failing-on-bad-geometry.patch157
-rw-r--r--thirdparty/xatlas/build-fix-limits.patch14
-rw-r--r--thirdparty/xatlas/xatlas.cpp2891
-rw-r--r--thirdparty/xatlas/xatlas.h36
-rw-r--r--thirdparty/zstd/common/compiler.h7
-rw-r--r--thirdparty/zstd/common/zstd_internal.h64
-rw-r--r--thirdparty/zstd/compress/zstd_compress.c292
-rw-r--r--thirdparty/zstd/compress/zstd_compress_internal.h104
-rw-r--r--thirdparty/zstd/compress/zstd_double_fast.c92
-rw-r--r--thirdparty/zstd/compress/zstd_fast.c49
-rw-r--r--thirdparty/zstd/compress/zstd_lazy.c15
-rw-r--r--thirdparty/zstd/compress/zstd_ldm.c2
-rw-r--r--thirdparty/zstd/compress/zstd_opt.c77
-rw-r--r--thirdparty/zstd/compress/zstdmt_compress.c25
-rw-r--r--thirdparty/zstd/compress/zstdmt_compress.h1
-rw-r--r--thirdparty/zstd/decompress/zstd_decompress.c21
-rw-r--r--thirdparty/zstd/decompress/zstd_decompress_block.c29
-rw-r--r--thirdparty/zstd/zstd.h74
184 files changed, 6832 insertions, 5049 deletions
diff --git a/thirdparty/README.md b/thirdparty/README.md
index c6817e2389..3f2fc6d8f9 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -4,7 +4,7 @@
## assimp
- Upstream: http://github.com/assimp/assimp
-- Version: git (d3d98a7ec0c8d38e1952b46dfe53f7e9233dc92d)
+- Version: git (1d565b0aab5a2ee00462f18c5b8a81f6a5454a48)
- License: BSD-3-Clause
@@ -200,6 +200,7 @@ Important: Some files have Godot-made changes.
They are marked with `// -- GODOT start --` and `// -- GODOT end --`
comments.
+
## libtheora
- Upstream: https://www.theora.org
@@ -262,18 +263,6 @@ changes to ensure they build for Javascript/HTML5. Those
changes are marked with `// -- GODOT --` comments.
-## wslay
-
-- Upstream: https://github.com/tatsuhiro-t/wslay
-- Version: 1.1.0
-- License: MIT
-
-File extracted from upstream release tarball:
-
-- All `*.c` and `*.h` in `lib/` and `lib/includes/`
-- `wslay.h` has a small Godot addition to fix MSVC build.
- See `thirdparty/wslay/msvcfix.diff`
-
## mbedtls
- Upstream: https://tls.mbed.org/
@@ -508,10 +497,23 @@ They can be reapplied using the patches included in the `vhacd`
folder.
+## wslay
+
+- Upstream: https://github.com/tatsuhiro-t/wslay
+- Version: 1.1.0
+- License: MIT
+
+File extracted from upstream release tarball:
+
+- All `*.c` and `*.h` in `lib/` and `lib/includes/`
+- `wslay.h` has a small Godot addition to fix MSVC build.
+ See `thirdparty/wslay/msvcfix.diff`
+
+
## xatlas
- Upstream: https://github.com/jpcy/xatlas
-- Version: git (f65a664, 2019)
+- Version: git (b4b5426, 2019)
- License: MIT
Files extracted from upstream source:
@@ -536,7 +538,7 @@ Files extracted from upstream source:
## zstd
- Upstream: https://github.com/facebook/zstd
-- Version: 1.4.0
+- Version: 1.4.1
- License: BSD-3-Clause
Files extracted from upstream source:
diff --git a/thirdparty/assimp/assimp/config.h b/thirdparty/assimp/assimp/config.h
index 8b0634d28b..d0e4817349 100644
--- a/thirdparty/assimp/assimp/config.h
+++ b/thirdparty/assimp/assimp/config.h
@@ -647,6 +647,21 @@ enum aiComponent {
"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
*
* ASSIMP does not support vertex keyframes (only bone animation is supported).
@@ -968,6 +983,13 @@ enum aiComponent {
#define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f
#endif // !! AI_DEBONE_THRESHOLD
+#define AI_CONFIG_APP_SCALE_KEY "APP_SCALE_FACTOR"
+
+#if (!defined AI_CONFIG_APP_SCALE_KEY)
+# define AI_CONFIG_APP_SCALE_KEY 1.0
+#endif // AI_CONFIG_APP_SCALE_KEY
+
+
// ---------- All the Build/Compile-time defines ------------
/** @brief Specifies if double precision is supported inside assimp
@@ -978,3 +1000,4 @@ enum aiComponent {
/* #cmakedefine ASSIMP_DOUBLE_PRECISION 1 */
#endif // !! AI_CONFIG_H_INC
+
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 <assimp/SceneCombiner.h>
+#include "Common/ScenePrivate.h"
+#include <assimp/Exporter.hpp>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API size_t aiGetExportFormatCount(void)
+{
+ return Exporter().GetExportFormatCount();
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API const aiExportFormatDesc* aiGetExportFormatDescription( size_t index)
+{
+ // Note: this is valid as the index always pertains to a built-in exporter,
+ // for which the returned structure is guaranteed to be of static storage duration.
+ Exporter exporter;
+ const aiExportFormatDesc* orig( exporter.GetExportFormatDescription( index ) );
+ if (NULL == orig) {
+ return NULL;
+ }
+
+ aiExportFormatDesc *desc = new aiExportFormatDesc;
+ desc->description = new char[ strlen( orig->description ) + 1 ]();
+ ::strncpy( (char*) desc->description, orig->description, strlen( orig->description ) );
+ desc->fileExtension = new char[ strlen( orig->fileExtension ) + 1 ]();
+ ::strncpy( ( char* ) desc->fileExtension, orig->fileExtension, strlen( orig->fileExtension ) );
+ desc->id = new char[ strlen( orig->id ) + 1 ]();
+ ::strncpy( ( char* ) desc->id, orig->id, strlen( orig->id ) );
+
+ return desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiReleaseExportFormatDescription( const aiExportFormatDesc *desc ) {
+ if (NULL == desc) {
+ return;
+ }
+
+ delete [] desc->description;
+ delete [] desc->fileExtension;
+ delete [] desc->id;
+ delete desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiCopyScene(const aiScene* pIn, aiScene** pOut)
+{
+ if (!pOut || !pIn) {
+ return;
+ }
+
+ SceneCombiner::CopyScene(pOut,pIn,true);
+ ScenePriv(*pOut)->mIsCopy = true;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiFreeScene(const C_STRUCT aiScene* pIn)
+{
+ // note: aiReleaseImport() is also able to delete scene copies, but in addition
+ // it also handles scenes with import metadata.
+ delete pIn;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiReturn aiExportScene( const aiScene* pScene, const char* pFormatId, const char* pFileName, unsigned int pPreprocessing )
+{
+ return ::aiExportSceneEx(pScene,pFormatId,pFileName,NULL,pPreprocessing);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiReturn aiExportSceneEx( const aiScene* pScene, const char* pFormatId, const char* pFileName, aiFileIO* pIO, unsigned int pPreprocessing )
+{
+ Exporter exp;
+
+ if (pIO) {
+ exp.SetIOHandler(new CIOSystemWrapper(pIO));
+ }
+ return exp.Export(pScene,pFormatId,pFileName,pPreprocessing);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing )
+{
+ Exporter exp;
+ if (!exp.ExportToBlob(pScene,pFormatId,pPreprocessing)) {
+ return NULL;
+ }
+ const aiExportDataBlob* blob = exp.GetOrphanedBlob();
+ ai_assert(blob);
+
+ return blob;
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API C_STRUCT void aiReleaseExportBlob( const aiExportDataBlob* pData )
+{
+ delete pData;
+}
+
+#endif // !ASSIMP_BUILD_NO_EXPORT
diff --git a/thirdparty/assimp/code/CInterfaceIOWrapper.cpp b/thirdparty/assimp/code/CApi/CInterfaceIOWrapper.cpp
index 5a3a49565a..5a3a49565a 100644
--- a/thirdparty/assimp/code/CInterfaceIOWrapper.cpp
+++ b/thirdparty/assimp/code/CApi/CInterfaceIOWrapper.cpp
diff --git a/thirdparty/assimp/code/CInterfaceIOWrapper.h b/thirdparty/assimp/code/CApi/CInterfaceIOWrapper.h
index 2162320302..2162320302 100644
--- a/thirdparty/assimp/code/CInterfaceIOWrapper.h
+++ b/thirdparty/assimp/code/CApi/CInterfaceIOWrapper.h
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 <assimp/cimport.h>
+#include <assimp/LogStream.hpp>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/Importer.hpp>
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/GenericProperty.h>
+#include <assimp/Exceptional.h>
+#include <assimp/BaseImporter.h>
+
+#include "CApi/CInterfaceIOWrapper.h"
+#include "Importer.h"
+#include "ScenePrivate.h"
+
+#include <list>
+
+// ------------------------------------------------------------------------------------------------
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+# include <thread>
+# include <mutex>
+#endif
+// ------------------------------------------------------------------------------------------------
+using namespace Assimp;
+
+namespace Assimp {
+ // underlying structure for aiPropertyStore
+ typedef BatchLoader::PropertyMap PropertyMap;
+
+ /** Stores the LogStream objects for all active C log streams */
+ struct mpred {
+ bool operator () (const aiLogStream& s0, const aiLogStream& s1) const {
+ return s0.callback<s1.callback&&s0.user<s1.user;
+ }
+ };
+ typedef std::map<aiLogStream, Assimp::LogStream*, mpred> LogStreamMap;
+
+ /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */
+ typedef std::list<Assimp::LogStream*> PredefLogStreamMap;
+
+ /** Local storage of all active log streams */
+ static LogStreamMap gActiveLogStreams;
+
+ /** Local storage of LogStreams allocated by #aiGetPredefinedLogStream */
+ static PredefLogStreamMap gPredefinedStreams;
+
+ /** Error message of the last failed import process */
+ static std::string gLastErrorString;
+
+ /** Verbose logging active or not? */
+ static aiBool gVerboseLogging = false;
+
+ /** will return all registered importers. */
+ void GetImporterInstanceList(std::vector< BaseImporter* >& out);
+
+ /** will delete all registered importers. */
+ void DeleteImporterInstanceList(std::vector< BaseImporter* >& out);
+} // namespace assimp
+
+
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+/** Global mutex to manage the access to the log-stream map */
+static std::mutex gLogStreamMutex;
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Custom LogStream implementation for the C-API
+class LogToCallbackRedirector : public LogStream {
+public:
+ explicit LogToCallbackRedirector(const aiLogStream& s)
+ : stream (s) {
+ ai_assert(NULL != s.callback);
+ }
+
+ ~LogToCallbackRedirector() {
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+ std::lock_guard<std::mutex> lock(gLogStreamMutex);
+#endif
+ // (HACK) Check whether the 'stream.user' pointer points to a
+ // custom LogStream allocated by #aiGetPredefinedLogStream.
+ // In this case, we need to delete it, too. Of course, this
+ // might cause strange problems, but the chance is quite low.
+
+ PredefLogStreamMap::iterator it = std::find(gPredefinedStreams.begin(),
+ gPredefinedStreams.end(), (Assimp::LogStream*)stream.user);
+
+ if (it != gPredefinedStreams.end()) {
+ delete *it;
+ gPredefinedStreams.erase(it);
+ }
+ }
+
+ /** @copydoc LogStream::write */
+ void write(const char* message) {
+ stream.callback(message,stream.user);
+ }
+
+private:
+ aiLogStream stream;
+};
+
+// ------------------------------------------------------------------------------------------------
+void ReportSceneNotFoundError() {
+ ASSIMP_LOG_ERROR("Unable to find the Assimp::Importer for this aiScene. "
+ "The C-API does not accept scenes produced by the C++ API and vice versa");
+
+ ai_assert(false);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the given file and returns its content.
+const aiScene* aiImportFile( const char* pFile, unsigned int pFlags) {
+ return aiImportFileEx(pFile,pFlags,NULL);
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiScene* aiImportFileEx( const char* pFile, unsigned int pFlags, aiFileIO* pFS) {
+ return aiImportFileExWithProperties(pFile, pFlags, pFS, NULL);
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiScene* aiImportFileExWithProperties( const char* pFile, unsigned int pFlags,
+ aiFileIO* pFS, const aiPropertyStore* props) {
+ ai_assert(NULL != pFile);
+
+ const aiScene* scene = NULL;
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // create an Importer for this file
+ Assimp::Importer* imp = new Assimp::Importer();
+
+ // copy properties
+ if(props) {
+ const PropertyMap* pp = reinterpret_cast<const PropertyMap*>(props);
+ ImporterPimpl* pimpl = imp->Pimpl();
+ pimpl->mIntProperties = pp->ints;
+ pimpl->mFloatProperties = pp->floats;
+ pimpl->mStringProperties = pp->strings;
+ pimpl->mMatrixProperties = pp->matrices;
+ }
+ // setup a custom IO system if necessary
+ if (pFS) {
+ imp->SetIOHandler( new CIOSystemWrapper (pFS) );
+ }
+
+ // and have it read the file
+ scene = imp->ReadFile( pFile, pFlags);
+
+ // if succeeded, store the importer in the scene and keep it alive
+ if( scene) {
+ ScenePrivateData* priv = const_cast<ScenePrivateData*>( ScenePriv(scene) );
+ priv->mOrigImporter = imp;
+ } else {
+ // if failed, extract error code and destroy the import
+ gLastErrorString = imp->GetErrorString();
+ delete imp;
+ }
+
+ // return imported data. If the import failed the pointer is NULL anyways
+ ASSIMP_END_EXCEPTION_REGION(const aiScene*);
+
+ return scene;
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiScene* aiImportFileFromMemory(
+ const char* pBuffer,
+ unsigned int pLength,
+ unsigned int pFlags,
+ const char* pHint)
+{
+ return aiImportFileFromMemoryWithProperties(pBuffer, pLength, pFlags, pHint, NULL);
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiScene* aiImportFileFromMemoryWithProperties(
+ const char* pBuffer,
+ unsigned int pLength,
+ unsigned int pFlags,
+ const char* pHint,
+ const aiPropertyStore* props)
+{
+ ai_assert( NULL != pBuffer );
+ ai_assert( 0 != pLength );
+
+ const aiScene* scene = NULL;
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // create an Importer for this file
+ Assimp::Importer* imp = new Assimp::Importer();
+
+ // copy properties
+ if(props) {
+ const PropertyMap* pp = reinterpret_cast<const PropertyMap*>(props);
+ ImporterPimpl* pimpl = imp->Pimpl();
+ pimpl->mIntProperties = pp->ints;
+ pimpl->mFloatProperties = pp->floats;
+ pimpl->mStringProperties = pp->strings;
+ pimpl->mMatrixProperties = pp->matrices;
+ }
+
+ // and have it read the file from the memory buffer
+ scene = imp->ReadFileFromMemory( pBuffer, pLength, pFlags,pHint);
+
+ // if succeeded, store the importer in the scene and keep it alive
+ if( scene) {
+ ScenePrivateData* priv = const_cast<ScenePrivateData*>( ScenePriv(scene) );
+ priv->mOrigImporter = imp;
+ }
+ else {
+ // if failed, extract error code and destroy the import
+ gLastErrorString = imp->GetErrorString();
+ delete imp;
+ }
+ // return imported data. If the import failed the pointer is NULL anyways
+ ASSIMP_END_EXCEPTION_REGION(const aiScene*);
+ return scene;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Releases all resources associated with the given import process.
+void aiReleaseImport( const aiScene* pScene)
+{
+ if (!pScene) {
+ return;
+ }
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // find the importer associated with this data
+ const ScenePrivateData* priv = ScenePriv(pScene);
+ if( !priv || !priv->mOrigImporter) {
+ delete pScene;
+ }
+ else {
+ // deleting the Importer also deletes the scene
+ // Note: the reason that this is not written as 'delete priv->mOrigImporter'
+ // is a suspected bug in gcc 4.4+ (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52339)
+ Importer* importer = priv->mOrigImporter;
+ delete importer;
+ }
+
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API const aiScene* aiApplyPostProcessing(const aiScene* pScene,
+ unsigned int pFlags)
+{
+ const aiScene* sc = NULL;
+
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // find the importer associated with this data
+ const ScenePrivateData* priv = ScenePriv(pScene);
+ if( !priv || !priv->mOrigImporter) {
+ ReportSceneNotFoundError();
+ return NULL;
+ }
+
+ sc = priv->mOrigImporter->ApplyPostProcessing(pFlags);
+
+ if (!sc) {
+ aiReleaseImport(pScene);
+ return NULL;
+ }
+
+ ASSIMP_END_EXCEPTION_REGION(const aiScene*);
+ return sc;
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API const aiScene *aiApplyCustomizedPostProcessing( const aiScene *scene,
+ BaseProcess* process,
+ bool requestValidation ) {
+ const aiScene* sc( NULL );
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // find the importer associated with this data
+ const ScenePrivateData* priv = ScenePriv( scene );
+ if ( NULL == priv || NULL == priv->mOrigImporter ) {
+ ReportSceneNotFoundError();
+ return NULL;
+ }
+
+ sc = priv->mOrigImporter->ApplyCustomizedPostProcessing( process, requestValidation );
+
+ if ( !sc ) {
+ aiReleaseImport( scene );
+ return NULL;
+ }
+
+ ASSIMP_END_EXCEPTION_REGION( const aiScene* );
+
+ return sc;
+}
+
+// ------------------------------------------------------------------------------------------------
+void CallbackToLogRedirector (const char* msg, char* dt)
+{
+ ai_assert( NULL != msg );
+ ai_assert( NULL != dt );
+ LogStream* s = (LogStream*)dt;
+
+ s->write(msg);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiLogStream aiGetPredefinedLogStream(aiDefaultLogStream pStream,const char* file)
+{
+ aiLogStream sout;
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ LogStream* stream = LogStream::createDefaultStream(pStream,file);
+ if (!stream) {
+ sout.callback = NULL;
+ sout.user = NULL;
+ }
+ else {
+ sout.callback = &CallbackToLogRedirector;
+ sout.user = (char*)stream;
+ }
+ gPredefinedStreams.push_back(stream);
+ ASSIMP_END_EXCEPTION_REGION(aiLogStream);
+ return sout;
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiAttachLogStream( const aiLogStream* stream )
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+ std::lock_guard<std::mutex> lock(gLogStreamMutex);
+#endif
+
+ LogStream* lg = new LogToCallbackRedirector(*stream);
+ gActiveLogStreams[*stream] = lg;
+
+ if (DefaultLogger::isNullLogger()) {
+ DefaultLogger::create(NULL,(gVerboseLogging == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL));
+ }
+ DefaultLogger::get()->attachStream(lg);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiReturn aiDetachLogStream( const aiLogStream* stream)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+ std::lock_guard<std::mutex> lock(gLogStreamMutex);
+#endif
+ // find the log-stream associated with this data
+ LogStreamMap::iterator it = gActiveLogStreams.find( *stream);
+ // it should be there... else the user is playing fools with us
+ if( it == gActiveLogStreams.end()) {
+ return AI_FAILURE;
+ }
+ DefaultLogger::get()->detatchStream( it->second );
+ delete it->second;
+
+ gActiveLogStreams.erase( it);
+
+ if (gActiveLogStreams.empty()) {
+ DefaultLogger::kill();
+ }
+ ASSIMP_END_EXCEPTION_REGION(aiReturn);
+ return AI_SUCCESS;
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiDetachAllLogStreams(void)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+ std::lock_guard<std::mutex> lock(gLogStreamMutex);
+#endif
+ Logger *logger( DefaultLogger::get() );
+ if ( NULL == logger ) {
+ return;
+ }
+
+ for (LogStreamMap::iterator it = gActiveLogStreams.begin(); it != gActiveLogStreams.end(); ++it) {
+ logger->detatchStream( it->second );
+ delete it->second;
+ }
+ gActiveLogStreams.clear();
+ DefaultLogger::kill();
+
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiEnableVerboseLogging(aiBool d)
+{
+ if (!DefaultLogger::isNullLogger()) {
+ DefaultLogger::get()->setLogSeverity((d == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL));
+ }
+ gVerboseLogging = d;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the error text of the last failed import process.
+const char* aiGetErrorString()
+{
+ return gLastErrorString.c_str();
+}
+
+// -----------------------------------------------------------------------------------------------
+// Return the description of a importer given its index
+const aiImporterDesc* aiGetImportFormatDescription( size_t pIndex)
+{
+ return Importer().GetImporterInfo(pIndex);
+}
+
+// -----------------------------------------------------------------------------------------------
+// Return the number of importers
+size_t aiGetImportFormatCount(void)
+{
+ return Importer().GetImporterCount();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the error text of the last failed import process.
+aiBool aiIsExtensionSupported(const char* szExtension)
+{
+ ai_assert(NULL != szExtension);
+ aiBool candoit=AI_FALSE;
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // FIXME: no need to create a temporary Importer instance just for that ..
+ Assimp::Importer tmp;
+ candoit = tmp.IsExtensionSupported(std::string(szExtension)) ? AI_TRUE : AI_FALSE;
+
+ ASSIMP_END_EXCEPTION_REGION(aiBool);
+ return candoit;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all file extensions supported by ASSIMP
+void aiGetExtensionList(aiString* szOut)
+{
+ ai_assert(NULL != szOut);
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // FIXME: no need to create a temporary Importer instance just for that ..
+ Assimp::Importer tmp;
+ tmp.GetExtensionList(*szOut);
+
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the memory requirements for a particular import.
+void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn,
+ C_STRUCT aiMemoryInfo* in)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // find the importer associated with this data
+ const ScenePrivateData* priv = ScenePriv(pIn);
+ if( !priv || !priv->mOrigImporter) {
+ ReportSceneNotFoundError();
+ return;
+ }
+
+ return priv->mOrigImporter->GetMemoryRequirements(*in);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiPropertyStore* aiCreatePropertyStore(void)
+{
+ return reinterpret_cast<aiPropertyStore*>( new PropertyMap() );
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiReleasePropertyStore(aiPropertyStore* p)
+{
+ delete reinterpret_cast<PropertyMap*>(p);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyInteger
+ASSIMP_API void aiSetImportPropertyInteger(aiPropertyStore* p, const char* szName, int value)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
+ SetGenericProperty<int>(pp->ints,szName,value);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyFloat
+ASSIMP_API void aiSetImportPropertyFloat(aiPropertyStore* p, const char* szName, ai_real value)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
+ SetGenericProperty<ai_real>(pp->floats,szName,value);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyString
+ASSIMP_API void aiSetImportPropertyString(aiPropertyStore* p, const char* szName,
+ const C_STRUCT aiString* st)
+{
+ if (!st) {
+ return;
+ }
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
+ SetGenericProperty<std::string>(pp->strings,szName,std::string(st->C_Str()));
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyMatrix
+ASSIMP_API void aiSetImportPropertyMatrix(aiPropertyStore* p, const char* szName,
+ const C_STRUCT aiMatrix4x4* mat)
+{
+ if (!mat) {
+ return;
+ }
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
+ SetGenericProperty<aiMatrix4x4>(pp->matrices,szName,*mat);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Rotation matrix to quaternion
+ASSIMP_API void aiCreateQuaternionFromMatrix(aiQuaternion* quat,const aiMatrix3x3* mat)
+{
+ ai_assert( NULL != quat );
+ ai_assert( NULL != mat );
+ *quat = aiQuaternion(*mat);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix decomposition
+ASSIMP_API void aiDecomposeMatrix(const aiMatrix4x4* mat,aiVector3D* scaling,
+ aiQuaternion* rotation,
+ aiVector3D* position)
+{
+ ai_assert( NULL != rotation );
+ ai_assert( NULL != position );
+ ai_assert( NULL != scaling );
+ ai_assert( NULL != mat );
+ mat->Decompose(*scaling,*rotation,*position);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix transpose
+ASSIMP_API void aiTransposeMatrix3(aiMatrix3x3* mat)
+{
+ ai_assert(NULL != mat);
+ mat->Transpose();
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiTransposeMatrix4(aiMatrix4x4* mat)
+{
+ ai_assert(NULL != mat);
+ mat->Transpose();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Vector transformation
+ASSIMP_API void aiTransformVecByMatrix3(aiVector3D* vec,
+ const aiMatrix3x3* mat)
+{
+ ai_assert( NULL != mat );
+ ai_assert( NULL != vec);
+ *vec *= (*mat);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiTransformVecByMatrix4(aiVector3D* vec,
+ const aiMatrix4x4* mat)
+{
+ ai_assert( NULL != mat );
+ ai_assert( NULL != vec );
+
+ *vec *= (*mat);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix multiplication
+ASSIMP_API void aiMultiplyMatrix4(
+ aiMatrix4x4* dst,
+ const aiMatrix4x4* src)
+{
+ ai_assert( NULL != dst );
+ ai_assert( NULL != src );
+ *dst = (*dst) * (*src);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiMultiplyMatrix3(
+ aiMatrix3x3* dst,
+ const aiMatrix3x3* src)
+{
+ ai_assert( NULL != dst );
+ ai_assert( NULL != src );
+ *dst = (*dst) * (*src);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix identity
+ASSIMP_API void aiIdentityMatrix3(
+ aiMatrix3x3* mat)
+{
+ ai_assert(NULL != mat);
+ *mat = aiMatrix3x3();
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiIdentityMatrix4(
+ aiMatrix4x4* mat)
+{
+ ai_assert(NULL != mat);
+ *mat = aiMatrix4x4();
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API C_STRUCT const aiImporterDesc* aiGetImporterDesc( const char *extension ) {
+ if( NULL == extension ) {
+ return NULL;
+ }
+ const aiImporterDesc *desc( NULL );
+ std::vector< BaseImporter* > out;
+ GetImporterInstanceList( out );
+ for( size_t i = 0; i < out.size(); ++i ) {
+ if( 0 == strncmp( out[ i ]->GetInfo()->mFileExtensions, extension, strlen( extension ) ) ) {
+ desc = out[ i ]->GetInfo();
+ break;
+ }
+ }
+
+ DeleteImporterInstanceList(out);
+
+ return desc;
+}
+
+// ------------------------------------------------------------------------------------------------
diff --git a/thirdparty/assimp/code/BaseImporter.cpp b/thirdparty/assimp/code/Common/BaseImporter.cpp
index 4803c6d6f2..de5018a250 100644
--- a/thirdparty/assimp/code/BaseImporter.cpp
+++ b/thirdparty/assimp/code/Common/BaseImporter.cpp
@@ -76,9 +76,25 @@ BaseImporter::~BaseImporter() {
// nothing to do here
}
+void BaseImporter::UpdateImporterScale( Importer* pImp )
+{
+ ai_assert(pImp != nullptr);
+ ai_assert(importerScale != 0.0);
+ ai_assert(fileScale != 0.0);
+
+ double activeScale = importerScale * fileScale;
+
+ // Set active scaling
+ pImp->SetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, activeScale);
+
+ ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: %f", activeScale );
+}
+
// ------------------------------------------------------------------------------------------------
// Imports the given file and returns the imported data.
-aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) {
+aiScene* BaseImporter::ReadFile(Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) {
+
+
m_progress = pImp->GetProgressHandler();
if (nullptr == m_progress) {
return nullptr;
@@ -100,6 +116,11 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile,
{
InternReadFile( pFile, sc.get(), &filter);
+ // Calculate import scale hook - required because pImp not available anywhere else
+ // passes scale into ScaleProcess
+ UpdateImporterScale(pImp);
+
+
} catch( const std::exception& err ) {
// extract error description
m_ErrorText = err.what();
@@ -112,7 +133,7 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile,
}
// ------------------------------------------------------------------------------------------------
-void BaseImporter::SetupProperties(const Importer* /*pImp*/)
+void BaseImporter::SetupProperties(const Importer* pImp)
{
// the default implementation does nothing
}
@@ -320,7 +341,11 @@ std::string BaseImporter::GetExtension( const std::string& file ) {
return false;
}
-#include "../contrib/utf8cpp/source/utf8.h"
+#ifdef ASSIMP_USE_HUNTER
+# include <utf8/utf8.h>
+#else
+# include "../contrib/utf8cpp/source/utf8.h"
+#endif
// ------------------------------------------------------------------------------------------------
// Convert to UTF8 data
@@ -584,6 +609,8 @@ aiScene* BatchLoader::GetImport( unsigned int which )
return nullptr;
}
+
+
// ------------------------------------------------------------------------------------------------
void BatchLoader::LoadAll()
{
diff --git a/thirdparty/assimp/code/BaseProcess.cpp b/thirdparty/assimp/code/Common/BaseProcess.cpp
index 18872c3693..e247be418d 100644
--- a/thirdparty/assimp/code/BaseProcess.cpp
+++ b/thirdparty/assimp/code/Common/BaseProcess.cpp
@@ -89,7 +89,7 @@ void BaseProcess::ExecuteOnScene( Importer* pImp)
// and kill the partially imported data
delete pImp->Pimpl()->mScene;
- pImp->Pimpl()->mScene = NULL;
+ pImp->Pimpl()->mScene = nullptr;
}
}
diff --git a/thirdparty/assimp/code/BaseProcess.h b/thirdparty/assimp/code/Common/BaseProcess.h
index 4d5c7a76be..4d5c7a76be 100644
--- a/thirdparty/assimp/code/BaseProcess.h
+++ b/thirdparty/assimp/code/Common/BaseProcess.h
diff --git a/thirdparty/assimp/code/Bitmap.cpp b/thirdparty/assimp/code/Common/Bitmap.cpp
index b22b71ea9e..b22b71ea9e 100644
--- a/thirdparty/assimp/code/Bitmap.cpp
+++ b/thirdparty/assimp/code/Common/Bitmap.cpp
diff --git a/thirdparty/assimp/code/CreateAnimMesh.cpp b/thirdparty/assimp/code/Common/CreateAnimMesh.cpp
index 1a052849bb..98b60e5319 100644
--- a/thirdparty/assimp/code/CreateAnimMesh.cpp
+++ b/thirdparty/assimp/code/Common/CreateAnimMesh.cpp
@@ -47,10 +47,6 @@ 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];
diff --git a/thirdparty/assimp/code/DefaultIOStream.cpp b/thirdparty/assimp/code/Common/DefaultIOStream.cpp
index 1c100b6189..1c100b6189 100644
--- a/thirdparty/assimp/code/DefaultIOStream.cpp
+++ b/thirdparty/assimp/code/Common/DefaultIOStream.cpp
diff --git a/thirdparty/assimp/code/DefaultIOSystem.cpp b/thirdparty/assimp/code/Common/DefaultIOSystem.cpp
index d40b67de32..d40b67de32 100644
--- a/thirdparty/assimp/code/DefaultIOSystem.cpp
+++ b/thirdparty/assimp/code/Common/DefaultIOSystem.cpp
diff --git a/thirdparty/assimp/code/DefaultLogger.cpp b/thirdparty/assimp/code/Common/DefaultLogger.cpp
index de3528d2b4..de3528d2b4 100644
--- a/thirdparty/assimp/code/DefaultLogger.cpp
+++ b/thirdparty/assimp/code/Common/DefaultLogger.cpp
diff --git a/thirdparty/assimp/code/DefaultProgressHandler.h b/thirdparty/assimp/code/Common/DefaultProgressHandler.h
index bd2cce00be..bd2cce00be 100644
--- a/thirdparty/assimp/code/DefaultProgressHandler.h
+++ b/thirdparty/assimp/code/Common/DefaultProgressHandler.h
diff --git a/thirdparty/assimp/code/Exporter.cpp b/thirdparty/assimp/code/Common/Exporter.cpp
index 8848e87f5b..090b561ae0 100644
--- a/thirdparty/assimp/code/Exporter.cpp
+++ b/thirdparty/assimp/code/Common/Exporter.cpp
@@ -61,15 +61,16 @@ Here we implement only the C++ interface (Assimp::Exporter).
#include <assimp/mesh.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
-
-#include "DefaultProgressHandler.h"
-#include "BaseProcess.h"
-#include "JoinVerticesProcess.h"
-#include "MakeVerboseFormat.h"
-#include "ConvertToLHProcess.h"
-#include "PretransformVertices.h"
#include <assimp/Exceptional.h>
-#include "ScenePrivate.h"
+
+#include "Common/DefaultProgressHandler.h"
+#include "Common/BaseProcess.h"
+#include "Common/ScenePrivate.h"
+#include "PostProcessing/CalcTangentsProcess.h"
+#include "PostProcessing/MakeVerboseFormat.h"
+#include "PostProcessing/JoinVerticesProcess.h"
+#include "PostProcessing/ConvertToLHProcess.h"
+#include "PostProcessing/PretransformVertices.h"
#include <memory>
@@ -101,6 +102,7 @@ void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperti
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
@@ -161,11 +163,11 @@ Exporter::ExportFormatEntry gExporters[] =
#endif
#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
- Exporter::ExportFormatEntry( "assbin", "Assimp Binary", "assbin" , &ExportSceneAssbin, 0 ),
+ Exporter::ExportFormatEntry( "assbin", "Assimp Binary File", "assbin" , &ExportSceneAssbin, 0 ),
#endif
#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
- Exporter::ExportFormatEntry( "assxml", "Assxml Document", "assxml" , &ExportSceneAssxml, 0 ),
+ Exporter::ExportFormatEntry( "assxml", "Assimp XML Document", "assxml" , &ExportSceneAssxml, 0 ),
#endif
#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
@@ -178,7 +180,11 @@ Exporter::ExportFormatEntry gExporters[] =
#endif
#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
- Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 )
+ 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
};
@@ -288,7 +294,7 @@ void Exporter::SetProgressHandler(ProgressHandler* pHandler) {
// ------------------------------------------------------------------------------------------------
const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId,
- unsigned int, const ExportProperties* /*pProperties*/ ) {
+ unsigned int pPreprocessing, const ExportProperties* pProperties) {
if (pimpl->blob) {
delete pimpl->blob;
pimpl->blob = nullptr;
@@ -298,7 +304,7 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha
BlobIOSystem* blobio = new BlobIOSystem();
pimpl->mIOSystem = std::shared_ptr<IOSystem>( blobio );
- if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName())) {
+ if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) {
pimpl->mIOSystem = old;
return nullptr;
}
diff --git a/thirdparty/assimp/code/FileLogStream.h b/thirdparty/assimp/code/Common/FileLogStream.h
index 740c503192..740c503192 100644
--- a/thirdparty/assimp/code/FileLogStream.h
+++ b/thirdparty/assimp/code/Common/FileLogStream.h
diff --git a/thirdparty/assimp/code/FileSystemFilter.h b/thirdparty/assimp/code/Common/FileSystemFilter.h
index 9923cdbdd3..9923cdbdd3 100644
--- a/thirdparty/assimp/code/FileSystemFilter.h
+++ b/thirdparty/assimp/code/Common/FileSystemFilter.h
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 <assimp/ByteSwapper.h>
+
+namespace Assimp {
+namespace IFF {
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Describes an IFF chunk header
+/////////////////////////////////////////////////////////////////////////////////
+struct ChunkHeader
+{
+ //! Type of the chunk header - FourCC
+ uint32_t type;
+
+ //! Length of the chunk data, in bytes
+ uint32_t length;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Describes an IFF sub chunk header
+/////////////////////////////////////////////////////////////////////////////////
+struct SubChunkHeader
+{
+ //! Type of the chunk header - FourCC
+ uint32_t type;
+
+ //! Length of the chunk data, in bytes
+ uint16_t length;
+};
+
+
+#define AI_IFF_FOURCC(a,b,c,d) ((uint32_t) (((uint8_t)a << 24u) | \
+ ((uint8_t)b << 16u) | ((uint8_t)c << 8u) | ((uint8_t)d)))
+
+
+#define AI_IFF_FOURCC_FORM AI_IFF_FOURCC('F','O','R','M')
+
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Load a chunk header
+//! @param outFile Pointer to the file data - points to the chunk data afterwards
+//! @return Copy of the chunk header
+/////////////////////////////////////////////////////////////////////////////////
+inline ChunkHeader LoadChunk(uint8_t*& outFile)
+{
+ ChunkHeader head;
+ ::memcpy(&head.type, outFile, 4);
+ outFile += 4;
+ ::memcpy(&head.length, outFile, 4);
+ outFile += 4;
+ AI_LSWAP4(head.length);
+ AI_LSWAP4(head.type);
+ return head;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Load a sub chunk header
+//! @param outFile Pointer to the file data - points to the chunk data afterwards
+//! @return Copy of the sub chunk header
+/////////////////////////////////////////////////////////////////////////////////
+inline SubChunkHeader LoadSubChunk(uint8_t*& outFile)
+{
+ SubChunkHeader head;
+ ::memcpy(&head.type, outFile, 4);
+ outFile += 4;
+ ::memcpy(&head.length, outFile, 2);
+ outFile += 2;
+ AI_LSWAP2(head.length);
+ AI_LSWAP4(head.type);
+ return head;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Read the file header and return the type of the file and its size
+//! @param outFile Pointer to the file data. The buffer must at
+//! least be 12 bytes large.
+//! @param fileType Receives the type of the file
+//! @return 0 if everything was OK, otherwise an error message
+/////////////////////////////////////////////////////////////////////////////////
+inline const char* ReadHeader(uint8_t* outFile, uint32_t& fileType)
+{
+ ChunkHeader head = LoadChunk(outFile);
+ if(AI_IFF_FOURCC_FORM != head.type)
+ {
+ return "The file is not an IFF file: FORM chunk is missing";
+ }
+ ::memcpy(&fileType, outFile, 4);
+ AI_LSWAP4(fileType);
+ return 0;
+}
+
+
+}}
+
+#endif // !! AI_IFF_H_INCLUDED
diff --git a/thirdparty/assimp/code/Importer.cpp b/thirdparty/assimp/code/Common/Importer.cpp
index 65b16471cc..91b50859a0 100644
--- a/thirdparty/assimp/code/Importer.cpp
+++ b/thirdparty/assimp/code/Common/Importer.cpp
@@ -64,15 +64,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// ------------------------------------------------------------------------------------------------
// Internal headers
// ------------------------------------------------------------------------------------------------
-#include "Importer.h"
-#include <assimp/BaseImporter.h>
-#include "BaseProcess.h"
+#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 "DefaultProgressHandler.h"
+#include <assimp/BaseImporter.h>
#include <assimp/GenericProperty.h>
-#include "ProcessHelper.h"
-#include "ScenePreprocessor.h"
-#include "ScenePrivate.h"
#include <assimp/MemoryIOWrapper.h>
#include <assimp/Profiler.h>
#include <assimp/TinyFormatter.h>
@@ -86,7 +86,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/DefaultIOSystem.h>
#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-# include "ValidateDataStructure.h"
+# include "PostProcessing/ValidateDataStructure.h"
#endif
using namespace Assimp::Profiling;
@@ -590,10 +590,12 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
// 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;
}
}
@@ -606,6 +608,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
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;
}
}
diff --git a/thirdparty/assimp/code/Importer.h b/thirdparty/assimp/code/Common/Importer.h
index a439d99c2f..a439d99c2f 100644
--- a/thirdparty/assimp/code/Importer.h
+++ b/thirdparty/assimp/code/Common/Importer.h
diff --git a/thirdparty/assimp/code/ImporterRegistry.cpp b/thirdparty/assimp/code/Common/ImporterRegistry.cpp
index 747815fa6f..32ac3b4168 100644
--- a/thirdparty/assimp/code/ImporterRegistry.cpp
+++ b/thirdparty/assimp/code/Common/ImporterRegistry.cpp
@@ -56,146 +56,146 @@ corresponding preprocessor flag to selectively disable formats.
// (include_new_importers_here)
// ------------------------------------------------------------------------------------------------
#ifndef ASSIMP_BUILD_NO_X_IMPORTER
-# include "XFileImporter.h"
+# include "X/XFileImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
-# include "AMFImporter.hpp"
+# include "AMF/AMFImporter.hpp"
#endif
#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
-# include "3DSLoader.h"
+# include "3DS/3DSLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER
-# include "MD3Loader.h"
+# include "MD3/MD3Loader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
-# include "MDLLoader.h"
+# include "MDL/MDLLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER
-# include "MD2Loader.h"
+# include "MD2/MD2Loader.h"
#endif
#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
-# include "PlyLoader.h"
+# include "Ply/PlyLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
-# include "ASELoader.h"
+# include "ASE/ASELoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
-# include "ObjFileImporter.h"
+# include "Obj/ObjFileImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_HMP_IMPORTER
-# include "HMPLoader.h"
+# include "HMP/HMPLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
-# include "SMDLoader.h"
+# include "SMD/SMDLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER
-# include "MDCLoader.h"
+# include "MDC/MDCLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
-# include "MD5Loader.h"
+# include "MD5/MD5Loader.h"
#endif
#ifndef ASSIMP_BUILD_NO_STL_IMPORTER
-# include "STLLoader.h"
+# include "STL/STLLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
-# include "LWOLoader.h"
+# include "LWO/LWOLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
-# include "DXFLoader.h"
+# include "DXF/DXFLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER
-# include "NFFLoader.h"
+# include "NFF/NFFLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
-# include "RawLoader.h"
+# include "Raw/RawLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_SIB_IMPORTER
-# include "SIBImporter.h"
+# include "SIB/SIBImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
-# include "OFFLoader.h"
+# include "OFF/OFFLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_AC_IMPORTER
-# include "ACLoader.h"
+# include "AC/ACLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
-# include "BVHLoader.h"
+# include "BVH/BVHLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER
-# include "IRRMeshLoader.h"
+# include "Irr/IRRMeshLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
-# include "IRRLoader.h"
+# include "Irr/IRRLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
-# include "Q3DLoader.h"
+# include "Q3D/Q3DLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
-# include "B3DImporter.h"
+# include "B3D/B3DImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
-# include "ColladaLoader.h"
+# include "Collada/ColladaLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
-# include "TerragenLoader.h"
+# include "Terragen/TerragenLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_CSM_IMPORTER
-# include "CSMLoader.h"
+# include "CSM/CSMLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_3D_IMPORTER
-# include "UnrealLoader.h"
+# include "Unreal/UnrealLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_LWS_IMPORTER
-# include "LWSLoader.h"
+# include "LWS/LWSLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
-# include "OgreImporter.h"
+# include "Ogre/OgreImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER
-# include "OpenGEXImporter.h"
+# include "OpenGEX/OpenGEXImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER
-# include "MS3DLoader.h"
+# include "MS3D/MS3DLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_COB_IMPORTER
-# include "COBLoader.h"
+# include "COB/COBLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
-# include "BlenderLoader.h"
+# include "Blender/BlenderLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
-# include "Q3BSPFileImporter.h"
+# include "Q3BSP/Q3BSPFileImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_NDO_IMPORTER
-# include "NDOLoader.h"
+# include "NDO/NDOLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
# include "Importer/IFC/IFCLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_XGL_IMPORTER
-# include "XGLLoader.h"
+# include "XGL/XGLLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
-# include "FBXImporter.h"
+# include "FBX/FBXImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
-# include "AssbinLoader.h"
+# include "Assbin/AssbinLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
-# include "glTFImporter.h"
-# include "glTF2Importer.h"
+# include "glTF/glTFImporter.h"
+# include "glTF2/glTF2Importer.h"
#endif
#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
-# include "C4DImporter.h"
+# include "C4D/C4DImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
-# include "D3MFImporter.h"
+# include "3MF/D3MFImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
-# include "X3DImporter.hpp"
+# include "X3D/X3DImporter.hpp"
#endif
#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
-# include "MMDImporter.h"
+# include "MMD/MMDImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER
# include "Importer/StepFile/StepFileImporter.h"
@@ -364,7 +364,7 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
void DeleteImporterInstanceList(std::vector< BaseImporter* >& deleteList){
for(size_t i= 0; i<deleteList.size();++i){
delete deleteList[i];
- deleteList[i]=NULL;
+ deleteList[i]=nullptr;
}//for
}
diff --git a/thirdparty/assimp/code/PolyTools.h b/thirdparty/assimp/code/Common/PolyTools.h
index fbbda0e7d1..fbbda0e7d1 100644
--- a/thirdparty/assimp/code/PolyTools.h
+++ b/thirdparty/assimp/code/Common/PolyTools.h
diff --git a/thirdparty/assimp/code/PostStepRegistry.cpp b/thirdparty/assimp/code/Common/PostStepRegistry.cpp
index 15b4a28843..ef58f8ddfd 100644
--- a/thirdparty/assimp/code/PostStepRegistry.cpp
+++ b/thirdparty/assimp/code/Common/PostStepRegistry.cpp
@@ -48,89 +48,93 @@ directly (unless you are adding new steps), instead use the
corresponding preprocessor flag to selectively disable steps.
*/
-#include "ProcessHelper.h"
+#include "PostProcessing/ProcessHelper.h"
#ifndef ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS
-# include "CalcTangentsProcess.h"
+# include "PostProcessing/CalcTangentsProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
-# include "JoinVerticesProcess.h"
+# 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 "ConvertToLHProcess.h"
+# include "PostProcessing/ConvertToLHProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
-# include "TriangulateProcess.h"
+# include "PostProcessing/TriangulateProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_DROPFACENORMALS_PROCESS
-# include "DropFaceNormalsProcess.h"
+# include "PostProcessing/DropFaceNormalsProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS
-# include "GenFaceNormalsProcess.h"
+# include "PostProcessing/GenFaceNormalsProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS
-# include "GenVertexNormalsProcess.h"
+# include "PostProcessing/GenVertexNormalsProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_REMOVEVC_PROCESS
-# include "RemoveVCProcess.h"
+# include "PostProcessing/RemoveVCProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS
-# include "SplitLargeMeshes.h"
+# include "PostProcessing/SplitLargeMeshes.h"
#endif
#ifndef ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS
-# include "PretransformVertices.h"
+# include "PostProcessing/PretransformVertices.h"
#endif
#ifndef ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS
-# include "LimitBoneWeightsProcess.h"
+# include "PostProcessing/LimitBoneWeightsProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-# include "ValidateDataStructure.h"
+# include "PostProcessing/ValidateDataStructure.h"
#endif
#ifndef ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS
-# include "ImproveCacheLocality.h"
+# include "PostProcessing/ImproveCacheLocality.h"
#endif
#ifndef ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS
-# include "FixNormalsStep.h"
+# include "PostProcessing/FixNormalsStep.h"
#endif
#ifndef ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS
-# include "RemoveRedundantMaterials.h"
+# include "PostProcessing/RemoveRedundantMaterials.h"
#endif
#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS)
-# include "EmbedTexturesProcess.h"
+# include "PostProcessing/EmbedTexturesProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
-# include "FindInvalidDataProcess.h"
+# include "PostProcessing/FindInvalidDataProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS
-# include "FindDegenerates.h"
+# include "PostProcessing/FindDegenerates.h"
#endif
#ifndef ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS
-# include "SortByPTypeProcess.h"
+# include "PostProcessing/SortByPTypeProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS
-# include "ComputeUVMappingProcess.h"
+# include "PostProcessing/ComputeUVMappingProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
-# include "TextureTransform.h"
+# include "PostProcessing/TextureTransform.h"
#endif
#ifndef ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS
-# include "FindInstancesProcess.h"
+# include "PostProcessing/FindInstancesProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS
-# include "OptimizeMeshes.h"
+# include "PostProcessing/OptimizeMeshes.h"
#endif
#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
-# include "OptimizeGraph.h"
+# include "PostProcessing/OptimizeGraph.h"
#endif
#ifndef ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS
-# include "SplitByBoneCountProcess.h"
+# include "Common/SplitByBoneCountProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS
-# include "DeboneProcess.h"
+# include "PostProcessing/DeboneProcess.h"
#endif
#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
-# include "ScaleProcess.h"
+# include "PostProcessing/ScaleProcess.h"
#endif
+#if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS)
+# include "PostProcessing/GenBoundingBoxesProcess.h"
+#endif
+
namespace Assimp {
@@ -246,6 +250,9 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out)
#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/RemoveComments.cpp b/thirdparty/assimp/code/Common/RemoveComments.cpp
index 91700a7699..91700a7699 100644
--- a/thirdparty/assimp/code/RemoveComments.cpp
+++ b/thirdparty/assimp/code/Common/RemoveComments.cpp
diff --git a/thirdparty/assimp/code/SGSpatialSort.cpp b/thirdparty/assimp/code/Common/SGSpatialSort.cpp
index 120070b0aa..120070b0aa 100644
--- a/thirdparty/assimp/code/SGSpatialSort.cpp
+++ b/thirdparty/assimp/code/Common/SGSpatialSort.cpp
diff --git a/thirdparty/assimp/code/SceneCombiner.cpp b/thirdparty/assimp/code/Common/SceneCombiner.cpp
index e445bd7434..e445bd7434 100644
--- a/thirdparty/assimp/code/SceneCombiner.cpp
+++ b/thirdparty/assimp/code/Common/SceneCombiner.cpp
diff --git a/thirdparty/assimp/code/ScenePreprocessor.cpp b/thirdparty/assimp/code/Common/ScenePreprocessor.cpp
index 432a3d7666..432a3d7666 100644
--- a/thirdparty/assimp/code/ScenePreprocessor.cpp
+++ b/thirdparty/assimp/code/Common/ScenePreprocessor.cpp
diff --git a/thirdparty/assimp/code/ScenePreprocessor.h b/thirdparty/assimp/code/Common/ScenePreprocessor.h
index 3f4c8d7c3f..3f4c8d7c3f 100644
--- a/thirdparty/assimp/code/ScenePreprocessor.h
+++ b/thirdparty/assimp/code/Common/ScenePreprocessor.h
diff --git a/thirdparty/assimp/code/ScenePrivate.h b/thirdparty/assimp/code/Common/ScenePrivate.h
index f336aafc9a..f336aafc9a 100644
--- a/thirdparty/assimp/code/ScenePrivate.h
+++ b/thirdparty/assimp/code/Common/ScenePrivate.h
diff --git a/thirdparty/assimp/code/SkeletonMeshBuilder.cpp b/thirdparty/assimp/code/Common/SkeletonMeshBuilder.cpp
index 06cfe034e9..06cfe034e9 100644
--- a/thirdparty/assimp/code/SkeletonMeshBuilder.cpp
+++ b/thirdparty/assimp/code/Common/SkeletonMeshBuilder.cpp
diff --git a/thirdparty/assimp/code/SpatialSort.cpp b/thirdparty/assimp/code/Common/SpatialSort.cpp
index a4f3a4e4b8..a4f3a4e4b8 100644
--- a/thirdparty/assimp/code/SpatialSort.cpp
+++ b/thirdparty/assimp/code/Common/SpatialSort.cpp
diff --git a/thirdparty/assimp/code/SplitByBoneCountProcess.cpp b/thirdparty/assimp/code/Common/SplitByBoneCountProcess.cpp
index 2ef66a9afc..2ef66a9afc 100644
--- a/thirdparty/assimp/code/SplitByBoneCountProcess.cpp
+++ b/thirdparty/assimp/code/Common/SplitByBoneCountProcess.cpp
diff --git a/thirdparty/assimp/code/SplitByBoneCountProcess.h b/thirdparty/assimp/code/Common/SplitByBoneCountProcess.h
index 6c904a9df4..6c904a9df4 100644
--- a/thirdparty/assimp/code/SplitByBoneCountProcess.h
+++ b/thirdparty/assimp/code/Common/SplitByBoneCountProcess.h
diff --git a/thirdparty/assimp/code/StandardShapes.cpp b/thirdparty/assimp/code/Common/StandardShapes.cpp
index 2e5100130f..2e5100130f 100644
--- a/thirdparty/assimp/code/StandardShapes.cpp
+++ b/thirdparty/assimp/code/Common/StandardShapes.cpp
diff --git a/thirdparty/assimp/code/StdOStreamLogStream.h b/thirdparty/assimp/code/Common/StdOStreamLogStream.h
index 893e261a2b..893e261a2b 100644
--- a/thirdparty/assimp/code/StdOStreamLogStream.h
+++ b/thirdparty/assimp/code/Common/StdOStreamLogStream.h
diff --git a/thirdparty/assimp/code/Subdivision.cpp b/thirdparty/assimp/code/Common/Subdivision.cpp
index 19db223a55..60c54939f5 100644
--- a/thirdparty/assimp/code/Subdivision.cpp
+++ b/thirdparty/assimp/code/Common/Subdivision.cpp
@@ -43,9 +43,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/Subdivision.h>
#include <assimp/SceneCombiner.h>
#include <assimp/SpatialSort.h>
-#include "ProcessHelper.h"
#include <assimp/Vertex.h>
#include <assimp/ai_assert.h>
+
+#include "PostProcessing/ProcessHelper.h"
+
#include <stdio.h>
using namespace Assimp;
@@ -56,8 +58,7 @@ void mydummy() {}
* 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
-{
+class CatmullClarkSubdivider : public Subdivider {
public:
void Subdivide (aiMesh* mesh, aiMesh*& out, unsigned int num, bool discard_input);
void Subdivide (aiMesh** smesh, size_t nmesh,
diff --git a/thirdparty/assimp/code/TargetAnimation.cpp b/thirdparty/assimp/code/Common/TargetAnimation.cpp
index b8062499ff..b8062499ff 100644
--- a/thirdparty/assimp/code/TargetAnimation.cpp
+++ b/thirdparty/assimp/code/Common/TargetAnimation.cpp
diff --git a/thirdparty/assimp/code/TargetAnimation.h b/thirdparty/assimp/code/Common/TargetAnimation.h
index 91634ab5aa..91634ab5aa 100644
--- a/thirdparty/assimp/code/TargetAnimation.h
+++ b/thirdparty/assimp/code/Common/TargetAnimation.h
diff --git a/thirdparty/assimp/code/Version.cpp b/thirdparty/assimp/code/Common/Version.cpp
index 0381037ff1..cc94340ac8 100644
--- a/thirdparty/assimp/code/Version.cpp
+++ b/thirdparty/assimp/code/Common/Version.cpp
@@ -134,7 +134,7 @@ ASSIMP_API aiScene::aiScene()
, mCameras(nullptr)
, mMetaData(nullptr)
, mPrivate(new Assimp::ScenePrivateData()) {
- // empty
+ // empty
}
// ------------------------------------------------------------------------------------------------
diff --git a/thirdparty/assimp/code/VertexTriangleAdjacency.cpp b/thirdparty/assimp/code/Common/VertexTriangleAdjacency.cpp
index 7cfd1a3505..7cfd1a3505 100644
--- a/thirdparty/assimp/code/VertexTriangleAdjacency.cpp
+++ b/thirdparty/assimp/code/Common/VertexTriangleAdjacency.cpp
diff --git a/thirdparty/assimp/code/VertexTriangleAdjacency.h b/thirdparty/assimp/code/Common/VertexTriangleAdjacency.h
index f3be47612d..f3be47612d 100644
--- a/thirdparty/assimp/code/VertexTriangleAdjacency.h
+++ b/thirdparty/assimp/code/Common/VertexTriangleAdjacency.h
diff --git a/thirdparty/assimp/code/Win32DebugLogStream.h b/thirdparty/assimp/code/Common/Win32DebugLogStream.h
index a6063a261e..a6063a261e 100644
--- a/thirdparty/assimp/code/Win32DebugLogStream.h
+++ b/thirdparty/assimp/code/Common/Win32DebugLogStream.h
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
+(<tt>.assbin</tt>) is fast loading. Optional deflate compression helps reduce file size. The XML
+flavour, <tt>.assxml</tt> or simply .xml, is just a plain-to-xml conversion of aiScene.
+
+ASSBIN is Assimp's binary interchange format. assimp_cmd (<tt>&lt;root&gt;/tools/assimp_cmd</tt>) is able to
+write it and the core library provides a loader for it.
+
+@section assxml XML File format
+
+The format is pretty much self-explanatory due to its similarity to the in-memory aiScene structure.
+With few exceptions, C structures are wrapped in XML elements.
+
+The DTD for ASSXML can be found in <tt>&lt;root&gt;/doc/AssXML_Scheme.xml</tt>. Or have look
+at the output files generated by assimp_cmd.
+
+@section assbin Binary file format
+
+The ASSBIN file format is composed of chunks to represent the hierarchical aiScene data structure.
+This makes the format extensible and allows backward-compatibility with future data structure
+versions. The <tt>&lt;root&gt;/code/assbin_chunks.h</tt> header contains some magic constants
+for use by stand-alone ASSBIN loaders. Also, Assimp's own file writer can be found
+in <tt>&lt;root&gt;/tools/assimp_cmd/WriteDumb.cpp</tt> (yes, the 'b' is no typo ...).
+
+@verbatim
+
+-------------------------------------------------------------------------------
+1. File structure:
+-------------------------------------------------------------------------------
+
+----------------------
+| Header (512 bytes) |
+----------------------
+| Variable chunks |
+----------------------
+
+-------------------------------------------------------------------------------
+2. Definitions:
+-------------------------------------------------------------------------------
+
+integer is four bytes wide, stored in little-endian byte order.
+short is two bytes wide, stored in little-endian byte order.
+byte is a single byte.
+string is an integer n followed by n UTF-8 characters, not terminated by zero
+float is an IEEE 754 single-precision floating-point value
+double is an IEEE 754 double-precision floating-point value
+t[n] is an array of n elements of type t
+
+-------------------------------------------------------------------------------
+2. Header:
+-------------------------------------------------------------------------------
+
+byte[44] Magic identification string for ASSBIN files.
+ 'ASSIMP.binary'
+
+integer Major version of the Assimp library which wrote the file
+integer Minor version of the Assimp library which wrote the file
+ match these against ASSBIN_VERSION_MAJOR and ASSBIN_VERSION_MINOR
+
+integer SVN revision of the Assimp library (intended for our internal
+ debugging - if you write Ass files from your own APPs, set this value to 0.
+integer Assimp compile flags
+
+short 0 for normal files, 1 for shortened dumps for regression tests
+ these should have the file extension assbin.regress
+
+short 1 if the data after the header is compressed with the DEFLATE algorithm,
+ 0 for uncompressed files.
+ For compressed files, the first integer after the header is
+ always the uncompressed data size
+
+byte[256] Zero-terminated source file name, UTF-8
+byte[128] Zero-terminated command line parameters passed to assimp_cmd, UTF-8
+
+byte[64] Reserved for future use
+---> Total length: 512 bytes
+
+-------------------------------------------------------------------------------
+3. Chunks:
+-------------------------------------------------------------------------------
+
+integer Magic chunk ID (ASSBIN_CHUNK_XXX)
+integer Chunk data length, in bytes
+ (unknown chunks are possible, a good reader skips over them)
+ (chunk-data-length does not include the first two integers)
+
+byte[n] chunk-data-length bytes of data, depending on the chunk type
+
+Chunks can contain nested chunks. Nested chunks are ALWAYS at the end of the chunk,
+their size is included in chunk-data-length.
+
+The chunk layout for all ASSIMP data structures is derived from their C declarations.
+The general 'rule' to get from Assimp headers to the serialized layout is:
+
+ 1. POD members (i.e. aiMesh::mPrimitiveTypes, aiMesh::mNumVertices),
+ in order of declaration.
+
+ 2. Array-members (aiMesh::mFaces, aiMesh::mVertices, aiBone::mWeights),
+ in order of declaration.
+
+ 2. Object array members (i.e aiMesh::mBones, aiScene::mMeshes) are stored in
+ subchunks directly following the data written in 1.) and 2.)
+
+
+ Of course, there are some exceptions to this general order:
+
+[[aiScene]]
+
+ - The root node holding the scene structure is naturally stored in
+ a ASSBIN_CHUNK_AINODE subchunk following 1.) and 2.) (which is
+ empty for aiScene).
+
+[[aiMesh]]
+
+ - mTextureCoords and mNumUVComponents are serialized as follows:
+
+ [number of used uv channels times]
+ integer mNumUVComponents[n]
+ float mTextureCoords[n][3]
+
+ -> more than AI_MAX_TEXCOORD_CHANNELS can be stored. This allows Assimp
+ builds with different settings for AI_MAX_TEXCOORD_CHANNELS to exchange
+ data.
+ -> the on-disk format always uses 3 floats to write UV coordinates.
+ If mNumUVComponents[0] is 1, the corresponding mTextureCoords array
+ consists of 3 floats.
+
+ - The array member block of aiMesh is prefixed with an integer that specifies
+ the kinds of vertex components actually present in the mesh. This is a
+ bitwise combination of the ASSBIN_MESH_HAS_xxx constants.
+
+[[aiFace]]
+
+ - mNumIndices is stored as short
+ - mIndices are written as short, if aiMesh::mNumVertices<65536
+
+[[aiNode]]
+
+ - mParent is omitted
+
+[[aiLight]]
+
+ - mAttenuationXXX not written if aiLight::mType == aiLightSource_DIRECTIONAL
+ - mAngleXXX not written if aiLight::mType != aiLightSource_SPOT
+
+[[aiMaterial]]
+
+ - mNumAllocated is omitted, for obvious reasons :-)
+
+
+ @endverbatim*/
+
+
+#define ASSBIN_HEADER_LENGTH 512
+
+// these are the magic chunk identifiers for the binary ASS file format
+#define ASSBIN_CHUNK_AICAMERA 0x1234
+#define ASSBIN_CHUNK_AILIGHT 0x1235
+#define ASSBIN_CHUNK_AITEXTURE 0x1236
+#define ASSBIN_CHUNK_AIMESH 0x1237
+#define ASSBIN_CHUNK_AINODEANIM 0x1238
+#define ASSBIN_CHUNK_AISCENE 0x1239
+#define ASSBIN_CHUNK_AIBONE 0x123a
+#define ASSBIN_CHUNK_AIANIMATION 0x123b
+#define ASSBIN_CHUNK_AINODE 0x123c
+#define ASSBIN_CHUNK_AIMATERIAL 0x123d
+#define ASSBIN_CHUNK_AIMATERIALPROPERTY 0x123e
+
+#define ASSBIN_MESH_HAS_POSITIONS 0x1
+#define ASSBIN_MESH_HAS_NORMALS 0x2
+#define ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS 0x4
+#define ASSBIN_MESH_HAS_TEXCOORD_BASE 0x100
+#define ASSBIN_MESH_HAS_COLOR_BASE 0x10000
+
+#define ASSBIN_MESH_HAS_TEXCOORD(n) (ASSBIN_MESH_HAS_TEXCOORD_BASE << n)
+#define ASSBIN_MESH_HAS_COLOR(n) (ASSBIN_MESH_HAS_COLOR_BASE << n)
+
+
+#endif // INCLUDED_ASSBIN_CHUNKS_H
diff --git a/thirdparty/assimp/code/scene.cpp b/thirdparty/assimp/code/Common/scene.cpp
index 2acb348d81..2acb348d81 100644
--- a/thirdparty/assimp/code/scene.cpp
+++ b/thirdparty/assimp/code/Common/scene.cpp
diff --git a/thirdparty/assimp/code/simd.cpp b/thirdparty/assimp/code/Common/simd.cpp
index 04615f408e..04615f408e 100644
--- a/thirdparty/assimp/code/simd.cpp
+++ b/thirdparty/assimp/code/Common/simd.cpp
diff --git a/thirdparty/assimp/code/simd.h b/thirdparty/assimp/code/Common/simd.h
index 3eecdd4581..3eecdd4581 100644
--- a/thirdparty/assimp/code/simd.h
+++ b/thirdparty/assimp/code/Common/simd.h
diff --git a/thirdparty/assimp/code/FBXAnimation.cpp b/thirdparty/assimp/code/FBX/FBXAnimation.cpp
index 874914431b..874914431b 100644
--- a/thirdparty/assimp/code/FBXAnimation.cpp
+++ b/thirdparty/assimp/code/FBX/FBXAnimation.cpp
diff --git a/thirdparty/assimp/code/FBXBinaryTokenizer.cpp b/thirdparty/assimp/code/FBX/FBXBinaryTokenizer.cpp
index 7138df4315..a4a2bc8e79 100644
--- a/thirdparty/assimp/code/FBXBinaryTokenizer.cpp
+++ b/thirdparty/assimp/code/FBX/FBXBinaryTokenizer.cpp
@@ -98,7 +98,7 @@ namespace FBX {
// return (flags & to_check) != 0;
//}
// ------------------------------------------------------------------------------------------------
-Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int offset)
+Token::Token(const char* sbegin, const char* send, TokenType type, size_t offset)
:
#ifdef DEBUG
contents(sbegin, static_cast<size_t>(send-sbegin)),
@@ -122,18 +122,18 @@ 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)
+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));
}
// ------------------------------------------------------------------------------------------------
-uint32_t Offset(const char* begin, const char* cursor) {
+size_t Offset(const char* begin, const char* cursor) {
ai_assert(begin <= cursor);
- return static_cast<unsigned int>(cursor - begin);
+ return cursor - begin;
}
// ------------------------------------------------------------------------------------------------
@@ -424,7 +424,7 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
// ------------------------------------------------------------------------------------------------
// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
-void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int length)
+void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
{
ai_assert(input);
diff --git a/thirdparty/assimp/code/FBXCommon.h b/thirdparty/assimp/code/FBX/FBXCommon.h
index fcb20a5cad..e516449130 100644
--- a/thirdparty/assimp/code/FBXCommon.h
+++ b/thirdparty/assimp/code/FBX/FBXCommon.h
@@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
-
+namespace Assimp {
namespace FBX
{
const std::string NULL_RECORD = { // 13 null bytes
@@ -80,7 +80,7 @@ namespace FBX
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/FBX/FBXCompileConfig.h
index 3a3841fa5b..3a3841fa5b 100644
--- a/thirdparty/assimp/code/FBXCompileConfig.h
+++ b/thirdparty/assimp/code/FBX/FBXCompileConfig.h
diff --git a/thirdparty/assimp/code/FBXConverter.cpp b/thirdparty/assimp/code/FBX/FBXConverter.cpp
index 09ae06a64f..9bd970098e 100644
--- a/thirdparty/assimp/code/FBXConverter.cpp
+++ b/thirdparty/assimp/code/FBX/FBXConverter.cpp
@@ -66,6 +66,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <vector>
#include <sstream>
#include <iomanip>
+#include <cstdint>
+
namespace Assimp {
namespace FBX {
@@ -76,20 +78,20 @@ namespace Assimp {
#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L
- FBXConverter::FBXConverter(aiScene* out, const Document& doc)
- : defaultMaterialIndex()
- , lights()
- , cameras()
- , textures()
- , materials_converted()
- , textures_converted()
- , meshes_converted()
- , node_anim_chain_bits()
- , mNodeNameInstances()
- , mNodeNames()
- , anim_fps()
- , out(out)
- , doc(doc) {
+ 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)
+ , 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.
@@ -138,12 +140,46 @@ namespace Assimp {
void FBXConverter::ConvertRootNode() {
out->mRootNode = new aiNode();
- out->mRootNode->mName.Set("RootNode");
+ 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<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
@@ -175,35 +211,18 @@ namespace Assimp {
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.
- GenerateTransformationNodeChain(*model, nodes_chain, post_nodes_chain);
+ const bool need_additional_node = GenerateTransformationNodeChain(*model, unique_name, 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();
+ if (need_additional_node) {
+ nodes_chain.push_back(new aiNode(unique_name));
}
//setup metadata on newest node
@@ -265,11 +284,11 @@ namespace Assimp {
ConvertNodes(model->ID(), *last_parent, new_abs_transform);
if (doc.Settings().readLights) {
- ConvertLights(*model, original_name);
+ ConvertLights(*model, unique_name);
}
if (doc.Settings().readCameras) {
- ConvertCameras(*model, original_name);
+ ConvertCameras(*model, unique_name);
}
nodes.push_back(nodes_chain.front());
@@ -387,6 +406,7 @@ namespace Assimp {
break;
default:
ai_assert(false);
+ break;
}
}
@@ -399,11 +419,6 @@ namespace Assimp {
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);
@@ -421,21 +436,16 @@ namespace Assimp {
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())
+ auto it_pair = mNodeNames.insert({ name, 0 }); // duplicate node name instance count
+ unsigned int& i = it_pair.first->second;
+ while (!it_pair.second)
{
- 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();
- }
+ i++;
+ std::ostringstream ext;
+ ext << name << std::setfill('0') << std::setw(3) << i;
+ uniqueName = ext.str();
+ it_pair = mNodeNames.insert({ uniqueName, 0 });
}
- mNodeNameInstances[name] = i;
- mNodeNames.insert(uniqueName);
}
const char* FBXConverter::NameTransformationComp(TransformationComp comp) {
@@ -651,8 +661,7 @@ namespace Assimp {
if ((v - all_ones).SquareLength() > zero_epsilon) {
return true;
}
- }
- else if (ok) {
+ } else if (ok) {
if (v.SquareLength() > zero_epsilon) {
return true;
}
@@ -667,7 +676,7 @@ namespace Assimp {
return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp);
}
- void FBXConverter::GenerateTransformationNodeChain(const Model& model, std::vector<aiNode*>& output_nodes,
+ bool FBXConverter::GenerateTransformationNodeChain(const Model& model, const std::string& name, std::vector<aiNode*>& output_nodes,
std::vector<aiNode*>& post_output_nodes) {
const PropertyTable& props = model.Props();
const Model::RotOrder rot = model.RotationOrder();
@@ -675,30 +684,37 @@ namespace Assimp {
bool ok;
aiMatrix4x4 chain[TransformationComp_MAXIMUM];
+
+ ai_assert(TransformationComp_MAXIMUM < 32);
+ std::uint32_t chainBits = 0;
+ // A node won't need a node chain if it only has these.
+ const std::uint32_t chainMaskSimple = (1 << TransformationComp_Translation) + (1 << TransformationComp_Scaling) + (1 << TransformationComp_Rotation);
+ // A node will need a node chain if it has any of these.
+ const std::uint32_t chainMaskComplex = ((1 << (TransformationComp_MAXIMUM)) - 1) - chainMaskSimple;
+
std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
// generate transformation matrices for all the different transformation components
const float zero_epsilon = 1e-6f;
const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
- bool is_complex = false;
const aiVector3D& PreRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
if (ok && PreRotation.SquareLength() > zero_epsilon) {
- is_complex = true;
+ chainBits = chainBits | (1 << TransformationComp_PreRotation);
GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PreRotation, chain[TransformationComp_PreRotation]);
}
const aiVector3D& PostRotation = PropertyGet<aiVector3D>(props, "PostRotation", ok);
if (ok && PostRotation.SquareLength() > zero_epsilon) {
- is_complex = true;
+ chainBits = chainBits | (1 << TransformationComp_PostRotation);
GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PostRotation, chain[TransformationComp_PostRotation]);
}
const aiVector3D& RotationPivot = PropertyGet<aiVector3D>(props, "RotationPivot", ok);
if (ok && RotationPivot.SquareLength() > zero_epsilon) {
- is_complex = true;
+ chainBits = chainBits | (1 << TransformationComp_RotationPivot) | (1 << TransformationComp_RotationPivotInverse);
aiMatrix4x4::Translation(RotationPivot, chain[TransformationComp_RotationPivot]);
aiMatrix4x4::Translation(-RotationPivot, chain[TransformationComp_RotationPivotInverse]);
@@ -706,21 +722,21 @@ namespace Assimp {
const aiVector3D& RotationOffset = PropertyGet<aiVector3D>(props, "RotationOffset", ok);
if (ok && RotationOffset.SquareLength() > zero_epsilon) {
- is_complex = true;
+ chainBits = chainBits | (1 << TransformationComp_RotationOffset);
aiMatrix4x4::Translation(RotationOffset, chain[TransformationComp_RotationOffset]);
}
const aiVector3D& ScalingOffset = PropertyGet<aiVector3D>(props, "ScalingOffset", ok);
if (ok && ScalingOffset.SquareLength() > zero_epsilon) {
- is_complex = true;
+ chainBits = chainBits | (1 << TransformationComp_ScalingOffset);
aiMatrix4x4::Translation(ScalingOffset, chain[TransformationComp_ScalingOffset]);
}
const aiVector3D& ScalingPivot = PropertyGet<aiVector3D>(props, "ScalingPivot", ok);
if (ok && ScalingPivot.SquareLength() > zero_epsilon) {
- is_complex = true;
+ chainBits = chainBits | (1 << TransformationComp_ScalingPivot) | (1 << TransformationComp_ScalingPivotInverse);
aiMatrix4x4::Translation(ScalingPivot, chain[TransformationComp_ScalingPivot]);
aiMatrix4x4::Translation(-ScalingPivot, chain[TransformationComp_ScalingPivotInverse]);
@@ -728,22 +744,28 @@ namespace Assimp {
const aiVector3D& Translation = PropertyGet<aiVector3D>(props, "Lcl Translation", ok);
if (ok && Translation.SquareLength() > zero_epsilon) {
+ chainBits = chainBits | (1 << TransformationComp_Translation);
+
aiMatrix4x4::Translation(Translation, chain[TransformationComp_Translation]);
}
const aiVector3D& Scaling = PropertyGet<aiVector3D>(props, "Lcl Scaling", ok);
if (ok && (Scaling - all_ones).SquareLength() > zero_epsilon) {
+ chainBits = chainBits | (1 << TransformationComp_Scaling);
+
aiMatrix4x4::Scaling(Scaling, chain[TransformationComp_Scaling]);
}
const aiVector3D& Rotation = PropertyGet<aiVector3D>(props, "Lcl Rotation", ok);
if (ok && Rotation.SquareLength() > zero_epsilon) {
+ chainBits = chainBits | (1 << TransformationComp_Rotation);
+
GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
}
const aiVector3D& GeometricScaling = PropertyGet<aiVector3D>(props, "GeometricScaling", ok);
if (ok && (GeometricScaling - all_ones).SquareLength() > zero_epsilon) {
- is_complex = true;
+ chainBits = chainBits | (1 << TransformationComp_GeometricScaling);
aiMatrix4x4::Scaling(GeometricScaling, chain[TransformationComp_GeometricScaling]);
aiVector3D GeometricScalingInverse = GeometricScaling;
bool canscale = true;
@@ -758,13 +780,14 @@ namespace Assimp {
}
}
if (canscale) {
+ chainBits = chainBits | (1 << TransformationComp_GeometricScalingInverse);
aiMatrix4x4::Scaling(GeometricScalingInverse, chain[TransformationComp_GeometricScalingInverse]);
}
}
const aiVector3D& GeometricRotation = PropertyGet<aiVector3D>(props, "GeometricRotation", ok);
if (ok && GeometricRotation.SquareLength() > zero_epsilon) {
- is_complex = true;
+ chainBits = chainBits | (1 << TransformationComp_GeometricRotation) | (1 << TransformationComp_GeometricRotationInverse);
GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotation]);
GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotationInverse]);
chain[TransformationComp_GeometricRotationInverse].Inverse();
@@ -772,7 +795,7 @@ namespace Assimp {
const aiVector3D& GeometricTranslation = PropertyGet<aiVector3D>(props, "GeometricTranslation", ok);
if (ok && GeometricTranslation.SquareLength() > zero_epsilon) {
- is_complex = true;
+ chainBits = chainBits | (1 << TransformationComp_GeometricTranslation) | (1 << TransformationComp_GeometricTranslationInverse);
aiMatrix4x4::Translation(GeometricTranslation, chain[TransformationComp_GeometricTranslation]);
aiMatrix4x4::Translation(-GeometricTranslation, chain[TransformationComp_GeometricTranslationInverse]);
}
@@ -780,14 +803,12 @@ namespace Assimp {
// 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());
+ ai_assert(NeedsComplexTransformationChain(model) == ((chainBits & chainMaskComplex) != 0));
// 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) {
+ if ((chainBits & chainMaskComplex) && doc.Settings().preservePivots) {
FBXImporter::LogInfo("generating full transformation chain for node: " + name);
// query the anim_chain_bits dictionary to find out which chain elements
@@ -800,7 +821,7 @@ namespace Assimp {
for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
const TransformationComp comp = static_cast<TransformationComp>(i);
- if (chain[i].IsIdentity() && (anim_chain_bitmask & bit) == 0) {
+ if ((chainBits & bit) == 0 && (anim_chain_bitmask & bit) == 0) {
continue;
}
@@ -825,20 +846,20 @@ namespace Assimp {
}
ai_assert(output_nodes.size());
- return;
+ return true;
}
// 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);
+ // 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)
@@ -977,7 +998,9 @@ namespace Assimp {
unsigned int epcount = 0;
for (unsigned i = 0; i < indices.size(); i++)
{
- if (indices[i] < 0) epcount++;
+ if (indices[i] < 0) {
+ epcount++;
+ }
}
unsigned int pcount = static_cast<unsigned int>( indices.size() );
unsigned int scount = out_mesh->mNumFaces = pcount - epcount;
@@ -1237,10 +1260,10 @@ namespace Assimp {
ai_assert(count_faces);
ai_assert(count_vertices);
- // mapping from output indices to DOM indexing, needed to resolve weights
+ // mapping from output indices to DOM indexing, needed to resolve weights or blendshapes
std::vector<unsigned int> reverseMapping;
-
- if (process_weights) {
+ std::map<unsigned int, unsigned int> translateIndexMap;
+ if (process_weights || mesh.GetBlendShapes().size() > 0) {
reverseMapping.resize(count_vertices);
}
@@ -1347,6 +1370,7 @@ namespace Assimp {
if (reverseMapping.size()) {
reverseMapping[cursor] = in_cursor;
+ translateIndexMap[in_cursor] = cursor;
}
out_mesh->mVertices[cursor] = vertices[in_cursor];
@@ -1378,6 +1402,50 @@ namespace Assimp {
ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping);
}
+ std::vector<aiAnimMesh*> animMeshes;
+ for (const BlendShape* blendShape : mesh.GetBlendShapes()) {
+ for (const BlendShapeChannel* blendShapeChannel : blendShape->BlendShapeChannels()) {
+ const std::vector<const ShapeGeometry*>& shapeGeometries = blendShapeChannel->GetShapeGeometries();
+ for (size_t i = 0; i < shapeGeometries.size(); i++) {
+ aiAnimMesh* animMesh = aiCreateAnimMesh(out_mesh);
+ const ShapeGeometry* shapeGeometry = shapeGeometries.at(i);
+ const std::vector<aiVector3D>& vertices = shapeGeometry->GetVertices();
+ const std::vector<aiVector3D>& normals = shapeGeometry->GetNormals();
+ const std::vector<unsigned int>& indices = shapeGeometry->GetIndices();
+ animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name()));
+ for (size_t j = 0; j < indices.size(); j++) {
+ unsigned int index = indices.at(j);
+ aiVector3D vertex = vertices.at(j);
+ aiVector3D normal = normals.at(j);
+ unsigned int count = 0;
+ const unsigned int* outIndices = mesh.ToOutputVertexIndex(index, count);
+ for (unsigned int k = 0; k < count; k++) {
+ unsigned int 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<unsigned int>(numAnimMeshes);
+ out_mesh->mAnimMeshes = new aiAnimMesh*[numAnimMeshes];
+ for (size_t i = 0; i < numAnimMeshes; i++) {
+ out_mesh->mAnimMeshes[i] = animMeshes.at(i);
+ }
+ }
+
return static_cast<unsigned int>(meshes.size() - 1);
}
@@ -1407,14 +1475,8 @@ namespace Assimp {
const WeightIndexArray& indices = cluster->GetIndices();
- if (indices.empty()) {
- continue;
- }
-
const MatIndexArray& mats = geo.GetMaterialIndices();
- bool ok = false;
-
const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
count_out_indices.clear();
@@ -1439,13 +1501,11 @@ namespace Assimp {
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 {
+ } else {
// this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn)
const std::vector<unsigned int>::iterator it = std::lower_bound(
outputVertStartIndices->begin(),
@@ -1456,19 +1516,16 @@ namespace Assimp {
out_indices.push_back(std::distance(outputVertStartIndices->begin(), it));
}
- ++count_out_indices.back();
- ok = true;
+ ++count_out_indices.back();
}
}
}
-
+
// 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,
+ ConvertCluster(bones, model, *cluster, out_indices, index_out_indices,
count_out_indices, node_global_transform);
- }
}
}
catch (std::exception&) {
@@ -1596,6 +1653,13 @@ namespace Assimp {
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<aiShadingMode>(&shadingMode, 1, AI_MATKEY_SHADING_MODEL);
+ }
+
// shading stuff and colors
SetShadingPropertiesCommon(out_mat, props);
SetShadingPropertiesRaw( out_mat, props, material.Textures(), mesh );
@@ -1621,7 +1685,7 @@ namespace Assimp {
out_tex->pcData = reinterpret_cast<aiTexel*>(const_cast<Video&>(video).RelinquishContent());
// try to extract a hint from the file extension
- const std::string& filename = video.FileName().empty() ? video.RelativeFilename() : video.FileName();
+ const std::string& filename = video.RelativeFilename().empty() ? video.FileName() : video.RelativeFilename();
std::string ext = BaseImporter::GetExtension(filename);
if (ext == "jpeg") {
@@ -1632,7 +1696,7 @@ namespace Assimp {
memcpy(out_tex->achFormatHint, ext.c_str(), ext.size());
}
- out_tex->mFilename.Set(video.FileName().c_str());
+ out_tex->mFilename.Set(filename.c_str());
return static_cast<unsigned int>(textures.size() - 1);
}
@@ -1678,9 +1742,8 @@ namespace Assimp {
}
void FBXConverter::TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures,
- const std::string& propName,
- aiTextureType target, const MeshGeometry* const mesh)
- {
+ const std::string& propName,
+ aiTextureType target, const MeshGeometry* const mesh) {
TextureMap::const_iterator it = textures.find(propName);
if (it == textures.end()) {
return;
@@ -3407,8 +3470,9 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
na->mNumScalingKeys = static_cast<unsigned int>(keys.size());
na->mScalingKeys = new aiVectorKey[keys.size()];
- if (keys.size() > 0)
+ if (keys.size() > 0) {
InterpolateKeys(na->mScalingKeys, keys, inputs, aiVector3D(1.0f, 1.0f, 1.0f), maxTime, minTime);
+ }
}
void FBXConverter::ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
@@ -3525,9 +3589,9 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
}
// ------------------------------------------------------------------------------------------------
- void ConvertToAssimpScene(aiScene* out, const Document& doc)
+ void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit)
{
- FBXConverter converter(out, doc);
+ FBXConverter converter(out, doc, removeEmptyBones, unit);
}
} // !FBX
diff --git a/thirdparty/assimp/code/FBXConverter.h b/thirdparty/assimp/code/FBX/FBXConverter.h
index 50637468b9..b458627392 100644
--- a/thirdparty/assimp/code/FBXConverter.h
+++ b/thirdparty/assimp/code/FBX/FBXConverter.h
@@ -52,6 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXUtil.h"
#include "FBXProperties.h"
#include "FBXImporter.h"
+
#include <assimp/anim.h>
#include <assimp/material.h>
#include <assimp/light.h>
@@ -76,12 +77,22 @@ 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 doc Parsed FBX document
+ * @param removeEmptyBones Will remove bones, which do not have any references to vertices.
*/
-void ConvertToAssimpScene(aiScene* out, const Document& doc);
+void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit);
/** Dummy class to encapsulate the conversion process */
class FBXConverter {
@@ -112,7 +123,7 @@ public:
};
public:
- FBXConverter(aiScene* out, const Document& doc);
+ FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit);
~FBXConverter();
private:
@@ -145,6 +156,11 @@ private:
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);
@@ -167,7 +183,7 @@ private:
/**
* note: memory for output_nodes will be managed by the caller
*/
- void GenerateTransformationNodeChain(const Model& model, std::vector<aiNode*>& output_nodes, std::vector<aiNode*>& post_output_nodes);
+ bool GenerateTransformationNodeChain(const Model& model, const std::string& name, std::vector<aiNode*>& output_nodes, std::vector<aiNode*>& post_output_nodes);
// ------------------------------------------------------------------------------------------------
void SetupNodeMetadata(const Model& model, aiNode& nd);
@@ -443,16 +459,14 @@ private:
NodeAnimBitMap node_anim_chain_bits;
// number of nodes with the same name
- using NodeAnimNameMap = std::unordered_map<std::string, unsigned int>;
- NodeAnimNameMap mNodeNameInstances;
-
- using NodeNameCache = std::unordered_set<std::string>;
+ using NodeNameCache = std::unordered_map<std::string, unsigned int>;
NodeNameCache mNodeNames;
double anim_fps;
aiScene* const out;
const FBX::Document& doc;
+ FbxUnit mCurrentUnit;
};
}
diff --git a/thirdparty/assimp/code/FBXDeformer.cpp b/thirdparty/assimp/code/FBX/FBXDeformer.cpp
index 6927553450..6927553450 100644
--- a/thirdparty/assimp/code/FBXDeformer.cpp
+++ b/thirdparty/assimp/code/FBX/FBXDeformer.cpp
diff --git a/thirdparty/assimp/code/FBXDocument.cpp b/thirdparty/assimp/code/FBX/FBXDocument.cpp
index 1af08fe6d8..506fd978dd 100644
--- a/thirdparty/assimp/code/FBXDocument.cpp
+++ b/thirdparty/assimp/code/FBX/FBXDocument.cpp
@@ -90,14 +90,6 @@ const Object* LazyObject::Get(bool dieOnError)
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();
diff --git a/thirdparty/assimp/code/FBXDocument.h b/thirdparty/assimp/code/FBX/FBXDocument.h
index c849defdcd..18e5c38f13 100644
--- a/thirdparty/assimp/code/FBXDocument.h
+++ b/thirdparty/assimp/code/FBX/FBXDocument.h
@@ -627,7 +627,7 @@ public:
return content;
}
- uint32_t ContentLength() const {
+ uint64_t ContentLength() const {
return contentLength;
}
@@ -643,7 +643,7 @@ private:
std::string fileName;
std::shared_ptr<const PropertyTable> props;
- uint32_t contentLength;
+ uint64_t contentLength;
uint8_t* content;
};
diff --git a/thirdparty/assimp/code/FBXDocumentUtil.cpp b/thirdparty/assimp/code/FBX/FBXDocumentUtil.cpp
index f84691479a..f84691479a 100644
--- a/thirdparty/assimp/code/FBXDocumentUtil.cpp
+++ b/thirdparty/assimp/code/FBX/FBXDocumentUtil.cpp
diff --git a/thirdparty/assimp/code/FBXDocumentUtil.h b/thirdparty/assimp/code/FBX/FBXDocumentUtil.h
index 2450109e59..2450109e59 100644
--- a/thirdparty/assimp/code/FBXDocumentUtil.h
+++ b/thirdparty/assimp/code/FBX/FBXDocumentUtil.h
diff --git a/thirdparty/assimp/code/FBXExportNode.cpp b/thirdparty/assimp/code/FBX/FBXExportNode.cpp
index e5215466a1..06c89cee46 100644
--- a/thirdparty/assimp/code/FBXExportNode.cpp
+++ b/thirdparty/assimp/code/FBX/FBXExportNode.cpp
@@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream> // ostringstream
#include <memory> // shared_ptr
+namespace Assimp {
// AddP70<type> helpers... there's no usable pattern here,
// so all are defined as separate functions.
// Even "animatable" properties are often completely different
@@ -252,7 +253,8 @@ void FBX::Node::DumpChildren(
} else {
std::ostringstream ss;
DumpChildrenAscii(ss, indent);
- s.PutString(ss.str());
+ if (ss.tellp() > 0)
+ s.PutString(ss.str());
}
}
@@ -266,7 +268,8 @@ void FBX::Node::End(
} else {
std::ostringstream ss;
EndAscii(ss, indent, has_children);
- s.PutString(ss.str());
+ if (ss.tellp() > 0)
+ s.PutString(ss.str());
}
}
@@ -367,7 +370,7 @@ void FBX::Node::EndBinary(
bool has_children
) {
// if there were children, add a null record
- if (has_children) { s.PutString(FBX::NULL_RECORD); }
+ if (has_children) { s.PutString(Assimp::FBX::NULL_RECORD); }
// now go back and write initial pos
this->end_pos = s.Tell();
@@ -563,6 +566,6 @@ void FBX::Node::WritePropertyNode(
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/FBX/FBXExportNode.h
index e1ebc36969..ef3bc781a4 100644
--- a/thirdparty/assimp/code/FBXExportNode.h
+++ b/thirdparty/assimp/code/FBX/FBXExportNode.h
@@ -54,16 +54,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <string>
#include <vector>
+namespace Assimp {
namespace FBX {
class Node;
}
-class FBX::Node
-{
-public: // public data members
+class FBX::Node {
+public:
// TODO: accessors
std::string name; // node name
- std::vector<FBX::Property> properties; // node properties
+ std::vector<FBX::FBXExportProperty> properties; // node properties
std::vector<FBX::Node> children; // child nodes
// some nodes always pretend they have children...
@@ -214,7 +214,7 @@ public: // static member functions
Assimp::StreamWriterLE& s,
bool binary, int indent
) {
- FBX::Property p(value);
+ FBX::FBXExportProperty p(value);
FBX::Node node(name, p);
node.Dump(s, binary, indent);
}
@@ -264,7 +264,7 @@ private: // static helper functions
);
};
-
+}
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
diff --git a/thirdparty/assimp/code/FBXExportProperty.cpp b/thirdparty/assimp/code/FBX/FBXExportProperty.cpp
index 9981d6b1c6..f8593e6295 100644
--- a/thirdparty/assimp/code/FBXExportProperty.cpp
+++ b/thirdparty/assimp/code/FBX/FBXExportProperty.cpp
@@ -52,187 +52,210 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <locale>
#include <sstream> // ostringstream
+namespace Assimp {
+namespace FBX {
// constructors for single element properties
-FBX::Property::Property(bool v)
- : type('C'), data(1)
-{
- data = {uint8_t(v)};
+FBXExportProperty::FBXExportProperty(bool v)
+: type('C')
+, data(1) {
+ data = {
+ uint8_t(v)
+ };
}
-FBX::Property::Property(int16_t v) : type('Y'), data(2)
-{
+FBXExportProperty::FBXExportProperty(int16_t v)
+: type('Y')
+, data(2) {
uint8_t* d = data.data();
(reinterpret_cast<int16_t*>(d))[0] = v;
}
-FBX::Property::Property(int32_t v) : type('I'), data(4)
-{
+FBXExportProperty::FBXExportProperty(int32_t v)
+: type('I')
+, data(4) {
uint8_t* d = data.data();
(reinterpret_cast<int32_t*>(d))[0] = v;
}
-FBX::Property::Property(float v) : type('F'), data(4)
-{
+FBXExportProperty::FBXExportProperty(float v)
+: type('F')
+, data(4) {
uint8_t* d = data.data();
(reinterpret_cast<float*>(d))[0] = v;
}
-FBX::Property::Property(double v) : type('D'), data(8)
-{
+FBXExportProperty::FBXExportProperty(double v)
+: type('D')
+, data(8) {
uint8_t* d = data.data();
(reinterpret_cast<double*>(d))[0] = v;
}
-FBX::Property::Property(int64_t v) : type('L'), data(8)
-{
+FBXExportProperty::FBXExportProperty(int64_t v)
+: type('L')
+, data(8) {
uint8_t* d = data.data();
(reinterpret_cast<int64_t*>(d))[0] = v;
}
-
// constructors for array-type properties
-FBX::Property::Property(const char* c, bool raw)
- : Property(std::string(c), raw)
-{}
+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
-FBX::Property::Property(const std::string& s, bool raw)
- : type(raw ? 'R' : 'S'), data(s.size())
-{
+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]);
}
}
-FBX::Property::Property(const std::vector<uint8_t>& r)
- : type('R'), data(r)
-{}
+FBXExportProperty::FBXExportProperty(const std::vector<uint8_t>& r)
+: type('R')
+, data(r) {
+ // empty
+}
-FBX::Property::Property(const std::vector<int32_t>& va)
- : type('i'), data(4*va.size())
-{
+FBXExportProperty::FBXExportProperty(const std::vector<int32_t>& va)
+: type('i')
+, data(4 * va.size() ) {
int32_t* d = reinterpret_cast<int32_t*>(data.data());
- for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+ for (size_t i = 0; i < va.size(); ++i) {
+ d[i] = va[i];
+ }
}
-FBX::Property::Property(const std::vector<int64_t>& va)
- : type('l'), data(8*va.size())
-{
+FBXExportProperty::FBXExportProperty(const std::vector<int64_t>& va)
+: type('l')
+, data(8 * va.size()) {
int64_t* d = reinterpret_cast<int64_t*>(data.data());
- for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+ for (size_t i = 0; i < va.size(); ++i) {
+ d[i] = va[i];
+ }
}
-FBX::Property::Property(const std::vector<float>& va)
- : type('f'), data(4*va.size())
-{
+FBXExportProperty::FBXExportProperty(const std::vector<float>& va)
+: type('f')
+, data(4 * va.size()) {
float* d = reinterpret_cast<float*>(data.data());
- for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+ for (size_t i = 0; i < va.size(); ++i) {
+ d[i] = va[i];
+ }
}
-FBX::Property::Property(const std::vector<double>& va)
- : type('d'), data(8*va.size())
-{
+FBXExportProperty::FBXExportProperty(const std::vector<double>& va)
+: type('d')
+, data(8 * va.size()) {
double* d = reinterpret_cast<double*>(data.data());
- for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+ for (size_t i = 0; i < va.size(); ++i) {
+ d[i] = va[i];
+ }
}
-FBX::Property::Property(const aiMatrix4x4& vm)
- : type('d'), data(8*16)
-{
+FBXExportProperty::FBXExportProperty(const aiMatrix4x4& vm)
+: type('d')
+, data(8 * 16) {
double* d = reinterpret_cast<double*>(data.data());
for (unsigned int c = 0; c < 4; ++c) {
for (unsigned int r = 0; r < 4; ++r) {
- d[4*c+r] = vm[r][c];
+ d[4 * c + r] = vm[r][c];
}
}
}
// public member functions
-size_t FBX::Property::size()
-{
+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");
+ 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)
-{
+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<uint8_t*>(d))); return;
- case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(d))); return;
- case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(d))); return;
- case 'F': s.PutF4(*(reinterpret_cast<float*>(d))); return;
- case 'D': s.PutF8(*(reinterpret_cast<double*>(d))); return;
- case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(d))); return;
- case 'S':
- case 'R':
- s.PutU4(uint32_t(data.size()));
- for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); }
- return;
- case 'i':
- N = data.size() / 4;
- s.PutU4(uint32_t(N)); // number of elements
- s.PutU4(0); // no encoding (1 would be zip-compressed)
- // TODO: compress if large?
- s.PutU4(uint32_t(data.size())); // data size
- for (size_t i = 0; i < N; ++i) {
- s.PutI4((reinterpret_cast<int32_t*>(d))[i]);
- }
- return;
- case 'l':
- N = data.size() / 8;
- s.PutU4(uint32_t(N)); // number of elements
- s.PutU4(0); // no encoding (1 would be zip-compressed)
- // TODO: compress if large?
- s.PutU4(uint32_t(data.size())); // data size
- for (size_t i = 0; i < N; ++i) {
- s.PutI8((reinterpret_cast<int64_t*>(d))[i]);
- }
- return;
- case 'f':
- N = data.size() / 4;
- s.PutU4(uint32_t(N)); // number of elements
- s.PutU4(0); // no encoding (1 would be zip-compressed)
- // TODO: compress if large?
- s.PutU4(uint32_t(data.size())); // data size
- for (size_t i = 0; i < N; ++i) {
- s.PutF4((reinterpret_cast<float*>(d))[i]);
- }
- return;
- case 'd':
- N = data.size() / 8;
- s.PutU4(uint32_t(N)); // number of elements
- s.PutU4(0); // no encoding (1 would be zip-compressed)
- // TODO: compress if large?
- s.PutU4(uint32_t(data.size())); // data size
- for (size_t i = 0; i < N; ++i) {
- s.PutF8((reinterpret_cast<double*>(d))[i]);
- }
- return;
- default:
- std::ostringstream err;
- err << "Tried to dump property with invalid type '";
- err << type << "'!";
- throw DeadlyExportError(err.str());
+ case 'C': s.PutU1(*(reinterpret_cast<uint8_t*>(d))); return;
+ case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(d))); return;
+ case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(d))); return;
+ case 'F': s.PutF4(*(reinterpret_cast<float*>(d))); return;
+ case 'D': s.PutF8(*(reinterpret_cast<double*>(d))); return;
+ case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(d))); return;
+ case 'S':
+ case 'R':
+ s.PutU4(uint32_t(data.size()));
+ for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); }
+ return;
+ case 'i':
+ N = data.size() / 4;
+ s.PutU4(uint32_t(N)); // number of elements
+ s.PutU4(0); // no encoding (1 would be zip-compressed)
+ // TODO: compress if large?
+ s.PutU4(uint32_t(data.size())); // data size
+ for (size_t i = 0; i < N; ++i) {
+ s.PutI4((reinterpret_cast<int32_t*>(d))[i]);
+ }
+ return;
+ case 'l':
+ N = data.size() / 8;
+ s.PutU4(uint32_t(N)); // number of elements
+ s.PutU4(0); // no encoding (1 would be zip-compressed)
+ // TODO: compress if large?
+ s.PutU4(uint32_t(data.size())); // data size
+ for (size_t i = 0; i < N; ++i) {
+ s.PutI8((reinterpret_cast<int64_t*>(d))[i]);
+ }
+ return;
+ case 'f':
+ N = data.size() / 4;
+ s.PutU4(uint32_t(N)); // number of elements
+ s.PutU4(0); // no encoding (1 would be zip-compressed)
+ // TODO: compress if large?
+ s.PutU4(uint32_t(data.size())); // data size
+ for (size_t i = 0; i < N; ++i) {
+ s.PutF4((reinterpret_cast<float*>(d))[i]);
+ }
+ return;
+ case 'd':
+ N = data.size() / 8;
+ s.PutU4(uint32_t(N)); // number of elements
+ s.PutU4(0); // no encoding (1 would be zip-compressed)
+ // TODO: compress if large?
+ s.PutU4(uint32_t(data.size())); // data size
+ for (size_t i = 0; i < N; ++i) {
+ s.PutF8((reinterpret_cast<double*>(d))[i]);
+ }
+ return;
+ default:
+ std::ostringstream err;
+ err << "Tried to dump property with invalid type '";
+ err << type << "'!";
+ throw DeadlyExportError(err.str());
}
}
-void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent)
-{
+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
@@ -240,8 +263,7 @@ void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent)
outstream.PutString(ss.str());
}
-void FBX::Property::DumpAscii(std::ostream& s, int indent)
-{
+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;
@@ -360,5 +382,8 @@ void FBX::Property::DumpAscii(std::ostream& s, int indent)
}
}
+} // Namespace FBX
+} // Namespace Assimp
+
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT
diff --git a/thirdparty/assimp/code/FBXExportProperty.h b/thirdparty/assimp/code/FBX/FBXExportProperty.h
index 9c9d37c362..d692fe6ee3 100644
--- a/thirdparty/assimp/code/FBXExportProperty.h
+++ b/thirdparty/assimp/code/FBX/FBXExportProperty.h
@@ -47,7 +47,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
-
#include <assimp/types.h> // aiMatrix4x4
#include <assimp/StreamWriter.h> // StreamWriterLE
@@ -56,11 +55,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <ostream>
#include <type_traits> // is_void
+namespace Assimp {
namespace FBX {
- class Property;
-}
-/** FBX::Property
+/** @brief FBX::Property
*
* Holds a value of any of FBX's recognized types,
* each represented by a particular one-character code.
@@ -78,35 +76,34 @@ namespace FBX {
* S : string (array of 1-byte char)
* R : raw data (array of bytes)
*/
-class FBX::Property
-{
+class FBXExportProperty {
public:
// constructors for basic types.
// all explicit to avoid accidental typecasting
- explicit Property(bool v);
+ explicit FBXExportProperty(bool v);
// TODO: determine if there is actually a byte type,
// or if this always means <bool>. 'C' seems to imply <char>,
// so possibly the above was intended to represent both.
- explicit Property(int16_t v);
- explicit Property(int32_t v);
- explicit Property(float v);
- explicit Property(double v);
- explicit Property(int64_t v);
+ 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 Property(const char* c, bool raw=false);
- explicit Property(const std::string& s, bool raw=false);
- explicit Property(const std::vector<uint8_t>& r);
- explicit Property(const std::vector<int32_t>& va);
- explicit Property(const std::vector<int64_t>& va);
- explicit Property(const std::vector<double>& va);
- explicit Property(const std::vector<float>& va);
- explicit Property(const aiMatrix4x4& vm);
+ explicit FBXExportProperty(const char* c, bool raw = false);
+ explicit FBXExportProperty(const std::string& s, bool raw = false);
+ explicit FBXExportProperty(const std::vector<uint8_t>& r);
+ explicit FBXExportProperty(const std::vector<int32_t>& va);
+ explicit FBXExportProperty(const std::vector<int64_t>& va);
+ explicit FBXExportProperty(const std::vector<double>& va);
+ explicit FBXExportProperty(const std::vector<float>& 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 <class T>
- explicit Property(T v) : type('X') {
+ explicit FBXExportProperty(T v) : type('X') {
static_assert(std::is_void<T>::value, "TRIED TO CREATE FBX PROPERTY WITH UNSUPPORTED TYPE, CHECK YOUR PROPERTY INSTANTIATION");
} // note: no line wrap so it appears verbatim on the compiler error
@@ -114,9 +111,9 @@ public:
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);
+ 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:
@@ -124,6 +121,9 @@ private:
std::vector<uint8_t> data;
};
+} // Namespace FBX
+} // Namespace Assimp
+
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
#endif // AI_FBXEXPORTPROPERTY_H_INC
diff --git a/thirdparty/assimp/code/FBXExporter.cpp b/thirdparty/assimp/code/FBX/FBXExporter.cpp
index acb1227144..8ebc8555a2 100644
--- a/thirdparty/assimp/code/FBXExporter.cpp
+++ b/thirdparty/assimp/code/FBX/FBXExporter.cpp
@@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXExportNode.h"
#include "FBXExportProperty.h"
#include "FBXCommon.h"
+#include "FBXUtil.h"
#include <assimp/version.h> // aiGetVersion
#include <assimp/IOSystem.hpp>
@@ -73,7 +74,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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
@@ -92,11 +97,6 @@ namespace FBX {
";------------------------------------------------------------------";
}
-using namespace Assimp;
-using namespace FBX;
-
-namespace Assimp {
-
// ---------------------------------------------------------------------
// Worker function for exporting a scene to binary FBX.
// Prototyped and registered in Exporter.cpp
@@ -121,6 +121,7 @@ namespace Assimp {
IOSystem* pIOSystem,
const aiScene* pScene,
const ExportProperties* pProperties
+
){
// initialize the exporter
FBXExporter exporter(pScene, pProperties);
@@ -1218,6 +1219,16 @@ void FBXExporter::WriteObjects ()
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);
@@ -1393,10 +1404,6 @@ void FBXExporter::WriteObjects ()
// 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???
@@ -1406,7 +1413,33 @@ void FBXExporter::WriteObjects ()
// 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;
+ 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<size_t>(embedded_texture->mWidth * std::max(embedded_texture->mHeight, 1u));
+ if (binary) {
+ // embed texture as binary data
+ std::vector<uint8_t> 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));
@@ -1419,17 +1452,17 @@ void FBXExporter::WriteObjects ()
// referenced by material_index/texture_type pairs.
std::map<std::pair<size_t,size_t>,int64_t> texture_uids;
const std::map<aiTextureType,std::string> prop_name_by_tt = {
- {aiTextureType_DIFFUSE, "DiffuseColor"},
- {aiTextureType_SPECULAR, "SpecularColor"},
- {aiTextureType_AMBIENT, "AmbientColor"},
- {aiTextureType_EMISSIVE, "EmissiveColor"},
- {aiTextureType_HEIGHT, "Bump"},
- {aiTextureType_NORMALS, "NormalMap"},
- {aiTextureType_SHININESS, "ShininessExponent"},
- {aiTextureType_OPACITY, "TransparentColor"},
+ {aiTextureType_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_REFLECTION, "ReflectionColor"}
//{aiTextureType_UNKNOWN, ""}
};
for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
@@ -1575,19 +1608,41 @@ void FBXExporter::WriteObjects ()
// 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.
- std::vector<std::set<const aiNode*>> skeleton_by_mesh(mScene->mNumMeshes);
+ // 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<std::set<const aiNode*, SortNodeByName>> skeleton_by_mesh(mScene->mNumMeshes);
// at the same time we can build a list of all the skeleton nodes,
// which will be used later to mark them as type "limbNode".
std::unordered_set<const aiNode*> limbnodes;
+
+ //actual bone nodes in fbx, without parenting-up
+ std::unordered_set<std::string> 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<std::string,aiNode*> node_by_bone;
for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
const aiMesh* m = mScene->mMeshes[mi];
- std::set<const aiNode*> skeleton;
+ std::set<const aiNode*, SortNodeByName> skeleton;
for (size_t bi =0; bi < m->mNumBones; ++bi) {
const aiBone* b = m->mBones[bi];
const std::string name(b->mName.C_Str());
@@ -1626,6 +1681,11 @@ void FBXExporter::WriteObjects ()
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?
@@ -1646,8 +1706,7 @@ void FBXExporter::WriteObjects ()
}
if (end) { break; }
}
- limbnodes.insert(parent);
- skeleton.insert(parent);
+
// if it was the skeleton root we can finish here
if (end) { break; }
}
@@ -1728,7 +1787,7 @@ void FBXExporter::WriteObjects ()
aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene);
// now make a subdeformer for each bone in the skeleton
- const std::set<const aiNode*> &skeleton = skeleton_by_mesh[mi];
+ const std::set<const aiNode*, SortNodeByName> 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;
@@ -1788,41 +1847,10 @@ void FBXExporter::WriteObjects ()
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;
- }
+ sdnode.AddChild("Transform", tr);
- // 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);
- }
+
+ sdnode.AddChild("TransformLink", bone_xform);
// 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.
@@ -2237,8 +2265,8 @@ void FBXExporter::WriteModelNode(
// not sure what these are for,
// but they seem to be omnipresent
- m.AddChild("Shading", Property(true));
- m.AddChild("Culling", Property("CullingOff"));
+ m.AddChild("Shading", FBXExportProperty(true));
+ m.AddChild("Culling", FBXExportProperty("CullingOff"));
m.Dump(outstream, binary, 1);
}
@@ -2351,7 +2379,7 @@ void FBXExporter::WriteModelNodes(
na.AddProperties(
node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode"
);
- na.AddChild("TypeFlags", Property("Skeleton"));
+ na.AddChild("TypeFlags", FBXExportProperty("Skeleton"));
na.Dump(outstream, binary, 1);
// and connect them
connections.emplace_back("C", "OO", node_attribute_uid, node_uid);
diff --git a/thirdparty/assimp/code/FBXExporter.h b/thirdparty/assimp/code/FBX/FBXExporter.h
index 71fb55c57f..71fb55c57f 100644
--- a/thirdparty/assimp/code/FBXExporter.h
+++ b/thirdparty/assimp/code/FBX/FBXExporter.h
diff --git a/thirdparty/assimp/code/FBXImportSettings.h b/thirdparty/assimp/code/FBX/FBXImportSettings.h
index d5e1c20608..1a4c80f8b2 100644
--- a/thirdparty/assimp/code/FBXImportSettings.h
+++ b/thirdparty/assimp/code/FBX/FBXImportSettings.h
@@ -53,19 +53,22 @@ namespace FBX {
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)
- {}
+ : 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:
@@ -141,8 +144,16 @@ struct ImportSettings
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;
};
diff --git a/thirdparty/assimp/code/FBXImporter.cpp b/thirdparty/assimp/code/FBX/FBXImporter.cpp
index 2cc8bffc29..bd359dbf29 100644
--- a/thirdparty/assimp/code/FBXImporter.cpp
+++ b/thirdparty/assimp/code/FBX/FBXImporter.cpp
@@ -140,6 +140,8 @@ void FBXImporter::SetupProperties(const Importer* pImp)
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);
}
// ------------------------------------------------------------------------------------------------
@@ -170,7 +172,7 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
bool is_binary = false;
if (!strncmp(begin,"Kaydara FBX Binary",18)) {
is_binary = true;
- TokenizeBinary(tokens,begin,static_cast<unsigned int>(contents.size()));
+ TokenizeBinary(tokens,begin,contents.size());
}
else {
Tokenize(tokens,begin);
@@ -183,8 +185,20 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
// 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);
+ ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones, unit);
+
+ // size relative to cm
+ float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor();
+
+ // Set FBX file scale is relative to CM must be converted to M for
+ // assimp universal format (M)
+ SetFileScale( size_relative_to_cm * 0.01f);
std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
}
diff --git a/thirdparty/assimp/code/FBXImporter.h b/thirdparty/assimp/code/FBX/FBXImporter.h
index c365b2cddf..c365b2cddf 100644
--- a/thirdparty/assimp/code/FBXImporter.h
+++ b/thirdparty/assimp/code/FBX/FBXImporter.h
diff --git a/thirdparty/assimp/code/FBXMaterial.cpp b/thirdparty/assimp/code/FBX/FBXMaterial.cpp
index f16f134404..f43a8b84b0 100644
--- a/thirdparty/assimp/code/FBXMaterial.cpp
+++ b/thirdparty/assimp/code/FBX/FBXMaterial.cpp
@@ -316,7 +316,7 @@ Video::Video(uint64_t id, const Element& element, const Document& doc, const std
relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0));
}
- if(Content) {
+ 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);
@@ -326,16 +326,40 @@ Video::Video(uint64_t id, const Element& element, const Document& doc, const std
DOMError("embedded content is not surrounded by quotation marks", &element);
}
else {
- const char* encodedData = data + 1;
- size_t encodedDataLen = static_cast<size_t>(token.end() - token.begin());
- // search for last quotation mark
- while (encodedDataLen > 1 && encodedData[encodedDataLen] != '"')
- encodedDataLen--;
- if (encodedDataLen % 4 != 0) {
- DOMError("embedded content is invalid, needs to be in base64", &element);
+ 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;
}
- else {
- contentLength = Util::DecodeBase64(encodedData, encodedDataLen, content);
+ if (targetLength == 0)
+ {
+ DOMError("Corrupted embedded content found", &element);
+ }
+ content = new uint8_t[targetLength];
+ contentLength = static_cast<uint64_t>(targetLength);
+ size_t dst_offset = 0;
+ for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx)
+ {
+ const Token& dataToken = GetRequiredToken(*Content, tokenIdx);
+ 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);
}
}
}
diff --git a/thirdparty/assimp/code/FBXMeshGeometry.cpp b/thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp
index d75476b826..5c9a0e309d 100644
--- a/thirdparty/assimp/code/FBXMeshGeometry.cpp
+++ b/thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp
@@ -115,7 +115,6 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
if(tempVerts.empty()) {
FBXImporter::LogWarn("encountered mesh with no vertices");
- return;
}
std::vector<int> tempFaces;
@@ -123,7 +122,6 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
if(tempFaces.empty()) {
FBXImporter::LogWarn("encountered mesh with no faces");
- return;
}
m_vertices.reserve(tempFaces.size());
@@ -568,15 +566,15 @@ void MeshGeometry::ReadVertexDataColors(std::vector<aiColor4D>& colors_out, cons
}
// ------------------------------------------------------------------------------------------------
-static const std::string TangentIndexToken = "TangentIndex";
-static const std::string TangentsIndexToken = "TangentsIndex";
+static const char *TangentIndexToken = "TangentIndex";
+static const char *TangentsIndexToken = "TangentsIndex";
void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D>& tangents_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent";
- const char * strIdx = source.Elements().count( "Tangents" ) > 0 ? TangentsIndexToken.c_str() : TangentIndexToken.c_str();
+ const char * strIdx = source.Elements().count( "Tangents" ) > 0 ? TangentsIndexToken : TangentIndexToken;
ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType,
str,
strIdx,
@@ -612,7 +610,10 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
const std::string& ReferenceInformationType)
{
const size_t face_count = m_faces.size();
- ai_assert(face_count);
+ if(face_count <= 0)
+ {
+ return;
+ }
// materials are handled separately. First of all, they are assigned per-face
// and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect
@@ -630,10 +631,11 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
materials_out.clear();
}
- m_materials.assign(m_vertices.size(),materials_out[0]);
+ materials_out.resize(m_vertices.size());
+ std::fill(materials_out.begin(), materials_out.end(), materials_out.at(0));
}
else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") {
- m_materials.resize(face_count);
+ materials_out.resize(face_count);
if(materials_out.size() != face_count) {
FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ")
diff --git a/thirdparty/assimp/code/FBXMeshGeometry.h b/thirdparty/assimp/code/FBX/FBXMeshGeometry.h
index d6d4512177..d6d4512177 100644
--- a/thirdparty/assimp/code/FBXMeshGeometry.h
+++ b/thirdparty/assimp/code/FBX/FBXMeshGeometry.h
diff --git a/thirdparty/assimp/code/FBXModel.cpp b/thirdparty/assimp/code/FBX/FBXModel.cpp
index 589af36ac7..589af36ac7 100644
--- a/thirdparty/assimp/code/FBXModel.cpp
+++ b/thirdparty/assimp/code/FBX/FBXModel.cpp
diff --git a/thirdparty/assimp/code/FBXNodeAttribute.cpp b/thirdparty/assimp/code/FBX/FBXNodeAttribute.cpp
index b72e5637ee..b72e5637ee 100644
--- a/thirdparty/assimp/code/FBXNodeAttribute.cpp
+++ b/thirdparty/assimp/code/FBX/FBXNodeAttribute.cpp
diff --git a/thirdparty/assimp/code/FBXParser.cpp b/thirdparty/assimp/code/FBX/FBXParser.cpp
index b255c47347..4a9346040d 100644
--- a/thirdparty/assimp/code/FBXParser.cpp
+++ b/thirdparty/assimp/code/FBX/FBXParser.cpp
@@ -117,7 +117,7 @@ namespace FBX {
Element::Element(const Token& key_token, Parser& parser)
: key_token(key_token)
{
- TokenPtr n = NULL;
+ TokenPtr n = nullptr;
do {
n = parser.AdvanceToNextToken();
if(!n) {
@@ -643,9 +643,9 @@ void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el)
if (type == 'd') {
const double* d = reinterpret_cast<const double*>(&buff[0]);
for (unsigned int i = 0; i < count3; ++i, d += 3) {
- out.push_back(aiVector3D(static_cast<float>(d[0]),
- static_cast<float>(d[1]),
- static_cast<float>(d[2])));
+ out.push_back(aiVector3D(static_cast<ai_real>(d[0]),
+ static_cast<ai_real>(d[1]),
+ static_cast<ai_real>(d[2])));
}
// for debugging
/*for ( size_t i = 0; i < out.size(); i++ ) {
@@ -963,7 +963,6 @@ void ParseVectorDataArray(std::vector<float>& out, const Element& el)
}
}
-
// ------------------------------------------------------------------------------------------------
// read an array of uints
void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el)
@@ -1280,7 +1279,6 @@ float ParseTokenAsFloat(const Token& t)
return i;
}
-
// ------------------------------------------------------------------------------------------------
// wrapper around ParseTokenAsInt() with ParseError handling
int ParseTokenAsInt(const Token& t)
@@ -1293,8 +1291,6 @@ int ParseTokenAsInt(const Token& t)
return i;
}
-
-
// ------------------------------------------------------------------------------------------------
// wrapper around ParseTokenAsInt64() with ParseError handling
int64_t ParseTokenAsInt64(const Token& t)
diff --git a/thirdparty/assimp/code/FBXParser.h b/thirdparty/assimp/code/FBX/FBXParser.h
index 7b0cf72039..7b0cf72039 100644
--- a/thirdparty/assimp/code/FBXParser.h
+++ b/thirdparty/assimp/code/FBX/FBXParser.h
diff --git a/thirdparty/assimp/code/FBXProperties.cpp b/thirdparty/assimp/code/FBX/FBXProperties.cpp
index 8d7036b6a9..8d7036b6a9 100644
--- a/thirdparty/assimp/code/FBXProperties.cpp
+++ b/thirdparty/assimp/code/FBX/FBXProperties.cpp
diff --git a/thirdparty/assimp/code/FBXProperties.h b/thirdparty/assimp/code/FBX/FBXProperties.h
index 58755542fc..58755542fc 100644
--- a/thirdparty/assimp/code/FBXProperties.h
+++ b/thirdparty/assimp/code/FBX/FBXProperties.h
diff --git a/thirdparty/assimp/code/FBXTokenizer.cpp b/thirdparty/assimp/code/FBX/FBXTokenizer.cpp
index 252cce3557..252cce3557 100644
--- a/thirdparty/assimp/code/FBXTokenizer.cpp
+++ b/thirdparty/assimp/code/FBX/FBXTokenizer.cpp
diff --git a/thirdparty/assimp/code/FBXTokenizer.h b/thirdparty/assimp/code/FBX/FBXTokenizer.h
index 2af29743f4..afa588a470 100644
--- a/thirdparty/assimp/code/FBXTokenizer.h
+++ b/thirdparty/assimp/code/FBX/FBXTokenizer.h
@@ -93,7 +93,7 @@ public:
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(const char* sbegin, const char* send, TokenType type, size_t offset);
~Token();
@@ -118,14 +118,14 @@ public:
return type;
}
- unsigned int Offset() const {
+ size_t Offset() const {
ai_assert(IsBinary());
return offset;
}
unsigned int Line() const {
ai_assert(!IsBinary());
- return line;
+ return static_cast<unsigned int>(line);
}
unsigned int Column() const {
@@ -147,8 +147,8 @@ private:
const TokenType type;
union {
- const unsigned int line;
- unsigned int offset;
+ size_t line;
+ size_t offset;
};
const unsigned int column;
};
@@ -178,7 +178,7 @@ void Tokenize(TokenList& output_tokens, const char* input);
* @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);
+void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length);
} // ! FBX
diff --git a/thirdparty/assimp/code/FBXUtil.cpp b/thirdparty/assimp/code/FBX/FBXUtil.cpp
index fb483161b2..c10e057c8c 100644
--- a/thirdparty/assimp/code/FBXUtil.cpp
+++ b/thirdparty/assimp/code/FBX/FBXUtil.cpp
@@ -86,7 +86,7 @@ const char* TokenTypeString(TokenType t)
// ------------------------------------------------------------------------------------------------
-std::string AddOffset(const std::string& prefix, const std::string& text, unsigned int offset)
+std::string AddOffset(const std::string& prefix, const std::string& text, size_t offset)
{
return static_cast<std::string>( (Formatter::format() << prefix << " (offset 0x" << std::hex << offset << ") " << text) );
}
@@ -114,47 +114,126 @@ std::string AddTokenText(const std::string& prefix, const std::string& text, con
text) );
}
+// Generated by this formula: T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
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
+ 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)
{
- return base64DecodeTable[size_t(ch)];
+ const auto idx = static_cast<uint8_t>(ch);
+ if (idx > 127)
+ return 255;
+ return base64DecodeTable[idx];
}
-size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out)
+size_t ComputeDecodedSizeBase64(const char* in, size_t inLength)
{
- if (inLength < 4) {
- out = 0;
+ if (inLength < 2)
+ {
+ return 0;
+ }
+ const size_t equals = size_t(in[inLength - 1] == '=') + size_t(in[inLength - 2] == '=');
+ const size_t full_length = (inLength * 3) >> 2; // div by 4
+ if (full_length < equals)
+ {
+ return 0;
+ }
+ return full_length - equals;
+}
+
+size_t DecodeBase64(const char* in, size_t inLength, uint8_t* out, size_t maxOutLength)
+{
+ if (maxOutLength == 0 || inLength < 2) {
return 0;
}
+ const size_t realLength = inLength - size_t(in[inLength - 1] == '=') - size_t(in[inLength - 2] == '=');
+ size_t dst_offset = 0;
+ int val = 0, valb = -8;
+ for (size_t src_offset = 0; src_offset < realLength; ++src_offset)
+ {
+ const uint8_t table_value = Util::DecodeBase64(in[src_offset]);
+ if (table_value == 255)
+ {
+ return 0;
+ }
+ val = (val << 6) + table_value;
+ valb += 6;
+ if (valb >= 0)
+ {
+ out[dst_offset++] = static_cast<uint8_t>((val >> valb) & 0xFF);
+ valb -= 8;
+ val &= 0xFFF;
+ }
+ }
+ return dst_offset;
+}
- const size_t outLength = (inLength * 3) / 4;
- out = new uint8_t[outLength];
- memset(out, 0, outLength);
+static const char to_base64_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+char EncodeBase64(char byte)
+{
+ return to_base64_string[(size_t)byte];
+}
- size_t i = 0;
- size_t j = 0;
- for (i = 0; i < inLength - 4; i += 4)
+/** 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++)
{
- 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);
+ 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 outLength;
+ return encoded_string;
}
} // !Util
diff --git a/thirdparty/assimp/code/FBXUtil.h b/thirdparty/assimp/code/FBX/FBXUtil.h
index 6890e015ba..b634418858 100644
--- a/thirdparty/assimp/code/FBXUtil.h
+++ b/thirdparty/assimp/code/FBX/FBXUtil.h
@@ -78,7 +78,7 @@ const char* TokenTypeString(TokenType t);
* @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);
+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.
@@ -105,13 +105,30 @@ std::string AddTokenText(const std::string& prefix, const std::string& text, con
* @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 Reference to pointer where we will store the decoded data.
+* @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 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);
}
}
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 <assimp/StringUtils.h>
-
-// Workaround for issue #1361
-// https://github.com/assimp/assimp/issues/1361
-#ifdef __ANDROID__
-# define _GLIBCXX_USE_C99 1
-#endif
-
-#include <assimp/Exceptional.h>
-#include <assimp/IOStream.hpp>
-#include <assimp/types.h>
-#include <assimp/MemoryIOWrapper.h>
-#include <assimp/irrXMLWrapper.h>
-#include "../contrib/utf8cpp/source/utf8.h"
-#include <assimp/fast_atof.h>
-#include <stack>
-#include <map>
-#include <iostream>
-#include <sstream>
-#include <iomanip>
-
-namespace Assimp {
-
-static const std::string parseErrorMessage = "Fast Infoset parse error";
-
-static const char *xmlDeclarations[] = {
- "<?xml encoding='finf'?>",
- "<?xml encoding='finf' standalone='yes'?>",
- "<?xml encoding='finf' standalone='no'?>",
- "<?xml version='1.0' encoding='finf'?>",
- "<?xml version='1.0' encoding='finf' standalone='yes'?>",
- "<?xml version='1.0' encoding='finf' standalone='no'?>",
- "<?xml version='1.1' encoding='finf'?>",
- "<?xml version='1.1' encoding='finf' standalone='yes'?>",
- "<?xml version='1.1' encoding='finf' standalone='no'?>"
-};
-
-static size_t parseMagic(const uint8_t *data, const uint8_t *dataEnd) {
- if (dataEnd - data < 4) {
- return 0;
- }
- uint32_t magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
- switch (magic) {
- case 0xe0000001:
- return 4;
- case 0x3c3f786d: // "<?xm"
- {
- size_t xmlDeclarationsLength = sizeof(xmlDeclarations) / sizeof(xmlDeclarations[0]);
- for (size_t i = 0; i < xmlDeclarationsLength; ++i) {
- auto xmlDeclaration = xmlDeclarations[i];
- ptrdiff_t xmlDeclarationLength = strlen(xmlDeclaration);
- if ((dataEnd - data >= xmlDeclarationLength) && (memcmp(xmlDeclaration, data, xmlDeclarationLength) == 0)) {
- data += xmlDeclarationLength;
- if (dataEnd - data < 4) {
- return 0;
- }
- magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
- return magic == 0xe0000001 ? xmlDeclarationLength + 4 : 0;
- }
- }
- return 0;
- }
- default:
- return 0;
- }
-}
-
-static std::string parseUTF8String(const uint8_t *data, size_t len) {
- return std::string((char*)data, len);
-}
-
-static std::string parseUTF16String(const uint8_t *data, size_t len) {
- if (len & 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- size_t numShorts = len / 2;
- std::vector<short> utf16;
- utf16.reserve(numShorts);
- for (size_t i = 0; i < numShorts; ++i) {
- short v = (data[0] << 8) | data[1];
- utf16.push_back(v);
- data += 2;
- }
- std::string result;
- utf8::utf16to8(utf16.begin(), utf16.end(), back_inserter(result));
- return result;
-}
-
-struct FIStringValueImpl: public FIStringValue {
- inline FIStringValueImpl(std::string &&value_) { value = std::move(value_); }
- virtual const std::string &toString() const /*override*/ { return value; }
-};
-
-std::shared_ptr<FIStringValue> FIStringValue::create(std::string &&value) {
- return std::make_shared<FIStringValueImpl>(std::move(value));
-}
-
-struct FIHexValueImpl: public FIHexValue {
- mutable std::string strValue;
- mutable bool strValueValid;
- inline FIHexValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); }
- virtual const std::string &toString() const /*override*/ {
- if (!strValueValid) {
- strValueValid = true;
- std::ostringstream os;
- os << std::hex << std::uppercase << std::setfill('0');
- std::for_each(value.begin(), value.end(), [&](uint8_t c) { os << std::setw(2) << static_cast<int>(c); });
- strValue = os.str();
- }
- return strValue;
- };
-};
-
-std::shared_ptr<FIHexValue> FIHexValue::create(std::vector<uint8_t> &&value) {
- return std::make_shared<FIHexValueImpl>(std::move(value));
-}
-
-struct FIBase64ValueImpl: public FIBase64Value {
- mutable std::string strValue;
- mutable bool strValueValid;
- inline FIBase64ValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); }
- virtual const std::string &toString() const /*override*/ {
- if (!strValueValid) {
- strValueValid = true;
- std::ostringstream os;
- uint8_t c1 = 0, c2;
- int imod3 = 0;
- std::vector<uint8_t>::size_type valueSize = value.size();
- for (std::vector<uint8_t>::size_type i = 0; i < valueSize; ++i) {
- c2 = value[i];
- switch (imod3) {
- case 0:
- os << basis_64[c2 >> 2];
- imod3 = 1;
- break;
- case 1:
- os << basis_64[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)];
- imod3 = 2;
- break;
- case 2:
- os << basis_64[((c1 & 0x0f) << 2) | ((c2 & 0xc0) >> 6)] << basis_64[c2 & 0x3f];
- imod3 = 0;
- break;
- }
- c1 = c2;
- }
- switch (imod3) {
- case 1:
- os << basis_64[(c1 & 0x03) << 4] << "==";
- break;
- case 2:
- os << basis_64[(c1 & 0x0f) << 2] << '=';
- break;
- }
- strValue = os.str();
- }
- return strValue;
- };
- static const char basis_64[];
-};
-
-const char FIBase64ValueImpl::basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-std::shared_ptr<FIBase64Value> FIBase64Value::create(std::vector<uint8_t> &&value) {
- return std::make_shared<FIBase64ValueImpl>(std::move(value));
-}
-
-struct FIShortValueImpl: public FIShortValue {
- mutable std::string strValue;
- mutable bool strValueValid;
- inline FIShortValueImpl(std::vector<int16_t> &&value_): strValueValid(false) { value = std::move(value_); }
- virtual const std::string &toString() const /*override*/ {
- if (!strValueValid) {
- strValueValid = true;
- std::ostringstream os;
- int n = 0;
- std::for_each(value.begin(), value.end(), [&](int16_t s) { if (++n > 1) os << ' '; os << s; });
- strValue = os.str();
- }
- return strValue;
- }
-};
-
-std::shared_ptr<FIShortValue> FIShortValue::create(std::vector<int16_t> &&value) {
- return std::make_shared<FIShortValueImpl>(std::move(value));
-}
-
-struct FIIntValueImpl: public FIIntValue {
- mutable std::string strValue;
- mutable bool strValueValid;
- inline FIIntValueImpl(std::vector<int32_t> &&value_): strValueValid(false) { value = std::move(value_); }
- virtual const std::string &toString() const /*override*/ {
- if (!strValueValid) {
- strValueValid = true;
- std::ostringstream os;
- int n = 0;
- std::for_each(value.begin(), value.end(), [&](int32_t i) { if (++n > 1) os << ' '; os << i; });
- strValue = os.str();
- }
- return strValue;
- };
-};
-
-std::shared_ptr<FIIntValue> FIIntValue::create(std::vector<int32_t> &&value) {
- return std::make_shared<FIIntValueImpl>(std::move(value));
-}
-
-struct FILongValueImpl: public FILongValue {
- mutable std::string strValue;
- mutable bool strValueValid;
- inline FILongValueImpl(std::vector<int64_t> &&value_): strValueValid(false) { value = std::move(value_); }
- virtual const std::string &toString() const /*override*/ {
- if (!strValueValid) {
- strValueValid = true;
- std::ostringstream os;
- int n = 0;
- std::for_each(value.begin(), value.end(), [&](int64_t l) { if (++n > 1) os << ' '; os << l; });
- strValue = os.str();
- }
- return strValue;
- };
-};
-
-std::shared_ptr<FILongValue> FILongValue::create(std::vector<int64_t> &&value) {
- return std::make_shared<FILongValueImpl>(std::move(value));
-}
-
-struct FIBoolValueImpl: public FIBoolValue {
- mutable std::string strValue;
- mutable bool strValueValid;
- inline FIBoolValueImpl(std::vector<bool> &&value_): strValueValid(false) { value = std::move(value_); }
- virtual const std::string &toString() const /*override*/ {
- if (!strValueValid) {
- strValueValid = true;
- std::ostringstream os;
- os << std::boolalpha;
- int n = 0;
- std::for_each(value.begin(), value.end(), [&](bool b) { if (++n > 1) os << ' '; os << b; });
- strValue = os.str();
- }
- return strValue;
- };
-};
-
-std::shared_ptr<FIBoolValue> FIBoolValue::create(std::vector<bool> &&value) {
- return std::make_shared<FIBoolValueImpl>(std::move(value));
-}
-
-struct FIFloatValueImpl: public FIFloatValue {
- mutable std::string strValue;
- mutable bool strValueValid;
- inline FIFloatValueImpl(std::vector<float> &&value_): strValueValid(false) { value = std::move(value_); }
- virtual const std::string &toString() const /*override*/ {
- if (!strValueValid) {
- strValueValid = true;
- std::ostringstream os;
- int n = 0;
- std::for_each(value.begin(), value.end(), [&](float f) { if (++n > 1) os << ' '; os << f; });
- strValue = os.str();
- }
- return strValue;
- }
-};
-
-std::shared_ptr<FIFloatValue> FIFloatValue::create(std::vector<float> &&value) {
- return std::make_shared<FIFloatValueImpl>(std::move(value));
-}
-
-struct FIDoubleValueImpl: public FIDoubleValue {
- mutable std::string strValue;
- mutable bool strValueValid;
- inline FIDoubleValueImpl(std::vector<double> &&value_): strValueValid(false) { value = std::move(value_); }
- virtual const std::string &toString() const /*override*/ {
- if (!strValueValid) {
- strValueValid = true;
- std::ostringstream os;
- int n = 0;
- std::for_each(value.begin(), value.end(), [&](double d) { if (++n > 1) os << ' '; os << d; });
- strValue = os.str();
- }
- return strValue;
- }
-};
-
-std::shared_ptr<FIDoubleValue> FIDoubleValue::create(std::vector<double> &&value) {
- return std::make_shared<FIDoubleValueImpl>(std::move(value));
-}
-
-struct FIUUIDValueImpl: public FIUUIDValue {
- mutable std::string strValue;
- mutable bool strValueValid;
- inline FIUUIDValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); }
- virtual const std::string &toString() const /*override*/ {
- if (!strValueValid) {
- strValueValid = true;
- std::ostringstream os;
- os << std::hex << std::uppercase << std::setfill('0');
- std::vector<uint8_t>::size_type valueSize = value.size();
- for (std::vector<uint8_t>::size_type i = 0; i < valueSize; ++i) {
- switch (i & 15) {
- case 0:
- if (i > 0) {
- os << ' ';
- }
- os << std::setw(2) << static_cast<int>(value[i]);
- break;
- case 4:
- case 6:
- case 8:
- case 10:
- os << '-';
- // intentionally fall through!
- case 1:
- case 2:
- case 3:
- case 5:
- case 7:
- case 9:
- case 11:
- case 12:
- case 13:
- case 14:
- case 15:
- os << std::setw(2) << static_cast<int>(value[i]);
- break;
- }
- }
- strValue = os.str();
- }
- return strValue;
- };
-};
-
-std::shared_ptr<FIUUIDValue> FIUUIDValue::create(std::vector<uint8_t> &&value) {
- return std::make_shared<FIUUIDValueImpl>(std::move(value));
-}
-
-struct FICDATAValueImpl: public FICDATAValue {
- inline FICDATAValueImpl(std::string &&value_) { value = std::move(value_); }
- virtual const std::string &toString() const /*override*/ { return value; }
-};
-
-std::shared_ptr<FICDATAValue> FICDATAValue::create(std::string &&value) {
- return std::make_shared<FICDATAValueImpl>(std::move(value));
-}
-
-struct FIHexDecoder: public FIDecoder {
- virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
- return FIHexValue::create(std::vector<uint8_t>(data, data + len));
- }
-};
-
-struct FIBase64Decoder: public FIDecoder {
- virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
- return FIBase64Value::create(std::vector<uint8_t>(data, data + len));
- }
-};
-
-struct FIShortDecoder: public FIDecoder {
- virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
- if (len & 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- std::vector<int16_t> value;
- size_t numShorts = len / 2;
- value.reserve(numShorts);
- for (size_t i = 0; i < numShorts; ++i) {
- int16_t v = (data[0] << 8) | data[1];
- value.push_back(v);
- data += 2;
- }
- return FIShortValue::create(std::move(value));
- }
-};
-
-struct FIIntDecoder: public FIDecoder {
- virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
- if (len & 3) {
- throw DeadlyImportError(parseErrorMessage);
- }
- std::vector<int32_t> value;
- size_t numInts = len / 4;
- value.reserve(numInts);
- for (size_t i = 0; i < numInts; ++i) {
- int32_t v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
- value.push_back(v);
- data += 4;
- }
- return FIIntValue::create(std::move(value));
- }
-};
-
-struct FILongDecoder: public FIDecoder {
- virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
- if (len & 7) {
- throw DeadlyImportError(parseErrorMessage);
- }
- std::vector<int64_t> value;
- size_t numLongs = len / 8;
- value.reserve(numLongs);
- for (size_t i = 0; i < numLongs; ++i) {
- int64_t b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7];
- int64_t v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
- value.push_back(v);
- data += 8;
- }
- return FILongValue::create(std::move(value));
- }
-};
-
-struct FIBoolDecoder: public FIDecoder {
- virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
- if (len < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- std::vector<bool> value;
- uint8_t b = *data++;
- size_t unusedBits = b >> 4;
- size_t numBools = (len * 8) - 4 - unusedBits;
- value.reserve(numBools);
- uint8_t mask = 1 << 3;
- for (size_t i = 0; i < numBools; ++i) {
- if (!mask) {
- mask = 1 << 7;
- b = *data++;
- }
- value.push_back((b & mask) != 0);
- }
- return FIBoolValue::create(std::move(value));
- }
-};
-
-struct FIFloatDecoder: public FIDecoder {
- virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
- if (len & 3) {
- throw DeadlyImportError(parseErrorMessage);
- }
- std::vector<float> value;
- size_t numFloats = len / 4;
- value.reserve(numFloats);
- for (size_t i = 0; i < numFloats; ++i) {
- int v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
- float f;
- memcpy(&f, &v, 4);
- value.push_back(f);
- data += 4;
- }
- return FIFloatValue::create(std::move(value));
- }
-};
-
-struct FIDoubleDecoder: public FIDecoder {
- virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
- if (len & 7) {
- throw DeadlyImportError(parseErrorMessage);
- }
- std::vector<double> value;
- size_t numDoubles = len / 8;
- value.reserve(numDoubles);
- for (size_t i = 0; i < numDoubles; ++i) {
- long long b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7];
- long long v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
- double f;
- memcpy(&f, &v, 8);
- value.push_back(f);
- data += 8;
- }
- return FIDoubleValue::create(std::move(value));
- }
-};
-
-struct FIUUIDDecoder: public FIDecoder {
- virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
- if (len & 15) {
- throw DeadlyImportError(parseErrorMessage);
- }
- return FIUUIDValue::create(std::vector<uint8_t>(data, data + len));
- }
-};
-
-struct FICDATADecoder: public FIDecoder {
- virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
- return FICDATAValue::create(parseUTF8String(data, len));
- }
-};
-
-class CFIReaderImpl: public FIReader {
-public:
-
- CFIReaderImpl(std::unique_ptr<uint8_t[]> data_, size_t size):
- data(std::move(data_)), dataP(data.get()), dataEnd(data.get() + size), currentNodeType(irr::io::EXN_NONE),
- emptyElement(false), headerPending(true), terminatorPending(false)
- {}
-
- virtual ~CFIReaderImpl() {}
-
- virtual bool read() /*override*/ {
- if (headerPending) {
- headerPending = false;
- parseHeader();
- }
- if (terminatorPending) {
- terminatorPending = false;
- if (elementStack.empty()) {
- return false;
- }
- else {
- nodeName = elementStack.top();
- elementStack.pop();
- currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END;
- return true;
- }
- }
- if (dataP >= dataEnd) {
- return false;
- }
- uint8_t b = *dataP;
- if (b < 0x80) { // Element (C.2.11.2, C.3.7.2)
- // C.3
- parseElement();
- return true;
- }
- else if (b < 0xc0) { // Characters (C.3.7.5)
- // C.7
- auto chars = parseNonIdentifyingStringOrIndex3(vocabulary.charactersTable);
- nodeName = chars->toString();
- currentNodeType = irr::io::EXN_TEXT;
- return true;
- }
- else if (b < 0xe0) {
- if ((b & 0xfc) == 0xc4) { // DTD (C.2.11.5)
- // C.9
- ++dataP;
- if (b & 0x02) {
- /*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
- }
- if (b & 0x01) {
- /*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
- }
- elementStack.push(EmptyString);
- currentNodeType = irr::io::EXN_UNKNOWN;
- return true;
- }
- else if ((b & 0xfc) == 0xc8) { // Unexpanded entity reference (C.3.7.4)
- // C.6
- ++dataP;
- /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
- if (b & 0x02) {
- /*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
- }
- if (b & 0x01) {
- /*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
- }
- currentNodeType = irr::io::EXN_UNKNOWN;
- return true;
- }
- }
- else if (b < 0xf0) {
- if (b == 0xe1) { // Processing instruction (C.2.11.3, C.3.7.3)
- // C.5
- ++dataP;
- /*const std::string &target =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- /*std::shared_ptr<const FIValue> data =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable);
- currentNodeType = irr::io::EXN_UNKNOWN;
- return true;
- }
- else if (b == 0xe2) { // Comment (C.2.11.4, C.3.7.6)
- // C.8
- ++dataP;
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- std::shared_ptr<const FIValue> comment = parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable);
- nodeName = comment->toString();
- currentNodeType = irr::io::EXN_COMMENT;
- return true;
- }
- }
- else { // Terminator (C.2.12, C.3.8)
- ++dataP;
- if (b == 0xff) {
- terminatorPending = true;
- }
- if (elementStack.empty()) {
- return false;
- }
- else {
- nodeName = elementStack.top();
- elementStack.pop();
- currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END;
- return true;
- }
- }
- throw DeadlyImportError(parseErrorMessage);
- }
-
- virtual irr::io::EXML_NODE getNodeType() const /*override*/ {
- return currentNodeType;
- }
-
- virtual int getAttributeCount() const /*override*/ {
- return static_cast<int>(attributes.size());
- }
-
- virtual const char* getAttributeName(int idx) const /*override*/ {
- if (idx < 0 || idx >= (int)attributes.size()) {
- return nullptr;
- }
- return attributes[idx].name.c_str();
- }
-
- virtual const char* getAttributeValue(int idx) const /*override*/ {
- if (idx < 0 || idx >= (int)attributes.size()) {
- return nullptr;
- }
- return attributes[idx].value->toString().c_str();
- }
-
- virtual const char* getAttributeValue(const char* name) const /*override*/ {
- const Attribute* attr = getAttributeByName(name);
- if (!attr) {
- return nullptr;
- }
- return attr->value->toString().c_str();
- }
-
- virtual const char* getAttributeValueSafe(const char* name) const /*override*/ {
- const Attribute* attr = getAttributeByName(name);
- if (!attr) {
- return EmptyString.c_str();
- }
- return attr->value->toString().c_str();
- }
-
- virtual int getAttributeValueAsInt(const char* name) const /*override*/ {
- const Attribute* attr = getAttributeByName(name);
- if (!attr) {
- return 0;
- }
- std::shared_ptr<const FIIntValue> intValue = std::dynamic_pointer_cast<const FIIntValue>(attr->value);
- if (intValue) {
- return intValue->value.size() == 1 ? intValue->value.front() : 0;
- }
- return atoi(attr->value->toString().c_str());
- }
-
- virtual int getAttributeValueAsInt(int idx) const /*override*/ {
- if (idx < 0 || idx >= (int)attributes.size()) {
- return 0;
- }
- std::shared_ptr<const FIIntValue> intValue = std::dynamic_pointer_cast<const FIIntValue>(attributes[idx].value);
- if (intValue) {
- return intValue->value.size() == 1 ? intValue->value.front() : 0;
- }
- return atoi(attributes[idx].value->toString().c_str());
- }
-
- virtual float getAttributeValueAsFloat(const char* name) const /*override*/ {
- const Attribute* attr = getAttributeByName(name);
- if (!attr) {
- return 0;
- }
- std::shared_ptr<const FIFloatValue> floatValue = std::dynamic_pointer_cast<const FIFloatValue>(attr->value);
- if (floatValue) {
- return floatValue->value.size() == 1 ? floatValue->value.front() : 0;
- }
-
- return fast_atof(attr->value->toString().c_str());
- }
-
- virtual float getAttributeValueAsFloat(int idx) const /*override*/ {
- if (idx < 0 || idx >= (int)attributes.size()) {
- return 0;
- }
- std::shared_ptr<const FIFloatValue> floatValue = std::dynamic_pointer_cast<const FIFloatValue>(attributes[idx].value);
- if (floatValue) {
- return floatValue->value.size() == 1 ? floatValue->value.front() : 0;
- }
- return fast_atof(attributes[idx].value->toString().c_str());
- }
-
- virtual const char* getNodeName() const /*override*/ {
- return nodeName.c_str();
- }
-
- virtual const char* getNodeData() const /*override*/ {
- return nodeName.c_str();
- }
-
- virtual bool isEmptyElement() const /*override*/ {
- return emptyElement;
- }
-
- virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ {
- return irr::io::ETF_UTF8;
- }
-
- virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ {
- return irr::io::ETF_UTF8;
- }
-
- virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int idx) const /*override*/ {
- if (idx < 0 || idx >= (int)attributes.size()) {
- return nullptr;
- }
- return attributes[idx].value;
- }
-
- virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* name) const /*override*/ {
- const Attribute* attr = getAttributeByName(name);
- if (!attr) {
- return nullptr;
- }
- return attr->value;
- }
-
- virtual void registerDecoder(const std::string &algorithmUri, std::unique_ptr<FIDecoder> decoder) /*override*/ {
- decoderMap[algorithmUri] = std::move(decoder);
- }
-
- virtual void registerVocabulary(const std::string &vocabularyUri, const FIVocabulary *vocabulary) /*override*/ {
- vocabularyMap[vocabularyUri] = vocabulary;
- }
-
-private:
-
- struct QName {
- std::string prefix;
- std::string uri;
- std::string name;
- inline QName() {}
- inline QName(const FIQName &qname): prefix(qname.prefix ? qname.prefix : ""), uri(qname.uri ? qname.uri : ""), name(qname.name) {}
- };
-
- struct Attribute {
- QName qname;
- std::string name;
- std::shared_ptr<const FIValue> value;
- };
-
- struct Vocabulary {
- std::vector<std::string> restrictedAlphabetTable;
- std::vector<std::string> encodingAlgorithmTable;
- std::vector<std::string> prefixTable;
- std::vector<std::string> namespaceNameTable;
- std::vector<std::string> localNameTable;
- std::vector<std::string> otherNCNameTable;
- std::vector<std::string> otherURITable;
- std::vector<std::shared_ptr<const FIValue>> attributeValueTable;
- std::vector<std::shared_ptr<const FIValue>> charactersTable;
- std::vector<std::shared_ptr<const FIValue>> otherStringTable;
- std::vector<QName> elementNameTable;
- std::vector<QName> attributeNameTable;
- Vocabulary() {
- prefixTable.push_back("xml");
- namespaceNameTable.push_back("http://www.w3.org/XML/1998/namespace");
- }
- };
-
- const Attribute* getAttributeByName(const char* name) const {
- if (!name) {
- return 0;
- }
- std::string n = name;
- for (int i=0; i<(int)attributes.size(); ++i) {
- if (attributes[i].name == n) {
- return &attributes[i];
- }
- }
- return 0;
- }
-
- size_t parseInt2() { // C.25
- uint8_t b = *dataP++;
- if (!(b & 0x40)) { // x0...... (C.25.2)
- return b & 0x3f;
- }
- else if ((b & 0x60) == 0x40) { // x10..... ........ (C.25.3)
- if (dataEnd - dataP > 0) {
- return (((b & 0x1f) << 8) | *dataP++) + 0x40;
- }
- }
- else if ((b & 0x70) == 0x60) { // x110.... ........ ........ (C.25.4)
- if (dataEnd - dataP > 1) {
- size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x2040;
- dataP += 2;
- return result;
- }
- }
- throw DeadlyImportError(parseErrorMessage);
- }
-
- size_t parseInt3() { // C.27
- uint8_t b = *dataP++;
- if (!(b & 0x20)) { // xx0..... (C.27.2)
- return b & 0x1f;
- }
- else if ((b & 0x38) == 0x20) { // xx100... ........ (C.27.3)
- if (dataEnd - dataP > 0) {
- return (((b & 0x07) << 8) | *dataP++) + 0x20;
- }
- }
- else if ((b & 0x38) == 0x28) { // xx101... ........ ........ (C.27.4)
- if (dataEnd - dataP > 1) {
- size_t result = (((b & 0x07) << 16) | (dataP[0] << 8) | dataP[1]) + 0x820;
- dataP += 2;
- return result;
- }
- }
- else if ((b & 0x3f) == 0x30) { // xx110000 0000.... ........ ........ (C.27.5)
- if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) {
- size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x80820;
- dataP += 3;
- return result;
- }
- }
- throw DeadlyImportError(parseErrorMessage);
- }
-
- size_t parseInt4() { // C.28
- uint8_t b = *dataP++;
- if (!(b & 0x10)) { // xxx0.... (C.28.2)
- return b & 0x0f;
- }
- else if ((b & 0x1c) == 0x10) { // xxx100.. ........ (C.28.3)
- if (dataEnd - dataP > 0) {
- return (((b & 0x03) << 8) | *dataP++) + 0x10;
- }
- }
- else if ((b & 0x1c) == 0x14) { // xxx101.. ........ ........ (C.28.4)
- if (dataEnd - dataP > 1) {
- size_t result = (((b & 0x03) << 16) | (dataP[0] << 8) | dataP[1]) + 0x410;
- dataP += 2;
- return result;
- }
- }
- else if ((b & 0x1f) == 0x18) { // xxx11000 0000.... ........ ........ (C.28.5)
- if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) {
- size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x40410;
- dataP += 3;
- return result;
- }
- }
- throw DeadlyImportError(parseErrorMessage);
- }
-
- size_t parseSequenceLen() { // C.21
- if (dataEnd - dataP > 0) {
- uint8_t b = *dataP++;
- if (b < 0x80) { // 0....... (C.21.2)
- return b;
- }
- else if ((b & 0xf0) == 0x80) { // 1000.... ........ ........ (C.21.3)
- if (dataEnd - dataP > 1) {
- size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x80;
- dataP += 2;
- return result;
- }
- }
- }
- throw DeadlyImportError(parseErrorMessage);
- }
-
- std::string parseNonEmptyOctetString2() { // C.22
- // Parse the length of the string
- uint8_t b = *dataP++ & 0x7f;
- size_t len;
- if (!(b & 0x40)) { // x0...... (C.22.3.1)
- len = b + 1;
- }
- else if (b == 0x40) { // x1000000 ........ (C.22.3.2)
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- len = *dataP++ + 0x41;
- }
- else if (b == 0x60) { // x1100000 ........ ........ ........ ........ (C.22.3.3)
- if (dataEnd - dataP < 4) {
- throw DeadlyImportError(parseErrorMessage);
- }
- len = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x141;
- dataP += 4;
- }
- else {
- throw DeadlyImportError(parseErrorMessage);
- }
-
- // Parse the string (C.22.4)
- if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
- throw DeadlyImportError(parseErrorMessage);
- }
- std::string s = parseUTF8String(dataP, len);
- dataP += len;
-
- return s;
- }
-
- size_t parseNonEmptyOctetString5Length() { // C.23
- // Parse the length of the string
- size_t b = *dataP++ & 0x0f;
- if (!(b & 0x08)) { // xxxx0... (C.23.3.1)
- return b + 1;
- }
- else if (b == 0x08) { // xxxx1000 ........ (C.23.3.2)
- if (dataEnd - dataP > 0) {
- return *dataP++ + 0x09;
- }
- }
- else if (b == 0x0c) { // xxxx1100 ........ ........ ........ ........ (C.23.3.3)
- if (dataEnd - dataP > 3) {
- size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x109;
- dataP += 4;
- return result;
- }
- }
- throw DeadlyImportError(parseErrorMessage);
- }
-
- size_t parseNonEmptyOctetString7Length() { // C.24
- // Parse the length of the string
- size_t b = *dataP++ & 0x03;
- if (!(b & 0x02)) { // xxxxxx0. (C.24.3.1)
- return b + 1;
- }
- else if (b == 0x02) { // xxxxxx10 ........ (C.24.3.2)
- if (dataEnd - dataP > 0) {
- return *dataP++ + 0x3;
- }
- }
- else if (b == 0x03) { // xxxxxx11 ........ ........ ........ ........ (C.24.3.3)
- if (dataEnd - dataP > 3) {
- size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x103;
- dataP += 4;
- return result;
- }
- }
- throw DeadlyImportError(parseErrorMessage);
- }
-
- std::shared_ptr<const FIValue> parseEncodedData(size_t index, size_t len) {
- if (index < 32) {
- FIDecoder *decoder = defaultDecoder[index];
- if (!decoder) {
- throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index));
- }
- return decoder->decode(dataP, len);
- }
- else {
- if (index - 32 >= vocabulary.encodingAlgorithmTable.size()) {
- throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index));
- }
- std::string uri = vocabulary.encodingAlgorithmTable[index - 32];
- auto it = decoderMap.find(uri);
- if (it == decoderMap.end()) {
- throw DeadlyImportError("Unsupported encoding algorithm " + uri);
- }
- else {
- return it->second->decode(dataP, len);
- }
- }
- }
-
- std::shared_ptr<const FIValue> parseRestrictedAlphabet(size_t index, size_t len) {
- std::string alphabet;
- if (index < 16) {
- switch (index) {
- case 0: // numeric
- alphabet = "0123456789-+.e ";
- break;
- case 1: // date and time
- alphabet = "0123456789-:TZ ";
- break;
- default:
- throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index));
- }
- }
- else {
- if (index - 16 >= vocabulary.restrictedAlphabetTable.size()) {
- throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index));
- }
- alphabet = vocabulary.restrictedAlphabetTable[index - 16];
- }
- std::vector<uint32_t> alphabetUTF32;
- utf8::utf8to32(alphabet.begin(), alphabet.end(), back_inserter(alphabetUTF32));
- std::string::size_type alphabetLength = alphabetUTF32.size();
- if (alphabetLength < 2) {
- throw DeadlyImportError("Invalid restricted alphabet length " + to_string(alphabetLength));
- }
- std::string::size_type bitsPerCharacter = 1;
- while ((1ull << bitsPerCharacter) <= alphabetLength) {
- ++bitsPerCharacter;
- }
- size_t bitsAvail = 0;
- uint8_t mask = (1 << bitsPerCharacter) - 1;
- uint32_t bits = 0;
- std::string s;
- for (size_t i = 0; i < len; ++i) {
- bits = (bits << 8) | dataP[i];
- bitsAvail += 8;
- while (bitsAvail >= bitsPerCharacter) {
- bitsAvail -= bitsPerCharacter;
- size_t charIndex = (bits >> bitsAvail) & mask;
- if (charIndex < alphabetLength) {
- s.push_back(alphabetUTF32[charIndex]);
- }
- else if (charIndex != mask) {
- throw DeadlyImportError(parseErrorMessage);
- }
- }
- }
- return FIStringValue::create(std::move(s));
- }
-
- std::shared_ptr<const FIValue> parseEncodedCharacterString3() { // C.19
- std::shared_ptr<const FIValue> result;
- size_t len;
- uint8_t b = *dataP;
- if (b & 0x20) {
- ++dataP;
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- size_t index = ((b & 0x0f) << 4) | ((*dataP & 0xf0) >> 4); // C.29
- len = parseNonEmptyOctetString5Length();
- if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
- throw DeadlyImportError(parseErrorMessage);
- }
- if (b & 0x10) {
- // encoding algorithm (C.19.3.4)
- result = parseEncodedData(index, len);
- }
- else {
- // Restricted alphabet (C.19.3.3)
- result = parseRestrictedAlphabet(index, len);
- }
- }
- else {
- len = parseNonEmptyOctetString5Length();
- if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
- throw DeadlyImportError(parseErrorMessage);
- }
- if (b & 0x10) {
- // UTF-16 (C.19.3.2)
- if (len & 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- result = FIStringValue::create(parseUTF16String(dataP, len));
- }
- else {
- // UTF-8 (C.19.3.1)
- result = FIStringValue::create(parseUTF8String(dataP, len));
- }
- }
- dataP += len;
- return result;
- }
-
- std::shared_ptr<const FIValue> parseEncodedCharacterString5() { // C.20
- std::shared_ptr<const FIValue> result;
- size_t len;
- uint8_t b = *dataP;
- if (b & 0x08) {
- ++dataP;
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- size_t index = ((b & 0x03) << 6) | ((*dataP & 0xfc) >> 2); /* C.29 */
- len = parseNonEmptyOctetString7Length();
- if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
- throw DeadlyImportError(parseErrorMessage);
- }
- if (b & 0x04) {
- // encoding algorithm (C.20.3.4)
- result = parseEncodedData(index, len);
- }
- else {
- // Restricted alphabet (C.20.3.3)
- result = parseRestrictedAlphabet(index, len);
- }
- }
- else {
- len = parseNonEmptyOctetString7Length();
- if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
- throw DeadlyImportError(parseErrorMessage);
- }
- if (b & 0x04) {
- // UTF-16 (C.20.3.2)
- if (len & 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- result = FIStringValue::create(parseUTF16String(dataP, len));
- }
- else {
- // UTF-8 (C.20.3.1)
- result = FIStringValue::create(parseUTF8String(dataP, len));
- }
- }
- dataP += len;
- return result;
- }
-
- const std::string &parseIdentifyingStringOrIndex(std::vector<std::string> &stringTable) { // C.13
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- uint8_t b = *dataP;
- if (b & 0x80) {
- // We have an index (C.13.4)
- size_t index = parseInt2();
- if (index >= stringTable.size()) {
- throw DeadlyImportError(parseErrorMessage);
- }
- return stringTable[index];
- }
- else {
- // We have a string (C.13.3)
- stringTable.push_back(parseNonEmptyOctetString2());
- return stringTable.back();
- }
- }
-
- QName parseNameSurrogate() { // C.16
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- uint8_t b = *dataP++;
- if (b & 0xfc) { // Padding '000000' C.2.5.5
- throw DeadlyImportError(parseErrorMessage);
- }
- QName result;
- size_t index;
- if (b & 0x02) { // prefix (C.16.3)
- if ((dataEnd - dataP < 1) || (*dataP & 0x80)) {
- throw DeadlyImportError(parseErrorMessage);
- }
- index = parseInt2();
- if (index >= vocabulary.prefixTable.size()) {
- throw DeadlyImportError(parseErrorMessage);
- }
- result.prefix = vocabulary.prefixTable[index];
- }
- if (b & 0x01) { // namespace-name (C.16.4)
- if ((dataEnd - dataP < 1) || (*dataP & 0x80)) {
- throw DeadlyImportError(parseErrorMessage);
- }
- index = parseInt2();
- if (index >= vocabulary.namespaceNameTable.size()) {
- throw DeadlyImportError(parseErrorMessage);
- }
- result.uri = vocabulary.namespaceNameTable[index];
- }
- // local-name
- if ((dataEnd - dataP < 1) || (*dataP & 0x80)) {
- throw DeadlyImportError(parseErrorMessage);
- }
- index = parseInt2();
- if (index >= vocabulary.localNameTable.size()) {
- throw DeadlyImportError(parseErrorMessage);
- }
- result.name = vocabulary.localNameTable[index];
- return result;
- }
-
- const QName &parseQualifiedNameOrIndex2(std::vector<QName> &qNameTable) { // C.17
- uint8_t b = *dataP;
- if ((b & 0x7c) == 0x78) { // x11110..
- // We have a literal (C.17.3)
- ++dataP;
- QName result;
- // prefix (C.17.3.1)
- result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string();
- // namespace-name (C.17.3.1)
- result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string();
- // local-name
- result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable);
- qNameTable.push_back(result);
- return qNameTable.back();
- }
- else {
- // We have an index (C.17.4)
- size_t index = parseInt2();
- if (index >= qNameTable.size()) {
- throw DeadlyImportError(parseErrorMessage);
- }
- return qNameTable[index];
- }
- }
-
- const QName &parseQualifiedNameOrIndex3(std::vector<QName> &qNameTable) { // C.18
- uint8_t b = *dataP;
- if ((b & 0x3c) == 0x3c) { // xx1111..
- // We have a literal (C.18.3)
- ++dataP;
- QName result;
- // prefix (C.18.3.1)
- result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string();
- // namespace-name (C.18.3.1)
- result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string();
- // local-name
- result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable);
- qNameTable.push_back(result);
- return qNameTable.back();
- }
- else {
- // We have an index (C.18.4)
- size_t index = parseInt3();
- if (index >= qNameTable.size()) {
- throw DeadlyImportError(parseErrorMessage);
- }
- return qNameTable[index];
- }
- }
-
- std::shared_ptr<const FIValue> parseNonIdentifyingStringOrIndex1(std::vector<std::shared_ptr<const FIValue>> &valueTable) { // C.14
- uint8_t b = *dataP;
- if (b == 0xff) { // C.26.2
- // empty string
- ++dataP;
- return EmptyFIString;
- }
- else if (b & 0x80) { // C.14.4
- // We have an index
- size_t index = parseInt2();
- if (index >= valueTable.size()) {
- throw DeadlyImportError(parseErrorMessage);
- }
- return valueTable[index];
- }
- else { // C.14.3
- // We have a literal
- std::shared_ptr<const FIValue> result = parseEncodedCharacterString3();
- if (b & 0x40) { // C.14.3.1
- valueTable.push_back(result);
- }
- return result;
- }
- }
-
- std::shared_ptr<const FIValue> parseNonIdentifyingStringOrIndex3(std::vector<std::shared_ptr<const FIValue>> &valueTable) { // C.15
- uint8_t b = *dataP;
- if (b & 0x20) { // C.15.4
- // We have an index
- size_t index = parseInt4();
- if (index >= valueTable.size()) {
- throw DeadlyImportError(parseErrorMessage);
- }
- return valueTable[index];
- }
- else { // C.15.3
- // We have a literal
- std::shared_ptr<const FIValue> result = parseEncodedCharacterString5();
- if (b & 0x10) { // C.15.3.1
- valueTable.push_back(result);
- }
- return result;
- }
- }
-
- void parseElement() {
- // C.3
-
- attributes.clear();
-
- uint8_t b = *dataP;
- bool hasAttributes = (b & 0x40) != 0; // C.3.3
- if ((b & 0x3f) == 0x38) { // C.3.4.1
- // Parse namespaces
- ++dataP;
- for (;;) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- b = *dataP++;
- if (b == 0xf0) { // C.3.4.3
- break;
- }
- if ((b & 0xfc) != 0xcc) { // C.3.4.2
- throw DeadlyImportError(parseErrorMessage);
- }
- // C.12
- Attribute attr;
- attr.qname.prefix = "xmlns";
- attr.qname.name = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string();
- attr.qname.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string();
- attr.name = attr.qname.name.empty() ? "xmlns" : "xmlns:" + attr.qname.name;
- attr.value = FIStringValue::create(std::string(attr.qname.uri));
- attributes.push_back(attr);
- }
- if ((dataEnd - dataP < 1) || (*dataP & 0xc0)) {
- throw DeadlyImportError(parseErrorMessage);
- }
- }
-
- // Parse Element name (C.3.5)
- const QName &elemName = parseQualifiedNameOrIndex3(vocabulary.elementNameTable);
- nodeName = elemName.prefix.empty() ? elemName.name : elemName.prefix + ':' + elemName.name;
-
- if (hasAttributes) {
- for (;;) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- b = *dataP;
- if (b < 0x80) { // C.3.6.1
- // C.4
- Attribute attr;
- attr.qname = parseQualifiedNameOrIndex2(vocabulary.attributeNameTable);
- attr.name = attr.qname.prefix.empty() ? attr.qname.name : attr.qname.prefix + ':' + attr.qname.name;
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- attr.value = parseNonIdentifyingStringOrIndex1(vocabulary.attributeValueTable);
- attributes.push_back(attr);
- }
- else {
- if ((b & 0xf0) != 0xf0) { // C.3.6.2
- throw DeadlyImportError(parseErrorMessage);
- }
- emptyElement = b == 0xff; // C.3.6.2, C.3.8
- ++dataP;
- break;
- }
- }
- }
- else {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- b = *dataP;
- switch (b) {
- case 0xff:
- terminatorPending = true;
- // Intentionally fall through
- case 0xf0:
- emptyElement = true;
- ++dataP;
- break;
- default:
- emptyElement = false;
- }
- }
- if (!emptyElement) {
- elementStack.push(nodeName);
- }
-
- currentNodeType = irr::io::EXN_ELEMENT;
- }
-
- void parseHeader() {
- // Parse header (C.1.3)
- size_t magicSize = parseMagic(dataP, dataEnd);
- if (!magicSize) {
- throw DeadlyImportError(parseErrorMessage);
- }
- dataP += magicSize;
- // C.2.3
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- uint8_t b = *dataP++;
- if (b & 0x40) {
- // Parse additional data (C.2.4)
- size_t len = parseSequenceLen();
- for (size_t i = 0; i < len; ++i) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- /*std::string id =*/ parseNonEmptyOctetString2();
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- /*std::string data =*/ parseNonEmptyOctetString2();
- }
- }
- if (b & 0x20) {
- // Parse initial vocabulary (C.2.5)
- if (dataEnd - dataP < 2) {
- throw DeadlyImportError(parseErrorMessage);
- }
- uint16_t b1 = (dataP[0] << 8) | dataP[1];
- dataP += 2;
- if (b1 & 0x1000) {
- // External vocabulary (C.2.5.2)
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- std::string uri = parseNonEmptyOctetString2();
- auto it = vocabularyMap.find(uri);
- if (it == vocabularyMap.end()) {
- throw DeadlyImportError("Unknown vocabulary " + uri);
- }
- const FIVocabulary *externalVocabulary = it->second;
- if (externalVocabulary->restrictedAlphabetTable) {
- std::copy(externalVocabulary->restrictedAlphabetTable, externalVocabulary->restrictedAlphabetTable + externalVocabulary->restrictedAlphabetTableSize, std::back_inserter(vocabulary.restrictedAlphabetTable));
- }
- if (externalVocabulary->encodingAlgorithmTable) {
- std::copy(externalVocabulary->encodingAlgorithmTable, externalVocabulary->encodingAlgorithmTable + externalVocabulary->encodingAlgorithmTableSize, std::back_inserter(vocabulary.encodingAlgorithmTable));
- }
- if (externalVocabulary->prefixTable) {
- std::copy(externalVocabulary->prefixTable, externalVocabulary->prefixTable + externalVocabulary->prefixTableSize, std::back_inserter(vocabulary.prefixTable));
- }
- if (externalVocabulary->namespaceNameTable) {
- std::copy(externalVocabulary->namespaceNameTable, externalVocabulary->namespaceNameTable + externalVocabulary->namespaceNameTableSize, std::back_inserter(vocabulary.namespaceNameTable));
- }
- if (externalVocabulary->localNameTable) {
- std::copy(externalVocabulary->localNameTable, externalVocabulary->localNameTable + externalVocabulary->localNameTableSize, std::back_inserter(vocabulary.localNameTable));
- }
- if (externalVocabulary->otherNCNameTable) {
- std::copy(externalVocabulary->otherNCNameTable, externalVocabulary->otherNCNameTable + externalVocabulary->otherNCNameTableSize, std::back_inserter(vocabulary.otherNCNameTable));
- }
- if (externalVocabulary->otherURITable) {
- std::copy(externalVocabulary->otherURITable, externalVocabulary->otherURITable + externalVocabulary->otherURITableSize, std::back_inserter(vocabulary.otherURITable));
- }
- if (externalVocabulary->attributeValueTable) {
- std::copy(externalVocabulary->attributeValueTable, externalVocabulary->attributeValueTable + externalVocabulary->attributeValueTableSize, std::back_inserter(vocabulary.attributeValueTable));
- }
- if (externalVocabulary->charactersTable) {
- std::copy(externalVocabulary->charactersTable, externalVocabulary->charactersTable + externalVocabulary->charactersTableSize, std::back_inserter(vocabulary.charactersTable));
- }
- if (externalVocabulary->otherStringTable) {
- std::copy(externalVocabulary->otherStringTable, externalVocabulary->otherStringTable + externalVocabulary->otherStringTableSize, std::back_inserter(vocabulary.otherStringTable));
- }
- if (externalVocabulary->elementNameTable) {
- std::copy(externalVocabulary->elementNameTable, externalVocabulary->elementNameTable + externalVocabulary->elementNameTableSize, std::back_inserter(vocabulary.elementNameTable));
- }
- if (externalVocabulary->attributeNameTable) {
- std::copy(externalVocabulary->attributeNameTable, externalVocabulary->attributeNameTable + externalVocabulary->attributeNameTableSize, std::back_inserter(vocabulary.attributeNameTable));
- }
- }
- if (b1 & 0x0800) {
- // Parse restricted alphabets (C.2.5.3)
- for (size_t len = parseSequenceLen(); len > 0; --len) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- vocabulary.restrictedAlphabetTable.push_back(parseNonEmptyOctetString2());
- }
- }
- if (b1 & 0x0400) {
- // Parse encoding algorithms (C.2.5.3)
- for (size_t len = parseSequenceLen(); len > 0; --len) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- vocabulary.encodingAlgorithmTable.push_back(parseNonEmptyOctetString2());
- }
- }
- if (b1 & 0x0200) {
- // Parse prefixes (C.2.5.3)
- for (size_t len = parseSequenceLen(); len > 0; --len) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- vocabulary.prefixTable.push_back(parseNonEmptyOctetString2());
- }
- }
- if (b1 & 0x0100) {
- // Parse namespace names (C.2.5.3)
- for (size_t len = parseSequenceLen(); len > 0; --len) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- vocabulary.namespaceNameTable.push_back(parseNonEmptyOctetString2());
- }
- }
- if (b1 & 0x0080) {
- // Parse local names (C.2.5.3)
- for (size_t len = parseSequenceLen(); len > 0; --len) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- vocabulary.localNameTable.push_back(parseNonEmptyOctetString2());
- }
- }
- if (b1 & 0x0040) {
- // Parse other ncnames (C.2.5.3)
- for (size_t len = parseSequenceLen(); len > 0; --len) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- vocabulary.otherNCNameTable.push_back(parseNonEmptyOctetString2());
- }
- }
- if (b1 & 0x0020) {
- // Parse other uris (C.2.5.3)
- for (size_t len = parseSequenceLen(); len > 0; --len) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- vocabulary.otherURITable.push_back(parseNonEmptyOctetString2());
- }
- }
- if (b1 & 0x0010) {
- // Parse attribute values (C.2.5.4)
- for (size_t len = parseSequenceLen(); len > 0; --len) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- vocabulary.attributeValueTable.push_back(parseEncodedCharacterString3());
- }
- }
- if (b1 & 0x0008) {
- // Parse content character chunks (C.2.5.4)
- for (size_t len = parseSequenceLen(); len > 0; --len) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- vocabulary.charactersTable.push_back(parseEncodedCharacterString3());
- }
- }
- if (b1 & 0x0004) {
- // Parse other strings (C.2.5.4)
- for (size_t len = parseSequenceLen(); len > 0; --len) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- vocabulary.otherStringTable.push_back(parseEncodedCharacterString3());
- }
- }
- if (b1 & 0x0002) {
- // Parse element name surrogates (C.2.5.5)
- for (size_t len = parseSequenceLen(); len > 0; --len) {
- vocabulary.elementNameTable.push_back(parseNameSurrogate());
- }
- }
- if (b1 & 0x0001) {
- // Parse attribute name surrogates (C.2.5.5)
- for (size_t len = parseSequenceLen(); len > 0; --len) {
- vocabulary.attributeNameTable.push_back(parseNameSurrogate());
- }
- }
- }
- if (b & 0x10) {
- // Parse notations (C.2.6)
- for (;;) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- uint8_t b1 = *dataP++;
- if (b1 == 0xf0) {
- break;
- }
- if ((b1 & 0xfc) != 0xc0) {
- throw DeadlyImportError(parseErrorMessage);
- }
- /* C.11 */
- /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
- if (b1 & 0x02) {
- /*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
- }
- if (b1 & 0x01) {
- /*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
- }
- }
- }
- if (b & 0x08) {
- // Parse unparsed entities (C.2.7)
- for (;;) {
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- uint8_t b1 = *dataP++;
- if (b1 == 0xf0) {
- break;
- }
- if ((b1 & 0xfe) != 0xd0) {
- throw DeadlyImportError(parseErrorMessage);
- }
- /* C.10 */
- /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
- /*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
- if (b1 & 0x01) {
- /*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
- }
- /*const std::string &notationName =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
- }
- }
- if (b & 0x04) {
- // Parse character encoding scheme (C.2.8)
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- /*std::string characterEncodingScheme =*/ parseNonEmptyOctetString2();
- }
- if (b & 0x02) {
- // Parse standalone flag (C.2.9)
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- uint8_t b1 = *dataP++;
- if (b1 & 0xfe) {
- throw DeadlyImportError(parseErrorMessage);
- }
- //bool standalone = b1 & 0x01;
- }
- if (b & 0x01) {
- // Parse version (C.2.10)
- if (dataEnd - dataP < 1) {
- throw DeadlyImportError(parseErrorMessage);
- }
- /*std::shared_ptr<const FIValue> version =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable);
- }
- }
-
- std::unique_ptr<uint8_t[]> data;
- uint8_t *dataP, *dataEnd;
- irr::io::EXML_NODE currentNodeType;
- bool emptyElement;
- bool headerPending;
- bool terminatorPending;
- Vocabulary vocabulary;
- std::vector<Attribute> attributes;
- std::stack<std::string> elementStack;
- std::string nodeName;
- std::map<std::string, std::unique_ptr<FIDecoder>> decoderMap;
- std::map<std::string, const FIVocabulary*> vocabularyMap;
-
- static const std::string EmptyString;
- static std::shared_ptr<const FIValue> EmptyFIString;
-
- static FIHexDecoder hexDecoder;
- static FIBase64Decoder base64Decoder;
- static FIShortDecoder shortDecoder;
- static FIIntDecoder intDecoder;
- static FILongDecoder longDecoder;
- static FIBoolDecoder boolDecoder;
- static FIFloatDecoder floatDecoder;
- static FIDoubleDecoder doubleDecoder;
- static FIUUIDDecoder uuidDecoder;
- static FICDATADecoder cdataDecoder;
- static FIDecoder *defaultDecoder[32];
-};
-
-const std::string CFIReaderImpl::EmptyString;
-std::shared_ptr<const FIValue> CFIReaderImpl::EmptyFIString = FIStringValue::create(std::string());
-
-FIHexDecoder CFIReaderImpl::hexDecoder;
-FIBase64Decoder CFIReaderImpl::base64Decoder;
-FIShortDecoder CFIReaderImpl::shortDecoder;
-FIIntDecoder CFIReaderImpl::intDecoder;
-FILongDecoder CFIReaderImpl::longDecoder;
-FIBoolDecoder CFIReaderImpl::boolDecoder;
-FIFloatDecoder CFIReaderImpl::floatDecoder;
-FIDoubleDecoder CFIReaderImpl::doubleDecoder;
-FIUUIDDecoder CFIReaderImpl::uuidDecoder;
-FICDATADecoder CFIReaderImpl::cdataDecoder;
-
-FIDecoder *CFIReaderImpl::defaultDecoder[32] = {
- &hexDecoder,
- &base64Decoder,
- &shortDecoder,
- &intDecoder,
- &longDecoder,
- &boolDecoder,
- &floatDecoder,
- &doubleDecoder,
- &uuidDecoder,
- &cdataDecoder
-};
-
-class CXMLReaderImpl : public FIReader
-{
-public:
-
- //! Constructor
- CXMLReaderImpl(std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>> reader_)
- : reader(std::move(reader_))
- {}
-
- virtual ~CXMLReaderImpl() {}
-
- virtual bool read() /*override*/ {
- return reader->read();
- }
-
- virtual irr::io::EXML_NODE getNodeType() const /*override*/ {
- return reader->getNodeType();
- }
-
- virtual int getAttributeCount() const /*override*/ {
- return reader->getAttributeCount();
- }
-
- virtual const char* getAttributeName(int idx) const /*override*/ {
- return reader->getAttributeName(idx);
- }
-
- virtual const char* getAttributeValue(int idx) const /*override*/ {
- return reader->getAttributeValue(idx);
- }
-
- virtual const char* getAttributeValue(const char* name) const /*override*/ {
- return reader->getAttributeValue(name);
- }
-
- virtual const char* getAttributeValueSafe(const char* name) const /*override*/ {
- return reader->getAttributeValueSafe(name);
- }
-
- virtual int getAttributeValueAsInt(const char* name) const /*override*/ {
- return reader->getAttributeValueAsInt(name);
- }
-
- virtual int getAttributeValueAsInt(int idx) const /*override*/ {
- return reader->getAttributeValueAsInt(idx);
- }
-
- virtual float getAttributeValueAsFloat(const char* name) const /*override*/ {
- return reader->getAttributeValueAsFloat(name);
- }
-
- virtual float getAttributeValueAsFloat(int idx) const /*override*/ {
- return reader->getAttributeValueAsFloat(idx);
- }
-
- virtual const char* getNodeName() const /*override*/ {
- return reader->getNodeName();
- }
-
- virtual const char* getNodeData() const /*override*/ {
- return reader->getNodeData();
- }
-
- virtual bool isEmptyElement() const /*override*/ {
- return reader->isEmptyElement();
- }
-
- virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ {
- return reader->getSourceFormat();
- }
-
- virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ {
- return reader->getParserFormat();
- }
-
- virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int /*idx*/) const /*override*/ {
- return nullptr;
- }
-
- virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* /*name*/) const /*override*/ {
- return nullptr;
- }
-
- virtual void registerDecoder(const std::string & /*algorithmUri*/, std::unique_ptr<FIDecoder> /*decoder*/) /*override*/ {}
-
-
- virtual void registerVocabulary(const std::string &/*vocabularyUri*/, const FIVocabulary * /*vocabulary*/) /*override*/ {}
-
-private:
-
- std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>> reader;
-};
-
-static std::unique_ptr<uint8_t[]> readFile(IOStream *stream, size_t &size, bool &isFI) {
- size = stream->FileSize();
- std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(new uint8_t[size]);
- if (stream->Read(data.get(), size, 1) != 1) {
- size = 0;
- data.reset();
- }
- isFI = parseMagic(data.get(), data.get() + size) > 0;
- return data;
-}
-
-std::unique_ptr<FIReader> FIReader::create(IOStream *stream)
-{
- size_t size;
- bool isFI;
- auto data = readFile(stream, size, isFI);
- if (isFI) {
- return std::unique_ptr<FIReader>(new CFIReaderImpl(std::move(data), size));
- }
- else {
- auto memios = std::unique_ptr<MemoryIOStream>(new MemoryIOStream(data.release(), size, true));
- auto callback = std::unique_ptr<CIrrXML_IOStreamReader>(new CIrrXML_IOStreamReader(memios.get()));
- return std::unique_ptr<FIReader>(new CXMLReaderImpl(std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>>(createIrrXMLReader(callback.get()))));
- }
-}
-
-}// namespace Assimp
-
-#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER
diff --git a/thirdparty/assimp/code/MMDCpp14.h b/thirdparty/assimp/code/MMD/MMDCpp14.h
index 638b0bfd2f..638b0bfd2f 100644
--- a/thirdparty/assimp/code/MMDCpp14.h
+++ b/thirdparty/assimp/code/MMD/MMDCpp14.h
diff --git a/thirdparty/assimp/code/MMDImporter.cpp b/thirdparty/assimp/code/MMD/MMDImporter.cpp
index 84b9e35a6b..e7744e4cd0 100644
--- a/thirdparty/assimp/code/MMDImporter.cpp
+++ b/thirdparty/assimp/code/MMD/MMDImporter.cpp
@@ -41,15 +41,17 @@ 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 "MMD/MMDImporter.h"
+#include "MMD/MMDPmdParser.h"
+#include "MMD/MMDPmxParser.h"
+#include "MMD/MMDVmdParser.h"
+#include "PostProcessing/ConvertToLHProcess.h"
+
#include <assimp/DefaultIOSystem.h>
#include <assimp/Importer.hpp>
#include <assimp/ai_assert.h>
#include <assimp/scene.h>
+
#include <fstream>
#include <iomanip>
#include <memory>
diff --git a/thirdparty/assimp/code/MMDImporter.h b/thirdparty/assimp/code/MMD/MMDImporter.h
index 4ee94eeb00..4ee94eeb00 100644
--- a/thirdparty/assimp/code/MMDImporter.h
+++ b/thirdparty/assimp/code/MMD/MMDImporter.h
diff --git a/thirdparty/assimp/code/MMDPmdParser.h b/thirdparty/assimp/code/MMD/MMDPmdParser.h
index d2f2224aa1..d2f2224aa1 100644
--- a/thirdparty/assimp/code/MMDPmdParser.h
+++ b/thirdparty/assimp/code/MMD/MMDPmdParser.h
diff --git a/thirdparty/assimp/code/MMDPmxParser.cpp b/thirdparty/assimp/code/MMD/MMDPmxParser.cpp
index 7425ceac22..80f0986dd7 100644
--- a/thirdparty/assimp/code/MMDPmxParser.cpp
+++ b/thirdparty/assimp/code/MMD/MMDPmxParser.cpp
@@ -42,7 +42,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <utility>
#include "MMDPmxParser.h"
#include <assimp/StringUtils.h>
-#include "../contrib/utf8cpp/source/utf8.h"
+#ifdef ASSIMP_USE_HUNTER
+# include <utf8/utf8.h>
+#else
+# include "../contrib/utf8cpp/source/utf8.h"
+#endif
#include <assimp/Exceptional.h>
namespace pmx
diff --git a/thirdparty/assimp/code/MMDPmxParser.h b/thirdparty/assimp/code/MMD/MMDPmxParser.h
index cf523a1298..cf523a1298 100644
--- a/thirdparty/assimp/code/MMDPmxParser.h
+++ b/thirdparty/assimp/code/MMD/MMDPmxParser.h
diff --git a/thirdparty/assimp/code/MMDVmdParser.h b/thirdparty/assimp/code/MMD/MMDVmdParser.h
index 947c3a2422..947c3a2422 100644
--- a/thirdparty/assimp/code/MMDVmdParser.h
+++ b/thirdparty/assimp/code/MMD/MMDVmdParser.h
diff --git a/thirdparty/assimp/code/MaterialSystem.cpp b/thirdparty/assimp/code/Material/MaterialSystem.cpp
index 03d5a18a34..d0b39093b6 100644
--- a/thirdparty/assimp/code/MaterialSystem.cpp
+++ b/thirdparty/assimp/code/Material/MaterialSystem.cpp
@@ -96,12 +96,12 @@ aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat,
ai_real* pOut,
unsigned int* pMax)
{
- ai_assert( pOut != NULL );
- ai_assert( pMat != NULL );
+ ai_assert( pOut != nullptr );
+ ai_assert( pMat != nullptr );
const aiMaterialProperty* prop;
aiGetMaterialProperty(pMat,pKey,type,index, (const aiMaterialProperty**) &prop);
- if (!prop) {
+ if ( nullptr == prop) {
return AI_FAILURE;
}
@@ -112,9 +112,11 @@ aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat,
if (pMax) {
iWrite = std::min(*pMax,iWrite); ;
}
- for (unsigned int a = 0; a < iWrite;++a) {
- pOut[a] = static_cast<ai_real> ( reinterpret_cast<float*>(prop->mData)[a] );
+
+ for (unsigned int a = 0; a < iWrite; ++a) {
+ pOut[ a ] = static_cast<ai_real> ( reinterpret_cast<float*>(prop->mData)[a] );
}
+
if (pMax) {
*pMax = iWrite;
}
diff --git a/thirdparty/assimp/code/MaterialSystem.h b/thirdparty/assimp/code/Material/MaterialSystem.h
index 67d53578cb..67d53578cb 100644
--- a/thirdparty/assimp/code/MaterialSystem.h
+++ b/thirdparty/assimp/code/Material/MaterialSystem.h
diff --git a/thirdparty/assimp/code/CalcTangentsProcess.cpp b/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.cpp
index b30f39c274..a3f7dd2557 100644
--- a/thirdparty/assimp/code/CalcTangentsProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.cpp
@@ -212,7 +212,7 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// 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();
+ localTangent.NormalizeSafe(); localBitangent.NormalizeSafe();
// 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);
@@ -220,10 +220,10 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
if (invalid_tangent != invalid_bitangent) {
if (invalid_tangent) {
localTangent = meshNorm[p] ^ localBitangent;
- localTangent.Normalize();
+ localTangent.NormalizeSafe();
} else {
localBitangent = localTangent ^ meshNorm[p];
- localBitangent.Normalize();
+ localBitangent.NormalizeSafe();
}
}
diff --git a/thirdparty/assimp/code/CalcTangentsProcess.h b/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.h
index 18775abcc7..3568a624f8 100644
--- a/thirdparty/assimp/code/CalcTangentsProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.h
@@ -42,11 +42,11 @@ 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.*/
+ bi-tangents on all imported meshes.*/
#ifndef AI_CALCTANGENTSPROCESS_H_INC
#define AI_CALCTANGENTSPROCESS_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
struct aiMesh;
diff --git a/thirdparty/assimp/code/ComputeUVMappingProcess.cpp b/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.cpp
index bb571a551b..bb571a551b 100644
--- a/thirdparty/assimp/code/ComputeUVMappingProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.cpp
diff --git a/thirdparty/assimp/code/ComputeUVMappingProcess.h b/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.h
index 24f6bb7218..a6d36e06ea 100644
--- a/thirdparty/assimp/code/ComputeUVMappingProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.h
@@ -45,7 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_COMPUTEUVMAPPING_H_INC
#define AI_COMPUTEUVMAPPING_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
+
#include <assimp/mesh.h>
#include <assimp/material.h>
#include <assimp/types.h>
diff --git a/thirdparty/assimp/code/ConvertToLHProcess.cpp b/thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.cpp
index b7cd4f0bc6..b7cd4f0bc6 100644
--- a/thirdparty/assimp/code/ConvertToLHProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.cpp
diff --git a/thirdparty/assimp/code/ConvertToLHProcess.h b/thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.h
index 63351568d0..f32b91fc39 100644
--- a/thirdparty/assimp/code/ConvertToLHProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/ConvertToLHProcess.h
@@ -52,7 +52,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define AI_CONVERTTOLHPROCESS_H_INC
#include <assimp/types.h>
-#include "BaseProcess.h"
+
+#include "Common/BaseProcess.h"
struct aiMesh;
struct aiNodeAnim;
diff --git a/thirdparty/assimp/code/DeboneProcess.cpp b/thirdparty/assimp/code/PostProcessing/DeboneProcess.cpp
index 83b8336bc9..83b8336bc9 100644
--- a/thirdparty/assimp/code/DeboneProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/DeboneProcess.cpp
diff --git a/thirdparty/assimp/code/DeboneProcess.h b/thirdparty/assimp/code/PostProcessing/DeboneProcess.h
index ba77aba70e..8b64c2acc6 100644
--- a/thirdparty/assimp/code/DeboneProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/DeboneProcess.h
@@ -44,17 +44,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_DEBONEPROCESS_H_INC
#define AI_DEBONEPROCESS_H_INC
-#include <vector>
-#include <utility>
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
#include <assimp/mesh.h>
#include <assimp/scene.h>
+#include <vector>
+#include <utility>
+
+#// Forward declarations
class DeboneTest;
-namespace Assimp
-{
+namespace Assimp {
#if (!defined AI_DEBONE_THRESHOLD)
# define AI_DEBONE_THRESHOLD 1.0f
@@ -66,14 +67,11 @@ namespace Assimp
* 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
-{
+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.
@@ -91,7 +89,6 @@ public:
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.
diff --git a/thirdparty/assimp/code/DropFaceNormalsProcess.cpp b/thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.cpp
index b11615bb82..b11615bb82 100644
--- a/thirdparty/assimp/code/DropFaceNormalsProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.cpp
diff --git a/thirdparty/assimp/code/DropFaceNormalsProcess.h b/thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.h
index 0d116663b7..c710c5a5ee 100644
--- a/thirdparty/assimp/code/DropFaceNormalsProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/DropFaceNormalsProcess.h
@@ -44,23 +44,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_DROPFACENORMALPROCESS_H_INC
#define AI_DROPFACENORMALPROCESS_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
+
#include <assimp/mesh.h>
-namespace Assimp
-{
+namespace Assimp {
// ---------------------------------------------------------------------------
/** The DropFaceNormalsProcess computes face normals for all faces of all meshes
*/
-class ASSIMP_API_WINONLY DropFaceNormalsProcess : public BaseProcess
-{
+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
diff --git a/thirdparty/assimp/code/EmbedTexturesProcess.cpp b/thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.cpp
index 739382a057..739382a057 100644
--- a/thirdparty/assimp/code/EmbedTexturesProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.cpp
diff --git a/thirdparty/assimp/code/EmbedTexturesProcess.h b/thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.h
index cdf40bef74..3c4b2eab4e 100644
--- a/thirdparty/assimp/code/EmbedTexturesProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/EmbedTexturesProcess.h
@@ -42,7 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
#include <string>
diff --git a/thirdparty/assimp/code/FindDegenerates.cpp b/thirdparty/assimp/code/PostProcessing/FindDegenerates.cpp
index 365f5d7447..50fac46dba 100644
--- a/thirdparty/assimp/code/FindDegenerates.cpp
+++ b/thirdparty/assimp/code/PostProcessing/FindDegenerates.cpp
@@ -228,6 +228,7 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
if ( area < 1e-6 ) {
if ( mConfigRemoveDegenerates ) {
remove_me[ a ] = true;
+ ++deg;
goto evil_jump_outside;
}
diff --git a/thirdparty/assimp/code/FindDegenerates.h b/thirdparty/assimp/code/PostProcessing/FindDegenerates.h
index 880f5f16a2..7a15e77cf1 100644
--- a/thirdparty/assimp/code/FindDegenerates.h
+++ b/thirdparty/assimp/code/PostProcessing/FindDegenerates.h
@@ -45,7 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_FINDDEGENERATESPROCESS_H_INC
#define AI_FINDDEGENERATESPROCESS_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
+
#include <assimp/mesh.h>
class FindDegeneratesProcessTest;
diff --git a/thirdparty/assimp/code/FindInstancesProcess.cpp b/thirdparty/assimp/code/PostProcessing/FindInstancesProcess.cpp
index be1138116e..64907458a1 100644
--- a/thirdparty/assimp/code/FindInstancesProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/FindInstancesProcess.cpp
@@ -137,6 +137,11 @@ void FindInstancesProcess::Execute( aiScene* pScene)
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])
{
@@ -154,12 +159,7 @@ void FindInstancesProcess::Execute( aiScene* pScene)
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,
+ // 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))
diff --git a/thirdparty/assimp/code/FindInstancesProcess.h b/thirdparty/assimp/code/PostProcessing/FindInstancesProcess.h
index ab4a371c71..64b838d7cc 100644
--- a/thirdparty/assimp/code/FindInstancesProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/FindInstancesProcess.h
@@ -46,8 +46,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_FINDINSTANCES_H_INC
#define AI_FINDINSTANCES_H_INC
-#include "BaseProcess.h"
-#include "ProcessHelper.h"
+#include "Common/BaseProcess.h"
+#include "PostProcessing/ProcessHelper.h"
class FindInstancesProcessTest;
namespace Assimp {
diff --git a/thirdparty/assimp/code/FindInvalidDataProcess.cpp b/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.cpp
index 433f042448..433f042448 100644
--- a/thirdparty/assimp/code/FindInvalidDataProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.cpp
diff --git a/thirdparty/assimp/code/FindInvalidDataProcess.h b/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.h
index 8504fb7b1f..ce7375f34f 100644
--- a/thirdparty/assimp/code/FindInvalidDataProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.h
@@ -46,7 +46,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_FINDINVALIDDATA_H_INC
#define AI_FINDINVALIDDATA_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
+
#include <assimp/types.h>
#include <assimp/anim.h>
diff --git a/thirdparty/assimp/code/FixNormalsStep.cpp b/thirdparty/assimp/code/PostProcessing/FixNormalsStep.cpp
index bbbe6899b4..bbbe6899b4 100644
--- a/thirdparty/assimp/code/FixNormalsStep.cpp
+++ b/thirdparty/assimp/code/PostProcessing/FixNormalsStep.cpp
diff --git a/thirdparty/assimp/code/FixNormalsStep.h b/thirdparty/assimp/code/PostProcessing/FixNormalsStep.h
index 6be27faef6..f60ce596a4 100644
--- a/thirdparty/assimp/code/FixNormalsStep.h
+++ b/thirdparty/assimp/code/PostProcessing/FixNormalsStep.h
@@ -45,7 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_FIXNORMALSPROCESS_H_INC
#define AI_FIXNORMALSPROCESS_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
struct aiMesh;
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 <assimp/postprocess.h>
+#include <assimp/scene.h>
+
+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/GenFaceNormalsProcess.cpp b/thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.cpp
index 028334dec7..028334dec7 100644
--- a/thirdparty/assimp/code/GenFaceNormalsProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.cpp
diff --git a/thirdparty/assimp/code/GenFaceNormalsProcess.h b/thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.h
index c80ec9fddc..c641fd6353 100644
--- a/thirdparty/assimp/code/GenFaceNormalsProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/GenFaceNormalsProcess.h
@@ -44,7 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_GENFACENORMALPROCESS_H_INC
#define AI_GENFACENORMALPROCESS_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
#include <assimp/mesh.h>
namespace Assimp
diff --git a/thirdparty/assimp/code/GenVertexNormalsProcess.cpp b/thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.cpp
index 3f6c2f86bd..3f6c2f86bd 100644
--- a/thirdparty/assimp/code/GenVertexNormalsProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.cpp
diff --git a/thirdparty/assimp/code/GenVertexNormalsProcess.h b/thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.h
index 9142ad26f5..2ceee17e85 100644
--- a/thirdparty/assimp/code/GenVertexNormalsProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/GenVertexNormalsProcess.h
@@ -45,24 +45,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_GENVERTEXNORMALPROCESS_H_INC
#define AI_GENVERTEXNORMALPROCESS_H_INC
-#include "BaseProcess.h"
+#include "Common/assbin_chunks.h"
+#include "Common/BaseProcess.h"
+
#include <assimp/mesh.h>
+// Forward declarations
class GenNormalsTest;
namespace Assimp {
// ---------------------------------------------------------------------------
-/** The GenFaceNormalsProcess computes vertex normals for all vertizes
+/** The GenFaceNormalsProcess computes vertex normals for all vertices
*/
-class ASSIMP_API GenVertexNormalsProcess : public BaseProcess
-{
+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.
@@ -88,13 +88,10 @@ public:
// setter for configMaxAngle
- inline void SetMaxSmoothAngle(ai_real f)
- {
+ inline void SetMaxSmoothAngle(ai_real f) {
configMaxAngle =f;
}
-public:
-
// -------------------------------------------------------------------
/** Computes normals for a specific mesh
* @param pcMesh Mesh
@@ -104,7 +101,6 @@ public:
bool GenMeshVertexNormals (aiMesh* pcMesh, unsigned int meshIndex);
private:
-
/** Configuration option: maximum smoothing angle, in radians*/
ai_real configMaxAngle;
mutable bool force_ = false;
diff --git a/thirdparty/assimp/code/ImproveCacheLocality.cpp b/thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.cpp
index ace9d95ff8..d0a016fa42 100644
--- a/thirdparty/assimp/code/ImproveCacheLocality.cpp
+++ b/thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.cpp
@@ -45,14 +45,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* <br>
* The algorithm is roughly basing on this paper:
* http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf
- * .. although overdraw rduction isn't implemented yet ...
+ * .. although overdraw reduction isn't implemented yet ...
*/
-
-
// internal headers
-#include "ImproveCacheLocality.h"
-#include "VertexTriangleAdjacency.h"
+#include "PostProcessing/ImproveCacheLocality.h"
+#include "Common/VertexTriangleAdjacency.h"
+
#include <assimp/StringUtils.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
@@ -64,36 +63,33 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
-ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() {
- configCacheDepth = PP_ICL_PTCACHE_SIZE;
+ImproveCacheLocalityProcess::ImproveCacheLocalityProcess()
+: mConfigCacheDepth(PP_ICL_PTCACHE_SIZE) {
+ // empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
-ImproveCacheLocalityProcess::~ImproveCacheLocalityProcess()
-{
+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
-{
+bool ImproveCacheLocalityProcess::IsActive( unsigned int pFlags) const {
return (pFlags & aiProcess_ImproveCacheLocality) != 0;
}
// ------------------------------------------------------------------------------------------------
// Setup configuration
-void ImproveCacheLocalityProcess::SetupProperties(const Importer* pImp)
-{
+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);
+ 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)
-{
+void ImproveCacheLocalityProcess::Execute( aiScene* pScene) {
if (!pScene->mNumMeshes) {
ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess skipped; there are no meshes");
return;
@@ -103,7 +99,7 @@ void ImproveCacheLocalityProcess::Execute( aiScene* pScene)
float out = 0.f;
unsigned int numf = 0, numm = 0;
- for( unsigned int a = 0; a < pScene->mNumMeshes; a++){
+ for( unsigned int a = 0; a < pScene->mNumMeshes; ++a ){
const float res = ProcessMesh( pScene->mMeshes[a],a);
if (res) {
numf += pScene->mMeshes[a]->mNumFaces;
@@ -121,44 +117,41 @@ void ImproveCacheLocalityProcess::Execute( aiScene* pScene)
// ------------------------------------------------------------------------------------------------
// Improves the cache coherency of a specific mesh
-float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshNum)
-{
+ai_real ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshNum) {
// TODO: rewrite this to use std::vector or boost::shared_array
- ai_assert(NULL != pMesh);
+ 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 0.f;
+ return static_cast<ai_real>(0.f);
if (pMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) {
ASSIMP_LOG_ERROR("This algorithm works on triangle meshes only");
- return 0.f;
+ return static_cast<ai_real>(0.f);
}
- if(pMesh->mNumVertices <= configCacheDepth) {
- return 0.f;
+ if(pMesh->mNumVertices <= mConfigCacheDepth) {
+ return static_cast<ai_real>(0.f);
}
- float fACMR = 3.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[configCacheDepth];
- memset(piFIFOStack,0xff,configCacheDepth*sizeof(unsigned int));
+ unsigned int* piFIFOStack = new unsigned int[mConfigCacheDepth];
+ memset(piFIFOStack,0xff,mConfigCacheDepth*sizeof(unsigned int));
unsigned int* piCur = piFIFOStack;
- const unsigned int* const piCurEnd = piFIFOStack + configCacheDepth;
+ 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
@@ -176,7 +169,7 @@ float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int mesh
}
}
delete[] piFIFOStack;
- fACMR = (float)iCacheMisses / pMesh->mNumFaces;
+ fACMR = (ai_real) iCacheMisses / pMesh->mNumFaces;
if (3.0 == fACMR) {
char szBuff[128]; // should be sufficiently large in every case
@@ -185,7 +178,7 @@ float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int mesh
// smaller than 3.0 ...
ai_snprintf(szBuff,128,"Mesh %u: Not suitable for vcache optimization",meshNum);
ASSIMP_LOG_WARN(szBuff);
- return 0.f;
+ return static_cast<ai_real>(0.f);
}
}
@@ -258,7 +251,7 @@ float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int mesh
int ivdx = 0;
int ics = 1;
- int iStampCnt = configCacheDepth+1;
+ int iStampCnt = mConfigCacheDepth+1;
while (ivdx >= 0) {
unsigned int icnt = piNumTriPtrNoModify[ivdx];
@@ -294,7 +287,7 @@ float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int mesh
*piCSIter++ = dp;
// if the vertex is not yet in cache, set its cache count
- if (iStampCnt-piCachingStamps[dp] > configCacheDepth) {
+ if (iStampCnt-piCachingStamps[dp] > mConfigCacheDepth) {
piCachingStamps[dp] = iStampCnt++;
++iCacheMisses;
}
@@ -319,7 +312,7 @@ float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int mesh
// will the vertex be in cache, even after fanning occurs?
unsigned int tmp;
- if ((tmp = iStampCnt-piCachingStamps[dp]) + 2*piNumTriPtr[dp] <= configCacheDepth) {
+ if ((tmp = iStampCnt-piCachingStamps[dp]) + 2*piNumTriPtr[dp] <= mConfigCacheDepth) {
priority = tmp;
}
@@ -356,7 +349,7 @@ float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int mesh
}
}
}
- float fACMR2 = 0.0f;
+ ai_real fACMR2 = 0.0f;
if (!DefaultLogger::isNullLogger()) {
fACMR2 = (float)iCacheMisses / pMesh->mNumFaces;
diff --git a/thirdparty/assimp/code/ImproveCacheLocality.h b/thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.h
index 1b29ee0d6e..de25ecd9fb 100644
--- a/thirdparty/assimp/code/ImproveCacheLocality.h
+++ b/thirdparty/assimp/code/PostProcessing/ImproveCacheLocality.h
@@ -45,7 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_IMPROVECACHELOCALITY_H_INC
#define AI_IMPROVECACHELOCALITY_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
+
#include <assimp/types.h>
struct aiMesh;
@@ -87,12 +88,12 @@ protected:
* @param pMesh The mesh to process.
* @param meshNum Index of the mesh to process
*/
- float ProcessMesh( aiMesh* pMesh, unsigned int meshNum);
+ 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 configCacheDepth;
+ unsigned int mConfigCacheDepth;
};
} // end of namespace Assimp
diff --git a/thirdparty/assimp/code/JoinVerticesProcess.cpp b/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp
index 914ec05b46..914ec05b46 100644
--- a/thirdparty/assimp/code/JoinVerticesProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp
diff --git a/thirdparty/assimp/code/JoinVerticesProcess.h b/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.h
index 66fa362de2..e017ae62db 100644
--- a/thirdparty/assimp/code/JoinVerticesProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.h
@@ -45,7 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_JOINVERTICESPROCESS_H_INC
#define AI_JOINVERTICESPROCESS_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
+
#include <assimp/types.h>
struct aiMesh;
@@ -61,13 +62,11 @@ namespace Assimp
* 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
-{
+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
@@ -83,15 +82,12 @@ public:
*/
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
diff --git a/thirdparty/assimp/code/LimitBoneWeightsProcess.cpp b/thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.cpp
index d560f19287..d560f19287 100644
--- a/thirdparty/assimp/code/LimitBoneWeightsProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.cpp
diff --git a/thirdparty/assimp/code/LimitBoneWeightsProcess.h b/thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.h
index 3602fd8edf..73c2a68d53 100644
--- a/thirdparty/assimp/code/LimitBoneWeightsProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/LimitBoneWeightsProcess.h
@@ -44,14 +44,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_LIMITBONEWEIGHTSPROCESS_H_INC
#define AI_LIMITBONEWEIGHTSPROCESS_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
+// Forward declarations
struct aiMesh;
class LimitBoneWeightsTest;
-namespace Assimp
-{
+namespace Assimp {
// NOTE: If you change these limits, don't forget to change the
// corresponding values in all Assimp ports
@@ -72,14 +72,11 @@ namespace Assimp
* The other weights on this bone are then renormalized to assure the sum weight
* to be 1.
*/
-class ASSIMP_API LimitBoneWeightsProcess : public BaseProcess
-{
+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.
@@ -96,8 +93,6 @@ public:
*/
void SetupProperties(const Importer* pImp);
-public:
-
// -------------------------------------------------------------------
/** Limits the bone weight count for all vertices in the given mesh.
* @param pMesh The mesh to process.
@@ -111,34 +106,29 @@ public:
*/
void Execute( aiScene* pScene);
-
-public:
-
// -------------------------------------------------------------------
/** Describes a bone weight on a vertex */
- struct Weight
- {
+ 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)
- { }
+ , mWeight(0.0f) {
+ // empty
+ }
Weight( unsigned int pBone, float pWeight)
- {
- mBone = pBone;
- mWeight = pWeight;
+ : mBone(pBone)
+ , mWeight(pWeight) {
+ // empty
}
/** Comparison operator to sort bone weights by descending weight */
- bool operator < (const Weight& pWeight) const
- {
+ bool operator < (const Weight& pWeight) const {
return mWeight > pWeight.mWeight;
}
};
-public:
/** Maximum number of bones influencing any single vertex. */
unsigned int mMaxWeights;
};
diff --git a/thirdparty/assimp/code/MakeVerboseFormat.cpp b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp
index 50ff5ed93d..50ff5ed93d 100644
--- a/thirdparty/assimp/code/MakeVerboseFormat.cpp
+++ b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp
diff --git a/thirdparty/assimp/code/MakeVerboseFormat.h b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h
index d12db63ae1..1adf8e2f69 100644
--- a/thirdparty/assimp/code/MakeVerboseFormat.h
+++ b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h
@@ -46,7 +46,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_MAKEVERBOSEFORMAT_H_INC
#define AI_MAKEVERBOSEFORMAT_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
+
struct aiMesh;
namespace Assimp {
diff --git a/thirdparty/assimp/code/OptimizeGraph.cpp b/thirdparty/assimp/code/PostProcessing/OptimizeGraph.cpp
index add9ab79e1..5db51f58b6 100644
--- a/thirdparty/assimp/code/OptimizeGraph.cpp
+++ b/thirdparty/assimp/code/PostProcessing/OptimizeGraph.cpp
@@ -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,
diff --git a/thirdparty/assimp/code/OptimizeGraph.h b/thirdparty/assimp/code/PostProcessing/OptimizeGraph.h
index e5bbed7679..82cc5db3fe 100644
--- a/thirdparty/assimp/code/OptimizeGraph.h
+++ b/thirdparty/assimp/code/PostProcessing/OptimizeGraph.h
@@ -46,13 +46,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_OPTIMIZEGRAPHPROCESS_H_INC
#define AI_OPTIMIZEGRAPHPROCESS_H_INC
-#include "BaseProcess.h"
-#include "ProcessHelper.h"
+#include "Common/BaseProcess.h"
+#include "PostProcessing/ProcessHelper.h"
+
#include <assimp/types.h>
+
#include <set>
+// Forward declarations
struct aiMesh;
+
class OptimizeGraphProcessTest;
+
namespace Assimp {
// -----------------------------------------------------------------------------
@@ -64,14 +69,11 @@ namespace Assimp {
* @see aiProcess_OptimizeGraph for a detailed description of the
* algorithm being applied.
*/
-class OptimizeGraphProcess : public BaseProcess
-{
+class OptimizeGraphProcess : public BaseProcess {
public:
-
OptimizeGraphProcess();
~OptimizeGraphProcess();
-public:
// -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const;
@@ -81,14 +83,12 @@ public:
// -------------------------------------------------------------------
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)
- {
+ inline void AddLockedNodeList(std::string& in) {
ConvertListToStrings (in,locked_nodes);
}
@@ -96,8 +96,7 @@ public:
/** @brief Add another node to be locked and not modified.
* @param name Name to be locked
*/
- inline void AddLockedNode(std::string& name)
- {
+ inline void AddLockedNode(std::string& name) {
locked_nodes.push_back(name);
}
@@ -105,25 +104,21 @@ public:
/** @brief Remove a node from the list of locked nodes.
* @param name Name to be unlocked
*/
- inline void RemoveLockedNode(std::string& name)
- {
+ inline void RemoveLockedNode(std::string& name) {
locked_nodes.remove(name);
}
protected:
-
void CollectNewChildren(aiNode* nd, std::list<aiNode*>& nodes);
void FindInstancedMeshes (aiNode* pNode);
private:
-
#ifdef AI_OG_USE_HASHING
typedef std::set<unsigned int> LockedSetType;
#else
typedef std::set<std::string> LockedSetType;
#endif
-
//! Scene we're working with
aiScene* mScene;
diff --git a/thirdparty/assimp/code/OptimizeMeshes.cpp b/thirdparty/assimp/code/PostProcessing/OptimizeMeshes.cpp
index 3f6765f6ca..3f6765f6ca 100644
--- a/thirdparty/assimp/code/OptimizeMeshes.cpp
+++ b/thirdparty/assimp/code/PostProcessing/OptimizeMeshes.cpp
diff --git a/thirdparty/assimp/code/OptimizeMeshes.h b/thirdparty/assimp/code/PostProcessing/OptimizeMeshes.h
index 9f46f349b4..dec4ab52de 100644
--- a/thirdparty/assimp/code/OptimizeMeshes.h
+++ b/thirdparty/assimp/code/PostProcessing/OptimizeMeshes.h
@@ -46,8 +46,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_OPTIMIZEMESHESPROCESS_H_INC
#define AI_OPTIMIZEMESHESPROCESS_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
+
#include <assimp/types.h>
+
#include <vector>
struct aiMesh;
@@ -64,16 +66,14 @@ namespace Assimp {
*
* @note Instanced meshes are currently not processed.
*/
-class OptimizeMeshesProcess : public BaseProcess
-{
+class OptimizeMeshesProcess : public BaseProcess {
public:
/// @brief The class constructor.
OptimizeMeshesProcess();
- /// @brief The class destcructor,
+ /// @brief The class destructor.
~OptimizeMeshesProcess();
-
/** @brief Internal utility to store additional mesh info
*/
struct MeshInfo {
diff --git a/thirdparty/assimp/code/PretransformVertices.cpp b/thirdparty/assimp/code/PostProcessing/PretransformVertices.cpp
index 52001a0578..52001a0578 100644
--- a/thirdparty/assimp/code/PretransformVertices.cpp
+++ b/thirdparty/assimp/code/PostProcessing/PretransformVertices.cpp
diff --git a/thirdparty/assimp/code/PretransformVertices.h b/thirdparty/assimp/code/PostProcessing/PretransformVertices.h
index b7329af130..b2982951e0 100644
--- a/thirdparty/assimp/code/PretransformVertices.h
+++ b/thirdparty/assimp/code/PostProcessing/PretransformVertices.h
@@ -47,13 +47,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_PRETRANSFORMVERTICES_H_INC
#define AI_PRETRANSFORMVERTICES_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
+
#include <assimp/mesh.h>
+
#include <list>
#include <vector>
+// Forward declarations
struct aiNode;
+
class PretransformVerticesTest;
+
namespace Assimp {
// ---------------------------------------------------------------------------
@@ -80,10 +85,10 @@ public:
// -------------------------------------------------------------------
/** @brief Toggle the 'keep hierarchy' option
- * @param d hm ... difficult to guess what this means, hu!?
+ * @param keep true for keep configuration.
*/
- void KeepHierarchy(bool d) {
- configKeepHierarchy = d;
+ void KeepHierarchy(bool keep) {
+ configKeepHierarchy = keep;
}
// -------------------------------------------------------------------
@@ -148,8 +153,6 @@ private:
// 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;
diff --git a/thirdparty/assimp/code/ProcessHelper.cpp b/thirdparty/assimp/code/PostProcessing/ProcessHelper.cpp
index 59869fdff7..59869fdff7 100644
--- a/thirdparty/assimp/code/ProcessHelper.cpp
+++ b/thirdparty/assimp/code/PostProcessing/ProcessHelper.cpp
diff --git a/thirdparty/assimp/code/ProcessHelper.h b/thirdparty/assimp/code/PostProcessing/ProcessHelper.h
index c59f3217bf..0afcc41420 100644
--- a/thirdparty/assimp/code/ProcessHelper.h
+++ b/thirdparty/assimp/code/PostProcessing/ProcessHelper.h
@@ -51,7 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/scene.h>
#include <assimp/SpatialSort.h>
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
#include <assimp/ParsingUtils.h>
#include <list>
diff --git a/thirdparty/assimp/code/RemoveRedundantMaterials.cpp b/thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.cpp
index 632bdca3fe..49ec8f5c47 100644
--- a/thirdparty/assimp/code/RemoveRedundantMaterials.cpp
+++ b/thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.cpp
@@ -49,7 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "RemoveRedundantMaterials.h"
#include <assimp/ParsingUtils.h>
#include "ProcessHelper.h"
-#include "MaterialSystem.h"
+#include "Material/MaterialSystem.h"
#include <stdio.h>
using namespace Assimp;
@@ -57,7 +57,7 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
RemoveRedundantMatsProcess::RemoveRedundantMatsProcess()
-: configFixedMaterials() {
+: mConfigFixedMaterials() {
// nothing to do here
}
@@ -80,7 +80,7 @@ bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const
void RemoveRedundantMatsProcess::SetupProperties(const Importer* pImp)
{
// Get value of AI_CONFIG_PP_RRM_EXCLUDE_LIST
- configFixedMaterials = pImp->GetPropertyString(AI_CONFIG_PP_RRM_EXCLUDE_LIST,"");
+ mConfigFixedMaterials = pImp->GetPropertyString(AI_CONFIG_PP_RRM_EXCLUDE_LIST,"");
}
// ------------------------------------------------------------------------------------------------
@@ -100,10 +100,10 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
// 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()) {
+ if (mConfigFixedMaterials.length()) {
std::list<std::string> strings;
- ConvertListToStrings(configFixedMaterials,strings);
+ ConvertListToStrings(mConfigFixedMaterials,strings);
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
aiMaterial* mat = pScene->mMaterials[i];
diff --git a/thirdparty/assimp/code/RemoveRedundantMaterials.h b/thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.h
index dbd4d44cc0..1f32a0abfb 100644
--- a/thirdparty/assimp/code/RemoveRedundantMaterials.h
+++ b/thirdparty/assimp/code/PostProcessing/RemoveRedundantMaterials.h
@@ -46,7 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_REMOVEREDUNDANTMATERIALS_H_INC
#define AI_REMOVEREDUNDANTMATERIALS_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
#include <assimp/mesh.h>
class RemoveRedundantMatsTest;
@@ -57,8 +57,7 @@ namespace Assimp {
/** RemoveRedundantMatsProcess: Post-processing step to remove redundant
* materials from the imported scene.
*/
-class ASSIMP_API RemoveRedundantMatsProcess : public BaseProcess
-{
+class ASSIMP_API RemoveRedundantMatsProcess : public BaseProcess {
public:
/// The default class constructor.
RemoveRedundantMatsProcess();
@@ -66,7 +65,6 @@ public:
/// The class destructor.
~RemoveRedundantMatsProcess();
-public:
// -------------------------------------------------------------------
// Check whether step is active
bool IsActive( unsigned int pFlags) const;
@@ -79,27 +77,25 @@ public:
// Setup import settings
void SetupProperties(const Importer* pImp);
-
// -------------------------------------------------------------------
- /** @brief Set list of fixed (unmutable) materials
+ /** @brief Set list of fixed (inmutable) materials
* @param fixed See #AI_CONFIG_PP_RRM_EXCLUDE_LIST
*/
void SetFixedMaterialsString(const std::string& fixed = "") {
- configFixedMaterials = fixed;
+ mConfigFixedMaterials = fixed;
}
// -------------------------------------------------------------------
- /** @brief Get list of fixed (unmutable) materials
+ /** @brief Get list of fixed (inmutable) materials
* @return See #AI_CONFIG_PP_RRM_EXCLUDE_LIST
*/
const std::string& GetFixedMaterialsString() const {
- return configFixedMaterials;
+ return mConfigFixedMaterials;
}
private:
-
//! Configuration option: list of all fixed materials
- std::string configFixedMaterials;
+ std::string mConfigFixedMaterials;
};
} // end of namespace Assimp
diff --git a/thirdparty/assimp/code/RemoveVCProcess.cpp b/thirdparty/assimp/code/PostProcessing/RemoveVCProcess.cpp
index 99fd47a3aa..99fd47a3aa 100644
--- a/thirdparty/assimp/code/RemoveVCProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/RemoveVCProcess.cpp
diff --git a/thirdparty/assimp/code/RemoveVCProcess.h b/thirdparty/assimp/code/PostProcessing/RemoveVCProcess.h
index 617d7b9b20..7bb21a8330 100644
--- a/thirdparty/assimp/code/RemoveVCProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/RemoveVCProcess.h
@@ -44,7 +44,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_REMOVEVCPROCESS_H_INCLUDED
#define AI_REMOVEVCPROCESS_H_INCLUDED
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
+
#include <assimp/mesh.h>
class RemoveVCProcessTest;
diff --git a/thirdparty/assimp/code/PostProcessing/ScaleProcess.cpp b/thirdparty/assimp/code/PostProcessing/ScaleProcess.cpp
new file mode 100644
index 0000000000..ac770c41f2
--- /dev/null
+++ b/thirdparty/assimp/code/PostProcessing/ScaleProcess.cpp
@@ -0,0 +1,208 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(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 "ScaleProcess.h"
+
+#include <assimp/scene.h>
+#include <assimp/postprocess.h>
+#include <assimp/BaseImporter.h>
+
+namespace Assimp {
+
+ScaleProcess::ScaleProcess()
+: BaseProcess()
+, mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) {
+}
+
+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 ) {
+ // User scaling
+ mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 1.0f );
+
+ // File scaling * Application Scaling
+ float importerScale = pImp->GetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, 1.0f );
+
+ // apply scale to the scale
+ // helps prevent bugs with backward compatibility for anyone using normal scaling.
+ mScale *= importerScale;
+}
+
+void ScaleProcess::Execute( aiScene* pScene ) {
+ if(mScale == 1.0f) {
+ return; // nothing to scale
+ }
+
+ ai_assert( mScale != 0 );
+ ai_assert( nullptr != pScene );
+ ai_assert( nullptr != pScene->mRootNode );
+
+ if ( nullptr == pScene ) {
+ return;
+ }
+
+ if ( nullptr == pScene->mRootNode ) {
+ return;
+ }
+
+ // Process animations and update position transform to new unit system
+ for( unsigned int animationID = 0; animationID < pScene->mNumAnimations; animationID++ )
+ {
+ aiAnimation* animation = pScene->mAnimations[animationID];
+
+ for( unsigned int animationChannel = 0; animationChannel < animation->mNumChannels; animationChannel++)
+ {
+ aiNodeAnim* anim = animation->mChannels[animationChannel];
+
+ for( unsigned int posKey = 0; posKey < anim->mNumPositionKeys; posKey++)
+ {
+ aiVectorKey& vectorKey = anim->mPositionKeys[posKey];
+ vectorKey.mValue *= mScale;
+ }
+ }
+ }
+
+ for( unsigned int meshID = 0; meshID < pScene->mNumMeshes; meshID++)
+ {
+ aiMesh *mesh = pScene->mMeshes[meshID];
+
+ // Reconstruct mesh vertexes to the new unit system
+ for( unsigned int vertexID = 0; vertexID < mesh->mNumVertices; vertexID++)
+ {
+ aiVector3D& vertex = mesh->mVertices[vertexID];
+ vertex *= mScale;
+ }
+
+
+ // bone placement / scaling
+ for( unsigned int boneID = 0; boneID < mesh->mNumBones; boneID++)
+ {
+ // Reconstruct matrix by transform rather than by scale
+ // This prevent scale values being changed which can
+ // be meaningful in some cases
+ // like when you want the modeller to see 1:1 compatibility.
+ aiBone* bone = mesh->mBones[boneID];
+
+ aiVector3D pos, scale;
+ aiQuaternion rotation;
+
+ bone->mOffsetMatrix.Decompose( scale, rotation, pos);
+
+ aiMatrix4x4 translation;
+ aiMatrix4x4::Translation( pos * mScale, translation );
+
+ aiMatrix4x4 scaling;
+ aiMatrix4x4::Scaling( aiVector3D(scale), scaling );
+
+ aiMatrix4x4 RotMatrix = aiMatrix4x4 (rotation.GetMatrix());
+
+ bone->mOffsetMatrix = translation * RotMatrix * scaling;
+ }
+
+
+ // animation mesh processing
+ // convert by position rather than scale.
+ for( unsigned int animMeshID = 0; animMeshID < mesh->mNumAnimMeshes; animMeshID++)
+ {
+ aiAnimMesh * animMesh = mesh->mAnimMeshes[animMeshID];
+
+ for( unsigned int vertexID = 0; vertexID < animMesh->mNumVertices; vertexID++)
+ {
+ aiVector3D& vertex = animMesh->mVertices[vertexID];
+ vertex *= mScale;
+ }
+ }
+ }
+
+ traverseNodes( pScene->mRootNode );
+}
+
+void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) {
+ applyScaling( node );
+
+ for( size_t i = 0; i < node->mNumChildren; i++)
+ {
+ // recurse into the tree until we are done!
+ traverseNodes( node->mChildren[i], nested_node_id+1 );
+ }
+}
+
+void ScaleProcess::applyScaling( aiNode *currentNode ) {
+ if ( nullptr != currentNode ) {
+ // Reconstruct matrix by transform rather than by scale
+ // This prevent scale values being changed which can
+ // be meaningful in some cases
+ // like when you want the modeller to
+ // see 1:1 compatibility.
+
+ aiVector3D pos, scale;
+ aiQuaternion rotation;
+ currentNode->mTransformation.Decompose( scale, rotation, pos);
+
+ aiMatrix4x4 translation;
+ aiMatrix4x4::Translation( pos * mScale, translation );
+
+ aiMatrix4x4 scaling;
+
+ // note: we do not use mScale here, this is on purpose.
+ aiMatrix4x4::Scaling( scale, scaling );
+
+ aiMatrix4x4 RotMatrix = aiMatrix4x4 (rotation.GetMatrix());
+
+ currentNode->mTransformation = translation * RotMatrix * scaling;
+ }
+}
+
+} // Namespace Assimp
diff --git a/thirdparty/assimp/code/ScaleProcess.h b/thirdparty/assimp/code/PostProcessing/ScaleProcess.h
index 55146ae064..468a216736 100644
--- a/thirdparty/assimp/code/ScaleProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/ScaleProcess.h
@@ -39,9 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
-#pragma once
+#ifndef SCALE_PROCESS_H_
+#define SCALE_PROCESS_H_
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
struct aiNode;
@@ -53,6 +54,11 @@ namespace Assimp {
// ---------------------------------------------------------------------------
/** ScaleProcess: Class to rescale the whole model.
+ * Now rescales animations, bones, and blend shapes properly.
+ * Please note this will not write to 'scale' transform it will rewrite mesh
+ * and matrixes so that your scale values
+ * from your model package are preserved, so this is completely intentional
+ * bugs should be reported as soon as they are found.
*/
class ASSIMP_API ScaleProcess : public BaseProcess {
public:
@@ -78,7 +84,7 @@ public:
virtual void Execute( aiScene* pScene );
private:
- void traverseNodes( aiNode *currentNode );
+ void traverseNodes( aiNode *currentNode, unsigned int nested_node_id = 0 );
void applyScaling( aiNode *currentNode );
private:
@@ -86,3 +92,6 @@ private:
};
} // Namespace Assimp
+
+
+#endif // SCALE_PROCESS_H_ \ No newline at end of file
diff --git a/thirdparty/assimp/code/SortByPTypeProcess.cpp b/thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.cpp
index 2e0cc54004..be8405a17b 100644
--- a/thirdparty/assimp/code/SortByPTypeProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.cpp
@@ -57,8 +57,8 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
SortByPTypeProcess::SortByPTypeProcess()
-{
- configRemoveMeshes = 0;
+: mConfigRemoveMeshes( 0 ) {
+ // empty
}
// ------------------------------------------------------------------------------------------------
@@ -78,7 +78,7 @@ bool SortByPTypeProcess::IsActive( unsigned int pFlags) const
// ------------------------------------------------------------------------------------------------
void SortByPTypeProcess::SetupProperties(const Importer* pImp)
{
- configRemoveMeshes = pImp->GetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE,0);
+ mConfigRemoveMeshes = pImp->GetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE,0);
}
// ------------------------------------------------------------------------------------------------
@@ -172,7 +172,7 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
}
if (1 == num) {
- if (!(configRemoveMeshes & mesh->mPrimitiveTypes)) {
+ if (!(mConfigRemoveMeshes & mesh->mPrimitiveTypes)) {
*meshIdx = static_cast<unsigned int>( outMeshes.size() );
outMeshes.push_back(mesh);
} else {
@@ -206,7 +206,7 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
VertexWeightTable* avw = ComputeVertexBoneWeightTable(mesh);
for (unsigned int real = 0; real < 4; ++real,++meshIdx)
{
- if ( !aiNumPerPType[real] || configRemoveMeshes & (1u << real))
+ if ( !aiNumPerPType[real] || mConfigRemoveMeshes & (1u << real))
{
continue;
}
@@ -392,10 +392,10 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
{
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" : ""));
+ 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/SortByPTypeProcess.h b/thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.h
index c9d9924d8f..1d7ccfc152 100644
--- a/thirdparty/assimp/code/SortByPTypeProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/SortByPTypeProcess.h
@@ -45,10 +45,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_SORTBYPTYPEPROCESS_H_INC
#define AI_SORTBYPTYPEPROCESS_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
#include <assimp/mesh.h>
class SortByPTypeProcessTest;
+
namespace Assimp {
@@ -57,14 +58,11 @@ namespace Assimp {
* A mesh with 5 lines, 3 points and 145 triangles would be split in 3
* submeshes.
*/
-class ASSIMP_API SortByPTypeProcess : public BaseProcess
-{
+class ASSIMP_API SortByPTypeProcess : public BaseProcess {
public:
-
SortByPTypeProcess();
~SortByPTypeProcess();
-public:
// -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const;
@@ -75,8 +73,7 @@ public:
void SetupProperties(const Importer* pImp);
private:
-
- int configRemoveMeshes;
+ int mConfigRemoveMeshes;
};
diff --git a/thirdparty/assimp/code/SplitLargeMeshes.cpp b/thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.cpp
index 1797b28d5a..1797b28d5a 100644
--- a/thirdparty/assimp/code/SplitLargeMeshes.cpp
+++ b/thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.cpp
diff --git a/thirdparty/assimp/code/SplitLargeMeshes.h b/thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.h
index 77f089ce7e..3f90576ea9 100644
--- a/thirdparty/assimp/code/SplitLargeMeshes.h
+++ b/thirdparty/assimp/code/PostProcessing/SplitLargeMeshes.h
@@ -4,7 +4,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,
@@ -40,21 +39,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
-/** @file Defines a post processing step to split large meshes into submeshes
+/** @file Defines a post processing step to split large meshes into sub-meshes
*/
#ifndef AI_SPLITLARGEMESHES_H_INC
#define AI_SPLITLARGEMESHES_H_INC
#include <vector>
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
#include <assimp/mesh.h>
#include <assimp/scene.h>
+// Forward declarations
class SplitLargeMeshesTest;
-namespace Assimp
-{
+namespace Assimp {
class SplitLargeMeshesProcess_Triangle;
class SplitLargeMeshesProcess_Vertex;
diff --git a/thirdparty/assimp/code/TextureTransform.cpp b/thirdparty/assimp/code/PostProcessing/TextureTransform.cpp
index 8ae2ba7218..8ae2ba7218 100644
--- a/thirdparty/assimp/code/TextureTransform.cpp
+++ b/thirdparty/assimp/code/PostProcessing/TextureTransform.cpp
diff --git a/thirdparty/assimp/code/TextureTransform.h b/thirdparty/assimp/code/PostProcessing/TextureTransform.h
index c556ff5d8c..2a5d623d7f 100644
--- a/thirdparty/assimp/code/TextureTransform.h
+++ b/thirdparty/assimp/code/PostProcessing/TextureTransform.h
@@ -45,7 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define AI_TEXTURE_TRANSFORM_H_INCLUDED
#include <assimp/BaseImporter.h>
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
#include <assimp/material.h>
#include <list>
diff --git a/thirdparty/assimp/code/TriangulateProcess.cpp b/thirdparty/assimp/code/PostProcessing/TriangulateProcess.cpp
index 0f68f47ddb..1040836bbe 100644
--- a/thirdparty/assimp/code/TriangulateProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/TriangulateProcess.cpp
@@ -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,9 +58,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* a file
*/
#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
-#include "TriangulateProcess.h"
-#include "ProcessHelper.h"
-#include "PolyTools.h"
+
+#include "PostProcessing/TriangulateProcess.h"
+#include "PostProcessing/ProcessHelper.h"
+#include "Common/PolyTools.h"
+
#include <memory>
//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
diff --git a/thirdparty/assimp/code/TriangulateProcess.h b/thirdparty/assimp/code/PostProcessing/TriangulateProcess.h
index 47bd2115ad..916b5103dd 100644
--- a/thirdparty/assimp/code/TriangulateProcess.h
+++ b/thirdparty/assimp/code/PostProcessing/TriangulateProcess.h
@@ -46,7 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_TRIANGULATEPROCESS_H_INC
#define AI_TRIANGULATEPROCESS_H_INC
-#include "BaseProcess.h"
+#include "Common/BaseProcess.h"
struct aiMesh;
@@ -59,14 +59,11 @@ namespace Assimp {
* into triangles. You usually want this to happen because the graphics cards
* need their data as triangles.
*/
-class ASSIMP_API TriangulateProcess : public BaseProcess
-{
+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
@@ -82,7 +79,6 @@ public:
*/
void Execute( aiScene* pScene);
-public:
// -------------------------------------------------------------------
/** Triangulates the given mesh.
* @param pMesh The mesh to triangulate.
diff --git a/thirdparty/assimp/code/ValidateDataStructure.cpp b/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.cpp
index 657b0361b7..712fd6943d 100644
--- a/thirdparty/assimp/code/ValidateDataStructure.cpp
+++ b/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.cpp
@@ -46,8 +46,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* the data structure returned by Assimp.
*/
-
-
// internal headers
#include "ValidateDataStructure.h"
#include <assimp/BaseImporter.h>
@@ -110,8 +108,8 @@ void ValidateDSProcess::ReportWarning(const char* msg,...)
}
// ------------------------------------------------------------------------------------------------
-inline int HasNameMatch(const aiString& in, aiNode* node)
-{
+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]);
@@ -121,9 +119,8 @@ inline int HasNameMatch(const aiString& in, aiNode* node)
// ------------------------------------------------------------------------------------------------
template <typename T>
-inline void ValidateDSProcess::DoValidation(T** parray, unsigned int size,
- const char* firstName, const char* secondName)
-{
+inline
+void ValidateDSProcess::DoValidation(T** parray, unsigned int size, const char* firstName, const char* secondName) {
// validate all entries
if (size)
{
@@ -181,7 +178,8 @@ inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size,
// ------------------------------------------------------------------------------------------------
template <typename T>
inline
-void ValidateDSProcess::DoValidationWithNameCheck(T** array, unsigned int size, const char* firstName, const char* secondName) {
+void ValidateDSProcess::DoValidationWithNameCheck(T** array, unsigned int size, const char* firstName,
+ const char* secondName) {
// validate all entries
DoValidationEx(array,size,firstName,secondName);
@@ -201,9 +199,8 @@ void ValidateDSProcess::DoValidationWithNameCheck(T** array, unsigned int size,
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
-void ValidateDSProcess::Execute( aiScene* pScene)
-{
- this->mScene = pScene;
+void ValidateDSProcess::Execute( aiScene* pScene) {
+ mScene = pScene;
ASSIMP_LOG_DEBUG("ValidateDataStructureProcess begin");
// validate the node graph of the scene
@@ -516,13 +513,11 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
}
// ------------------------------------------------------------------------------------------------
-void ValidateDSProcess::Validate( const aiMesh* pMesh,
- const aiBone* pBone,float* afSum)
-{
+void ValidateDSProcess::Validate( const aiMesh* pMesh, const aiBone* pBone,float* afSum) {
this->Validate(&pBone->mName);
if (!pBone->mNumWeights) {
- ReportError("aiBone::mNumWeights is zero");
+ //ReportError("aiBone::mNumWeights is zero");
}
// check whether all vertices affected by this bone are valid
@@ -563,9 +558,6 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation)
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");
}
// ------------------------------------------------------------------------------------------------
@@ -746,8 +738,9 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
"AI_MATKEY_SHININESS_STRENGTH key is 0.0");
}
break;
- default: ;
- };
+ default:
+ break;
+ }
}
if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01)) {
diff --git a/thirdparty/assimp/code/ValidateDataStructure.h b/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.h
index bd21e88545..0b891ef414 100644
--- a/thirdparty/assimp/code/ValidateDataStructure.h
+++ b/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.h
@@ -48,7 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/types.h>
#include <assimp/material.h>
-#include "BaseProcess.h"
+
+#include "Common/BaseProcess.h"
struct aiBone;
struct aiMesh;
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 <assimp/ParsingUtils.h>
-#include <assimp/fast_atof.h>
-#include <memory>
-#include <assimp/IOSystem.hpp>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/scene.h>
-#include <assimp/importerdesc.h>
-
-using namespace Assimp;
-
-static const aiImporterDesc desc = {
- "Raw Importer",
- "",
- "",
- "",
- aiImporterFlags_SupportTextFlavour,
- 0,
- 0,
- 0,
- 0,
- "raw"
-};
-
-// ------------------------------------------------------------------------------------------------
-// Constructor to be privately used by Importer
-RAWImporter::RAWImporter()
-{}
-
-// ------------------------------------------------------------------------------------------------
-// Destructor, private as well
-RAWImporter::~RAWImporter()
-{}
-
-// ------------------------------------------------------------------------------------------------
-// Returns whether the class can handle the format of the given file.
-bool RAWImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const
-{
- return SimpleExtensionCheck(pFile,"raw");
-}
-
-// ------------------------------------------------------------------------------------------------
-const aiImporterDesc* RAWImporter::GetInfo () const
-{
- return &desc;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Imports the given file into the given scene structure.
-void RAWImporter::InternReadFile( const std::string& pFile,
- aiScene* pScene, IOSystem* pIOHandler)
-{
- std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
-
- // Check whether we can read from the file
- if( file.get() == NULL) {
- throw DeadlyImportError( "Failed to open RAW file " + pFile + ".");
- }
-
- // allocate storage and copy the contents of the file to a memory buffer
- // (terminate it with zero)
- std::vector<char> mBuffer2;
- TextFileToBuffer(file.get(),mBuffer2);
- const char* buffer = &mBuffer2[0];
-
- // list of groups loaded from the file
- std::vector< GroupInformation > outGroups(1,GroupInformation("<default>"));
- std::vector< GroupInformation >::iterator curGroup = outGroups.begin();
-
- // now read all lines
- char line[4096];
- while (GetNextLine(buffer,line))
- {
- // if the line starts with a non-numeric identifier, it marks
- // the beginning of a new group
- const char* sz = line;SkipSpaces(&sz);
- if (IsLineEnd(*sz))continue;
- if (!IsNumeric(*sz))
- {
- const char* sz2 = sz;
- while (!IsSpaceOrNewLine(*sz2))++sz2;
- const unsigned int length = (unsigned int)(sz2-sz);
-
- // find an existing group with this name
- for (std::vector< GroupInformation >::iterator it = outGroups.begin(), end = outGroups.end();
- it != end;++it)
- {
- if (length == (*it).name.length() && !::strcmp(sz,(*it).name.c_str()))
- {
- curGroup = it;sz2 = NULL;
- break;
- }
- }
- if (sz2)
- {
- outGroups.push_back(GroupInformation(std::string(sz,length)));
- curGroup = outGroups.end()-1;
- }
- }
- else
- {
- // there can be maximally 12 floats plus an extra texture file name
- float data[12];
- unsigned int num;
- for (num = 0; num < 12;++num)
- {
- if(!SkipSpaces(&sz) || !IsNumeric(*sz))break;
- sz = fast_atoreal_move<float>(sz,data[num]);
- }
- if (num != 12 && num != 9)
- {
- ASSIMP_LOG_ERROR("A line may have either 9 or 12 floats and an optional texture");
- continue;
- }
-
- MeshInformation* output = NULL;
-
- const char* sz2 = sz;
- unsigned int length;
- if (!IsLineEnd(*sz))
- {
- while (!IsSpaceOrNewLine(*sz2))++sz2;
- length = (unsigned int)(sz2-sz);
- }
- else if (9 == num)
- {
- sz = "%default%";
- length = 9;
- }
- else
- {
- sz = "";
- length = 0;
- }
-
- // search in the list of meshes whether we have one with this texture
- for (auto &mesh : (*curGroup).meshes)
- {
- if (length == mesh.name.length() && (length ? !::strcmp(sz, mesh.name.c_str()) : true))
- {
- output = &mesh;
- break;
- }
- }
- // if we don't have the mesh, create it
- if (!output)
- {
- (*curGroup).meshes.push_back(MeshInformation(std::string(sz,length)));
- output = &((*curGroup).meshes.back());
- }
- if (12 == num)
- {
- aiColor4D v(data[0],data[1],data[2],1.0f);
- output->colors.push_back(v);
- output->colors.push_back(v);
- output->colors.push_back(v);
-
- output->vertices.push_back(aiVector3D(data[3],data[4],data[5]));
- output->vertices.push_back(aiVector3D(data[6],data[7],data[8]));
- output->vertices.push_back(aiVector3D(data[9],data[10],data[11]));
- }
- else
- {
- output->vertices.push_back(aiVector3D(data[0],data[1],data[2]));
- output->vertices.push_back(aiVector3D(data[3],data[4],data[5]));
- output->vertices.push_back(aiVector3D(data[6],data[7],data[8]));
- }
- }
- }
-
- pScene->mRootNode = new aiNode();
- pScene->mRootNode->mName.Set("<RawRoot>");
-
- // count the number of valid groups
- // (meshes can't be empty)
- for (auto & outGroup : outGroups)
- {
- if (!outGroup.meshes.empty())
- {
- ++pScene->mRootNode->mNumChildren;
- pScene->mNumMeshes += (unsigned int) outGroup.meshes.size();
- }
- }
-
- if (!pScene->mNumMeshes)
- {
- throw DeadlyImportError("RAW: No meshes loaded. The file seems to be corrupt or empty.");
- }
-
- pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
- aiNode** cc;
- if (1 == pScene->mRootNode->mNumChildren)
- {
- cc = &pScene->mRootNode;
- pScene->mRootNode->mNumChildren = 0;
- } else {
- cc = new aiNode*[pScene->mRootNode->mNumChildren];
- memset(cc, 0, sizeof(aiNode*) * pScene->mRootNode->mNumChildren);
- pScene->mRootNode->mChildren = cc;
- }
-
- pScene->mNumMaterials = pScene->mNumMeshes;
- aiMaterial** mats = pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
-
- unsigned int meshIdx = 0;
- for (auto & outGroup : outGroups)
- {
- if (outGroup.meshes.empty())continue;
-
- aiNode* node;
- if (pScene->mRootNode->mNumChildren)
- {
- node = *cc = new aiNode();
- node->mParent = pScene->mRootNode;
- }
- else node = *cc;
- node->mName.Set(outGroup.name);
-
- // add all meshes
- node->mNumMeshes = (unsigned int) outGroup.meshes.size();
- unsigned int* pi = node->mMeshes = new unsigned int[ node->mNumMeshes ];
- for (std::vector< MeshInformation >::iterator it2 = outGroup.meshes.begin(),
- end2 = outGroup.meshes.end(); it2 != end2; ++it2)
- {
- ai_assert(!(*it2).vertices.empty());
-
- // allocate the mesh
- *pi++ = meshIdx;
- aiMesh* mesh = pScene->mMeshes[meshIdx] = new aiMesh();
- mesh->mMaterialIndex = meshIdx++;
-
- mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
-
- // allocate storage for the vertex components and copy them
- mesh->mNumVertices = (unsigned int)(*it2).vertices.size();
- mesh->mVertices = new aiVector3D[ mesh->mNumVertices ];
- ::memcpy(mesh->mVertices,&(*it2).vertices[0],sizeof(aiVector3D)*mesh->mNumVertices);
-
- if ((*it2).colors.size())
- {
- ai_assert((*it2).colors.size() == mesh->mNumVertices);
-
- mesh->mColors[0] = new aiColor4D[ mesh->mNumVertices ];
- ::memcpy(mesh->mColors[0],&(*it2).colors[0],sizeof(aiColor4D)*mesh->mNumVertices);
- }
-
- // generate triangles
- ai_assert(0 == mesh->mNumVertices % 3);
- aiFace* fc = mesh->mFaces = new aiFace[ mesh->mNumFaces = mesh->mNumVertices/3 ];
- aiFace* const fcEnd = fc + mesh->mNumFaces;
- unsigned int n = 0;
- while (fc != fcEnd)
- {
- aiFace& f = *fc++;
- f.mIndices = new unsigned int[f.mNumIndices = 3];
- for (unsigned int m = 0; m < 3;++m)
- f.mIndices[m] = n++;
- }
-
- // generate a material for the mesh
- aiMaterial* mat = new aiMaterial();
-
- aiColor4D clr(1.0f,1.0f,1.0f,1.0f);
- if ("%default%" == (*it2).name) // a gray default material
- {
- clr.r = clr.g = clr.b = 0.6f;
- }
- else if ((*it2).name.length() > 0) // a texture
- {
- aiString s;
- s.Set((*it2).name);
- mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
- }
- mat->AddProperty<aiColor4D>(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
- *mats++ = mat;
- }
- }
-}
-
-#endif // !! ASSIMP_BUILD_NO_RAW_IMPORTER
diff --git a/thirdparty/assimp/code/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 <assimp/scene.h>
-#include <assimp/postprocess.h>
-
-namespace Assimp {
-
-ScaleProcess::ScaleProcess()
-: BaseProcess()
-, mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) {
- // empty
-}
-
-ScaleProcess::~ScaleProcess() {
- // empty
-}
-
-void ScaleProcess::setScale( ai_real scale ) {
- mScale = scale;
-}
-
-ai_real ScaleProcess::getScale() const {
- return mScale;
-}
-
-bool ScaleProcess::IsActive( unsigned int pFlags ) const {
- return ( pFlags & aiProcess_GlobalScale ) != 0;
-}
-
-void ScaleProcess::SetupProperties( const Importer* pImp ) {
- mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 0 );
-}
-
-void ScaleProcess::Execute( aiScene* pScene ) {
- if ( nullptr == pScene ) {
- return;
- }
-
- if ( nullptr == pScene->mRootNode ) {
- return;
- }
-
- traverseNodes( pScene->mRootNode );
-}
-
-void ScaleProcess::traverseNodes( aiNode *node ) {
- applyScaling( node );
-}
-
-void ScaleProcess::applyScaling( aiNode *currentNode ) {
- if ( nullptr != currentNode ) {
- currentNode->mTransformation.a1 = currentNode->mTransformation.a1 * mScale;
- currentNode->mTransformation.b2 = currentNode->mTransformation.b2 * mScale;
- currentNode->mTransformation.c3 = currentNode->mTransformation.c3 * mScale;
- }
-}
-
-} // Namespace Assimp
-
-#endif // !! ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS
diff --git a/thirdparty/assimp/contrib/utf8cpp/doc/ReleaseNotes b/thirdparty/assimp/contrib/utf8cpp/doc/ReleaseNotes
new file mode 100644
index 0000000000..364411a23d
--- /dev/null
+++ b/thirdparty/assimp/contrib/utf8cpp/doc/ReleaseNotes
@@ -0,0 +1,12 @@
+utf8 cpp library
+Release 2.3.4
+
+A minor bug fix release. Thanks to all who reported bugs.
+
+Note: Version 2.3.3 contained a regression, and therefore was removed.
+
+Changes from version 2.3.2
+- Bug fix [39]: checked.h Line 273 and unchecked.h Line 182 have an extra ';'
+- Bug fix [36]: replace_invalid() only works with back_inserter
+
+Files included in the release: utf8.h, core.h, checked.h, unchecked.h, utf8cpp.html, ReleaseNotes
diff --git a/thirdparty/assimp/contrib/utf8cpp/doc/utf8cpp.html b/thirdparty/assimp/contrib/utf8cpp/doc/utf8cpp.html
new file mode 100644
index 0000000000..6f2aacbe7b
--- /dev/null
+++ b/thirdparty/assimp/contrib/utf8cpp/doc/utf8cpp.html
@@ -0,0 +1,1789 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <meta name="generator" content=
+ "HTML Tidy for Linux/x86 (vers 1st November 2002), see www.w3.org">
+ <meta name="description" content=
+ "A simple, portable and lightweigt C++ library for easy handling of UTF-8 encoded strings">
+ <meta name="keywords" content="UTF-8 C++ portable utf8 unicode generic templates">
+ <meta name="author" content="Nemanja Trifunovic">
+ <title>
+ UTF8-CPP: UTF-8 with C++ in a Portable Way
+ </title>
+ <style type="text/css">
+ <!--
+ span.return_value {
+ color: brown;
+ }
+ span.keyword {
+ color: blue;
+ }
+ span.preprocessor {
+ color: navy;
+ }
+ span.literal {
+ color: olive;
+ }
+ span.comment {
+ color: green;
+ }
+ code {
+ font-weight: bold;
+ }
+ ul.toc {
+ list-style-type: none;
+ }
+ p.version {
+ font-size: small;
+ font-style: italic;
+ }
+ -->
+ </style>
+ </head>
+ <body>
+ <h1>
+ UTF8-CPP: UTF-8 with C++ in a Portable Way
+ </h1>
+ <p>
+ <a href="https://sourceforge.net/projects/utfcpp">The Sourceforge project page</a>
+ </p>
+ <div id="toc">
+ <h2>
+ Table of Contents
+ </h2>
+ <ul class="toc">
+ <li>
+ <a href="#introduction">Introduction</a>
+ </li>
+ <li>
+ <a href="#examples">Examples of Use</a>
+ <ul class="toc">
+ <li>
+ <a href=#introsample>Introductionary Sample </a>
+ </li>
+ <li>
+ <a href=#validfile>Checking if a file contains valid UTF-8 text</a>
+ </li>
+ <li>
+ <a href=#fixinvalid>Ensure that a string contains valid UTF-8 text</a>
+ </li>
+ </ul>
+ <li>
+ <a href="#reference">Reference</a>
+ <ul class="toc">
+ <li>
+ <a href="#funutf8">Functions From utf8 Namespace </a>
+ </li>
+ <li>
+ <a href="#typesutf8">Types From utf8 Namespace </a>
+ </li>
+ <li>
+ <a href="#fununchecked">Functions From utf8::unchecked Namespace </a>
+ </li>
+ <li>
+ <a href="#typesunchecked">Types From utf8::unchecked Namespace </a>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <a href="#points">Points of Interest</a>
+ </li>
+ <li>
+ <a href="#links">Links</a>
+ </li>
+ </ul>
+ </div>
+ <h2 id="introduction">
+ Introduction
+ </h2>
+ <p>
+ Many C++ developers miss an easy and portable way of handling Unicode encoded
+ strings. The original C++ Standard (known as C++98 or C++03) is Unicode agnostic.
+ C++11 provides some support for Unicode on core language and library level:
+ u8, u, and U character and string literals, char16_t and char32_t character types,
+ u16string and u32string library classes, and codecvt support for conversions
+ between Unicode encoding forms.
+ In the meantime, developers use third party libraries like ICU, OS specific capabilities, or simply
+ roll out their own solutions.
+ </p>
+ <p>
+ In order to easily handle UTF-8 encoded Unicode strings, I came up with a small
+ generic library. For anybody used to work with STL algorithms and iterators, it should be
+ easy and natural to use. The code is freely available for any purpose - check out
+ the license at the beginning of the utf8.h file. If you run into
+ bugs or performance issues, please let me know and I'll do my best to address them.
+ </p>
+ <p>
+ The purpose of this article is not to offer an introduction to Unicode in general,
+ and UTF-8 in particular. If you are not familiar with Unicode, be sure to check out
+ <a href="http://www.unicode.org/">Unicode Home Page</a> or some other source of
+ information for Unicode. Also, it is not my aim to advocate the use of UTF-8
+ encoded strings in C++ programs; if you want to handle UTF-8 encoded strings from
+ C++, I am sure you have good reasons for it.
+ </p>
+ <h2 id="examples">
+ Examples of use
+ </h2>
+ <h3 id="introsample">
+ Introductionary Sample
+ </h3>
+ <p>
+ To illustrate the use of the library, let's start with a small but complete program
+ that opens a file containing UTF-8 encoded text, reads it line by line, checks each line
+ for invalid UTF-8 byte sequences, and converts it to UTF-16 encoding and back to UTF-8:
+ </p>
+<pre>
+<span class="preprocessor">#include &lt;fstream&gt;</span>
+<span class="preprocessor">#include &lt;iostream&gt;</span>
+<span class="preprocessor">#include &lt;string&gt;</span>
+<span class="preprocessor">#include &lt;vector&gt;</span>
+<span class="preprocessor">#include "utf8.h"</span>
+<span class="keyword">using namespace</span> std;
+<span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">char</span>** argv)
+{
+ <span class="keyword">if</span> (argc != <span class="literal">2</span>) {
+ cout &lt;&lt; <span class="literal">"\nUsage: docsample filename\n"</span>;
+ <span class="keyword">return</span> <span class="literal">0</span>;
+ }
+
+ <span class="keyword">const char</span>* test_file_path = argv[1];
+ <span class="comment">// Open the test file (contains UTF-8 encoded text)</span>
+ ifstream fs8(test_file_path);
+ <span class="keyword">if</span> (!fs8.is_open()) {
+ cout &lt;&lt; <span class=
+"literal">"Could not open "</span> &lt;&lt; test_file_path &lt;&lt; endl;
+ <span class="keyword">return</span> <span class="literal">0</span>;
+ }
+
+ <span class="keyword">unsigned</span> line_count = <span class="literal">1</span>;
+ string line;
+ <span class="comment">// Play with all the lines in the file</span>
+ <span class="keyword">while</span> (getline(fs8, line)) {
+ <span class="comment">// check for invalid utf-8 (for a simple yes/no check, there is also utf8::is_valid function)</span>
+ string::iterator end_it = utf8::find_invalid(line.begin(), line.end());
+ <span class="keyword">if</span> (end_it != line.end()) {
+ cout &lt;&lt; <span class=
+"literal">"Invalid UTF-8 encoding detected at line "</span> &lt;&lt; line_count &lt;&lt; <span
+ class="literal">"\n"</span>;
+ cout &lt;&lt; <span class=
+"literal">"This part is fine: "</span> &lt;&lt; string(line.begin(), end_it) &lt;&lt; <span
+ class="literal">"\n"</span>;
+ }
+
+ <span class="comment">// Get the line length (at least for the valid part)</span>
+ <span class="keyword">int</span> length = utf8::distance(line.begin(), end_it);
+ cout &lt;&lt; <span class=
+"literal">"Length of line "</span> &lt;&lt; line_count &lt;&lt; <span class=
+"literal">" is "</span> &lt;&lt; length &lt;&lt; <span class="literal">"\n"</span>;
+
+ <span class="comment">// Convert it to utf-16</span>
+ vector&lt;unsigned short&gt; utf16line;
+ utf8::utf8to16(line.begin(), end_it, back_inserter(utf16line));
+
+ <span class="comment">// And back to utf-8</span>
+ string utf8line;
+ utf8::utf16to8(utf16line.begin(), utf16line.end(), back_inserter(utf8line));
+
+ <span class="comment">// Confirm that the conversion went OK:</span>
+ <span class="keyword">if</span> (utf8line != string(line.begin(), end_it))
+ cout &lt;&lt; <span class=
+"literal">"Error in UTF-16 conversion at line: "</span> &lt;&lt; line_count &lt;&lt; <span
+ class="literal">"\n"</span>;
+
+ line_count++;
+ }
+ <span class="keyword">return</span> <span class="literal">0</span>;
+}
+</pre>
+ <p>
+ In the previous code sample, for each line we performed
+ a detection of invalid UTF-8 sequences with <code>find_invalid</code>; the number
+ of characters (more precisely - the number of Unicode code points, including the end
+ of line and even BOM if there is one) in each line was
+ determined with a use of <code>utf8::distance</code>; finally, we have converted
+ each line to UTF-16 encoding with <code>utf8to16</code> and back to UTF-8 with
+ <code>utf16to8</code>.
+ </p>
+ <h3 id="validfile">Checking if a file contains valid UTF-8 text</h3>
+<p>
+Here is a function that checks whether the content of a file is valid UTF-8 encoded text without
+reading the content into the memory:
+</p>
+<pre>
+<span class="keyword">bool</span> valid_utf8_file(i<span class="keyword">const char</span>* file_name)
+{
+ ifstream ifs(file_name);
+ <span class="keyword">if</span> (!ifs)
+ <span class="keyword">return false</span>; <span class="comment">// even better, throw here</span>
+
+ istreambuf_iterator&lt;<span class="keyword">char</span>&gt; it(ifs.rdbuf());
+ istreambuf_iterator&lt;<span class="keyword">char</span>&gt; eos;
+
+ <span class="keyword">return</span> utf8::is_valid(it, eos);
+}
+</pre>
+<p>
+Because the function <code>utf8::is_valid()</code> works with input iterators, we were able
+to pass an <code>istreambuf_iterator</code> to it and read the content of the file directly
+without loading it to the memory first.</p>
+<p>
+Note that other functions that take input iterator arguments can be used in a similar way. For
+instance, to read the content of a UTF-8 encoded text file and convert the text to UTF-16, just
+do something like:
+</p>
+<pre>
+ utf8::utf8to16(it, eos, back_inserter(u16string));
+</pre>
+ <h3 id="fixinvalid">Ensure that a string contains valid UTF-8 text</h3>
+<p>
+If we have some text that "probably" contains UTF-8 encoded text and we want to
+replace any invalid UTF-8 sequence with a replacement character, something like
+the following function may be used:
+</p>
+<pre>
+<span class="keyword">void</span> fix_utf8_string(std::string&amp; str)
+{
+ std::string temp;
+ utf8::replace_invalid(str.begin(), str.end(), back_inserter(temp));
+ str = temp;
+}
+</pre>
+<p>The function will replace any invalid UTF-8 sequence with a Unicode replacement character.
+There is an overloaded function that enables the caller to supply their own replacement character.
+</p>
+ <h2 id="reference">
+ Reference
+ </h2>
+ <h3 id="funutf8">
+ Functions From utf8 Namespace
+ </h3>
+ <h4>
+ utf8::append
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence
+ to a UTF-8 string.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator append(uint32_t cp, octet_iterator result);
+
+</pre>
+ <p>
+ <code>octet_iterator</code>: an output iterator.<br>
+ <code>cp</code>: a 32 bit integer representing a code point to append to the
+ sequence.<br>
+ <code>result</code>: an output iterator to the place in the sequence where to
+ append the code point.<br>
+ <span class="return_value">Return value</span>: an iterator pointing to the place
+ after the newly appended sequence.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">unsigned char</span> u[<span class="literal">5</span>] = {<span
+class="literal">0</span>,<span class="literal">0</span>,<span class=
+"literal">0</span>,<span class="literal">0</span>,<span class="literal">0</span>};
+<span class="keyword">unsigned char</span>* end = append(<span class=
+"literal">0x0448</span>, u);
+assert (u[<span class="literal">0</span>] == <span class=
+"literal">0xd1</span> &amp;&amp; u[<span class="literal">1</span>] == <span class=
+"literal">0x88</span> &amp;&amp; u[<span class="literal">2</span>] == <span class=
+"literal">0</span> &amp;&amp; u[<span class="literal">3</span>] == <span class=
+"literal">0</span> &amp;&amp; u[<span class="literal">4</span>] == <span class=
+"literal">0</span>);
+</pre>
+ <p>
+ Note that <code>append</code> does not allocate any memory - it is the burden of
+ the caller to make sure there is enough memory allocated for the operation. To make
+ things more interesting, <code>append</code> can add anywhere between 1 and 4
+ octets to the sequence. In practice, you would most often want to use
+ <code>std::back_inserter</code> to ensure that the necessary memory is allocated.
+ </p>
+ <p>
+ In case of an invalid code point, a <code>utf8::invalid_code_point</code> exception
+ is thrown.
+ </p>
+ <h4>
+ utf8::next
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Given the iterator to the beginning of the UTF-8 sequence, it returns the code
+ point and moves the iterator to the next position.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t next(octet_iterator&amp; it, octet_iterator end);
+
+</pre>
+ <p>
+ <code>octet_iterator</code>: an input iterator.<br>
+ <code>it</code>: a reference to an iterator pointing to the beginning of an UTF-8
+ encoded code point. After the function returns, it is incremented to point to the
+ beginning of the next code point.<br>
+ <code>end</code>: end of the UTF-8 sequence to be processed. If <code>it</code>
+ gets equal to <code>end</code> during the extraction of a code point, an
+ <code>utf8::not_enough_room</code> exception is thrown.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ processed UTF-8 code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+<span class="keyword">int</span> cp = next(w, twochars + <span class="literal">6</span>);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars + <span class="literal">3</span>);
+</pre>
+ <p>
+ This function is typically used to iterate through a UTF-8 encoded string.
+ </p>
+ <p>
+ In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+ thrown.
+ </p>
+ <h4>
+ utf8::peek_next
+ </h4>
+ <p class="version">
+ Available in version 2.1 and later.
+ </p>
+ <p>
+ Given the iterator to the beginning of the UTF-8 sequence, it returns the code
+ point for the following sequence without changing the value of the iterator.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t peek_next(octet_iterator it, octet_iterator end);
+
+</pre>
+ <p>
+ <code>octet_iterator</code>: an input iterator.<br>
+ <code>it</code>: an iterator pointing to the beginning of an UTF-8
+ encoded code point.<br>
+ <code>end</code>: end of the UTF-8 sequence to be processed. If <code>it</code>
+ gets equal to <code>end</code> during the extraction of a code point, an
+ <code>utf8::not_enough_room</code> exception is thrown.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ processed UTF-8 code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+<span class="keyword">int</span> cp = peek_next(w, twochars + <span class="literal">6</span>);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+ <p>
+ In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+ thrown.
+ </p>
+ <h4>
+ utf8::prior
+ </h4>
+ <p class="version">
+ Available in version 1.02 and later.
+ </p>
+ <p>
+ Given a reference to an iterator pointing to an octet in a UTF-8 sequence, it
+ decreases the iterator until it hits the beginning of the previous UTF-8 encoded
+ code point and returns the 32 bits representation of the code point.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t prior(octet_iterator&amp; it, octet_iterator start);
+
+</pre>
+ <p>
+ <code>octet_iterator</code>: a bidirectional iterator.<br>
+ <code>it</code>: a reference pointing to an octet within a UTF-8 encoded string.
+ After the function returns, it is decremented to point to the beginning of the
+ previous code point.<br>
+ <code>start</code>: an iterator to the beginning of the sequence where the search
+ for the beginning of a code point is performed. It is a
+ safety measure to prevent passing the beginning of the string in the search for a
+ UTF-8 lead octet.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ previous code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">unsigned char</span>* w = twochars + <span class=
+"literal">3</span>;
+<span class="keyword">int</span> cp = prior (w, twochars);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+ <p>
+ This function has two purposes: one is two iterate backwards through a UTF-8
+ encoded string. Note that it is usually a better idea to iterate forward instead,
+ since <code>utf8::next</code> is faster. The second purpose is to find a beginning
+ of a UTF-8 sequence if we have a random position within a string. Note that in that
+ case <code>utf8::prior</code> may not detect an invalid UTF-8 sequence in some scenarios:
+ for instance if there are superfluous trail octets, it will just skip them.
+ </p>
+ <p>
+ <code>it</code> will typically point to the beginning of
+ a code point, and <code>start</code> will point to the
+ beginning of the string to ensure we don't go backwards too far. <code>it</code> is
+ decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence
+ beginning with that octet is decoded to a 32 bit representation and returned.
+ </p>
+ <p>
+ In case <code>start</code> is reached before a UTF-8 lead octet is hit, or if an
+ invalid UTF-8 sequence is started by the lead octet, an <code>invalid_utf8</code>
+ exception is thrown.
+ </p>
+ <p>In case <code>start</code> equals <code>it</code>, a <code>not_enough_room</code>
+ exception is thrown.
+ <h4>
+ utf8::previous
+ </h4>
+ <p class="version">
+ Deprecated in version 1.02 and later.
+ </p>
+ <p>
+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it
+ decreases the iterator until it hits the beginning of the previous UTF-8 encoded
+ code point and returns the 32 bits representation of the code point.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t previous(octet_iterator&amp; it, octet_iterator pass_start);
+
+</pre>
+ <p>
+ <code>octet_iterator</code>: a random access iterator.<br>
+ <code>it</code>: a reference pointing to an octet within a UTF-8 encoded string.
+ After the function returns, it is decremented to point to the beginning of the
+ previous code point.<br>
+ <code>pass_start</code>: an iterator to the point in the sequence where the search
+ for the beginning of a code point is aborted if no result was reached. It is a
+ safety measure to prevent passing the beginning of the string in the search for a
+ UTF-8 lead octet.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ previous code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">unsigned char</span>* w = twochars + <span class=
+"literal">3</span>;
+<span class="keyword">int</span> cp = previous (w, twochars - <span class=
+"literal">1</span>);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+ <p>
+ <code>utf8::previous</code> is deprecated, and <code>utf8::prior</code> should
+ be used instead, although the existing code can continue using this function.
+ The problem is the parameter <code>pass_start</code> that points to the position
+ just before the beginning of the sequence. Standard containers don't have the
+ concept of "pass start" and the function can not be used with their iterators.
+ </p>
+ <p>
+ <code>it</code> will typically point to the beginning of
+ a code point, and <code>pass_start</code> will point to the octet just before the
+ beginning of the string to ensure we don't go backwards too far. <code>it</code> is
+ decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence
+ beginning with that octet is decoded to a 32 bit representation and returned.
+ </p>
+ <p>
+ In case <code>pass_start</code> is reached before a UTF-8 lead octet is hit, or if an
+ invalid UTF-8 sequence is started by the lead octet, an <code>invalid_utf8</code>
+ exception is thrown
+ </p>
+ <h4>
+ utf8::advance
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Advances an iterator by the specified number of code points within an UTF-8
+ sequence.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, typename distance_type&gt;
+<span class=
+"keyword">void</span> advance (octet_iterator&amp; it, distance_type n, octet_iterator end);
+
+</pre>
+ <p>
+ <code>octet_iterator</code>: an input iterator.<br>
+ <code>distance_type</code>: an integral type convertible to <code>octet_iterator</code>'s difference type.<br>
+ <code>it</code>: a reference to an iterator pointing to the beginning of an UTF-8
+ encoded code point. After the function returns, it is incremented to point to the
+ nth following code point.<br>
+ <code>n</code>: a positive integer that shows how many code points we want to
+ advance.<br>
+ <code>end</code>: end of the UTF-8 sequence to be processed. If <code>it</code>
+ gets equal to <code>end</code> during the extraction of a code point, an
+ <code>utf8::not_enough_room</code> exception is thrown.<br>
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">unsigned char</span>* w = twochars;
+advance (w, <span class="literal">2</span>, twochars + <span class="literal">6</span>);
+assert (w == twochars + <span class="literal">5</span>);
+</pre>
+ <p>
+ This function works only "forward". In case of a negative <code>n</code>, there is
+ no effect.
+ </p>
+ <p>
+ In case of an invalid code point, a <code>utf8::invalid_code_point</code> exception
+ is thrown.
+ </p>
+ <h4>
+ utf8::distance
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Given the iterators to two UTF-8 encoded code points in a seqence, returns the
+ number of code points between them.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+<span class=
+"keyword">typename</span> std::iterator_traits&lt;octet_iterator&gt;::difference_type distance (octet_iterator first, octet_iterator last);
+
+</pre>
+ <p>
+ <code>octet_iterator</code>: an input iterator.<br>
+ <code>first</code>: an iterator to a beginning of a UTF-8 encoded code point.<br>
+ <code>last</code>: an iterator to a "post-end" of the last UTF-8 encoded code
+ point in the sequence we are trying to determine the length. It can be the
+ beginning of a new code point, or not.<br>
+ <span class="return_value">Return value</span> the distance between the iterators,
+ in code points.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+size_t dist = utf8::distance(twochars, twochars + <span class="literal">5</span>);
+assert (dist == <span class="literal">2</span>);
+</pre>
+ <p>
+ This function is used to find the length (in code points) of a UTF-8 encoded
+ string. The reason it is called <em>distance</em>, rather than, say,
+ <em>length</em> is mainly because developers are used that <em>length</em> is an
+ O(1) function. Computing the length of an UTF-8 string is a linear operation, and
+ it looked better to model it after <code>std::distance</code> algorithm.
+ </p>
+ <p>
+ In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+ thrown. If <code>last</code> does not point to the past-of-end of a UTF-8 seqence,
+ a <code>utf8::not_enough_room</code> exception is thrown.
+ </p>
+ <h4>
+ utf8::utf16to8
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts a UTF-16 encoded string to UTF-8.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> u16bit_iterator, <span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result);
+
+</pre>
+ <p>
+ <code>u16bit_iterator</code>: an input iterator.<br>
+ <code>octet_iterator</code>: an output iterator.<br>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-16 encoded
+ string to convert.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-16 encoded
+ string to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-8 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-8 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">unsigned short</span> utf16string[] = {<span class=
+"literal">0x41</span>, <span class="literal">0x0448</span>, <span class=
+"literal">0x65e5</span>, <span class="literal">0xd834</span>, <span class=
+"literal">0xdd1e</span>};
+vector&lt;<span class="keyword">unsigned char</span>&gt; utf8result;
+utf16to8(utf16string, utf16string + <span class=
+"literal">5</span>, back_inserter(utf8result));
+assert (utf8result.size() == <span class="literal">10</span>);
+</pre>
+ <p>
+ In case of invalid UTF-16 sequence, a <code>utf8::invalid_utf16</code> exception is
+ thrown.
+ </p>
+ <h4>
+ utf8::utf8to16
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts an UTF-8 encoded string to UTF-16
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> u16bit_iterator, typename octet_iterator&gt;
+u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result);
+
+</pre>
+ <p>
+ <code>octet_iterator</code>: an input iterator.<br>
+ <code>u16bit_iterator</code>: an output iterator.<br>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded
+ string to convert. &lt; br /&gt; <code>end</code>: an iterator pointing to
+ pass-the-end of the UTF-8 encoded string to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-16 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-16 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span> utf8_with_surrogates[] = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"</span>;
+vector &lt;<span class="keyword">unsigned short</span>&gt; utf16result;
+utf8to16(utf8_with_surrogates, utf8_with_surrogates + <span class=
+"literal">9</span>, back_inserter(utf16result));
+assert (utf16result.size() == <span class="literal">4</span>);
+assert (utf16result[<span class="literal">2</span>] == <span class=
+"literal">0xd834</span>);
+assert (utf16result[<span class="literal">3</span>] == <span class=
+"literal">0xdd1e</span>);
+</pre>
+ <p>
+ In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+ thrown. If <code>end</code> does not point to the past-of-end of a UTF-8 seqence, a
+ <code>utf8::not_enough_room</code> exception is thrown.
+ </p>
+ <h4>
+ utf8::utf32to8
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts a UTF-32 encoded string to UTF-8.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, typename u32bit_iterator&gt;
+octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result);
+
+</pre>
+ <p>
+ <code>octet_iterator</code>: an output iterator.<br>
+ <code>u32bit_iterator</code>: an input iterator.<br>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-32 encoded
+ string to convert.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-32 encoded
+ string to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-8 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-8 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">int</span> utf32string[] = {<span class=
+"literal">0x448</span>, <span class="literal">0x65E5</span>, <span class=
+"literal">0x10346</span>, <span class="literal">0</span>};
+vector&lt;<span class="keyword">unsigned char</span>&gt; utf8result;
+utf32to8(utf32string, utf32string + <span class=
+"literal">3</span>, back_inserter(utf8result));
+assert (utf8result.size() == <span class="literal">9</span>);
+</pre>
+ <p>
+ In case of invalid UTF-32 string, a <code>utf8::invalid_code_point</code> exception
+ is thrown.
+ </p>
+ <h4>
+ utf8::utf8to32
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts a UTF-8 encoded string to UTF-32.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, <span class=
+"keyword">typename</span> u32bit_iterator&gt;
+u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result);
+
+</pre>
+ <p>
+ <code>octet_iterator</code>: an input iterator.<br>
+ <code>u32bit_iterator</code>: an output iterator.<br>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded
+ string to convert.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 encoded string
+ to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-32 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-32 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+vector&lt;<span class="keyword">int</span>&gt; utf32result;
+utf8to32(twochars, twochars + <span class=
+"literal">5</span>, back_inserter(utf32result));
+assert (utf32result.size() == <span class="literal">2</span>);
+</pre>
+ <p>
+ In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+ thrown. If <code>end</code> does not point to the past-of-end of a UTF-8 seqence, a
+ <code>utf8::not_enough_room</code> exception is thrown.
+ </p>
+ <h4>
+ utf8::find_invalid
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Detects an invalid sequence within a UTF-8 string.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator find_invalid(octet_iterator start, octet_iterator end);
+</pre>
+ <p>
+ <code>octet_iterator</code>: an input iterator.<br>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 string to
+ test for validity.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 string to test
+ for validity.<br>
+ <span class="return_value">Return value</span>: an iterator pointing to the first
+ invalid octet in the UTF-8 string. In case none were found, equals
+ <code>end</code>.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span> utf_invalid[] = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88\xfa"</span>;
+<span class=
+"keyword">char</span>* invalid = find_invalid(utf_invalid, utf_invalid + <span class=
+"literal">6</span>);
+assert (invalid == utf_invalid + <span class="literal">5</span>);
+</pre>
+ <p>
+ This function is typically used to make sure a UTF-8 string is valid before
+ processing it with other functions. It is especially important to call it if before
+ doing any of the <em>unchecked</em> operations on it.
+ </p>
+ <h4>
+ utf8::is_valid
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Checks whether a sequence of octets is a valid UTF-8 string.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+<span class="keyword">bool</span> is_valid(octet_iterator start, octet_iterator end);
+
+</pre>
+ <p>
+ <code>octet_iterator</code>: an input iterator.<br>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 string to
+ test for validity.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 string to test
+ for validity.<br>
+ <span class="return_value">Return value</span>: <code>true</code> if the sequence
+ is a valid UTF-8 string; <code>false</code> if not.
+ </p>
+ Example of use:
+<pre>
+<span class="keyword">char</span> utf_invalid[] = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88\xfa"</span>;
+<span class="keyword">bool</span> bvalid = is_valid(utf_invalid, utf_invalid + <span
+class="literal">6</span>);
+assert (bvalid == false);
+</pre>
+ <p>
+ <code>is_valid</code> is a shorthand for <code>find_invalid(start, end) ==
+ end;</code>. You may want to use it to make sure that a byte seqence is a valid
+ UTF-8 string without the need to know where it fails if it is not valid.
+ </p>
+ <h4>
+ utf8::replace_invalid
+ </h4>
+ <p class="version">
+ Available in version 2.0 and later.
+ </p>
+ <p>
+ Replaces all invalid UTF-8 sequences within a string with a replacement marker.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, <span class=
+"keyword">typename</span> output_iterator&gt;
+output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement);
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, <span class=
+"keyword">typename</span> output_iterator&gt;
+output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out);
+
+</pre>
+ <p>
+ <code>octet_iterator</code>: an input iterator.<br>
+ <code>output_iterator</code>: an output iterator.<br>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 string to
+ look for invalid UTF-8 sequences.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 string to look
+ for invalid UTF-8 sequences.<br>
+ <code>out</code>: An output iterator to the range where the result of replacement
+ is stored.<br>
+ <code>replacement</code>: A Unicode code point for the replacement marker. The
+ version without this parameter assumes the value <code>0xfffd</code><br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the UTF-8 string with replaced invalid sequences.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span> invalid_sequence[] = <span class=
+"literal">"a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"</span>;
+vector&lt;<span class="keyword">char</span>&gt; replace_invalid_result;
+replace_invalid (invalid_sequence, invalid_sequence + sizeof(invalid_sequence), back_inserter(replace_invalid_result), <span
+ class="literal">'?'</span>);
+bvalid = is_valid(replace_invalid_result.begin(), replace_invalid_result.end());
+assert (bvalid);
+<span class="keyword">char</span>* fixed_invalid_sequence = <span class=
+"literal">"a????z"</span>;
+assert (std::equal(replace_invalid_result.begin(), replace_invalid_result.end(), fixed_invalid_sequence));
+</pre>
+ <p>
+ <code>replace_invalid</code> does not perform in-place replacement of invalid
+ sequences. Rather, it produces a copy of the original string with the invalid
+ sequences replaced with a replacement marker. Therefore, <code>out</code> must not
+ be in the <code>[start, end]</code> range.
+ </p>
+ <p>
+ If <code>end</code> does not point to the past-of-end of a UTF-8 sequence, a
+ <code>utf8::not_enough_room</code> exception is thrown.
+ </p>
+ <h4>
+ utf8::starts_with_bom
+ </h4>
+ <p class="version">
+ Available in version 2.3 and later. Relaces deprecated <code>is_bom()</code> function.
+ </p>
+ <p>
+ Checks whether an octet sequence starts with a UTF-8 byte order mark (BOM)
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+<span class="keyword">bool</span> starts_with_bom (octet_iterator it, octet_iterator end);
+</pre>
+ <p>
+ <code>octet_iterator</code>: an input iterator.<br>
+ <code>it</code>: beginning of the octet sequence to check<br>
+ <code>end</code>: pass-end of the sequence to check<br>
+ <span class="return_value">Return value</span>: <code>true</code> if the sequence
+ starts with a UTF-8 byte order mark; <code>false</code> if not.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">unsigned char</span> byte_order_mark[] = {<span class=
+"literal">0xef</span>, <span class="literal">0xbb</span>, <span class=
+"literal">0xbf</span>};
+<span class="keyword">bool</span> bbom = starts_with_bom(byte_order_mark, byte_order_mark + <span class="keyword">sizeof</span>(byte_order_mark));
+assert (bbom == <span class="literal">true</span>);
+</pre>
+ <p>
+ The typical use of this function is to check the first three bytes of a file. If
+ they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8
+ encoded text.
+ </p>
+ <h4>
+ utf8::is_bom
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later. Deprecated in version 2.3. <code>starts_with_bom()</code> should be used
+ instead.
+ </p>
+ <p>
+ Checks whether a sequence of three octets is a UTF-8 byte order mark (BOM)
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+<span class="keyword">bool</span> is_bom (octet_iterator it); <span class="comment"> // Deprecated</span>
+</pre>
+ <p>
+ <code>octet_iterator</code>: an input iterator.<br>
+ <code>it</code>: beginning of the 3-octet sequence to check<br>
+ <span class="return_value">Return value</span>: <code>true</code> if the sequence
+ is UTF-8 byte order mark; <code>false</code> if not.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">unsigned char</span> byte_order_mark[] = {<span class=
+"literal">0xef</span>, <span class="literal">0xbb</span>, <span class=
+"literal">0xbf</span>};
+<span class="keyword">bool</span> bbom = is_bom(byte_order_mark);
+assert (bbom == <span class="literal">true</span>);
+</pre>
+ <p>
+ The typical use of this function is to check the first three bytes of a file. If
+ they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8
+ encoded text.
+ </p>
+ <p>
+ If a sequence is
+ shorter than three bytes, an invalid iterator will be dereferenced. Therefore, this function is deprecated
+ in favor of <code>starts_with_bom()</code>that takes the end of sequence as an argument.
+ </p>
+ <h3 id="typesutf8">
+ Types From utf8 Namespace
+ </h3>
+ <h4>utf8::exception
+ </h4>
+ <p class="version">
+ Available in version 2.3 and later.
+ </p>
+ <p>
+ Base class for the exceptions thrown by UTF CPP library functions.
+ </p>
+<pre>
+<span class="keyword">class</span> exception : <span class="keyword">public</span> std::exception {};
+</pre>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">try</span> {
+ code_that_uses_utf_cpp_library();
+}
+<span class="keyword">catch</span>(<span class="keyword">const</span> utf8::exception&amp; utfcpp_ex) {
+ cerr &lt;&lt; utfcpp_ex.what();
+}
+</pre>
+
+ <h4>utf8::invalid_code_point
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Thrown by UTF8 CPP functions such as <code>advance</code> and <code>next</code> if an UTF-8 sequence represents and invalid code point.
+ </p>
+
+<pre>
+<span class="keyword">class</span> invalid_code_point : <span class="keyword">public</span> exception {
+<span class="keyword">public</span>:
+ uint32_t code_point() <span class="keyword">const</span>;
+};
+
+</pre>
+ <p>
+ Member function <code>code_point()</code> can be used to determine the invalid code point that
+ caused the exception to be thrown.
+ </p>
+ <h4>utf8::invalid_utf8
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Thrown by UTF8 CPP functions such as <code>next</code> and <code>prior</code> if an invalid UTF-8 sequence
+ is detected during decoding.
+ </p>
+
+<pre>
+<span class="keyword">class</span> invalid_utf8 : <span class="keyword">public</span> exception {
+<span class="keyword">public</span>:
+ uint8_t utf8_octet() <span class="keyword">const</span>;
+};
+</pre>
+
+ <p>
+ Member function <code>utf8_octet()</code> can be used to determine the beginning of the byte
+ sequence that caused the exception to be thrown.
+ </p>
+</pre>
+ <h4>utf8::invalid_utf16
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Thrown by UTF8 CPP function <code>utf16to8</code> if an invalid UTF-16 sequence
+ is detected during decoding.
+ </p>
+
+<pre>
+<span class="keyword">class</span> invalid_utf16 : <span class="keyword">public</span> exception {
+<span class="keyword">public</span>:
+ uint16_t utf16_word() <span class="keyword">const</span>;
+};
+</pre>
+
+ <p>
+ Member function <code>utf16_word()</code> can be used to determine the UTF-16 code unit
+ that caused the exception to be thrown.
+ </p>
+ <h4>utf8::not_enough_room
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Thrown by UTF8 CPP functions such as <code>next</code> if the end of the decoded UTF-8 sequence
+ was reached before the code point was decoded.
+ </p>
+
+<pre>
+<span class="keyword">class</span> not_enough_room : <span class="keyword">public</span> exception {};
+</pre>
+ <h4>
+ utf8::iterator
+ </h4>
+ <p class="version">
+ Available in version 2.0 and later.
+ </p>
+ <p>
+ Adapts the underlying octet iterator to iterate over the sequence of code points,
+ rather than raw octets.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class="keyword">typename</span> octet_iterator&gt;
+<span class="keyword">class</span> iterator;
+</pre>
+
+ <h5>Member functions</h5>
+ <dl>
+ <dt><code>iterator();</code> <dd> the deafult constructor; the underlying <code>octet_iterator</code> is
+ constructed with its default constructor.
+ <dt><code><span class="keyword">explicit</span> iterator (const octet_iterator&amp; octet_it,
+ const octet_iterator&amp; range_start,
+ const octet_iterator&amp; range_end);</code> <dd> a constructor
+ that initializes the underlying <code>octet_iterator</code> with <code>octet_it</code>
+ and sets the range in which the iterator is considered valid.
+ <dt><code>octet_iterator base () <span class="keyword">const</span>;</code> <dd> returns the
+ underlying <code>octet_iterator</code>.
+ <dt><code>uint32_t operator * () <span class="keyword">const</span>;</code> <dd> decodes the utf-8 sequence
+ the underlying <code>octet_iterator</code> is pointing to and returns the code point.
+ <dt><code><span class="keyword">bool operator</span> == (const iterator&amp; rhs)
+ <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span>
+ if the two underlaying iterators are equal.
+ <dt><code><span class="keyword">bool operator</span> != (const iterator&amp; rhs)
+ <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span>
+ if the two underlaying iterators are not equal.
+ <dt><code>iterator&amp; <span class="keyword">operator</span> ++ (); </code> <dd> the prefix increment - moves
+ the iterator to the next UTF-8 encoded code point.
+ <dt><code>iterator <span class="keyword">operator</span> ++ (<span class="keyword">int</span>); </code> <dd>
+ the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one.
+ <dt><code>iterator&amp; <span class="keyword">operator</span> -- (); </code> <dd> the prefix decrement - moves
+ the iterator to the previous UTF-8 encoded code point.
+ <dt><code>iterator <span class="keyword">operator</span> -- (<span class="keyword">int</span>); </code> <dd>
+ the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one.
+ </dl>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* threechars = <span class="literal">"\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"</span>;
+utf8::iterator&lt;<span class="keyword">char</span>*&gt; it(threechars, threechars, threechars + <span class="literal">9</span>);
+utf8::iterator&lt;<span class="keyword">char</span>*&gt; it2 = it;
+assert (it2 == it);
+assert (*it == <span class="literal">0x10346</span>);
+assert (*(++it) == <span class="literal">0x65e5</span>);
+assert ((*it++) == <span class="literal">0x65e5</span>);
+assert (*it == <span class="literal">0x0448</span>);
+assert (it != it2);
+utf8::iterator&lt;<span class="keyword">char</span>*&gt; endit (threechars + <span class="literal">9</span>, threechars, threechars + <span class="literal">9</span>);
+assert (++it == endit);
+assert (*(--it) == <span class="literal">0x0448</span>);
+assert ((*it--) == <span class="literal">0x0448</span>);
+assert (*it == <span class="literal">0x65e5</span>);
+assert (--it == utf8::iterator&lt;<span class="keyword">char</span>*&gt;(threechars, threechars, threechars + <span class="literal">9</span>));
+assert (*it == <span class="literal">0x10346</span>);
+</pre>
+ <p>
+ The purpose of <code>utf8::iterator</code> adapter is to enable easy iteration as well as the use of STL
+ algorithms with UTF-8 encoded strings. Increment and decrement operators are implemented in terms of
+ <code>utf8::next()</code> and <code>utf8::prior()</code> functions.
+ </p>
+ <p>
+ Note that <code>utf8::iterator</code> adapter is a checked iterator. It operates on the range specified in
+ the constructor; any attempt to go out of that range will result in an exception. Even the comparison operators
+ require both iterator object to be constructed against the same range - otherwise an exception is thrown. Typically,
+ the range will be determined by sequence container functions <code>begin</code> and <code>end</code>, i.e.:
+ </p>
+<pre>
+std::string s = <span class="literal">"example"</span>;
+utf8::iterator i (s.begin(), s.begin(), s.end());
+</pre>
+ <h3 id="fununchecked">
+ Functions From utf8::unchecked Namespace
+ </h3>
+ <h4>
+ utf8::unchecked::append
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence
+ to a UTF-8 string.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator append(uint32_t cp, octet_iterator result);
+
+</pre>
+ <p>
+ <code>cp</code>: A 32 bit integer representing a code point to append to the
+ sequence.<br>
+ <code>result</code>: An output iterator to the place in the sequence where to
+ append the code point.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the newly appended sequence.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">unsigned char</span> u[<span class="literal">5</span>] = {<span
+class="literal">0</span>,<span class="literal">0</span>,<span class=
+"literal">0</span>,<span class="literal">0</span>,<span class="literal">0</span>};
+<span class="keyword">unsigned char</span>* end = unchecked::append(<span class=
+"literal">0x0448</span>, u);
+assert (u[<span class="literal">0</span>] == <span class=
+"literal">0xd1</span> &amp;&amp; u[<span class="literal">1</span>] == <span class=
+"literal">0x88</span> &amp;&amp; u[<span class="literal">2</span>] == <span class=
+"literal">0</span> &amp;&amp; u[<span class="literal">3</span>] == <span class=
+"literal">0</span> &amp;&amp; u[<span class="literal">4</span>] == <span class=
+"literal">0</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::append</code>. It does not
+ check for validity of the supplied code point, and may produce an invalid UTF-8
+ sequence.
+ </p>
+ <h4>
+ utf8::unchecked::next
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Given the iterator to the beginning of a UTF-8 sequence, it returns the code point
+ and moves the iterator to the next position.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t next(octet_iterator&amp; it);
+
+</pre>
+ <p>
+ <code>it</code>: a reference to an iterator pointing to the beginning of an UTF-8
+ encoded code point. After the function returns, it is incremented to point to the
+ beginning of the next code point.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ processed UTF-8 code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+<span class="keyword">int</span> cp = unchecked::next(w);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars + <span class="literal">3</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::next</code>. It does not
+ check for validity of the supplied UTF-8 sequence.
+ </p>
+ <h4>
+ utf8::unchecked::peek_next
+ </h4>
+ <p class="version">
+ Available in version 2.1 and later.
+ </p>
+ <p>
+ Given the iterator to the beginning of a UTF-8 sequence, it returns the code point.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t peek_next(octet_iterator it);
+
+</pre>
+ <p>
+ <code>it</code>: an iterator pointing to the beginning of an UTF-8
+ encoded code point.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ processed UTF-8 code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+<span class="keyword">int</span> cp = unchecked::peek_next(w);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::peek_next</code>. It does not
+ check for validity of the supplied UTF-8 sequence.
+ </p>
+ <h4>
+ utf8::unchecked::prior
+ </h4>
+ <p class="version">
+ Available in version 1.02 and later.
+ </p>
+ <p>
+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it
+ decreases the iterator until it hits the beginning of the previous UTF-8 encoded
+ code point and returns the 32 bits representation of the code point.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t prior(octet_iterator&amp; it);
+
+</pre>
+ <p>
+ <code>it</code>: a reference pointing to an octet within a UTF-8 encoded string.
+ After the function returns, it is decremented to point to the beginning of the
+ previous code point.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ previous code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars + <span class="literal">3</span>;
+<span class="keyword">int</span> cp = unchecked::prior (w);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::prior</code>. It does not
+ check for validity of the supplied UTF-8 sequence and offers no boundary checking.
+ </p>
+ <h4>
+ utf8::unchecked::previous (deprecated, see utf8::unchecked::prior)
+ </h4>
+ <p class="version">
+ Deprecated in version 1.02 and later.
+ </p>
+ <p>
+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it
+ decreases the iterator until it hits the beginning of the previous UTF-8 encoded
+ code point and returns the 32 bits representation of the code point.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t previous(octet_iterator&amp; it);
+
+</pre>
+ <p>
+ <code>it</code>: a reference pointing to an octet within a UTF-8 encoded string.
+ After the function returns, it is decremented to point to the beginning of the
+ previous code point.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ previous code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars + <span class="literal">3</span>;
+<span class="keyword">int</span> cp = unchecked::previous (w);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+ <p>
+ The reason this function is deprecated is just the consistency with the "checked"
+ versions, where <code>prior</code> should be used instead of <code>previous</code>.
+ In fact, <code>unchecked::previous</code> behaves exactly the same as <code>
+ unchecked::prior</code>
+ </p>
+ <p>
+ This is a faster but less safe version of <code>utf8::previous</code>. It does not
+ check for validity of the supplied UTF-8 sequence and offers no boundary checking.
+ </p>
+ <h4>
+ utf8::unchecked::advance
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Advances an iterator by the specified number of code points within an UTF-8
+ sequence.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, typename distance_type&gt;
+<span class="keyword">void</span> advance (octet_iterator&amp; it, distance_type n);
+
+</pre>
+ <p>
+ <code>it</code>: a reference to an iterator pointing to the beginning of an UTF-8
+ encoded code point. After the function returns, it is incremented to point to the
+ nth following code point.<br>
+ <code>n</code>: a positive integer that shows how many code points we want to
+ advance.<br>
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+unchecked::advance (w, <span class="literal">2</span>);
+assert (w == twochars + <span class="literal">5</span>);
+</pre>
+ <p>
+ This function works only "forward". In case of a negative <code>n</code>, there is
+ no effect.
+ </p>
+ <p>
+ This is a faster but less safe version of <code>utf8::advance</code>. It does not
+ check for validity of the supplied UTF-8 sequence and offers no boundary checking.
+ </p>
+ <h4>
+ utf8::unchecked::distance
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Given the iterators to two UTF-8 encoded code points in a seqence, returns the
+ number of code points between them.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+<span class=
+"keyword">typename</span> std::iterator_traits&lt;octet_iterator&gt;::difference_type distance (octet_iterator first, octet_iterator last);
+</pre>
+ <p>
+ <code>first</code>: an iterator to a beginning of a UTF-8 encoded code point.<br>
+ <code>last</code>: an iterator to a "post-end" of the last UTF-8 encoded code
+ point in the sequence we are trying to determine the length. It can be the
+ beginning of a new code point, or not.<br>
+ <span class="return_value">Return value</span> the distance between the iterators,
+ in code points.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+size_t dist = utf8::unchecked::distance(twochars, twochars + <span class=
+"literal">5</span>);
+assert (dist == <span class="literal">2</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::distance</code>. It does not
+ check for validity of the supplied UTF-8 sequence.
+ </p>
+ <h4>
+ utf8::unchecked::utf16to8
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts a UTF-16 encoded string to UTF-8.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> u16bit_iterator, <span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-16 encoded
+ string to convert.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-16 encoded
+ string to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-8 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-8 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">unsigned short</span> utf16string[] = {<span class=
+"literal">0x41</span>, <span class="literal">0x0448</span>, <span class=
+"literal">0x65e5</span>, <span class="literal">0xd834</span>, <span class=
+"literal">0xdd1e</span>};
+vector&lt;<span class="keyword">unsigned char</span>&gt; utf8result;
+unchecked::utf16to8(utf16string, utf16string + <span class=
+"literal">5</span>, back_inserter(utf8result));
+assert (utf8result.size() == <span class="literal">10</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::utf16to8</code>. It does not
+ check for validity of the supplied UTF-16 sequence.
+ </p>
+ <h4>
+ utf8::unchecked::utf8to16
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts an UTF-8 encoded string to UTF-16
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> u16bit_iterator, typename octet_iterator&gt;
+u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded
+ string to convert. &lt; br /&gt; <code>end</code>: an iterator pointing to
+ pass-the-end of the UTF-8 encoded string to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-16 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-16 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span> utf8_with_surrogates[] = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"</span>;
+vector &lt;<span class="keyword">unsigned short</span>&gt; utf16result;
+unchecked::utf8to16(utf8_with_surrogates, utf8_with_surrogates + <span class=
+"literal">9</span>, back_inserter(utf16result));
+assert (utf16result.size() == <span class="literal">4</span>);
+assert (utf16result[<span class="literal">2</span>] == <span class=
+"literal">0xd834</span>);
+assert (utf16result[<span class="literal">3</span>] == <span class=
+"literal">0xdd1e</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::utf8to16</code>. It does not
+ check for validity of the supplied UTF-8 sequence.
+ </p>
+ <h4>
+ utf8::unchecked::utf32to8
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts a UTF-32 encoded string to UTF-8.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, <span class=
+"keyword">typename</span> u32bit_iterator&gt;
+octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-32 encoded
+ string to convert.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-32 encoded
+ string to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-8 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-8 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">int</span> utf32string[] = {<span class=
+"literal">0x448</span>, <span class="literal">0x65e5</span>, <span class=
+"literal">0x10346</span>, <span class="literal">0</span>};
+vector&lt;<span class="keyword">unsigned char</span>&gt; utf8result;
+utf32to8(utf32string, utf32string + <span class=
+"literal">3</span>, back_inserter(utf8result));
+assert (utf8result.size() == <span class="literal">9</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::utf32to8</code>. It does not
+ check for validity of the supplied UTF-32 sequence.
+ </p>
+ <h4>
+ utf8::unchecked::utf8to32
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts a UTF-8 encoded string to UTF-32.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, typename u32bit_iterator&gt;
+u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded
+ string to convert.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 encoded string
+ to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-32 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-32 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+vector&lt;<span class="keyword">int</span>&gt; utf32result;
+unchecked::utf8to32(twochars, twochars + <span class=
+"literal">5</span>, back_inserter(utf32result));
+assert (utf32result.size() == <span class="literal">2</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::utf8to32</code>. It does not
+ check for validity of the supplied UTF-8 sequence.
+ </p>
+ <h3 id="typesunchecked">
+ Types From utf8::unchecked Namespace
+ </h3>
+ <h4>
+ utf8::iterator
+ </h4>
+ <p class="version">
+ Available in version 2.0 and later.
+ </p>
+ <p>
+ Adapts the underlying octet iterator to iterate over the sequence of code points,
+ rather than raw octets.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class="keyword">typename</span> octet_iterator&gt;
+<span class="keyword">class</span> iterator;
+</pre>
+
+ <h5>Member functions</h5>
+ <dl>
+ <dt><code>iterator();</code> <dd> the deafult constructor; the underlying <code>octet_iterator</code> is
+ constructed with its default constructor.
+ <dt><code><span class="keyword">explicit</span> iterator (const octet_iterator&amp; octet_it);
+ </code> <dd> a constructor
+ that initializes the underlying <code>octet_iterator</code> with <code>octet_it</code>
+ <dt><code>octet_iterator base () <span class="keyword">const</span>;</code> <dd> returns the
+ underlying <code>octet_iterator</code>.
+ <dt><code>uint32_t operator * () <span class="keyword">const</span>;</code> <dd> decodes the utf-8 sequence
+ the underlying <code>octet_iterator</code> is pointing to and returns the code point.
+ <dt><code><span class="keyword">bool operator</span> == (const iterator&amp; rhs)
+ <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span>
+ if the two underlaying iterators are equal.
+ <dt><code><span class="keyword">bool operator</span> != (const iterator&amp; rhs)
+ <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span>
+ if the two underlaying iterators are not equal.
+ <dt><code>iterator&amp; <span class="keyword">operator</span> ++ (); </code> <dd> the prefix increment - moves
+ the iterator to the next UTF-8 encoded code point.
+ <dt><code>iterator <span class="keyword">operator</span> ++ (<span class="keyword">int</span>); </code> <dd>
+ the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one.
+ <dt><code>iterator&amp; <span class="keyword">operator</span> -- (); </code> <dd> the prefix decrement - moves
+ the iterator to the previous UTF-8 encoded code point.
+ <dt><code>iterator <span class="keyword">operator</span> -- (<span class="keyword">int</span>); </code> <dd>
+ the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one.
+ </dl>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* threechars = <span class="literal">"\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"</span>;
+utf8::unchecked::iterator&lt;<span class="keyword">char</span>*&gt; un_it(threechars);
+utf8::unchecked::iterator&lt;<span class="keyword">char</span>*&gt; un_it2 = un_it;
+assert (un_it2 == un_it);
+assert (*un_it == <span class="literal">0x10346</span>);
+assert (*(++un_it) == <span class="literal">0x65e5</span>);
+assert ((*un_it++) == <span class="literal">0x65e5</span>);
+assert (*un_it == <span class="literal">0x0448</span>);
+assert (un_it != un_it2);
+utf8::::unchecked::iterator&lt;<span class="keyword">char</span>*&gt; un_endit (threechars + <span class="literal">9</span>);
+assert (++un_it == un_endit);
+assert (*(--un_it) == <span class="literal">0x0448</span>);
+assert ((*un_it--) == <span class="literal">0x0448</span>);
+assert (*un_it == <span class="literal">0x65e5</span>);
+assert (--un_it == utf8::unchecked::iterator&lt;<span class="keyword">char</span>*&gt;(threechars));
+assert (*un_it == <span class="literal">0x10346</span>);
+</pre>
+ <p>
+ This is an unchecked version of <code>utf8::iterator</code>. It is faster in many cases, but offers
+ no validity or range checks.
+ </p>
+ <h2 id="points">
+ Points of interest
+ </h2>
+ <h4>
+ Design goals and decisions
+ </h4>
+ <p>
+ The library was designed to be:
+ </p>
+ <ol>
+ <li>
+ Generic: for better or worse, there are many C++ string classes out there, and
+ the library should work with as many of them as possible.
+ </li>
+ <li>
+ Portable: the library should be portable both accross different platforms and
+ compilers. The only non-portable code is a small section that declares unsigned
+ integers of different sizes: three typedefs. They can be changed by the users of
+ the library if they don't match their platform. The default setting should work
+ for Windows (both 32 and 64 bit), and most 32 bit and 64 bit Unix derivatives.
+ </li>
+ <li>
+ Lightweight: follow the "pay only for what you use" guideline.
+ </li>
+ <li>
+ Unintrusive: avoid forcing any particular design or even programming style on the
+ user. This is a library, not a framework.
+ </li>
+ </ol>
+ <h4>
+ Alternatives
+ </h4>
+ <p>
+ In case you want to look into other means of working with UTF-8 strings from C++,
+ here is the list of solutions I am aware of:
+ </p>
+ <ol>
+ <li>
+ <a href="http://icu.sourceforge.net/">ICU Library</a>. It is very powerful,
+ complete, feature-rich, mature, and widely used. Also big, intrusive,
+ non-generic, and doesn't play well with the Standard Library. I definitelly
+ recommend looking at ICU even if you don't plan to use it.
+ </li>
+ <li>
+ C++11 language and library features. Still far from complete, and not widely
+ supported by compiler vendors.
+ </li>
+ <li>
+ <a href=
+ "http://www.gtkmm.org/gtkmm2/docs/tutorial/html/ch03s04.html">Glib::ustring</a>.
+ A class specifically made to work with UTF-8 strings, and also feel like
+ <code>std::string</code>. If you prefer to have yet another string class in your
+ code, it may be worth a look. Be aware of the licensing issues, though.
+ </li>
+ <li>
+ Platform dependent solutions: Windows and POSIX have functions to convert strings
+ from one encoding to another. That is only a subset of what my library offers,
+ but if that is all you need it may be good enough.
+ </li>
+ </ol>
+ <h2 id="links">
+ Links
+ </h2>
+ <ol>
+ <li>
+ <a href="http://www.unicode.org/">The Unicode Consortium</a>.
+ </li>
+ <li>
+ <a href="http://icu.sourceforge.net/">ICU Library</a>.
+ </li>
+ <li>
+ <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8 at Wikipedia</a>
+ </li>
+ <li>
+ <a href="http://www.cl.cam.ac.uk/~mgk25/unicode.html">UTF-8 and Unicode FAQ for
+ Unix/Linux</a>
+ </li>
+ </ol>
+ </body>
+</html>
diff --git a/thirdparty/assimp/include/assimp/.editorconfig b/thirdparty/assimp/include/assimp/.editorconfig
deleted file mode 100644
index 9ea66423ad..0000000000
--- a/thirdparty/assimp/include/assimp/.editorconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-# See <http://EditorConfig.org> for details
-
-[*.{h,hpp,inl}]
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true
-indent_size = 4
-indent_style = space
diff --git a/thirdparty/assimp/include/assimp/BaseImporter.h b/thirdparty/assimp/include/assimp/BaseImporter.h
index 48dfc8ed8b..55f7fe3754 100644
--- a/thirdparty/assimp/include/assimp/BaseImporter.h
+++ b/thirdparty/assimp/include/assimp/BaseImporter.h
@@ -48,8 +48,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <vector>
#include <set>
+#include <map>
#include <assimp/types.h>
#include <assimp/ProgressHandler.hpp>
+#include <assimp/ai_assert.h>
struct aiScene;
struct aiImporterDesc;
@@ -80,6 +82,10 @@ class IOStream;
class ASSIMP_API BaseImporter {
friend class Importer;
+private:
+ /* Pushes state into importer for the importer scale */
+ virtual void UpdateImporterScale( Importer* pImp );
+
public:
/** Constructor to be privately used by #Importer */
@@ -132,7 +138,7 @@ public:
* a suitable response to the caller.
*/
aiScene* ReadFile(
- const Importer* pImp,
+ Importer* pImp,
const std::string& pFile,
IOSystem* pIOHandler
);
@@ -161,14 +167,65 @@ public:
* some loader features. Importers must provide this information. */
virtual const aiImporterDesc* GetInfo() const = 0;
+ /**
+ * Will be called only by scale process when scaling is requested.
+ */
+ virtual void SetFileScale(double scale)
+ {
+ fileScale = scale;
+ }
+
+ virtual double GetFileScale() const
+ {
+ return fileScale;
+ }
+
+ enum ImporterUnits {
+ M,
+ MM,
+ CM,
+ INCHES,
+ FEET
+ };
+
+ /**
+ * Assimp Importer
+ * unit conversions available
+ * if you need another measurment unit add it below.
+ * it's currently defined in assimp that we prefer meters.
+ * */
+ std::map<ImporterUnits, double> importerUnits = {
+ {ImporterUnits::M, 1},
+ {ImporterUnits::CM, 0.01},
+ {ImporterUnits::MM, 0.001},
+ {ImporterUnits::INCHES, 0.0254},
+ {ImporterUnits::FEET, 0.3048}
+ };
+
+ virtual void SetApplicationUnits( const ImporterUnits& unit )
+ {
+ importerScale = importerUnits[unit];
+ applicationUnits = unit;
+ }
+
+ virtual const ImporterUnits& GetApplicationUnits()
+ {
+ return applicationUnits;
+ }
+
// -------------------------------------------------------------------
/** Called by #Importer::GetExtensionList for each loaded importer.
* Take the extension list contained in the structure returned by
* #GetInfo and insert all file extensions into the given set.
* @param extension set to collect file extensions in*/
void GetExtensionList(std::set<std::string>& extensions);
+
+protected:
+ ImporterUnits applicationUnits = ImporterUnits::M;
+ double importerScale = 1.0;
+ double fileScale = 1.0;
+
-protected:
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure. The
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 <class char_t>
-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 <assimp/vector3.h>
+
+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 <b>relative</b> to the coordinate system defined
* by the node which corresponds to the camera. This allows for camera
* animations. For static cameras parameters like the 'look-at' or 'up' vectors
@@ -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<TReal>::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<TReal>::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..3a6379bf40 100644
--- a/thirdparty/assimp/include/assimp/config.h.in
+++ b/thirdparty/assimp/include/assimp/config.h.in
@@ -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"
@@ -980,6 +999,13 @@ enum aiComponent
# define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f
#endif // !! AI_DEBONE_THRESHOLD
+#define AI_CONFIG_APP_SCALE_KEY "APP_SCALE_FACTOR"
+
+#if (!defined AI_CONFIG_APP_SCALE_KEY)
+# define AI_CONFIG_APP_SCALE_KEY 1.0
+#endif // AI_CONFIG_APP_SCALE_KEY
+
+
// ---------- All the Build/Compile-time defines ------------
/** @brief Specifies if double precision is supported inside assimp
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/irrXMLWrapper.h b/thirdparty/assimp/include/assimp/irrXMLWrapper.h
deleted file mode 100644
index ec8ee7c76e..0000000000
--- a/thirdparty/assimp/include/assimp/irrXMLWrapper.h
+++ /dev/null
@@ -1,144 +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 INCLUDED_AI_IRRXML_WRAPPER
-#define INCLUDED_AI_IRRXML_WRAPPER
-
-// some long includes ....
-#include <irrXML.h>
-#include "IOStream.hpp"
-#include "BaseImporter.h"
-#include <vector>
-
-namespace Assimp {
-
-// ---------------------------------------------------------------------------------
-/** @brief Utility class to make IrrXML work together with our custom IO system
- * See the IrrXML docs for more details.
- *
- * Construct IrrXML-Reader in BaseImporter::InternReadFile():
- * @code
- * // open the file
- * std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
- * if( file.get() == NULL) {
- * throw DeadlyImportError( "Failed to open file " + pFile + ".");
- * }
- *
- * // generate a XML reader for it
- * std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper( new CIrrXML_IOStreamReader( file.get()));
- * mReader = irr::io::createIrrXMLReader( mIOWrapper.get());
- * if( !mReader) {
- * ThrowException( "xxxx: Unable to open file.");
- * }
- * @endcode
- **/
-class CIrrXML_IOStreamReader : public irr::io::IFileReadCallBack {
-public:
-
- // ----------------------------------------------------------------------------------
- //! Construction from an existing IOStream
- explicit CIrrXML_IOStreamReader(IOStream* _stream)
- : stream (_stream)
- , t (0)
- {
-
- // Map the buffer into memory and convert it to UTF8. IrrXML provides its
- // own conversion, which is merely a cast from uintNN_t to uint8_t. Thus,
- // it is not suitable for our purposes and we have to do it BEFORE IrrXML
- // gets the buffer. Sadly, this forces us to map the whole file into
- // memory.
-
- data.resize(stream->FileSize());
- stream->Read(&data[0],data.size(),1);
-
- // Remove null characters from the input sequence otherwise the parsing will utterly fail
- unsigned int size = 0;
- unsigned int size_max = static_cast<unsigned int>(data.size());
- for(unsigned int i = 0; i < size_max; i++) {
- if(data[i] != '\0') {
- data[size++] = data[i];
- }
- }
- data.resize(size);
-
- BaseImporter::ConvertToUTF8(data);
- }
-
- // ----------------------------------------------------------------------------------
- //! Virtual destructor
- virtual ~CIrrXML_IOStreamReader() {}
-
- // ----------------------------------------------------------------------------------
- //! Reads an amount of bytes from the file.
- /** @param buffer: Pointer to output buffer.
- * @param sizeToRead: Amount of bytes to read
- * @return Returns how much bytes were read. */
- virtual int read(void* buffer, int sizeToRead) {
- if(sizeToRead<0) {
- return 0;
- }
- if(t+sizeToRead>data.size()) {
- sizeToRead = static_cast<int>(data.size()-t);
- }
-
- memcpy(buffer,&data.front()+t,sizeToRead);
-
- t += sizeToRead;
- return sizeToRead;
- }
-
- // ----------------------------------------------------------------------------------
- //! Returns size of file in bytes
- virtual int getSize() {
- return (int)data.size();
- }
-
-private:
- IOStream* stream;
- std::vector<char> data;
- size_t t;
-
-}; // ! class CIrrXML_IOStreamReader
-
-} // ! Assimp
-
-#endif // !! INCLUDED_AI_IRRXML_WRAPPER
diff --git a/thirdparty/assimp/include/assimp/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 <assimp/types.h>
+#include <assimp/aabb.h>
#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,
// -------------------------------------------------------------------------
- /** <hr>A postprocessing step to reduce the number of meshes.
+ /** <hr>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
// -------------------------------------------------------------------------
- /** <hr>A postprocessing step to optimize the scene hierarchy.
+ /** <hr>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
// -------------------------------------------------------------------------
/** <hr>This step splits meshes with many bones into sub-meshes so that each
- * su-bmesh has fewer or as many bones as a given limit.
+ * 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 <tt>#AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY</tt> to setup the global scaing factor.
+ * Use <tt>#AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY</tt> 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..2667db85b3 100644
--- a/thirdparty/assimp/include/assimp/scene.h
+++ b/thirdparty/assimp/include/assimp/scene.h
@@ -58,6 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "metadata.h"
#ifdef __cplusplus
+# include <cstdlib>
extern "C" {
#endif
@@ -389,6 +390,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<unsigned>(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());
diff --git a/thirdparty/jpeg-compressor/jpgd.cpp b/thirdparty/jpeg-compressor/jpgd.cpp
index fad9a37a9a..62fbd1b72d 100644
--- a/thirdparty/jpeg-compressor/jpgd.cpp
+++ b/thirdparty/jpeg-compressor/jpgd.cpp
@@ -29,6 +29,10 @@
#define JPGD_MAX(a,b) (((a)>(b)) ? (a) : (b))
#define JPGD_MIN(a,b) (((a)<(b)) ? (a) : (b))
+// TODO: Move to header and use these constants when declaring the arrays.
+#define JPGD_HUFF_TREE_MAX_LENGTH 512
+#define JPGD_HUFF_CODE_SIZE_MAX_LENGTH 256
+
namespace jpgd {
static inline void *jpgd_malloc(size_t nSize) { return malloc(nSize); }
@@ -491,8 +495,9 @@ inline uint jpeg_decoder::get_bits_no_markers(int num_bits)
// Decodes a Huffman encoded symbol.
inline int jpeg_decoder::huff_decode(huff_tables *pH)
{
- int symbol;
+ JPGD_ASSERT(pH);
+ int symbol;
// Check first 8-bits: do we have a complete symbol?
if ((symbol = pH->look_up[m_bit_buf >> 24]) < 0)
{
@@ -500,14 +505,19 @@ inline int jpeg_decoder::huff_decode(huff_tables *pH)
int ofs = 23;
do
{
- symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))];
+ unsigned int idx = -(int)(symbol + ((m_bit_buf >> ofs) & 1));
+ JPGD_ASSERT(idx < JPGD_HUFF_TREE_MAX_LENGTH);
+ symbol = pH->tree[idx];
ofs--;
} while (symbol < 0);
get_bits_no_markers(8 + (23 - ofs));
}
else
+ {
+ JPGD_ASSERT(symbol < JPGD_HUFF_CODE_SIZE_MAX_LENGTH);
get_bits_no_markers(pH->code_size[symbol]);
+ }
return symbol;
}
@@ -517,6 +527,8 @@ inline int jpeg_decoder::huff_decode(huff_tables *pH, int& extra_bits)
{
int symbol;
+ JPGD_ASSERT(pH);
+
// Check first 8-bits: do we have a complete symbol?
if ((symbol = pH->look_up2[m_bit_buf >> 24]) < 0)
{
@@ -524,7 +536,9 @@ inline int jpeg_decoder::huff_decode(huff_tables *pH, int& extra_bits)
int ofs = 23;
do
{
- symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))];
+ unsigned int idx = -(int)(symbol + ((m_bit_buf >> ofs) & 1));
+ JPGD_ASSERT(idx < JPGD_HUFF_TREE_MAX_LENGTH);
+ symbol = pH->tree[idx];
ofs--;
} while (symbol < 0);
@@ -1495,6 +1509,12 @@ void jpeg_decoder::fix_in_buffer()
void jpeg_decoder::transform_mcu(int mcu_row)
{
jpgd_block_t* pSrc_ptr = m_pMCU_coefficients;
+ if (m_freq_domain_chroma_upsample) {
+ JPGD_ASSERT(mcu_row * m_blocks_per_mcu < m_expanded_blocks_per_row);
+ }
+ else {
+ JPGD_ASSERT(mcu_row * m_blocks_per_mcu < m_max_blocks_per_row);
+ }
uint8* pDst_ptr = m_pSample_buf + mcu_row * m_blocks_per_mcu * 64;
for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++)
@@ -1650,6 +1670,7 @@ void jpeg_decoder::load_next_row()
for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++)
{
component_id = m_mcu_org[mcu_block];
+ JPGD_ASSERT(m_comp_quant[component_id] < JPGD_MAX_QUANT_TABLES);
q = m_quant[m_comp_quant[component_id]];
p = m_pMCU_coefficients + 64 * mcu_block;
@@ -1770,6 +1791,7 @@ void jpeg_decoder::decode_next_row()
for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++, p += 64)
{
int component_id = m_mcu_org[mcu_block];
+ JPGD_ASSERT(m_comp_quant[component_id] < JPGD_MAX_QUANT_TABLES);
jpgd_quant_t* q = m_quant[m_comp_quant[component_id]];
int r, s;
@@ -2229,7 +2251,10 @@ void jpeg_decoder::make_huff_table(int index, huff_tables *pH)
for (l = 1; l <= 16; l++)
{
for (i = 1; i <= m_huff_num[index][l]; i++)
+ {
+ JPGD_ASSERT(p < 257);
huffsize[p++] = static_cast<uint8>(l);
+ }
}
huffsize[p] = 0;
@@ -2244,6 +2269,7 @@ void jpeg_decoder::make_huff_table(int index, huff_tables *pH)
{
while (huffsize[p] == si)
{
+ JPGD_ASSERT(p < 257);
huffcode[p++] = code;
code++;
}
@@ -2275,7 +2301,8 @@ void jpeg_decoder::make_huff_table(int index, huff_tables *pH)
for (l = 1 << (8 - code_size); l > 0; l--)
{
- JPGD_ASSERT(i < 256);
+ JPGD_ASSERT(i < JPGD_HUFF_CODE_SIZE_MAX_LENGTH);
+ JPGD_ASSERT(code < JPGD_HUFF_CODE_SIZE_MAX_LENGTH);
pH->look_up[code] = i;
@@ -2325,16 +2352,19 @@ void jpeg_decoder::make_huff_table(int index, huff_tables *pH)
if ((code & 0x8000) == 0)
currententry--;
- if (pH->tree[-currententry - 1] == 0)
+ unsigned int idx = -currententry - 1;
+ JPGD_ASSERT(idx < JPGD_HUFF_TREE_MAX_LENGTH);
+ if (pH->tree[idx] == 0)
{
- pH->tree[-currententry - 1] = nextfreeentry;
+ pH->tree[idx] = nextfreeentry;
currententry = nextfreeentry;
nextfreeentry -= 2;
}
- else
- currententry = pH->tree[-currententry - 1];
+ else {
+ currententry = pH->tree[idx];
+ }
code <<= 1;
}
@@ -2636,7 +2666,9 @@ void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int
for (k = pD->m_spectral_start; k <= pD->m_spectral_end; k++)
{
- s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]);
+ unsigned int idx = pD->m_comp_ac_tab[component_id];
+ JPGD_ASSERT(idx < JPGD_MAX_HUFF_TABLES);
+ s = pD->huff_decode(pD->m_pHuff_tabs[idx]);
r = s >> 4;
s &= 15;
@@ -2679,7 +2711,6 @@ void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, in
int p1 = 1 << pD->m_successive_low;
int m1 = (-1) << pD->m_successive_low;
jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
-
JPGD_ASSERT(pD->m_spectral_end <= 63);
k = pD->m_spectral_start;
@@ -2688,7 +2719,9 @@ void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, in
{
for ( ; k <= pD->m_spectral_end; k++)
{
- s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]);
+ unsigned int idx = pD->m_comp_ac_tab[component_id];
+ JPGD_ASSERT(idx < JPGD_MAX_HUFF_TABLES);
+ s = pD->huff_decode(pD->m_pHuff_tabs[idx]);
r = s >> 4;
s &= 15;
diff --git a/thirdparty/xatlas/avoid-failing-on-bad-geometry.patch b/thirdparty/xatlas/avoid-failing-on-bad-geometry.patch
deleted file mode 100644
index a28cd9f82b..0000000000
--- a/thirdparty/xatlas/avoid-failing-on-bad-geometry.patch
+++ /dev/null
@@ -1,157 +0,0 @@
-diff --git a/thirdparty/xatlas/xatlas.cpp b/thirdparty/xatlas/xatlas.cpp
-index df5ef94db..eb0824a51 100644
---- a/thirdparty/xatlas/xatlas.cpp
-+++ b/thirdparty/xatlas/xatlas.cpp
-@@ -1276,6 +1276,9 @@ class Vertex
- {
- public:
- uint32_t id;
-+ // -- GODOT start --
-+ uint32_t original_id;
-+ // -- GODOT end --
- Edge *edge;
- Vertex *next;
- Vertex *prev;
-@@ -1283,7 +1286,10 @@ public:
- Vector3 nor;
- Vector2 tex;
-
-- Vertex(uint32_t id) : id(id), edge(NULL), pos(0.0f), nor(0.0f), tex(0.0f)
-+ // -- GODOT start --
-+ //Vertex(uint32_t id) : id(id), edge(NULL), pos(0.0f), nor(0.0f), tex(0.0f)
-+ Vertex(uint32_t id) : id(id), original_id(id), edge(NULL), pos(0.0f), nor(0.0f), tex(0.0f)
-+ // -- GODOT end --
- {
- next = this;
- prev = this;
-@@ -1934,6 +1940,64 @@ public:
- return f;
- }
-
-+ // -- GODOT start --
-+ Face *addUniqueFace(uint32_t v0, uint32_t v1, uint32_t v2) {
-+
-+ int base_vertex = m_vertexArray.size();
-+
-+ uint32_t ids[3] = { v0, v1, v2 };
-+
-+ Vector3 base[3] = {
-+ m_vertexArray[v0]->pos,
-+ m_vertexArray[v1]->pos,
-+ m_vertexArray[v2]->pos,
-+ };
-+
-+ //make sure its not a degenerate
-+ bool degenerate = distanceSquared(base[0], base[1]) < NV_EPSILON || distanceSquared(base[0], base[2]) < NV_EPSILON || distanceSquared(base[1], base[2]) < NV_EPSILON;
-+ xaDebugAssert(!degenerate);
-+
-+ float min_x = 0;
-+
-+ for (int i = 0; i < 3; i++) {
-+ if (i == 0 || m_vertexArray[v0]->pos.x < min_x) {
-+ min_x = m_vertexArray[v0]->pos.x;
-+ }
-+ }
-+
-+ float max_x = 0;
-+
-+ for (int j = 0; j < m_vertexArray.size(); j++) {
-+ if (j == 0 || m_vertexArray[j]->pos.x > max_x) { //vertex already exists
-+ max_x = m_vertexArray[j]->pos.x;
-+ }
-+ }
-+
-+ //separate from everything else, in x axis
-+ for (int i = 0; i < 3; i++) {
-+
-+ base[i].x -= min_x;
-+ base[i].x += max_x + 10.0;
-+ }
-+
-+ for (int i = 0; i < 3; i++) {
-+ Vertex *v = new Vertex(m_vertexArray.size());
-+ v->pos = base[i];
-+ v->nor = m_vertexArray[ids[i]]->nor,
-+ v->tex = m_vertexArray[ids[i]]->tex,
-+
-+ v->original_id = ids[i];
-+ m_vertexArray.push_back(v);
-+ }
-+
-+ uint32_t indexArray[3];
-+ indexArray[0] = base_vertex + 0;
-+ indexArray[1] = base_vertex + 1;
-+ indexArray[2] = base_vertex + 2;
-+ return addFace(indexArray, 3, 0, 3);
-+ }
-+ // -- GODOT end --
-+
- // These functions disconnect the given element from the mesh and delete it.
-
- // @@ We must always disconnect edge pairs simultaneously.
-@@ -2915,6 +2979,14 @@ Mesh *triangulate(const Mesh *inputMesh)
- Vector2 p0 = polygonPoints[i0];
- Vector2 p1 = polygonPoints[i1];
- Vector2 p2 = polygonPoints[i2];
-+
-+ // -- GODOT start --
-+ bool degenerate = distance(p0, p1) < NV_EPSILON || distance(p0, p2) < NV_EPSILON || distance(p1, p2) < NV_EPSILON;
-+ if (degenerate) {
-+ continue;
-+ }
-+ // -- GODOT end --
-+
- float d = clamp(dot(p0 - p1, p2 - p1) / (length(p0 - p1) * length(p2 - p1)), -1.0f, 1.0f);
- float angle = acosf(d);
- float area = triangleArea(p0, p1, p2);
-@@ -2938,6 +3010,11 @@ Mesh *triangulate(const Mesh *inputMesh)
- }
- }
- }
-+ // -- GODOT start --
-+ if (!bestIsValid)
-+ break;
-+ // -- GODOT end --
-+
- xaDebugAssert(minAngle <= 2 * PI);
- // Clip best ear:
- uint32_t i0 = (bestEar + size - 1) % size;
-@@ -5606,7 +5683,10 @@ public:
- }
- if (chartMeshIndices[vertex->id] == ~0) {
- chartMeshIndices[vertex->id] = m_chartMesh->vertexCount();
-- m_chartToOriginalMap.push_back(vertex->id);
-+ // -- GODOT start --
-+ //m_chartToOriginalMap.push_back(vertex->id);
-+ m_chartToOriginalMap.push_back(vertex->original_id);
-+ // -- GODOT end --
- m_chartToUnifiedMap.push_back(unifiedMeshIndices[unifiedVertex->id]);
- halfedge::Vertex *v = m_chartMesh->addVertex(vertex->pos);
- v->nor = vertex->nor;
-@@ -5699,7 +5779,10 @@ public:
- const halfedge::Vertex *vertex = it.current()->vertex;
- if (chartMeshIndices[vertex->id] == ~0) {
- chartMeshIndices[vertex->id] = m_chartMesh->vertexCount();
-- m_chartToOriginalMap.push_back(vertex->id);
-+ // -- GODOT start --
-+ //m_chartToOriginalMap.push_back(vertex->id);
-+ m_chartToOriginalMap.push_back(vertex->original_id);
-+ // -- GODOT end --
- halfedge::Vertex *v = m_chartMesh->addVertex(vertex->pos);
- v->nor = vertex->nor;
- v->tex = vertex->tex; // @@ Not necessary.
-@@ -7573,6 +7656,14 @@ AddMeshError AddMesh(Atlas *atlas, const InputMesh &mesh, bool useColocalVertice
- }
- }
- internal::halfedge::Face *face = heMesh->addFace(tri[0], tri[1], tri[2]);
-+
-+ // -- GODOT start --
-+ if (!face && heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::AlreadyAddedEdge) {
-+ //there is still hope for this, no reason to not add, at least add as separate
-+ face = heMesh->addUniqueFace(tri[0], tri[1], tri[2]);
-+ }
-+ // -- GODOT end --
-+
- if (!face) {
- if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::AlreadyAddedEdge)
- error.code = AddMeshErrorCode::AlreadyAddedEdge;
diff --git a/thirdparty/xatlas/build-fix-limits.patch b/thirdparty/xatlas/build-fix-limits.patch
deleted file mode 100644
index 00d07371c0..0000000000
--- a/thirdparty/xatlas/build-fix-limits.patch
+++ /dev/null
@@ -1,14 +0,0 @@
-diff --git a/thirdparty/xatlas/xatlas.h b/thirdparty/xatlas/xatlas.h
-index 7e556c6c3..dbf8ca08c 100644
---- a/thirdparty/xatlas/xatlas.h
-+++ b/thirdparty/xatlas/xatlas.h
-@@ -3,6 +3,9 @@
- #ifndef XATLAS_H
- #define XATLAS_H
- #include <float.h> // FLT_MAX
-+// -- GODOT start --
-+#include <limits.h> // INT_MAX, UINT_MAX
-+// -- GODOT end --
-
- namespace xatlas {
-
diff --git a/thirdparty/xatlas/xatlas.cpp b/thirdparty/xatlas/xatlas.cpp
index 1b30305cd4..56794211a6 100644
--- a/thirdparty/xatlas/xatlas.cpp
+++ b/thirdparty/xatlas/xatlas.cpp
@@ -93,8 +93,24 @@ Copyright (c) 2012 Brandon Pelfrey
#define XA_ALLOC(tag, type) (type *)internal::Realloc(nullptr, sizeof(type), tag, __FILE__, __LINE__)
#define XA_ALLOC_ARRAY(tag, type, num) (type *)internal::Realloc(nullptr, sizeof(type) * num, tag, __FILE__, __LINE__)
#define XA_REALLOC(tag, ptr, type, num) (type *)internal::Realloc(ptr, sizeof(type) * num, tag, __FILE__, __LINE__)
+#define XA_REALLOC_SIZE(tag, ptr, size) (uint8_t *)internal::Realloc(ptr, size, tag, __FILE__, __LINE__)
#define XA_FREE(ptr) internal::Realloc(ptr, 0, internal::MemTag::Default, __FILE__, __LINE__)
-#define XA_NEW(tag, type, ...) new (XA_ALLOC(tag, type)) type(__VA_ARGS__)
+#define XA_NEW(tag, type) new (XA_ALLOC(tag, type)) type()
+#define XA_NEW_ARGS(tag, type, ...) new (XA_ALLOC(tag, type)) type(__VA_ARGS__)
+
+#ifdef _MSC_VER
+#define XA_INLINE __forceinline
+#else
+#define XA_INLINE inline
+#endif
+
+#if defined(__clang__) || defined(__GNUC__)
+#define XA_NODISCARD [[nodiscard]]
+#elif defined(_MSC_VER)
+#define XA_NODISCARD _Check_return_
+#else
+#define XA_NODISCARD
+#endif
#define XA_UNUSED(a) ((void)(a))
@@ -102,6 +118,7 @@ Copyright (c) 2012 Brandon Pelfrey
#define XA_MERGE_CHARTS 1
#define XA_MERGE_CHARTS_MIN_NORMAL_DEVIATION 0.5f
#define XA_RECOMPUTE_CHARTS 1
+#define XA_SKIP_PARAMETERIZATION 0 // Use the orthogonal parameterization from segment::Atlas
#define XA_CLOSE_HOLES_CHECK_EDGE_INTERSECTION 0
#define XA_DEBUG_HEAP 0
@@ -140,6 +157,7 @@ namespace xatlas {
namespace internal {
static ReallocFunc s_realloc = realloc;
+static FreeFunc s_free = free;
static PrintFunc s_print = printf;
static bool s_printVerbose = false;
@@ -167,6 +185,7 @@ struct AllocHeader
const char *file;
int line;
int tag;
+ uint32_t id;
AllocHeader *prev, *next;
bool free;
};
@@ -174,6 +193,7 @@ struct AllocHeader
static std::mutex s_allocMutex;
static AllocHeader *s_allocRoot = nullptr;
static size_t s_allocTotalSize = 0, s_allocPeakSize = 0, s_allocTotalTagSize[MemTag::Count] = { 0 }, s_allocPeakTagSize[MemTag::Count] = { 0 };
+static uint32_t s_allocId =0 ;
static constexpr uint32_t kAllocRedzone = 0x12345678;
static void *Realloc(void *ptr, size_t size, int tag, const char *file, int line)
@@ -214,6 +234,7 @@ static void *Realloc(void *ptr, size_t size, int tag, const char *file, int line
header->file = file;
header->line = line;
header->tag = tag;
+ header->id = s_allocId++;
header->free = false;
if (!s_allocRoot) {
s_allocRoot = header;
@@ -242,7 +263,7 @@ static void ReportLeaks()
AllocHeader *header = s_allocRoot;
while (header) {
if (!header->free) {
- printf(" Leak: %zu bytes %s %d\n", header->size, header->file, header->line);
+ printf(" Leak: ID %u, %zu bytes, %s %d\n", header->id, header->size, header->file, header->line);
anyLeaks = true;
}
auto redzone = (const uint32_t *)((const uint8_t *)header + header->size - sizeof(kAllocRedzone));
@@ -287,6 +308,10 @@ static void PrintMemoryUsage()
#else
static void *Realloc(void *ptr, size_t size, int /*tag*/, const char * /*file*/, int /*line*/)
{
+ if (ptr && size == 0 && s_free) {
+ s_free(ptr);
+ return nullptr;
+ }
void *mem = s_realloc(ptr, size);
if (size > 0) {
XA_DEBUG_ASSERT(mem);
@@ -304,6 +329,7 @@ static void *Realloc(void *ptr, size_t size, int /*tag*/, const char * /*file*/,
struct ProfileData
{
clock_t addMeshReal;
+ clock_t addMeshCopyData;
std::atomic<clock_t> addMeshThread;
std::atomic<clock_t> addMeshCreateColocals;
std::atomic<clock_t> addMeshCreateFaceGroups;
@@ -312,11 +338,14 @@ struct ProfileData
std::atomic<clock_t> addMeshCreateChartGroupsThread;
clock_t computeChartsReal;
std::atomic<clock_t> computeChartsThread;
- std::atomic<clock_t> atlasBuilder;
- std::atomic<clock_t> atlasBuilderInit;
- std::atomic<clock_t> atlasBuilderCreateInitialCharts;
- std::atomic<clock_t> atlasBuilderGrowCharts;
- std::atomic<clock_t> atlasBuilderMergeCharts;
+ std::atomic<clock_t> buildAtlas;
+ std::atomic<clock_t> buildAtlasInit;
+ std::atomic<clock_t> buildAtlasPlaceSeeds;
+ std::atomic<clock_t> buildAtlasRelocateSeeds;
+ std::atomic<clock_t> buildAtlasResetCharts;
+ std::atomic<clock_t> buildAtlasGrowCharts;
+ std::atomic<clock_t> buildAtlasMergeCharts;
+ std::atomic<clock_t> buildAtlasFillHoles;
std::atomic<clock_t> createChartMeshesReal;
std::atomic<clock_t> createChartMeshesThread;
std::atomic<clock_t> fixChartMeshTJunctions;
@@ -327,11 +356,15 @@ struct ProfileData
std::atomic<clock_t> parameterizeChartsLSCM;
std::atomic<clock_t> parameterizeChartsEvaluateQuality;
clock_t packCharts;
+ clock_t packChartsAddCharts;
+ std::atomic<clock_t> packChartsAddChartsThread;
+ std::atomic<clock_t> packChartsAddChartsRestoreTexcoords;
clock_t packChartsRasterize;
clock_t packChartsDilate;
clock_t packChartsFindLocation;
std::atomic<clock_t> packChartsFindLocationThread;
clock_t packChartsBlit;
+ clock_t buildOutputMeshes;
};
static ProfileData s_profile;
@@ -540,10 +573,10 @@ static bool operator!=(const Vector2 &a, const Vector2 &b)
return a.x != b.x || a.y != b.y;
}
-static Vector2 operator+(const Vector2 &a, const Vector2 &b)
+/*static Vector2 operator+(const Vector2 &a, const Vector2 &b)
{
return Vector2(a.x + b.x, a.y + b.y);
-}
+}*/
static Vector2 operator-(const Vector2 &a, const Vector2 &b)
{
@@ -738,11 +771,6 @@ static Vector3 operator*(const Vector3 &v, float s)
return Vector3(v.x * s, v.y * s, v.z * s);
}
-static Vector3 operator*(float s, const Vector3 &v)
-{
- return Vector3(v.x * s, v.y * s, v.z * s);
-}
-
static Vector3 operator/(const Vector3 &v, float s)
{
return v * (1.0f / s);
@@ -949,282 +977,202 @@ struct AABB
Vector3 min, max;
};
-template <typename T>
-static void construct_range(T * ptr, uint32_t new_size, uint32_t old_size) {
- for (uint32_t i = old_size; i < new_size; i++) {
- new(ptr+i) T; // placement new
- }
-}
-
-template <typename T>
-static void construct_range(T * ptr, uint32_t new_size, uint32_t old_size, const T & elem) {
- for (uint32_t i = old_size; i < new_size; i++) {
- new(ptr+i) T(elem); // placement new
- }
-}
-
-template <typename T>
-static void construct_range(T * ptr, uint32_t new_size, uint32_t old_size, const T * src) {
- for (uint32_t i = old_size; i < new_size; i++) {
- new(ptr+i) T(src[i]); // placement new
- }
-}
-
-template <typename T>
-static void destroy_range(T * ptr, uint32_t new_size, uint32_t old_size) {
- for (uint32_t i = new_size; i < old_size; i++) {
- (ptr+i)->~T(); // Explicit call to the destructor
- }
-}
-
-/**
-* Replacement for std::vector that is easier to debug and provides
-* some nice foreach enumerators.
-*/
-template<typename T>
-class Array {
-public:
- typedef uint32_t size_type;
-
- Array(int memTag = MemTag::Default) : m_memTag(memTag), m_buffer(nullptr), m_capacity(0), m_size(0) {}
+struct ArrayBase
+{
+ ArrayBase(uint32_t elementSize, int memTag = MemTag::Default) : buffer(nullptr), elementSize(elementSize), size(0), capacity(0), memTag(memTag) {}
- Array(const Array &a) : m_memTag(a.m_memTag), m_buffer(nullptr), m_capacity(0), m_size(0)
+ ~ArrayBase()
{
- copy(a.m_buffer, a.m_size);
+ XA_FREE(buffer);
}
- ~Array()
+ XA_INLINE void clear()
{
- destroy();
+ size = 0;
}
- const Array<T> &operator=(const Array<T> &other)
+ void copyTo(ArrayBase &other) const
{
- m_memTag = other.m_memTag;
- m_buffer = other.m_buffer;
- m_capacity = other.m_capacity;
- m_size = other.m_size;
- return *this;
+ XA_DEBUG_ASSERT(elementSize == other.elementSize);
+ other.resize(size, true);
+ memcpy(other.buffer, buffer, size * elementSize);
}
- const T & operator[]( uint32_t index ) const
- {
- XA_DEBUG_ASSERT(index < m_size);
- return m_buffer[index];
- }
-
- T & operator[] ( uint32_t index )
+ void destroy()
{
- XA_DEBUG_ASSERT(index < m_size);
- return m_buffer[index];
+ size = 0;
+ XA_FREE(buffer);
+ buffer = nullptr;
+ capacity = 0;
+ size = 0;
}
- uint32_t size() const { return m_size; }
- const T * data() const { return m_buffer; }
- T * data() { return m_buffer; }
- T * begin() { return m_buffer; }
- T * end() { return m_buffer + m_size; }
- const T * begin() const { return m_buffer; }
- const T * end() const { return m_buffer + m_size; }
- bool isEmpty() const { return m_size == 0; }
-
- void push_back( const T & val )
+ // Insert the given element at the given index shifting all the elements up.
+ void insertAt(uint32_t index, const uint8_t *value)
{
- XA_DEBUG_ASSERT(&val < m_buffer || &val >= m_buffer+m_size);
- uint32_t old_size = m_size;
- uint32_t new_size = m_size + 1;
- setArraySize(new_size);
- construct_range(m_buffer, new_size, old_size, val);
+ XA_DEBUG_ASSERT(index >= 0 && index <= size);
+ resize(size + 1, false);
+ if (index < size - 1)
+ memmove(buffer + elementSize * (index + 1), buffer + elementSize * index, elementSize * (size - 1 - index));
+ memcpy(&buffer[index * elementSize], value, elementSize);
}
- void pop_back()
+ void moveTo(ArrayBase &other)
{
- XA_DEBUG_ASSERT( m_size > 0 );
- resize( m_size - 1 );
+ XA_DEBUG_ASSERT(elementSize == other.elementSize);
+ other.destroy();
+ other.buffer = buffer;
+ other.elementSize = elementSize;
+ other.size = size;
+ other.capacity = capacity;
+ other.memTag = memTag;
+ buffer = nullptr;
+ elementSize = size = capacity = 0;
}
- const T & back() const
+ void pop_back()
{
- XA_DEBUG_ASSERT( m_size > 0 );
- return m_buffer[m_size-1];
+ XA_DEBUG_ASSERT(size > 0);
+ resize(size - 1, false);
}
- T & back()
+ void push_back(const uint8_t *value)
{
- XA_DEBUG_ASSERT( m_size > 0 );
- return m_buffer[m_size-1];
+ XA_DEBUG_ASSERT(value < buffer || value >= buffer + size);
+ resize(size + 1, false);
+ memcpy(&buffer[(size - 1) * elementSize], value, elementSize);
}
- const T & front() const
+ // Remove the element at the given index. This is an expensive operation!
+ void removeAt(uint32_t index)
{
- XA_DEBUG_ASSERT( m_size > 0 );
- return m_buffer[0];
+ XA_DEBUG_ASSERT(index >= 0 && index < size);
+ if (size != 1)
+ memmove(buffer + elementSize * index, buffer + elementSize * (index + 1), elementSize * (size - 1 - index));
+ size--;
}
- T & front()
+ void reserve(uint32_t desiredSize)
{
- XA_DEBUG_ASSERT( m_size > 0 );
- return m_buffer[0];
+ if (desiredSize > capacity)
+ setArrayCapacity(desiredSize);
}
- // Remove the element at the given index. This is an expensive operation!
- void removeAt(uint32_t index)
+ void resize(uint32_t newSize, bool exact)
{
- XA_DEBUG_ASSERT(index >= 0 && index < m_size);
- if (m_size == 1) {
- clear();
- }
- else {
- m_buffer[index].~T();
- memmove(m_buffer+index, m_buffer+index+1, sizeof(T) * (m_size - 1 - index));
- m_size--;
+ size = newSize;
+ if (size > capacity) {
+ // First allocation is always exact. Otherwise, following allocations grow array to 150% of desired size.
+ uint32_t newBufferSize;
+ if (capacity == 0 || exact)
+ newBufferSize = size;
+ else
+ newBufferSize = size + (size >> 2);
+ setArrayCapacity(newBufferSize);
}
}
- // Insert the given element at the given index shifting all the elements up.
- void insertAt(uint32_t index, const T & val = T())
+ void setArrayCapacity(uint32_t newCapacity)
{
- XA_DEBUG_ASSERT( index >= 0 && index <= m_size );
- setArraySize(m_size + 1);
- if (index < m_size - 1) {
- memmove(m_buffer+index+1, m_buffer+index, sizeof(T) * (m_size - 1 - index));
+ XA_DEBUG_ASSERT(newCapacity >= size);
+ if (newCapacity == 0) {
+ // free the buffer.
+ if (buffer != nullptr) {
+ XA_FREE(buffer);
+ buffer = nullptr;
+ }
+ } else {
+ // realloc the buffer
+ buffer = XA_REALLOC_SIZE(memTag, buffer, newCapacity * elementSize);
}
- // Copy-construct into the newly opened slot.
- new(m_buffer+index) T(val);
- }
-
- void append(const Array<T> & other)
- {
- append(other.m_buffer, other.m_size);
+ capacity = newCapacity;
}
- void resize(uint32_t new_size)
- {
- uint32_t old_size = m_size;
- // Destruct old elements (if we're shrinking).
- destroy_range(m_buffer, new_size, old_size);
- setArraySize(new_size);
- // Call default constructors
- construct_range(m_buffer, new_size, old_size);
- }
+ uint8_t *buffer;
+ uint32_t elementSize;
+ uint32_t size;
+ uint32_t capacity;
+ int memTag;
+};
- void resize(uint32_t new_size, const T & elem)
- {
- XA_DEBUG_ASSERT(&elem < m_buffer || &elem > m_buffer+m_size);
- uint32_t old_size = m_size;
- // Destruct old elements (if we're shrinking).
- destroy_range(m_buffer, new_size, old_size);
- setArraySize(new_size);
- // Call copy constructors
- construct_range(m_buffer, new_size, old_size, elem);
- }
+template<typename T>
+class Array
+{
+public:
+ Array(int memTag = MemTag::Default) : m_base(sizeof(T), memTag) {}
+ Array(const Array&) = delete;
+ const Array &operator=(const Array &) = delete;
- void clear()
+ XA_INLINE const T &operator[](uint32_t index) const
{
- // Destruct old elements
- destroy_range(m_buffer, 0, m_size);
- m_size = 0;
+ XA_DEBUG_ASSERT(index < m_base.size);
+ return ((const T *)m_base.buffer)[index];
}
- void destroy()
+ XA_INLINE T &operator[](uint32_t index)
{
- clear();
- XA_FREE(m_buffer);
- m_buffer = nullptr;
- m_capacity = 0;
- m_size = 0;
+ XA_DEBUG_ASSERT(index < m_base.size);
+ return ((T *)m_base.buffer)[index];
}
- void reserve(uint32_t desired_size)
+ XA_INLINE const T &back() const
{
- if (desired_size > m_capacity) {
- setArrayCapacity(desired_size);
- }
+ XA_DEBUG_ASSERT(!isEmpty());
+ return ((const T *)m_base.buffer)[m_base.size - 1];
}
- void copy(const T * data, uint32_t count)
- {
- destroy_range(m_buffer, 0, m_size);
- setArraySize(count);
- construct_range(m_buffer, count, 0, data);
- }
+ XA_INLINE T *begin() { return (T *)m_base.buffer; }
+ XA_INLINE void clear() { m_base.clear(); }
+ void copyTo(Array &other) const { m_base.copyTo(other.m_base); }
+ XA_INLINE const T *data() const { return (const T *)m_base.buffer; }
+ XA_INLINE T *data() { return (T *)m_base.buffer; }
+ XA_INLINE T *end() { return (T *)m_base.buffer + m_base.size; }
+ XA_INLINE bool isEmpty() const { return m_base.size == 0; }
+ void insertAt(uint32_t index, const T &value) { m_base.insertAt(index, (const uint8_t *)&value); }
+ void moveTo(Array &other) { m_base.moveTo(other.m_base); }
+ void push_back(const T &value) { m_base.push_back((const uint8_t *)&value); }
+ void pop_back() { m_base.pop_back(); }
+ void removeAt(uint32_t index) { m_base.removeAt(index); }
+ void reserve(uint32_t desiredSize) { m_base.reserve(desiredSize); }
+ void resize(uint32_t newSize) { m_base.resize(newSize, true); }
- void moveTo(Array<T> &other)
+ void setAll(const T &value)
{
- other.destroy();
- swap(m_buffer, other.m_buffer);
- swap(m_capacity, other.m_capacity);
- swap(m_size, other.m_size);
+ auto buffer = (T *)m_base.buffer;
+ for (uint32_t i = 0; i < m_base.size; i++)
+ buffer[i] = value;
}
-protected:
- void setArraySize(uint32_t new_size)
- {
- m_size = new_size;
- if (new_size > m_capacity) {
- uint32_t new_buffer_size;
- if (m_capacity == 0) {
- // first allocation is exact
- new_buffer_size = new_size;
- }
- else {
- // following allocations grow array by 25%
- new_buffer_size = new_size + (new_size >> 2);
- }
- setArrayCapacity( new_buffer_size );
- }
- }
- void setArrayCapacity(uint32_t new_capacity)
- {
- XA_DEBUG_ASSERT(new_capacity >= m_size);
- if (new_capacity == 0) {
- // free the buffer.
- if (m_buffer != nullptr) {
- XA_FREE(m_buffer);
- m_buffer = nullptr;
- }
- }
- else {
- // realloc the buffer
- m_buffer = XA_REALLOC(m_memTag, m_buffer, T, new_capacity);
- }
- m_capacity = new_capacity;
- }
+ XA_INLINE uint32_t size() const { return m_base.size; }
+ XA_INLINE void zeroOutMemory() { memset(m_base.buffer, 0, m_base.elementSize * m_base.size); }
- int m_memTag;
- T * m_buffer;
- uint32_t m_capacity;
- uint32_t m_size;
+private:
+ ArrayBase m_base;
};
-/// Basis class to compute tangent space basis, ortogonalizations and to
-/// transform vectors from one space to another.
+/// Basis class to compute tangent space basis, ortogonalizations and to transform vectors from one space to another.
struct Basis
{
- void buildFrameForDirection(const Vector3 &d, float angle = 0)
+ XA_NODISCARD static Vector3 computeTangent(const Vector3 &normal)
{
- XA_ASSERT(isNormalized(d));
- normal = d;
+ XA_ASSERT(isNormalized(normal));
// Choose minimum axis.
- if (fabsf(normal.x) < fabsf(normal.y) && fabsf(normal.x) < fabsf(normal.z)) {
+ Vector3 tangent;
+ if (fabsf(normal.x) < fabsf(normal.y) && fabsf(normal.x) < fabsf(normal.z))
tangent = Vector3(1, 0, 0);
- } else if (fabsf(normal.y) < fabsf(normal.z)) {
+ else if (fabsf(normal.y) < fabsf(normal.z))
tangent = Vector3(0, 1, 0);
- } else {
+ else
tangent = Vector3(0, 0, 1);
- }
// Ortogonalize
tangent -= normal * dot(normal, tangent);
tangent = normalize(tangent, kEpsilon);
- bitangent = cross(normal, tangent);
- // Rotate frame around normal according to angle.
- if (angle != 0.0f) {
- float c = cosf(angle);
- float s = sinf(angle);
- Vector3 tmp = c * tangent - s * bitangent;
- bitangent = s * tangent + c * bitangent;
- tangent = tmp;
- }
+ return tangent;
+ }
+
+ XA_NODISCARD static Vector3 computeBitangent(const Vector3 &normal, const Vector3 &tangent)
+ {
+ return cross(normal, tangent);
}
Vector3 tangent = Vector3(0.0f);
@@ -1246,7 +1194,7 @@ public:
void resize(uint32_t new_size)
{
m_size = new_size;
- m_wordArray.resize( (m_size + 31) >> 5 );
+ m_wordArray.resize((m_size + 31) >> 5);
}
/// Get bit.
@@ -1286,35 +1234,28 @@ public:
{
m_rowStride = (m_width + 63) >> 6;
m_data.resize(m_rowStride * m_height);
+ m_data.zeroOutMemory();
}
- BitImage(const BitImage &other)
- {
- m_width = other.m_width;
- m_height = other.m_height;
- m_rowStride = other.m_rowStride;
- m_data.resize(m_rowStride * m_height);
- memcpy(m_data.data(), other.m_data.data(), m_rowStride * m_height * sizeof(uint64_t));
- }
+ BitImage(const BitImage &other) = delete;
+ const BitImage &operator=(const BitImage &other) = delete;
+ uint32_t width() const { return m_width; }
+ uint32_t height() const { return m_height; }
- const BitImage &operator=(const BitImage &other)
+ void copyTo(BitImage &other)
{
- m_width = other.m_width;
- m_height = other.m_height;
- m_rowStride = other.m_rowStride;
- m_data = other.m_data;
- return *this;
+ other.m_width = m_width;
+ other.m_height = m_height;
+ other.m_rowStride = m_rowStride;
+ m_data.copyTo(other.m_data);
}
- uint32_t width() const { return m_width; }
- uint32_t height() const { return m_height; }
-
void resize(uint32_t w, uint32_t h, bool discard)
{
const uint32_t rowStride = (w + 63) >> 6;
if (discard) {
m_data.resize(rowStride * h);
- memset(m_data.data(), 0, m_data.size() * sizeof(uint64_t));
+ m_data.zeroOutMemory();
} else {
Array<uint64_t> tmp;
tmp.resize(rowStride * h);
@@ -1351,7 +1292,7 @@ public:
void clearAll()
{
- memset(m_data.data(), 0, m_data.size() * sizeof(uint64_t));
+ m_data.zeroOutMemory();
}
bool canBlit(const BitImage &image, uint32_t offsetX, uint32_t offsetY) const
@@ -1405,7 +1346,7 @@ public:
tmp.setBitAt(x, y);
}
}
- swap(m_data, tmp.m_data);
+ tmp.m_data.copyTo(m_data);
}
}
@@ -1754,36 +1695,28 @@ class FullVector
{
public:
FullVector(uint32_t dim) { m_array.resize(dim); }
- FullVector(const FullVector &v) : m_array(v.m_array) {}
-
- const FullVector &operator=(const FullVector &v)
- {
- XA_ASSERT(dimension() == v.dimension());
- m_array = v.m_array;
- return *this;
- }
-
- uint32_t dimension() const { return m_array.size(); }
- const float &operator[]( uint32_t index ) const { return m_array[index]; }
- float &operator[] ( uint32_t index ) { return m_array[index]; }
+ FullVector(const FullVector &v) { v.m_array.copyTo(m_array); }
+ const FullVector &operator=(const FullVector &v) = delete;
+ XA_INLINE uint32_t dimension() const { return m_array.size(); }
+ XA_INLINE const float &operator[](uint32_t index) const { return m_array[index]; }
+ XA_INLINE float &operator[](uint32_t index) { return m_array[index]; }
void fill(float f)
{
const uint32_t dim = dimension();
- for (uint32_t i = 0; i < dim; i++) {
+ for (uint32_t i = 0; i < dim; i++)
m_array[i] = f;
- }
}
private:
Array<float> m_array;
};
-template<typename Key, typename Value, typename H = Hash<Key>, typename E = Equal<Key> >
+template<typename Key, typename H = Hash<Key>, typename E = Equal<Key> >
class HashMap
{
public:
- HashMap(int memTag, uint32_t size) : m_memTag(memTag), m_size(size), m_numSlots(0), m_slots(nullptr), m_keys(memTag), m_values(memTag), m_next(memTag)
+ HashMap(int memTag, uint32_t size) : m_memTag(memTag), m_size(size), m_numSlots(0), m_slots(nullptr), m_keys(memTag), m_next(memTag)
{
}
@@ -1793,15 +1726,12 @@ public:
XA_FREE(m_slots);
}
- const Value &value(uint32_t index) const { return m_values[index]; }
-
- void add(const Key &key, const Value &value)
+ void add(const Key &key)
{
if (!m_slots)
alloc();
const uint32_t hash = computeHash(key);
m_keys.push_back(key);
- m_values.push_back(value);
m_next.push_back(m_slots[hash]);
m_slots[hash] = m_next.size() - 1;
}
@@ -1842,7 +1772,6 @@ private:
for (uint32_t i = 0; i < m_numSlots; i++)
m_slots[i] = UINT32_MAX;
m_keys.reserve(m_size);
- m_values.reserve(m_size);
m_next.reserve(m_size);
}
@@ -1857,7 +1786,6 @@ private:
uint32_t m_numSlots;
uint32_t *m_slots;
Array<Key> m_keys;
- Array<Value> m_values;
Array<uint32_t> m_next;
};
@@ -2190,7 +2118,7 @@ private:
}
}
// Remove duplicate element.
- XA_DEBUG_ASSERT(output.front() == output.back());
+ XA_DEBUG_ASSERT(output.size() > 0);
output.pop_back();
}
@@ -2284,7 +2212,7 @@ public:
const EdgeKey key(vertex0, vertex1);
if (m_edgeMap.get(key) != UINT32_MAX)
result = AddFaceResult::DuplicateEdge;
- m_edgeMap.add(key, firstIndex + i);
+ m_edgeMap.add(key);
}
}
return result;
@@ -2301,7 +2229,9 @@ public:
Array<uint32_t> colocals;
Array<uint32_t> potential;
m_colocalVertexCount = 0;
- m_nextColocalVertex.resize(vertexCount, UINT32_MAX);
+ m_nextColocalVertex.resize(vertexCount);
+ for (uint32_t i = 0; i < vertexCount; i++)
+ m_nextColocalVertex[i] = UINT32_MAX;
for (uint32_t i = 0; i < vertexCount; i++) {
if (m_nextColocalVertex[i] != UINT32_MAX)
continue; // Already linked.
@@ -2467,12 +2397,10 @@ public:
void linkBoundaries()
{
const uint32_t edgeCount = m_indices.size();
- HashMap<uint32_t, uint32_t> vertexToEdgeMap(MemTag::Mesh, edgeCount);
+ HashMap<uint32_t> vertexToEdgeMap(MemTag::Mesh, edgeCount); // Edge is index / 2
for (uint32_t i = 0; i < edgeCount; i++) {
- const uint32_t vertex0 = m_indices[meshEdgeIndex0(i)];
- const uint32_t vertex1 = m_indices[meshEdgeIndex1(i)];
- vertexToEdgeMap.add(vertex0, i);
- vertexToEdgeMap.add(vertex1, i);
+ vertexToEdgeMap.add(m_indices[meshEdgeIndex0(i)]);
+ vertexToEdgeMap.add(m_indices[meshEdgeIndex1(i)]);
}
m_nextBoundaryEdges.resize(edgeCount);
for (uint32_t i = 0; i < edgeCount; i++)
@@ -2497,9 +2425,9 @@ public:
const uint32_t startVertex = m_indices[meshEdgeIndex1(currentEdge)];
uint32_t bestNextEdge = UINT32_MAX;
for (ColocalVertexIterator it(this, startVertex); !it.isDone(); it.advance()) {
- uint32_t mapOtherEdgeIndex = vertexToEdgeMap.get(it.vertex());
- while (mapOtherEdgeIndex != UINT32_MAX) {
- const uint32_t otherEdge = vertexToEdgeMap.value(mapOtherEdgeIndex);
+ uint32_t mapIndex = vertexToEdgeMap.get(it.vertex());
+ while (mapIndex != UINT32_MAX) {
+ const uint32_t otherEdge = mapIndex / 2; // Two vertices added per edge.
if (m_oppositeEdges[otherEdge] != UINT32_MAX)
goto next; // Not a boundary edge.
if (linkedEdges.bitAt(otherEdge))
@@ -2515,7 +2443,7 @@ public:
if (bestNextEdge != firstEdge && (bestNextEdge == UINT32_MAX || it.vertex() == startVertex))
bestNextEdge = otherEdge;
next:
- mapOtherEdgeIndex = vertexToEdgeMap.getNext(mapOtherEdgeIndex);
+ mapIndex = vertexToEdgeMap.getNext(mapIndex);
}
}
if (bestNextEdge == UINT32_MAX) {
@@ -2567,9 +2495,8 @@ public:
uint32_t result = UINT32_MAX;
if (m_nextColocalVertex.isEmpty()) {
EdgeKey key(vertex0, vertex1);
- uint32_t mapEdgeIndex = m_edgeMap.get(key);
- while (mapEdgeIndex != UINT32_MAX) {
- const uint32_t edge = m_edgeMap.value(mapEdgeIndex);
+ uint32_t edge = m_edgeMap.get(key);
+ while (edge != UINT32_MAX) {
// Don't find edges of ignored faces.
if ((faceGroup == UINT32_MAX || m_faceGroups[meshEdgeFace(edge)] == faceGroup) && !isFaceIgnored(meshEdgeFace(edge))) {
//XA_DEBUG_ASSERT(m_id != UINT32_MAX || (m_id == UINT32_MAX && result == UINT32_MAX)); // duplicate edge - ignore on initial meshes
@@ -2578,15 +2505,14 @@ public:
return result;
#endif
}
- mapEdgeIndex = m_edgeMap.getNext(mapEdgeIndex);
+ edge = m_edgeMap.getNext(edge);
}
} else {
for (ColocalVertexIterator it0(this, vertex0); !it0.isDone(); it0.advance()) {
for (ColocalVertexIterator it1(this, vertex1); !it1.isDone(); it1.advance()) {
EdgeKey key(it0.vertex(), it1.vertex());
- uint32_t mapEdgeIndex = m_edgeMap.get(key);
- while (mapEdgeIndex != UINT32_MAX) {
- const uint32_t edge = m_edgeMap.value(mapEdgeIndex);
+ uint32_t edge = m_edgeMap.get(key);
+ while (edge != UINT32_MAX) {
// Don't find edges of ignored faces.
if ((faceGroup == UINT32_MAX || m_faceGroups[meshEdgeFace(edge)] == faceGroup) && !isFaceIgnored(meshEdgeFace(edge))) {
XA_DEBUG_ASSERT(m_id != UINT32_MAX || (m_id == UINT32_MAX && result == UINT32_MAX)); // duplicate edge - ignore on initial meshes
@@ -2595,7 +2521,7 @@ public:
return result;
#endif
}
- mapEdgeIndex = m_edgeMap.getNext(mapEdgeIndex);
+ edge = m_edgeMap.getNext(edge);
}
}
}
@@ -2802,24 +2728,24 @@ public:
return false;
}
- float epsilon() const { return m_epsilon; }
- uint32_t edgeCount() const { return m_indices.size(); }
- uint32_t oppositeEdge(uint32_t edge) const { return m_oppositeEdges[edge]; }
- bool isBoundaryEdge(uint32_t edge) const { return m_oppositeEdges[edge] == UINT32_MAX; }
- bool isBoundaryVertex(uint32_t vertex) const { return m_boundaryVertices[vertex]; }
- uint32_t colocalVertexCount() const { return m_colocalVertexCount; }
- uint32_t vertexCount() const { return m_positions.size(); }
- uint32_t vertexAt(uint32_t i) const { return m_indices[i]; }
- const Vector3 &position(uint32_t vertex) const { return m_positions[vertex]; }
- const Vector3 &normal(uint32_t vertex) const { XA_DEBUG_ASSERT(m_flags & MeshFlags::HasNormals); return m_normals[vertex]; }
- const Vector2 &texcoord(uint32_t vertex) const { return m_texcoords[vertex]; }
- Vector2 &texcoord(uint32_t vertex) { return m_texcoords[vertex]; }
- Vector2 *texcoords() { return m_texcoords.data(); }
- uint32_t faceCount() const { return m_indices.size() / 3; }
- uint32_t faceGroupCount() const { XA_DEBUG_ASSERT(m_flags & MeshFlags::HasFaceGroups); return m_faceGroups.size(); }
- uint32_t faceGroupAt(uint32_t face) const { XA_DEBUG_ASSERT(m_flags & MeshFlags::HasFaceGroups); return m_faceGroups[face]; }
- const uint32_t *indices() const { return m_indices.data(); }
- uint32_t indexCount() const { return m_indices.size(); }
+ XA_INLINE float epsilon() const { return m_epsilon; }
+ XA_INLINE uint32_t edgeCount() const { return m_indices.size(); }
+ XA_INLINE uint32_t oppositeEdge(uint32_t edge) const { return m_oppositeEdges[edge]; }
+ XA_INLINE bool isBoundaryEdge(uint32_t edge) const { return m_oppositeEdges[edge] == UINT32_MAX; }
+ XA_INLINE bool isBoundaryVertex(uint32_t vertex) const { return m_boundaryVertices[vertex]; }
+ XA_INLINE uint32_t colocalVertexCount() const { return m_colocalVertexCount; }
+ XA_INLINE uint32_t vertexCount() const { return m_positions.size(); }
+ XA_INLINE uint32_t vertexAt(uint32_t i) const { return m_indices[i]; }
+ XA_INLINE const Vector3 &position(uint32_t vertex) const { return m_positions[vertex]; }
+ XA_INLINE const Vector3 &normal(uint32_t vertex) const { XA_DEBUG_ASSERT(m_flags & MeshFlags::HasNormals); return m_normals[vertex]; }
+ XA_INLINE const Vector2 &texcoord(uint32_t vertex) const { return m_texcoords[vertex]; }
+ XA_INLINE Vector2 &texcoord(uint32_t vertex) { return m_texcoords[vertex]; }
+ XA_INLINE Vector2 *texcoords() { return m_texcoords.data(); }
+ XA_INLINE uint32_t faceCount() const { return m_indices.size() / 3; }
+ XA_INLINE uint32_t faceGroupCount() const { XA_DEBUG_ASSERT(m_flags & MeshFlags::HasFaceGroups); return m_faceGroups.size(); }
+ XA_INLINE uint32_t faceGroupAt(uint32_t face) const { XA_DEBUG_ASSERT(m_flags & MeshFlags::HasFaceGroups); return m_faceGroups[face]; }
+ XA_INLINE const uint32_t *indices() const { return m_indices.data(); }
+ XA_INLINE uint32_t indexCount() const { return m_indices.size(); }
private:
bool isFaceIgnored(uint32_t face) const { return (m_flags & MeshFlags::HasIgnoredFaces) && m_faceIgnore[face]; }
@@ -2865,7 +2791,7 @@ private:
uint32_t v1;
};
- HashMap<EdgeKey, uint32_t> m_edgeMap;
+ HashMap<EdgeKey> m_edgeMap;
public:
class BoundaryEdgeIterator
@@ -2950,37 +2876,37 @@ public:
bool isDone() const
{
- return m_vertex0It.isDone() && m_vertex1It.isDone() && m_mapEdgeIndex == UINT32_MAX;
+ return m_vertex0It.isDone() && m_vertex1It.isDone() && m_edge == UINT32_MAX;
}
uint32_t edge() const
{
- return m_mesh->m_edgeMap.value(m_mapEdgeIndex);
+ return m_edge;
}
private:
void resetElement()
{
- m_mapEdgeIndex = m_mesh->m_edgeMap.get(Mesh::EdgeKey(m_vertex0It.vertex(), m_vertex1It.vertex()));
- while (m_mapEdgeIndex != UINT32_MAX) {
+ m_edge = m_mesh->m_edgeMap.get(Mesh::EdgeKey(m_vertex0It.vertex(), m_vertex1It.vertex()));
+ while (m_edge != UINT32_MAX) {
if (!isIgnoredFace())
break;
- m_mapEdgeIndex = m_mesh->m_edgeMap.getNext(m_mapEdgeIndex);
+ m_edge = m_mesh->m_edgeMap.getNext(m_edge);
}
- if (m_mapEdgeIndex == UINT32_MAX)
+ if (m_edge == UINT32_MAX)
advanceVertex1();
}
void advanceElement()
{
for (;;) {
- m_mapEdgeIndex = m_mesh->m_edgeMap.getNext(m_mapEdgeIndex);
- if (m_mapEdgeIndex == UINT32_MAX)
+ m_edge = m_mesh->m_edgeMap.getNext(m_edge);
+ if (m_edge == UINT32_MAX)
break;
if (!isIgnoredFace())
break;
}
- if (m_mapEdgeIndex == UINT32_MAX)
+ if (m_edge == UINT32_MAX)
advanceVertex1();
}
@@ -3004,14 +2930,13 @@ public:
bool isIgnoredFace() const
{
- const uint32_t edge = m_mesh->m_edgeMap.value(m_mapEdgeIndex);
- return m_mesh->m_faceIgnore[meshEdgeFace(edge)];
+ return m_mesh->m_faceIgnore[meshEdgeFace(m_edge)];
}
const Mesh *m_mesh;
ColocalVertexIterator m_vertex0It, m_vertex1It;
const uint32_t m_vertex1;
- uint32_t m_mapEdgeIndex;
+ uint32_t m_edge;
};
class FaceEdgeIterator
@@ -3334,7 +3259,7 @@ static Mesh *meshFixTJunctions(const Mesh &inputMesh, bool *duplicatedEdge, bool
if (splitEdges.isEmpty())
return nullptr;
const uint32_t faceCount = inputMesh.faceCount();
- Mesh *mesh = XA_NEW(MemTag::Mesh, Mesh, inputMesh.epsilon(), vertexCount + splitEdges.size(), faceCount);
+ Mesh *mesh = XA_NEW_ARGS(MemTag::Mesh, Mesh, inputMesh.epsilon(), vertexCount + splitEdges.size(), faceCount);
for (uint32_t v = 0; v < vertexCount; v++)
mesh->addVertex(inputMesh.position(v));
Array<uint32_t> indexArray;
@@ -3567,8 +3492,9 @@ public:
}
m_workers.resize(std::thread::hardware_concurrency() <= 1 ? 1 : std::thread::hardware_concurrency() - 1);
for (uint32_t i = 0; i < m_workers.size(); i++) {
+ new (&m_workers[i]) Worker();
m_workers[i].wakeup = false;
- m_workers[i].thread = XA_NEW(MemTag::Default, std::thread, workerThread, this, &m_workers[i]);
+ m_workers[i].thread = XA_NEW_ARGS(MemTag::Default, std::thread, workerThread, this, &m_workers[i]);
}
}
@@ -3584,6 +3510,7 @@ public:
worker.thread->join();
worker.thread->~thread();
XA_FREE(worker.thread);
+ worker.~Worker();
}
for (uint32_t i = 0; i < m_maxGroups; i++)
m_groups[i].~TaskGroup();
@@ -3770,6 +3697,7 @@ private:
struct UvMeshChart
{
+ Array<uint32_t> faces;
Array<uint32_t> indices;
uint32_t material;
};
@@ -3828,7 +3756,7 @@ public:
m_numVertices = p;
}
- void clipVerticalPlane(float offset, float clipdirection )
+ void clipVerticalPlane(float offset, float clipdirection)
{
Vector2 *v = m_vertexBuffers[m_activeVertexBuffer];
m_activeVertexBuffer ^= 1;
@@ -3877,7 +3805,7 @@ public:
computeArea();
}
- float area()
+ float area() const
{
return m_area;
}
@@ -3959,9 +3887,8 @@ struct Triangle
if ( (aC >= BK_INSIDE) && (bC >= BK_INSIDE) && (cC >= BK_INSIDE) ) {
for (float y = y0; y < y0 + BK_SIZE; y++) {
for (float x = x0; x < x0 + BK_SIZE; x++) {
- if (!cb(param, (int)x, (int)y)) {
+ if (!cb(param, (int)x, (int)y))
return false;
- }
}
}
} else { // Partially covered block
@@ -3974,17 +3901,15 @@ struct Triangle
float CX3 = CY3;
for (float x = x0; x < x0 + BK_SIZE; x++) { // @@ This is not clipping to scissor rectangle correctly.
if (CX1 >= PX_INSIDE && CX2 >= PX_INSIDE && CX3 >= PX_INSIDE) {
- if (!cb(param, (int)x, (int)y)) {
+ if (!cb(param, (int)x, (int)y))
return false;
- }
} else if ((CX1 >= PX_OUTSIDE) && (CX2 >= PX_OUTSIDE) && (CX3 >= PX_OUTSIDE)) {
// triangle partially covers pixel. do clipping.
ClippedTriangle ct(v1 - Vector2(x, y), v2 - Vector2(x, y), v3 - Vector2(x, y));
ct.clipAABox(-0.5, -0.5, 0.5, 0.5);
if (ct.area() > 0.0f) {
- if (!cb(param, (int)x, (int)y)) {
+ if (!cb(param, (int)x, (int)y))
return false;
- }
}
}
CX1 += n1.x;
@@ -4065,18 +3990,28 @@ public:
float v; // value
};
- Matrix(uint32_t d) : m_width(d) { m_array.resize(d); }
- Matrix(uint32_t w, uint32_t h) : m_width(w) { m_array.resize(h); }
- Matrix(const Matrix &m) : m_width(m.m_width) { m_array = m.m_array; }
-
- const Matrix &operator=(const Matrix &m)
+ Matrix(uint32_t d) : m_width(d)
{
- XA_ASSERT(width() == m.width());
- XA_ASSERT(height() == m.height());
- m_array = m.m_array;
- return *this;
+ m_array.resize(d);
+ for (uint32_t i = 0; i < m_array.size(); i++)
+ new (&m_array[i]) Array<Coefficient>();
+ }
+
+ Matrix(uint32_t w, uint32_t h) : m_width(w)
+ {
+ m_array.resize(h);
+ for (uint32_t i = 0; i < m_array.size(); i++)
+ new (&m_array[i]) Array<Coefficient>();
+ }
+
+ ~Matrix()
+ {
+ for (uint32_t i = 0; i < m_array.size(); i++)
+ m_array[i].~Array();
}
+ Matrix(const Matrix &m) = delete;
+ const Matrix &operator=(const Matrix &m) = delete;
uint32_t width() const { return m_width; }
uint32_t height() const { return m_array.size(); }
bool isSquare() const { return width() == height(); }
@@ -4274,428 +4209,7 @@ static void mult(const Matrix &A, const Matrix &B, Matrix &C)
} // namespace sparse
-class JacobiPreconditioner
-{
-public:
- JacobiPreconditioner(const sparse::Matrix &M, bool symmetric) : m_inverseDiagonal(M.width())
- {
- XA_ASSERT(M.isSquare());
- for (uint32_t x = 0; x < M.width(); x++) {
- float elem = M.getCoefficient(x, x);
- //XA_DEBUG_ASSERT( elem != 0.0f ); // This can be zero in the presence of zero area triangles.
- if (symmetric) {
- m_inverseDiagonal[x] = (elem != 0) ? 1.0f / sqrtf(fabsf(elem)) : 1.0f;
- } else {
- m_inverseDiagonal[x] = (elem != 0) ? 1.0f / elem : 1.0f;
- }
- }
- }
-
- void apply(const FullVector &x, FullVector &y) const
- {
- XA_DEBUG_ASSERT(x.dimension() == m_inverseDiagonal.dimension());
- XA_DEBUG_ASSERT(y.dimension() == m_inverseDiagonal.dimension());
- // @@ Wrap vector component-wise product into a separate function.
- const uint32_t D = x.dimension();
- for (uint32_t i = 0; i < D; i++) {
- y[i] = m_inverseDiagonal[i] * x[i];
- }
- }
-
-private:
- FullVector m_inverseDiagonal;
-};
-
-// Linear solvers.
-class Solver
-{
-public:
- // Solve the symmetric system: At·A·x = At·b
- static bool LeastSquaresSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon = 1e-5f)
- {
- XA_DEBUG_ASSERT(A.width() == x.dimension());
- XA_DEBUG_ASSERT(A.height() == b.dimension());
- XA_DEBUG_ASSERT(A.height() >= A.width()); // @@ If height == width we could solve it directly...
- const uint32_t D = A.width();
- sparse::Matrix At(A.height(), A.width());
- sparse::transpose(A, At);
- FullVector Atb(D);
- sparse::mult(At, b, Atb);
- sparse::Matrix AtA(D);
- sparse::mult(At, A, AtA);
- return SymmetricSolver(AtA, Atb, x, epsilon);
- }
-
- // See section 10.4.3 in: Mesh Parameterization: Theory and Practice, Siggraph Course Notes, August 2007
- static bool LeastSquaresSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, const uint32_t *lockedParameters, uint32_t lockedCount, float epsilon = 1e-5f)
- {
- XA_DEBUG_ASSERT(A.width() == x.dimension());
- XA_DEBUG_ASSERT(A.height() == b.dimension());
- XA_DEBUG_ASSERT(A.height() >= A.width() - lockedCount);
- // @@ This is not the most efficient way of building a system with reduced degrees of freedom. It would be faster to do it on the fly.
- const uint32_t D = A.width() - lockedCount;
- XA_DEBUG_ASSERT(D > 0);
- // Compute: b - Al * xl
- FullVector b_Alxl(b);
- for (uint32_t y = 0; y < A.height(); y++) {
- const uint32_t count = A.getRow(y).size();
- for (uint32_t e = 0; e < count; e++) {
- uint32_t column = A.getRow(y)[e].x;
- bool isFree = true;
- for (uint32_t i = 0; i < lockedCount; i++) {
- isFree &= (lockedParameters[i] != column);
- }
- if (!isFree) {
- b_Alxl[y] -= x[column] * A.getRow(y)[e].v;
- }
- }
- }
- // Remove locked columns from A.
- sparse::Matrix Af(D, A.height());
- for (uint32_t y = 0; y < A.height(); y++) {
- const uint32_t count = A.getRow(y).size();
- for (uint32_t e = 0; e < count; e++) {
- uint32_t column = A.getRow(y)[e].x;
- uint32_t ix = column;
- bool isFree = true;
- for (uint32_t i = 0; i < lockedCount; i++) {
- isFree &= (lockedParameters[i] != column);
- if (column > lockedParameters[i]) ix--; // shift columns
- }
- if (isFree) {
- Af.setCoefficient(ix, y, A.getRow(y)[e].v);
- }
- }
- }
- // Remove elements from x
- FullVector xf(D);
- for (uint32_t i = 0, j = 0; i < A.width(); i++) {
- bool isFree = true;
- for (uint32_t l = 0; l < lockedCount; l++) {
- isFree &= (lockedParameters[l] != i);
- }
- if (isFree) {
- xf[j++] = x[i];
- }
- }
- // Solve reduced system.
- bool result = LeastSquaresSolver(Af, b_Alxl, xf, epsilon);
- // Copy results back to x.
- for (uint32_t i = 0, j = 0; i < A.width(); i++) {
- bool isFree = true;
- for (uint32_t l = 0; l < lockedCount; l++) {
- isFree &= (lockedParameters[l] != i);
- }
- if (isFree) {
- x[i] = xf[j++];
- }
- }
- return result;
- }
-
-private:
- /**
- * Compute the solution of the sparse linear system Ab=x using the Conjugate
- * Gradient method.
- *
- * Solving sparse linear systems:
- * (1) A·x = b
- *
- * The conjugate gradient algorithm solves (1) only in the case that A is
- * symmetric and positive definite. It is based on the idea of minimizing the
- * function
- *
- * (2) f(x) = 1/2·x·A·x - b·x
- *
- * This function is minimized when its gradient
- *
- * (3) df = A·x - b
- *
- * is zero, which is equivalent to (1). The minimization is carried out by
- * generating a succession of search directions p.k and improved minimizers x.k.
- * At each stage a quantity alfa.k is found that minimizes f(x.k + alfa.k·p.k),
- * and x.k+1 is set equal to the new point x.k + alfa.k·p.k. The p.k and x.k are
- * built up in such a way that x.k+1 is also the minimizer of f over the whole
- * vector space of directions already taken, {p.1, p.2, . . . , p.k}. After N
- * iterations you arrive at the minimizer over the entire vector space, i.e., the
- * solution to (1).
- *
- * For a really good explanation of the method see:
- *
- * "An Introduction to the Conjugate Gradient Method Without the Agonizing Pain",
- * Jonhathan Richard Shewchuk.
- *
- **/
- // Conjugate gradient with preconditioner.
- static bool ConjugateGradientSolver(const JacobiPreconditioner &preconditioner, const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon)
- {
- XA_DEBUG_ASSERT( A.isSquare() );
- XA_DEBUG_ASSERT( A.width() == b.dimension() );
- XA_DEBUG_ASSERT( A.width() == x.dimension() );
- int i = 0;
- const int D = A.width();
- const int i_max = 4 * D; // Convergence should be linear, but in some cases, it's not.
- FullVector r(D); // residual
- FullVector p(D); // search direction
- FullVector q(D); //
- FullVector s(D); // preconditioned
- float delta_0;
- float delta_old;
- float delta_new;
- float alpha;
- float beta;
- // r = b - A·x
- sparse::copy(b, r);
- sparse::sgemv(-1, A, x, 1, r);
- // p = M^-1 · r
- preconditioner.apply(r, p);
- delta_new = sparse::dot(r, p);
- delta_0 = delta_new;
- while (i < i_max && delta_new > epsilon * epsilon * delta_0) {
- i++;
- // q = A·p
- sparse::mult(A, p, q);
- // alpha = delta_new / p·q
- alpha = delta_new / sparse::dot(p, q);
- // x = alfa·p + x
- sparse::saxpy(alpha, p, x);
- if ((i & 31) == 0) { // recompute r after 32 steps
- // r = b - A·x
- sparse::copy(b, r);
- sparse::sgemv(-1, A, x, 1, r);
- } else {
- // r = r - alfa·q
- sparse::saxpy(-alpha, q, r);
- }
- // s = M^-1 · r
- preconditioner.apply(r, s);
- delta_old = delta_new;
- delta_new = sparse::dot( r, s );
- beta = delta_new / delta_old;
- // p = s + beta·p
- sparse::scal(beta, p);
- sparse::saxpy(1, s, p);
- }
- return delta_new <= epsilon * epsilon * delta_0;
- }
-
- static bool SymmetricSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon = 1e-5f)
- {
- XA_DEBUG_ASSERT(A.height() == A.width());
- XA_DEBUG_ASSERT(A.height() == b.dimension());
- XA_DEBUG_ASSERT(b.dimension() == x.dimension());
- JacobiPreconditioner jacobi(A, true);
- return ConjugateGradientSolver(jacobi, A, b, x, epsilon);
- }
-};
-
-namespace param {
-
-// Fast sweep in 3 directions
-static bool findApproximateDiameterVertices(Mesh *mesh, uint32_t *a, uint32_t *b)
-{
- XA_DEBUG_ASSERT(a != nullptr);
- XA_DEBUG_ASSERT(b != nullptr);
- const uint32_t vertexCount = mesh->vertexCount();
- uint32_t minVertex[3];
- uint32_t maxVertex[3];
- minVertex[0] = minVertex[1] = minVertex[2] = UINT32_MAX;
- maxVertex[0] = maxVertex[1] = maxVertex[2] = UINT32_MAX;
- for (uint32_t v = 1; v < vertexCount; v++) {
- if (mesh->isBoundaryVertex(v)) {
- minVertex[0] = minVertex[1] = minVertex[2] = v;
- maxVertex[0] = maxVertex[1] = maxVertex[2] = v;
- break;
- }
- }
- if (minVertex[0] == UINT32_MAX) {
- // Input mesh has not boundaries.
- return false;
- }
- for (uint32_t v = 1; v < vertexCount; v++) {
- if (!mesh->isBoundaryVertex(v)) {
- // Skip interior vertices.
- continue;
- }
- const Vector3 &pos = mesh->position(v);
- if (pos.x < mesh->position(minVertex[0]).x)
- minVertex[0] = v;
- else if (pos.x > mesh->position(maxVertex[0]).x)
- maxVertex[0] = v;
- if (pos.y < mesh->position(minVertex[1]).y)
- minVertex[1] = v;
- else if (pos.y > mesh->position(maxVertex[1]).y)
- maxVertex[1] = v;
- if (pos.z < mesh->position(minVertex[2]).z)
- minVertex[2] = v;
- else if (pos.z > mesh->position(maxVertex[2]).z)
- maxVertex[2] = v;
- }
- float lengths[3];
- for (int i = 0; i < 3; i++) {
- lengths[i] = length(mesh->position(minVertex[i]) - mesh->position(maxVertex[i]));
- }
- if (lengths[0] > lengths[1] && lengths[0] > lengths[2]) {
- *a = minVertex[0];
- *b = maxVertex[0];
- } else if (lengths[1] > lengths[2]) {
- *a = minVertex[1];
- *b = maxVertex[1];
- } else {
- *a = minVertex[2];
- *b = maxVertex[2];
- }
- return true;
-}
-
-// Conformal relations from Brecht Van Lommel (based on ABF):
-
-static float vec_angle_cos(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3)
-{
- Vector3 d1 = v1 - v2;
- Vector3 d2 = v3 - v2;
- return clamp(dot(d1, d2) / (length(d1) * length(d2)), -1.0f, 1.0f);
-}
-
-static float vec_angle(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3)
-{
- float dot = vec_angle_cos(v1, v2, v3);
- return acosf(dot);
-}
-
-static void triangle_angles(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, float *a1, float *a2, float *a3)
-{
- *a1 = vec_angle(v3, v1, v2);
- *a2 = vec_angle(v1, v2, v3);
- *a3 = kPi - *a2 - *a1;
-}
-
-static void setup_abf_relations(sparse::Matrix &A, int row, int id0, int id1, int id2, const Vector3 &p0, const Vector3 &p1, const Vector3 &p2)
-{
- // @@ IC: Wouldn't it be more accurate to return cos and compute 1-cos^2?
- // It does indeed seem to be a little bit more robust.
- // @@ Need to revisit this more carefully!
- float a0, a1, a2;
- triangle_angles(p0, p1, p2, &a0, &a1, &a2);
- float s0 = sinf(a0);
- float s1 = sinf(a1);
- float s2 = sinf(a2);
- if (s1 > s0 && s1 > s2) {
- swap(s1, s2);
- swap(s0, s1);
- swap(a1, a2);
- swap(a0, a1);
- swap(id1, id2);
- swap(id0, id1);
- } else if (s0 > s1 && s0 > s2) {
- swap(s0, s2);
- swap(s0, s1);
- swap(a0, a2);
- swap(a0, a1);
- swap(id0, id2);
- swap(id0, id1);
- }
- float c0 = cosf(a0);
- float ratio = (s2 == 0.0f) ? 1.0f : s1 / s2;
- float cosine = c0 * ratio;
- float sine = s0 * ratio;
- // Note : 2*id + 0 --> u
- // 2*id + 1 --> v
- int u0_id = 2 * id0 + 0;
- int v0_id = 2 * id0 + 1;
- int u1_id = 2 * id1 + 0;
- int v1_id = 2 * id1 + 1;
- int u2_id = 2 * id2 + 0;
- int v2_id = 2 * id2 + 1;
- // Real part
- A.setCoefficient(u0_id, 2 * row + 0, cosine - 1.0f);
- A.setCoefficient(v0_id, 2 * row + 0, -sine);
- A.setCoefficient(u1_id, 2 * row + 0, -cosine);
- A.setCoefficient(v1_id, 2 * row + 0, sine);
- A.setCoefficient(u2_id, 2 * row + 0, 1);
- // Imaginary part
- A.setCoefficient(u0_id, 2 * row + 1, sine);
- A.setCoefficient(v0_id, 2 * row + 1, cosine - 1.0f);
- A.setCoefficient(u1_id, 2 * row + 1, -sine);
- A.setCoefficient(v1_id, 2 * row + 1, -cosine);
- A.setCoefficient(v2_id, 2 * row + 1, 1);
-}
-
-static bool computeLeastSquaresConformalMap(Mesh *mesh)
-{
- // For this to work properly, mesh should not have colocals that have the same
- // attributes, unless you want the vertices to actually have different texcoords.
- const uint32_t vertexCount = mesh->vertexCount();
- const uint32_t D = 2 * vertexCount;
- const uint32_t N = 2 * mesh->faceCount();
- // N is the number of equations (one per triangle)
- // D is the number of variables (one per vertex; there are 2 pinned vertices).
- if (N < D - 4) {
- return false;
- }
- sparse::Matrix A(D, N);
- FullVector b(N);
- FullVector x(D);
- // Fill b:
- b.fill(0.0f);
- // Fill x:
- uint32_t v0, v1;
- if (!findApproximateDiameterVertices(mesh, &v0, &v1)) {
- // Mesh has no boundaries.
- return false;
- }
- if (mesh->texcoord(v0) == mesh->texcoord(v1)) {
- // LSCM expects an existing parameterization.
- return false;
- }
- for (uint32_t v = 0; v < vertexCount; v++) {
- // Initial solution.
- x[2 * v + 0] = mesh->texcoord(v).x;
- x[2 * v + 1] = mesh->texcoord(v).y;
- }
- // Fill A:
- const uint32_t faceCount = mesh->faceCount();
- for (uint32_t f = 0, t = 0; f < faceCount; f++) {
- const uint32_t vertex0 = mesh->vertexAt(f * 3 + 0);
- const uint32_t vertex1 = mesh->vertexAt(f * 3 + 1);
- const uint32_t vertex2 = mesh->vertexAt(f * 3 + 2);
- setup_abf_relations(A, t, vertex0, vertex1, vertex2, mesh->position(vertex0), mesh->position(vertex1), mesh->position(vertex2));
- t++;
- }
- const uint32_t lockedParameters[] = {
- 2 * v0 + 0,
- 2 * v0 + 1,
- 2 * v1 + 0,
- 2 * v1 + 1
- };
- // Solve
- Solver::LeastSquaresSolver(A, b, x, lockedParameters, 4, 0.000001f);
- // Map x back to texcoords:
- for (uint32_t v = 0; v < vertexCount; v++)
- mesh->texcoord(v) = Vector2(x[2 * v + 0], x[2 * v + 1]);
- return true;
-}
-
-static bool computeOrthogonalProjectionMap(Mesh *mesh)
-{
- uint32_t vertexCount = mesh->vertexCount();
- // Avoid redundant computations.
- float matrix[6];
- Fit::computeCovariance(vertexCount, &mesh->position(0), matrix);
- if (matrix[0] == 0 && matrix[3] == 0 && matrix[5] == 0)
- return false;
- float eigenValues[3];
- Vector3 eigenVectors[3];
- if (!Fit::eigenSolveSymmetric3(matrix, eigenValues, eigenVectors))
- return false;
- Vector3 axis[2];
- axis[0] = normalize(eigenVectors[0], kEpsilon);
- axis[1] = normalize(eigenVectors[1], kEpsilon);
- // Project vertices to plane.
- for (uint32_t i = 0; i < vertexCount; i++)
- mesh->texcoord(i) = Vector2(dot(axis[0], mesh->position(i)), dot(axis[1], mesh->position(i)));
- return true;
-}
+namespace segment {
// Dummy implementation of a priority queue using sort at insertion.
// - Insertion is o(n)
@@ -4729,6 +4243,7 @@ struct PriorityQueue
uint32_t pop()
{
+ XA_DEBUG_ASSERT(!pairs.isEmpty());
uint32_t f = pairs.back().face;
pairs.pop_back();
return f;
@@ -4740,12 +4255,12 @@ struct PriorityQueue
std::sort(pairs.begin(), pairs.end());
}
- void clear()
+ XA_INLINE void clear()
{
pairs.clear();
}
- uint32_t count() const
+ XA_INLINE uint32_t count() const
{
return pairs.size();
}
@@ -4771,7 +4286,7 @@ struct PriorityQueue
Array<Pair> pairs;
};
-struct ChartBuildData
+struct Chart
{
int id = -1;
Vector3 averageNormal = Vector3(0.0f);
@@ -4786,31 +4301,39 @@ struct ChartBuildData
Basis basis; // Of first face.
};
-struct AtlasBuilder
+struct Atlas
{
// @@ Hardcoded to 10?
- AtlasBuilder(const Mesh *mesh, Array<uint32_t> *meshFaces, const ChartOptions &options) : m_mesh(mesh), m_meshFaces(meshFaces), m_facesLeft(mesh->faceCount()), m_bestTriangles(10), m_options(options)
+ Atlas(const Mesh *mesh, Array<uint32_t> *meshFaces, const ChartOptions &options) : m_mesh(mesh), m_meshFaces(meshFaces), m_facesLeft(mesh->faceCount()), m_bestTriangles(10), m_options(options)
{
- XA_PROFILE_START(atlasBuilderInit)
+ XA_PROFILE_START(buildAtlasInit)
const uint32_t faceCount = m_mesh->faceCount();
if (meshFaces) {
- m_ignoreFaces.resize(faceCount, true);
+ m_ignoreFaces.resize(faceCount);
+ m_ignoreFaces.setAll(true);
for (uint32_t f = 0; f < meshFaces->size(); f++)
m_ignoreFaces[(*meshFaces)[f]] = false;
m_facesLeft = meshFaces->size();
} else {
- m_ignoreFaces.resize(faceCount, false);
+ m_ignoreFaces.resize(faceCount);
+ m_ignoreFaces.setAll(false);
}
- m_faceChartArray.resize(faceCount, -1);
- m_faceCandidateArray.resize(faceCount, (uint32_t)-1);
+ m_faceChartArray.resize(faceCount);
+ m_faceChartArray.setAll(-1);
+ m_faceCandidateCharts.resize(faceCount);
+ m_faceCandidateCosts.resize(faceCount);
m_texcoords.resize(faceCount * 3);
// @@ Floyd for the whole mesh is too slow. We could compute floyd progressively per patch as the patch grows. We need a better solution to compute most central faces.
//computeShortestPaths();
// Precompute edge lengths and face areas.
const uint32_t edgeCount = m_mesh->edgeCount();
- m_edgeLengths.resize(edgeCount, 0.0f);
- m_faceAreas.resize(m_mesh->faceCount(), 0.0f);
- m_faceNormals.resize(m_mesh->faceCount());
+ m_edgeLengths.resize(edgeCount);
+ m_edgeLengths.zeroOutMemory();
+ m_faceAreas.resize(faceCount);
+ m_faceAreas.zeroOutMemory();
+ m_faceNormals.resize(faceCount);
+ m_faceTangents.resize(faceCount);
+ m_faceBitangents.resize(faceCount);
for (uint32_t f = 0; f < faceCount; f++) {
if (m_ignoreFaces[f])
continue;
@@ -4821,15 +4344,52 @@ struct AtlasBuilder
m_faceAreas[f] = mesh->faceArea(f);
XA_DEBUG_ASSERT(m_faceAreas[f] > 0.0f);
m_faceNormals[f] = m_mesh->triangleNormal(f);
+ m_faceTangents[f] = Basis::computeTangent(m_faceNormals[f]);
+ m_faceBitangents[f] = Basis::computeBitangent(m_faceNormals[f], m_faceTangents[f]);
}
- XA_PROFILE_END(atlasBuilderInit)
+#if XA_GROW_CHARTS_COPLANAR
+ // Precompute regions of coplanar incident faces.
+ m_nextPlanarRegionFace.resize(faceCount);
+ for (uint32_t f = 0; f < faceCount; f++)
+ m_nextPlanarRegionFace[f] = f;
+ Array<uint32_t> faceStack;
+ faceStack.reserve(min(faceCount, 16u));
+ for (uint32_t f = 0; f < faceCount; f++) {
+ if (m_nextPlanarRegionFace[f] != f)
+ continue; // Already assigned.
+ if (m_ignoreFaces[f])
+ continue;
+ faceStack.clear();
+ faceStack.push_back(f);
+ for (;;) {
+ if (faceStack.isEmpty())
+ break;
+ const uint32_t face = faceStack.back();
+ faceStack.pop_back();
+ for (Mesh::FaceEdgeIterator it(m_mesh, face); !it.isDone(); it.advance()) {
+ const uint32_t oface = it.oppositeFace();
+ if (it.isBoundary() || m_ignoreFaces[oface])
+ continue;
+ if (m_nextPlanarRegionFace[oface] != oface)
+ continue; // Already assigned.
+ if (!equal(dot(m_faceNormals[face], m_faceNormals[oface]), 1.0f, kEpsilon))
+ continue; // Not coplanar.
+ const uint32_t next = m_nextPlanarRegionFace[face];
+ m_nextPlanarRegionFace[face] = oface;
+ m_nextPlanarRegionFace[oface] = next;
+ faceStack.push_back(oface);
+ }
+ }
+ }
+#endif
+ XA_PROFILE_END(buildAtlasInit)
}
- ~AtlasBuilder()
+ ~Atlas()
{
const uint32_t chartCount = m_chartArray.size();
for (uint32_t i = 0; i < chartCount; i++) {
- m_chartArray[i]->~ChartBuildData();
+ m_chartArray[i]->~Chart();
XA_FREE(m_chartArray[i]);
}
}
@@ -4838,9 +4398,11 @@ struct AtlasBuilder
uint32_t chartCount() const { return m_chartArray.size(); }
const Array<uint32_t> &chartFaces(uint32_t i) const { return m_chartArray[i]->faces; }
const Basis &chartBasis(uint32_t chartIndex) const { return m_chartArray[chartIndex]->basis; }
+ const Vector2 *faceTexcoords(uint32_t face) const { return &m_texcoords[face * 3]; }
void placeSeeds(float threshold)
{
+ XA_PROFILE_START(buildAtlasPlaceSeeds)
// Instead of using a predefiened number of seeds:
// - Add seeds one by one, growing chart until a certain treshold.
// - Undo charts and restart growing process.
@@ -4849,43 +4411,44 @@ struct AtlasBuilder
// - how do we weight the probabilities?
while (m_facesLeft > 0)
createRandomChart(threshold);
+ XA_PROFILE_END(buildAtlasPlaceSeeds)
}
// Returns true if any of the charts can grow more.
- bool growCharts(float threshold, uint32_t faceCount)
- {
- XA_PROFILE_START(atlasBuilderGrowCharts)
- // Using one global list.
- faceCount = min(faceCount, m_facesLeft);
+ bool growCharts(float threshold)
+ {
+ XA_PROFILE_START(buildAtlasGrowCharts)
+ // Build global candidate list.
+ m_faceCandidateCharts.zeroOutMemory();
+ for (uint32_t i = 0; i < m_chartArray.size(); i++)
+ addChartCandidateToGlobalCandidates(m_chartArray[i]);
+ // Add one candidate face per chart (threshold permitting).
+ const uint32_t faceCount = m_mesh->faceCount();
bool canAddAny = false;
- for (uint32_t i = 0; i < faceCount; i++) {
- const Candidate &candidate = getBestCandidate();
- if (candidate.metric > threshold) {
- XA_PROFILE_END(atlasBuilderGrowCharts)
- return false; // Can't grow more.
- }
- createFaceTexcoords(candidate.chart, candidate.face);
- if (!canAddFaceToChart(candidate.chart, candidate.face))
+ for (uint32_t f = 0; f < faceCount; f++) {
+ Chart *chart = m_faceCandidateCharts[f];
+ if (!chart || m_faceCandidateCosts[f] > threshold)
+ continue;
+ createFaceTexcoords(chart, f);
+ if (!canAddFaceToChart(chart, f))
continue;
- addFaceToChart(candidate.chart, candidate.face);
+ addFaceToChart(chart, f);
canAddAny = true;
}
- XA_PROFILE_END(atlasBuilderGrowCharts)
+ XA_PROFILE_END(buildAtlasGrowCharts)
return canAddAny && m_facesLeft != 0; // Can continue growing.
}
void resetCharts()
{
+ XA_PROFILE_START(buildAtlasResetCharts)
const uint32_t faceCount = m_mesh->faceCount();
- for (uint32_t i = 0; i < faceCount; i++) {
+ for (uint32_t i = 0; i < faceCount; i++)
m_faceChartArray[i] = -1;
- m_faceCandidateArray[i] = (uint32_t)-1;
- }
m_facesLeft = m_meshFaces ? m_meshFaces->size() : faceCount;
- m_candidateArray.clear();
const uint32_t chartCount = m_chartArray.size();
for (uint32_t i = 0; i < chartCount; i++) {
- ChartBuildData *chart = m_chartArray[i];
+ Chart *chart = m_chartArray[i];
const uint32_t seed = chart->seeds.back();
chart->area = 0.0f;
chart->boundaryLength = 0.0f;
@@ -4898,30 +4461,32 @@ struct AtlasBuilder
}
#if XA_GROW_CHARTS_COPLANAR
for (uint32_t i = 0; i < chartCount; i++) {
- ChartBuildData *chart = m_chartArray[i];
+ Chart *chart = m_chartArray[i];
growChartCoplanar(chart);
}
#endif
+ XA_PROFILE_END(buildAtlasResetCharts)
}
- void updateCandidates(ChartBuildData *chart, uint32_t f)
+ void updateChartCandidates(Chart *chart, uint32_t f)
{
// Traverse neighboring faces, add the ones that do not belong to any chart yet.
for (Mesh::FaceEdgeIterator it(m_mesh, f); !it.isDone(); it.advance()) {
if (!it.isBoundary() && !m_ignoreFaces[it.oppositeFace()] && m_faceChartArray[it.oppositeFace()] == -1)
chart->candidates.push(it.oppositeFace());
}
- }
-
- void updateProxies()
- {
- const uint32_t chartCount = m_chartArray.size();
- for (uint32_t i = 0; i < chartCount; i++)
- updateProxy(m_chartArray[i]);
+ // Re-evaluate all candidate priorities.
+ uint32_t candidateCount = chart->candidates.count();
+ for (uint32_t i = 0; i < candidateCount; i++) {
+ PriorityQueue::Pair &pair = chart->candidates.pairs[i];
+ pair.priority = evaluateCost(chart, pair.face);
+ }
+ chart->candidates.sort();
}
bool relocateSeeds()
{
+ XA_PROFILE_START(buildAtlasRelocateSeeds)
bool anySeedChanged = false;
const uint32_t chartCount = m_chartArray.size();
for (uint32_t i = 0; i < chartCount; i++) {
@@ -4929,19 +4494,22 @@ struct AtlasBuilder
anySeedChanged = true;
}
}
+ XA_PROFILE_END(buildAtlasRelocateSeeds)
return anySeedChanged;
}
void fillHoles(float threshold)
{
+ XA_PROFILE_START(buildAtlasFillHoles)
while (m_facesLeft > 0)
createRandomChart(threshold);
+ XA_PROFILE_END(buildAtlasFillHoles)
}
#if XA_MERGE_CHARTS
void mergeCharts()
{
- XA_PROFILE_START(atlasBuilderMergeCharts)
+ XA_PROFILE_START(buildAtlasMergeCharts)
Array<float> sharedBoundaryLengths;
Array<float> sharedBoundaryLengthsNoSeams;
Array<uint32_t> sharedBoundaryEdgeCountNoSeams;
@@ -4951,16 +4519,19 @@ struct AtlasBuilder
for (;;) {
bool merged = false;
for (int c = chartCount - 1; c >= 0; c--) {
- ChartBuildData *chart = m_chartArray[c];
+ Chart *chart = m_chartArray[c];
if (chart == nullptr)
continue;
float externalBoundaryLength = 0.0f;
sharedBoundaryLengths.clear();
- sharedBoundaryLengths.resize(chartCount, 0.0f);
+ sharedBoundaryLengths.resize(chartCount);
+ sharedBoundaryLengths.zeroOutMemory();
sharedBoundaryLengthsNoSeams.clear();
- sharedBoundaryLengthsNoSeams.resize(chartCount, 0.0f);
+ sharedBoundaryLengthsNoSeams.resize(chartCount);
+ sharedBoundaryLengthsNoSeams.zeroOutMemory();
sharedBoundaryEdgeCountNoSeams.clear();
- sharedBoundaryEdgeCountNoSeams.resize(chartCount, 0u);
+ sharedBoundaryEdgeCountNoSeams.resize(chartCount);
+ sharedBoundaryEdgeCountNoSeams.zeroOutMemory();
const uint32_t faceCount = chart->faces.size();
for (uint32_t i = 0; i < faceCount; i++) {
const uint32_t f = chart->faces[i];
@@ -4985,7 +4556,7 @@ struct AtlasBuilder
for (int cc = chartCount - 1; cc >= 0; cc--) {
if (cc == c)
continue;
- ChartBuildData *chart2 = m_chartArray[cc];
+ Chart *chart2 = m_chartArray[cc];
if (chart2 == nullptr)
continue;
// Compare proxies.
@@ -5013,16 +4584,19 @@ struct AtlasBuilder
continue;
merge:
// Create texcoords for chart 2 using chart 1 basis. Backup chart 2 texcoords for restoration if charts cannot be merged.
- tempTexcoords.resize(chart2->faces.size());
+ tempTexcoords.resize(chart2->faces.size() * 3);
for (uint32_t i = 0; i < chart2->faces.size(); i++) {
const uint32_t face = chart2->faces[i];
- tempTexcoords[i] = m_texcoords[face];
+ for (uint32_t j = 0; j < 3; j++)
+ tempTexcoords[i * 3 + j] = m_texcoords[face * 3 + j];
createFaceTexcoords(chart, face);
}
if (!canMergeCharts(chart, chart2)) {
// Restore chart 2 texcoords.
- for (uint32_t i = 0; i < chart2->faces.size(); i++)
- m_texcoords[chart2->faces[i]] = tempTexcoords[i];
+ for (uint32_t i = 0; i < chart2->faces.size(); i++) {
+ for (uint32_t j = 0; j < 3; j++)
+ m_texcoords[chart2->faces[i] * 3 + j] = tempTexcoords[i * 3 + j];
+ }
continue;
}
mergeChart(chart, chart2, sharedBoundaryLengthsNoSeams[cc]);
@@ -5053,14 +4627,14 @@ struct AtlasBuilder
c++;
}
}
- XA_PROFILE_END(atlasBuilderMergeCharts)
+ XA_PROFILE_END(buildAtlasMergeCharts)
}
#endif
private:
void createRandomChart(float threshold)
{
- ChartBuildData *chart = XA_NEW(MemTag::Default, ChartBuildData);
+ Chart *chart = XA_NEW(MemTag::Default, Chart);
chart->id = (int)m_chartArray.size();
m_chartArray.push_back(chart);
// Pick random face that is not used by any chart yet.
@@ -5070,15 +4644,52 @@ private:
face = 0;
}
chart->seeds.push_back(face);
- addFaceToChart(chart, face, true);
+ addFaceToChart(chart, face);
#if XA_GROW_CHARTS_COPLANAR
growChartCoplanar(chart);
#endif
// Grow the chart as much as possible within the given threshold.
- growChart(chart, threshold, m_facesLeft);
+ for (uint32_t i = 0; i < m_facesLeft; ) {
+ if (chart->candidates.count() == 0 || chart->candidates.firstPriority() > threshold)
+ break;
+ const uint32_t f = chart->candidates.pop();
+ if (m_faceChartArray[f] != -1)
+ continue;
+ createFaceTexcoords(chart, f);
+ if (!canAddFaceToChart(chart, f))
+ continue;
+ addFaceToChart(chart, f);
+ i++;
+ }
+ }
+
+ void addChartCandidateToGlobalCandidates(Chart *chart)
+ {
+ if (chart->candidates.count() == 0)
+ return;
+ const float cost = chart->candidates.firstPriority();
+ const uint32_t face = chart->candidates.pop();
+ if (m_faceChartArray[face] != -1) {
+ addChartCandidateToGlobalCandidates(chart);
+ } else if (!m_faceCandidateCharts[face]) {
+ // No candidate assigned to this face yet.
+ m_faceCandidateCharts[face] = chart;
+ m_faceCandidateCosts[face] = cost;
+ } else {
+ if (cost < m_faceCandidateCosts[face]) {
+ // This is a better candidate for this face (lower cost). The other chart can choose another candidate.
+ Chart *otherChart = m_faceCandidateCharts[face];
+ m_faceCandidateCharts[face] = chart;
+ m_faceCandidateCosts[face] = cost;
+ addChartCandidateToGlobalCandidates(otherChart);
+ } else {
+ // Existing candidate is better. This chart can choose another candidate.
+ addChartCandidateToGlobalCandidates(chart);
+ }
+ }
}
- void createFaceTexcoords(ChartBuildData *chart, uint32_t face)
+ void createFaceTexcoords(Chart *chart, uint32_t face)
{
for (uint32_t i = 0; i < 3; i++) {
const Vector3 &pos = m_mesh->position(m_mesh->vertexAt(face * 3 + i));
@@ -5086,30 +4697,71 @@ private:
}
}
- bool isChartBoundaryEdge(ChartBuildData *chart, uint32_t edge) const
+ bool isChartBoundaryEdge(const Chart *chart, uint32_t edge) const
{
const uint32_t oppositeEdge = m_mesh->oppositeEdge(edge);
const uint32_t oppositeFace = meshEdgeFace(oppositeEdge);
return oppositeEdge == UINT32_MAX || m_ignoreFaces[oppositeFace] || m_faceChartArray[oppositeFace] != chart->id;
}
- bool canAddFaceToChart(ChartBuildData *chart, uint32_t face)
+ bool edgeArraysIntersect(const uint32_t *edges1, uint32_t edges1Count, const uint32_t *edges2, uint32_t edges2Count)
+ {
+ for (uint32_t i = 0; i < edges1Count; i++) {
+ const uint32_t edge1 = edges1[i];
+ for (uint32_t j = 0; j < edges2Count; j++) {
+ const uint32_t edge2 = edges2[j];
+ const Vector2 &a1 = m_texcoords[meshEdgeIndex0(edge1)];
+ const Vector2 &a2 = m_texcoords[meshEdgeIndex1(edge1)];
+ const Vector2 &b1 = m_texcoords[meshEdgeIndex0(edge2)];
+ const Vector2 &b2 = m_texcoords[meshEdgeIndex1(edge2)];
+ if (linesIntersect(a1, a2, b1, b2, m_mesh->epsilon()))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool isFaceFlipped(uint32_t face) const
+ {
+ const float t1 = m_texcoords[face * 3 + 0].x;
+ const float s1 = m_texcoords[face * 3 + 0].y;
+ const float t2 = m_texcoords[face * 3 + 1].x;
+ const float s2 = m_texcoords[face * 3 + 1].y;
+ const float t3 = m_texcoords[face * 3 + 2].x;
+ const float s3 = m_texcoords[face * 3 + 2].y;
+ const float parametricArea = ((s2 - s1) * (t3 - t1) - (s3 - s1) * (t2 - t1)) / 2;
+ return parametricArea < 0.0f;
+ }
+
+ void computeChartBoundaryEdges(const Chart *chart, Array<uint32_t> *dest) const
{
- // Find face edges that are on a mesh boundary or form a boundary with another chart.
- uint32_t edgesToCompare[3];
+ dest->clear();
+ for (uint32_t f = 0; f < chart->faces.size(); f++) {
+ const uint32_t face = chart->faces[f];
+ for (uint32_t i = 0; i < 3; i++) {
+ const uint32_t edge = face * 3 + i;
+ if (isChartBoundaryEdge(chart, edge))
+ dest->push_back(edge);
+ }
+ }
+ }
+
+ bool canAddFaceToChart(Chart *chart, uint32_t face)
+ {
+ // Check for flipped triangles.
+ if (isFaceFlipped(face))
+ return false;
+ // Find face edges that don't border this chart.
+ m_tempEdges1.clear();
for (uint32_t i = 0; i < 3; i++) {
const uint32_t edge = face * 3 + i;
- const uint32_t oppositeEdge = m_mesh->oppositeEdge(edge);
- const uint32_t oppositeFace = meshEdgeFace(oppositeEdge);
- if (oppositeEdge == UINT32_MAX || m_ignoreFaces[oppositeFace] || m_faceChartArray[oppositeFace] != chart->id)
- edgesToCompare[i] = edge;
- else
- edgesToCompare[i] = UINT32_MAX;
+ if (isChartBoundaryEdge(chart, edge))
+ m_tempEdges1.push_back(edge);
}
- // All edges on boundary? This can happen if the face is surrounded by the chart.
- if (edgesToCompare[0] == UINT32_MAX && edgesToCompare[1] == UINT32_MAX && edgesToCompare[2] == UINT32_MAX)
- return true;
- // Check if any valid face edge intersects the chart boundary.
+ if (m_tempEdges1.isEmpty())
+ return true; // This can happen if the face is surrounded by the chart.
+ // Get chart boundary edges, except those that border the face.
+ m_tempEdges2.clear();
for (uint32_t i = 0; i < chart->faces.size(); i++) {
const uint32_t chartFace = chart->faces[i];
for (uint32_t j = 0; j < 3; j++) {
@@ -5120,47 +4772,65 @@ private:
const uint32_t oppositeChartEdge = m_mesh->oppositeEdge(chartEdge);
if (meshEdgeFace(oppositeChartEdge) == face)
continue;
- for (uint32_t k = 0; k < 3; k++) {
- if (edgesToCompare[k] == UINT32_MAX)
- continue;
- const uint32_t e1 = chartEdge;
- const uint32_t e2 = edgesToCompare[k];
- if (linesIntersect(m_texcoords[meshEdgeIndex0(e1)], m_texcoords[meshEdgeIndex1(e1)], m_texcoords[meshEdgeIndex0(e2)], m_texcoords[meshEdgeIndex1(e2)], m_mesh->epsilon()))
- return false;
- }
+ m_tempEdges2.push_back(chartEdge);
}
}
- return true;
- }
-
- bool canMergeCharts(ChartBuildData *chart1, ChartBuildData *chart2)
- {
- for (uint32_t f1 = 0; f1 < chart1->faces.size(); f1++) {
- const uint32_t face1 = chart1->faces[f1];
- for (uint32_t i = 0; i < 3; i++) {
- const uint32_t edge1 = face1 * 3 + i;
- if (!isChartBoundaryEdge(chart1, edge1))
- continue;
- for (uint32_t f2 = 0; f2 < chart2->faces.size(); f2++) {
- const uint32_t face2 = chart2->faces[f2];
+ const bool intersect = edgeArraysIntersect(m_tempEdges1.data(), m_tempEdges1.size(), m_tempEdges2.data(), m_tempEdges2.size());
+#if 0
+ if (intersect) {
+ static std::atomic<uint32_t> count = 0;
+ char filename[256];
+ XA_SPRINTF(filename, sizeof(filename), "intersect%04u.obj", count.fetch_add(1));
+ FILE *file;
+ XA_FOPEN(file, filename, "w");
+ if (file) {
+ for (uint32_t i = 0; i < m_texcoords.size(); i++)
+ fprintf(file, "v %g %g 0.0\n", m_texcoords[i].x, m_texcoords[i].y);
+ fprintf(file, "s off\n");
+ fprintf(file, "o face\n");
+ {
+ fprintf(file, "f ");
for (uint32_t j = 0; j < 3; j++) {
- const uint32_t edge2 = face2 * 3 + j;
- if (!isChartBoundaryEdge(chart2, edge2))
- continue;
- if (linesIntersect(m_texcoords[meshEdgeIndex0(edge1)], m_texcoords[meshEdgeIndex1(edge1)], m_texcoords[meshEdgeIndex0(edge2)], m_texcoords[meshEdgeIndex1(edge2)], m_mesh->epsilon()))
- return false;
+ const uint32_t index = face * 3 + j + 1; // 1-indexed
+ fprintf(file, "%d/%d/%d%c", index, index, index, j == 2 ? '\n' : ' ');
+ }
+ }
+ fprintf(file, "s off\n");
+ fprintf(file, "o chart\n");
+ for (uint32_t i = 0; i < chart->faces.size(); i++) {
+ const uint32_t chartFace = chart->faces[i];
+ fprintf(file, "f ");
+ for (uint32_t j = 0; j < 3; j++) {
+ const uint32_t index = chartFace * 3 + j + 1; // 1-indexed
+ fprintf(file, "%d/%d/%d%c", index, index, index, j == 2 ? '\n' : ' ');
}
}
+ fclose(file);
}
}
- return true;
+#endif
+ return !intersect;
+ }
+
+ bool canMergeCharts(Chart *chart1, Chart *chart2)
+ {
+ for (uint32_t i = 0; i < chart2->faces.size(); i++) {
+ if (isFaceFlipped(chart2->faces[i]))
+ return false;
+ }
+ computeChartBoundaryEdges(chart1, &m_tempEdges1);
+ computeChartBoundaryEdges(chart2, &m_tempEdges2);
+ return !edgeArraysIntersect(m_tempEdges1.data(), m_tempEdges1.size(), m_tempEdges2.data(), m_tempEdges2.size());
}
- void addFaceToChart(ChartBuildData *chart, uint32_t f, bool recomputeProxy = false)
+ void addFaceToChart(Chart *chart, uint32_t f)
{
+ const bool firstFace = chart->faces.isEmpty();
// Use the first face normal as the chart basis.
- if (chart->faces.isEmpty()) {
- chart->basis.buildFrameForDirection(m_faceNormals[f]);
+ if (firstFace) {
+ chart->basis.normal = m_faceNormals[f];
+ chart->basis.tangent = m_faceTangents[f];
+ chart->basis.bitangent = m_faceBitangents[f];
createFaceTexcoords(chart, f);
}
// Add face to chart.
@@ -5169,74 +4839,36 @@ private:
m_faceChartArray[f] = chart->id;
m_facesLeft--;
// Update area and boundary length.
- chart->area = evaluateChartArea(chart, f);
- chart->boundaryLength = evaluateBoundaryLength(chart, f);
- chart->normalSum = evaluateChartNormalSum(chart, f);
+ chart->area = chart->area + m_faceAreas[f];
+ chart->boundaryLength = computeBoundaryLength(chart, f);
+ chart->normalSum += m_mesh->triangleNormalAreaScaled(f);
+ chart->averageNormal = normalizeSafe(chart->normalSum, Vector3(0), 0.0f);
chart->centroidSum += m_mesh->triangleCenter(f);
- if (recomputeProxy) {
- // Update proxy and candidate's priorities.
- updateProxy(chart);
- }
+ chart->centroid = chart->centroidSum / float(chart->faces.size());
// Update candidates.
- removeCandidate(f);
- updateCandidates(chart, f);
- updatePriorities(chart);
- }
-
- bool growChart(ChartBuildData *chart, float threshold, uint32_t faceCount)
- {
- // Try to add faceCount faces within threshold to chart.
- for (uint32_t i = 0; i < faceCount; ) {
- if (chart->candidates.count() == 0 || chart->candidates.firstPriority() > threshold)
- return false;
- const uint32_t f = chart->candidates.pop();
- if (m_faceChartArray[f] != -1)
- continue;
- createFaceTexcoords(chart, f);
- if (!canAddFaceToChart(chart, f))
- continue;
- addFaceToChart(chart, f);
- i++;
- }
- if (chart->candidates.count() == 0 || chart->candidates.firstPriority() > threshold)
- return false;
- return true;
+ updateChartCandidates(chart, f);
}
#if XA_GROW_CHARTS_COPLANAR
- void growChartCoplanar(ChartBuildData *chart)
+ void growChartCoplanar(Chart *chart)
{
XA_DEBUG_ASSERT(!chart->faces.isEmpty());
- const Vector3 chartNormal = m_faceNormals[chart->faces[0]];
- m_growFaces.clear();
- for (uint32_t f = 0; f < chart->faces.size(); f++)
- m_growFaces.push_back(chart->faces[f]);
- for (;;) {
- if (m_growFaces.isEmpty())
- break;
- const uint32_t face = m_growFaces.back();
- m_growFaces.pop_back();
- for (Mesh::FaceEdgeIterator it(m_mesh, face); !it.isDone(); it.advance()) {
- if (it.isBoundary() || m_ignoreFaces[it.oppositeFace()] || m_faceChartArray[it.oppositeFace()] != -1)
- continue;
- if (equal(dot(chartNormal, m_faceNormals[it.oppositeFace()]), 1.0f, kEpsilon)) {
- createFaceTexcoords(chart, it.oppositeFace());
- addFaceToChart(chart, it.oppositeFace());
- m_growFaces.push_back(it.oppositeFace());
+ for (uint32_t i = 0; i < chart->faces.size(); i++) {
+ const uint32_t chartFace = chart->faces[i];
+ uint32_t face = m_nextPlanarRegionFace[chartFace];
+ while (face != chartFace) {
+ // Not assigned to a chart?
+ if (m_faceChartArray[face] == -1) {
+ createFaceTexcoords(chart, face);
+ addFaceToChart(chart, face);
}
+ face = m_nextPlanarRegionFace[face];
}
}
}
#endif
- void updateProxy(ChartBuildData *chart) const
- {
- //#pragma message(NV_FILE_LINE "TODO: Use best fit plane instead of average normal.")
- chart->averageNormal = normalizeSafe(chart->normalSum, Vector3(0), 0.0f);
- chart->centroid = chart->centroidSum / float(chart->faces.size());
- }
-
- bool relocateSeed(ChartBuildData *chart)
+ bool relocateSeed(Chart *chart)
{
// Find the first N triangles that fit the proxy best.
const uint32_t faceCount = chart->faces.size();
@@ -5272,26 +4904,12 @@ private:
return true;
}
- void updatePriorities(ChartBuildData *chart)
- {
- // Re-evaluate candidate priorities.
- uint32_t candidateCount = chart->candidates.count();
- for (uint32_t i = 0; i < candidateCount; i++) {
- PriorityQueue::Pair &pair = chart->candidates.pairs[i];
- pair.priority = evaluatePriority(chart, pair.face);
- if (m_faceChartArray[pair.face] == -1)
- updateCandidate(chart, pair.face, pair.priority);
- }
- // Sort candidates.
- chart->candidates.sort();
- }
-
// Evaluate combined metric.
- float evaluatePriority(ChartBuildData *chart, uint32_t face) const
+ float evaluateCost(Chart *chart, uint32_t face) const
{
// Estimate boundary length and area:
- const float newChartArea = evaluateChartArea(chart, face);
- const float newBoundaryLength = evaluateBoundaryLength(chart, face);
+ const float newChartArea = chart->area + m_faceAreas[face];
+ const float newBoundaryLength = computeBoundaryLength(chart, face);
// Enforce limits strictly:
if (m_options.maxChartArea > 0.0f && newChartArea > m_options.maxChartArea)
return FLT_MAX;
@@ -5323,14 +4941,14 @@ private:
}
// Returns a value in [0-1].
- float evaluateProxyFitMetric(ChartBuildData *chart, uint32_t f) const
+ float evaluateProxyFitMetric(Chart *chart, uint32_t f) const
{
const Vector3 faceNormal = m_faceNormals[f];
// Use plane fitting metric for now:
return 1 - dot(faceNormal, chart->averageNormal); // @@ normal deviations should be weighted by face area
}
- float evaluateRoundnessMetric(ChartBuildData *chart, uint32_t /*face*/, float newBoundaryLength, float newChartArea) const
+ float evaluateRoundnessMetric(Chart *chart, uint32_t /*face*/, float newBoundaryLength, float newChartArea) const
{
float roundness = square(chart->boundaryLength) / chart->area;
float newRoundness = square(newBoundaryLength) / newChartArea;
@@ -5342,7 +4960,7 @@ private:
}
}
- float evaluateStraightnessMetric(ChartBuildData *chart, uint32_t f) const
+ float evaluateStraightnessMetric(Chart *chart, uint32_t f) const
{
float l_out = 0.0f;
float l_in = 0.0f;
@@ -5378,7 +4996,7 @@ private:
return m_faceNormals[meshEdgeFace(edge)] != m_faceNormals[meshEdgeFace(oppositeEdge)];
}
- float evaluateNormalSeamMetric(ChartBuildData *chart, uint32_t f) const
+ float evaluateNormalSeamMetric(Chart *chart, uint32_t f) const
{
float seamFactor = 0.0f;
float totalLength = 0.0f;
@@ -5414,7 +5032,7 @@ private:
return seamFactor / totalLength;
}
- float evaluateTextureSeamMetric(ChartBuildData *chart, uint32_t f) const
+ float evaluateTextureSeamMetric(Chart *chart, uint32_t f) const
{
float seamLength = 0.0f;
float totalLength = 0.0f;
@@ -5436,12 +5054,7 @@ private:
return seamLength / totalLength;
}
- float evaluateChartArea(ChartBuildData *chart, uint32_t f) const
- {
- return chart->area + m_faceAreas[f];
- }
-
- float evaluateBoundaryLength(ChartBuildData *chart, uint32_t f) const
+ float computeBoundaryLength(Chart *chart, uint32_t f) const
{
float boundaryLength = chart->boundaryLength;
// Add new edges, subtract edges shared with the chart.
@@ -5459,73 +5072,7 @@ private:
return max(0.0f, boundaryLength); // @@ Hack!
}
- Vector3 evaluateChartNormalSum(ChartBuildData *chart, uint32_t f) const
- {
- return chart->normalSum + m_mesh->triangleNormalAreaScaled(f);
- }
-
- // @@ Cleanup.
- struct Candidate {
- ChartBuildData *chart;
- uint32_t face;
- float metric;
- };
-
- // @@ Get N best candidates in one pass.
- const Candidate &getBestCandidate() const
- {
- uint32_t best = 0;
- float bestCandidateMetric = FLT_MAX;
- const uint32_t candidateCount = m_candidateArray.size();
- XA_ASSERT(candidateCount > 0);
- for (uint32_t i = 0; i < candidateCount; i++) {
- const Candidate &candidate = m_candidateArray[i];
- if (candidate.metric < bestCandidateMetric) {
- bestCandidateMetric = candidate.metric;
- best = i;
- }
- }
- return m_candidateArray[best];
- }
-
- void removeCandidate(uint32_t f)
- {
- int c = m_faceCandidateArray[f];
- if (c != -1) {
- m_faceCandidateArray[f] = (uint32_t)-1;
- if (c == int(m_candidateArray.size() - 1)) {
- m_candidateArray.pop_back();
- } else {
- // Replace with last.
- m_candidateArray[c] = m_candidateArray[m_candidateArray.size() - 1];
- m_candidateArray.pop_back();
- m_faceCandidateArray[m_candidateArray[c].face] = c;
- }
- }
- }
-
- void updateCandidate(ChartBuildData *chart, uint32_t f, float metric)
- {
- if (m_faceCandidateArray[f] == (uint32_t)-1) {
- const uint32_t index = m_candidateArray.size();
- m_faceCandidateArray[f] = index;
- m_candidateArray.resize(index + 1);
- m_candidateArray[index].face = f;
- m_candidateArray[index].chart = chart;
- m_candidateArray[index].metric = metric;
- } else {
- const uint32_t c = m_faceCandidateArray[f];
- XA_DEBUG_ASSERT(c != (uint32_t)-1);
- Candidate &candidate = m_candidateArray[c];
- XA_DEBUG_ASSERT(candidate.face == f);
- if (metric < candidate.metric || chart == candidate.chart) {
- candidate.metric = metric;
- candidate.chart = chart;
- }
- }
- }
-
- void mergeChart(ChartBuildData *owner, ChartBuildData *chart, float sharedBoundaryLength)
+ void mergeChart(Chart *owner, Chart *chart, float sharedBoundaryLength)
{
const uint32_t faceCount = chart->faces.size();
for (uint32_t i = 0; i < faceCount; i++) {
@@ -5538,10 +5085,10 @@ private:
owner->area += chart->area;
owner->boundaryLength += chart->boundaryLength - sharedBoundaryLength;
owner->normalSum += chart->normalSum;
- updateProxy(owner);
+ owner->averageNormal = normalizeSafe(owner->normalSum, Vector3(0), 0.0f);
// Delete chart.
m_chartArray[chart->id] = nullptr;
- chart->~ChartBuildData();
+ chart->~Chart();
XA_FREE(chart);
}
@@ -5551,18 +5098,448 @@ private:
Array<float> m_edgeLengths;
Array<float> m_faceAreas;
Array<Vector3> m_faceNormals;
+ Array<Vector3> m_faceTangents;
+ Array<Vector3> m_faceBitangents;
Array<Vector2> m_texcoords;
- Array<uint32_t> m_growFaces;
uint32_t m_facesLeft;
Array<int> m_faceChartArray;
- Array<ChartBuildData *> m_chartArray;
- Array<Candidate> m_candidateArray;
- Array<uint32_t> m_faceCandidateArray; // Map face index to candidate index.
+ Array<Chart *> m_chartArray;
PriorityQueue m_bestTriangles;
KISSRng m_rand;
ChartOptions m_options;
+ Array<Chart *> m_faceCandidateCharts;
+ Array<float> m_faceCandidateCosts;
+#if XA_GROW_CHARTS_COPLANAR
+ Array<uint32_t> m_nextPlanarRegionFace;
+#endif
+ Array<uint32_t> m_tempEdges1, m_tempEdges2;
+};
+
+} // namespace segment
+
+namespace param {
+
+class JacobiPreconditioner
+{
+public:
+ JacobiPreconditioner(const sparse::Matrix &M, bool symmetric) : m_inverseDiagonal(M.width())
+ {
+ XA_ASSERT(M.isSquare());
+ for (uint32_t x = 0; x < M.width(); x++) {
+ float elem = M.getCoefficient(x, x);
+ //XA_DEBUG_ASSERT( elem != 0.0f ); // This can be zero in the presence of zero area triangles.
+ if (symmetric) {
+ m_inverseDiagonal[x] = (elem != 0) ? 1.0f / sqrtf(fabsf(elem)) : 1.0f;
+ } else {
+ m_inverseDiagonal[x] = (elem != 0) ? 1.0f / elem : 1.0f;
+ }
+ }
+ }
+
+ void apply(const FullVector &x, FullVector &y) const
+ {
+ XA_DEBUG_ASSERT(x.dimension() == m_inverseDiagonal.dimension());
+ XA_DEBUG_ASSERT(y.dimension() == m_inverseDiagonal.dimension());
+ // @@ Wrap vector component-wise product into a separate function.
+ const uint32_t D = x.dimension();
+ for (uint32_t i = 0; i < D; i++) {
+ y[i] = m_inverseDiagonal[i] * x[i];
+ }
+ }
+
+private:
+ FullVector m_inverseDiagonal;
};
+// Linear solvers.
+class Solver
+{
+public:
+ // Solve the symmetric system: At·A·x = At·b
+ static bool LeastSquaresSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon = 1e-5f)
+ {
+ XA_DEBUG_ASSERT(A.width() == x.dimension());
+ XA_DEBUG_ASSERT(A.height() == b.dimension());
+ XA_DEBUG_ASSERT(A.height() >= A.width()); // @@ If height == width we could solve it directly...
+ const uint32_t D = A.width();
+ sparse::Matrix At(A.height(), A.width());
+ sparse::transpose(A, At);
+ FullVector Atb(D);
+ sparse::mult(At, b, Atb);
+ sparse::Matrix AtA(D);
+ sparse::mult(At, A, AtA);
+ return SymmetricSolver(AtA, Atb, x, epsilon);
+ }
+
+ // See section 10.4.3 in: Mesh Parameterization: Theory and Practice, Siggraph Course Notes, August 2007
+ static bool LeastSquaresSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, const uint32_t *lockedParameters, uint32_t lockedCount, float epsilon = 1e-5f)
+ {
+ XA_DEBUG_ASSERT(A.width() == x.dimension());
+ XA_DEBUG_ASSERT(A.height() == b.dimension());
+ XA_DEBUG_ASSERT(A.height() >= A.width() - lockedCount);
+ // @@ This is not the most efficient way of building a system with reduced degrees of freedom. It would be faster to do it on the fly.
+ const uint32_t D = A.width() - lockedCount;
+ XA_DEBUG_ASSERT(D > 0);
+ // Compute: b - Al * xl
+ FullVector b_Alxl(b);
+ for (uint32_t y = 0; y < A.height(); y++) {
+ const uint32_t count = A.getRow(y).size();
+ for (uint32_t e = 0; e < count; e++) {
+ uint32_t column = A.getRow(y)[e].x;
+ bool isFree = true;
+ for (uint32_t i = 0; i < lockedCount; i++) {
+ isFree &= (lockedParameters[i] != column);
+ }
+ if (!isFree) {
+ b_Alxl[y] -= x[column] * A.getRow(y)[e].v;
+ }
+ }
+ }
+ // Remove locked columns from A.
+ sparse::Matrix Af(D, A.height());
+ for (uint32_t y = 0; y < A.height(); y++) {
+ const uint32_t count = A.getRow(y).size();
+ for (uint32_t e = 0; e < count; e++) {
+ uint32_t column = A.getRow(y)[e].x;
+ uint32_t ix = column;
+ bool isFree = true;
+ for (uint32_t i = 0; i < lockedCount; i++) {
+ isFree &= (lockedParameters[i] != column);
+ if (column > lockedParameters[i]) ix--; // shift columns
+ }
+ if (isFree) {
+ Af.setCoefficient(ix, y, A.getRow(y)[e].v);
+ }
+ }
+ }
+ // Remove elements from x
+ FullVector xf(D);
+ for (uint32_t i = 0, j = 0; i < A.width(); i++) {
+ bool isFree = true;
+ for (uint32_t l = 0; l < lockedCount; l++) {
+ isFree &= (lockedParameters[l] != i);
+ }
+ if (isFree) {
+ xf[j++] = x[i];
+ }
+ }
+ // Solve reduced system.
+ bool result = LeastSquaresSolver(Af, b_Alxl, xf, epsilon);
+ // Copy results back to x.
+ for (uint32_t i = 0, j = 0; i < A.width(); i++) {
+ bool isFree = true;
+ for (uint32_t l = 0; l < lockedCount; l++) {
+ isFree &= (lockedParameters[l] != i);
+ }
+ if (isFree) {
+ x[i] = xf[j++];
+ }
+ }
+ return result;
+ }
+
+private:
+ /**
+ * Compute the solution of the sparse linear system Ab=x using the Conjugate
+ * Gradient method.
+ *
+ * Solving sparse linear systems:
+ * (1) A·x = b
+ *
+ * The conjugate gradient algorithm solves (1) only in the case that A is
+ * symmetric and positive definite. It is based on the idea of minimizing the
+ * function
+ *
+ * (2) f(x) = 1/2·x·A·x - b·x
+ *
+ * This function is minimized when its gradient
+ *
+ * (3) df = A·x - b
+ *
+ * is zero, which is equivalent to (1). The minimization is carried out by
+ * generating a succession of search directions p.k and improved minimizers x.k.
+ * At each stage a quantity alfa.k is found that minimizes f(x.k + alfa.k·p.k),
+ * and x.k+1 is set equal to the new point x.k + alfa.k·p.k. The p.k and x.k are
+ * built up in such a way that x.k+1 is also the minimizer of f over the whole
+ * vector space of directions already taken, {p.1, p.2, . . . , p.k}. After N
+ * iterations you arrive at the minimizer over the entire vector space, i.e., the
+ * solution to (1).
+ *
+ * For a really good explanation of the method see:
+ *
+ * "An Introduction to the Conjugate Gradient Method Without the Agonizing Pain",
+ * Jonhathan Richard Shewchuk.
+ *
+ **/
+ // Conjugate gradient with preconditioner.
+ static bool ConjugateGradientSolver(const JacobiPreconditioner &preconditioner, const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon)
+ {
+ XA_DEBUG_ASSERT( A.isSquare() );
+ XA_DEBUG_ASSERT( A.width() == b.dimension() );
+ XA_DEBUG_ASSERT( A.width() == x.dimension() );
+ int i = 0;
+ const int D = A.width();
+ const int i_max = 4 * D; // Convergence should be linear, but in some cases, it's not.
+ FullVector r(D); // residual
+ FullVector p(D); // search direction
+ FullVector q(D); //
+ FullVector s(D); // preconditioned
+ float delta_0;
+ float delta_old;
+ float delta_new;
+ float alpha;
+ float beta;
+ // r = b - A·x
+ sparse::copy(b, r);
+ sparse::sgemv(-1, A, x, 1, r);
+ // p = M^-1 · r
+ preconditioner.apply(r, p);
+ delta_new = sparse::dot(r, p);
+ delta_0 = delta_new;
+ while (i < i_max && delta_new > epsilon * epsilon * delta_0) {
+ i++;
+ // q = A·p
+ sparse::mult(A, p, q);
+ // alpha = delta_new / p·q
+ alpha = delta_new / sparse::dot(p, q);
+ // x = alfa·p + x
+ sparse::saxpy(alpha, p, x);
+ if ((i & 31) == 0) { // recompute r after 32 steps
+ // r = b - A·x
+ sparse::copy(b, r);
+ sparse::sgemv(-1, A, x, 1, r);
+ } else {
+ // r = r - alfa·q
+ sparse::saxpy(-alpha, q, r);
+ }
+ // s = M^-1 · r
+ preconditioner.apply(r, s);
+ delta_old = delta_new;
+ delta_new = sparse::dot( r, s );
+ beta = delta_new / delta_old;
+ // p = s + beta·p
+ sparse::scal(beta, p);
+ sparse::saxpy(1, s, p);
+ }
+ return delta_new <= epsilon * epsilon * delta_0;
+ }
+
+ static bool SymmetricSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon = 1e-5f)
+ {
+ XA_DEBUG_ASSERT(A.height() == A.width());
+ XA_DEBUG_ASSERT(A.height() == b.dimension());
+ XA_DEBUG_ASSERT(b.dimension() == x.dimension());
+ JacobiPreconditioner jacobi(A, true);
+ return ConjugateGradientSolver(jacobi, A, b, x, epsilon);
+ }
+};
+
+// Fast sweep in 3 directions
+static bool findApproximateDiameterVertices(Mesh *mesh, uint32_t *a, uint32_t *b)
+{
+ XA_DEBUG_ASSERT(a != nullptr);
+ XA_DEBUG_ASSERT(b != nullptr);
+ const uint32_t vertexCount = mesh->vertexCount();
+ uint32_t minVertex[3];
+ uint32_t maxVertex[3];
+ minVertex[0] = minVertex[1] = minVertex[2] = UINT32_MAX;
+ maxVertex[0] = maxVertex[1] = maxVertex[2] = UINT32_MAX;
+ for (uint32_t v = 1; v < vertexCount; v++) {
+ if (mesh->isBoundaryVertex(v)) {
+ minVertex[0] = minVertex[1] = minVertex[2] = v;
+ maxVertex[0] = maxVertex[1] = maxVertex[2] = v;
+ break;
+ }
+ }
+ if (minVertex[0] == UINT32_MAX) {
+ // Input mesh has not boundaries.
+ return false;
+ }
+ for (uint32_t v = 1; v < vertexCount; v++) {
+ if (!mesh->isBoundaryVertex(v)) {
+ // Skip interior vertices.
+ continue;
+ }
+ const Vector3 &pos = mesh->position(v);
+ if (pos.x < mesh->position(minVertex[0]).x)
+ minVertex[0] = v;
+ else if (pos.x > mesh->position(maxVertex[0]).x)
+ maxVertex[0] = v;
+ if (pos.y < mesh->position(minVertex[1]).y)
+ minVertex[1] = v;
+ else if (pos.y > mesh->position(maxVertex[1]).y)
+ maxVertex[1] = v;
+ if (pos.z < mesh->position(minVertex[2]).z)
+ minVertex[2] = v;
+ else if (pos.z > mesh->position(maxVertex[2]).z)
+ maxVertex[2] = v;
+ }
+ float lengths[3];
+ for (int i = 0; i < 3; i++) {
+ lengths[i] = length(mesh->position(minVertex[i]) - mesh->position(maxVertex[i]));
+ }
+ if (lengths[0] > lengths[1] && lengths[0] > lengths[2]) {
+ *a = minVertex[0];
+ *b = maxVertex[0];
+ } else if (lengths[1] > lengths[2]) {
+ *a = minVertex[1];
+ *b = maxVertex[1];
+ } else {
+ *a = minVertex[2];
+ *b = maxVertex[2];
+ }
+ return true;
+}
+
+// Conformal relations from Brecht Van Lommel (based on ABF):
+
+static float vec_angle_cos(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3)
+{
+ Vector3 d1 = v1 - v2;
+ Vector3 d2 = v3 - v2;
+ return clamp(dot(d1, d2) / (length(d1) * length(d2)), -1.0f, 1.0f);
+}
+
+static float vec_angle(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3)
+{
+ float dot = vec_angle_cos(v1, v2, v3);
+ return acosf(dot);
+}
+
+static void triangle_angles(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, float *a1, float *a2, float *a3)
+{
+ *a1 = vec_angle(v3, v1, v2);
+ *a2 = vec_angle(v1, v2, v3);
+ *a3 = kPi - *a2 - *a1;
+}
+
+static void setup_abf_relations(sparse::Matrix &A, int row, int id0, int id1, int id2, const Vector3 &p0, const Vector3 &p1, const Vector3 &p2)
+{
+ // @@ IC: Wouldn't it be more accurate to return cos and compute 1-cos^2?
+ // It does indeed seem to be a little bit more robust.
+ // @@ Need to revisit this more carefully!
+ float a0, a1, a2;
+ triangle_angles(p0, p1, p2, &a0, &a1, &a2);
+ float s0 = sinf(a0);
+ float s1 = sinf(a1);
+ float s2 = sinf(a2);
+ if (s1 > s0 && s1 > s2) {
+ swap(s1, s2);
+ swap(s0, s1);
+ swap(a1, a2);
+ swap(a0, a1);
+ swap(id1, id2);
+ swap(id0, id1);
+ } else if (s0 > s1 && s0 > s2) {
+ swap(s0, s2);
+ swap(s0, s1);
+ swap(a0, a2);
+ swap(a0, a1);
+ swap(id0, id2);
+ swap(id0, id1);
+ }
+ float c0 = cosf(a0);
+ float ratio = (s2 == 0.0f) ? 1.0f : s1 / s2;
+ float cosine = c0 * ratio;
+ float sine = s0 * ratio;
+ // Note : 2*id + 0 --> u
+ // 2*id + 1 --> v
+ int u0_id = 2 * id0 + 0;
+ int v0_id = 2 * id0 + 1;
+ int u1_id = 2 * id1 + 0;
+ int v1_id = 2 * id1 + 1;
+ int u2_id = 2 * id2 + 0;
+ int v2_id = 2 * id2 + 1;
+ // Real part
+ A.setCoefficient(u0_id, 2 * row + 0, cosine - 1.0f);
+ A.setCoefficient(v0_id, 2 * row + 0, -sine);
+ A.setCoefficient(u1_id, 2 * row + 0, -cosine);
+ A.setCoefficient(v1_id, 2 * row + 0, sine);
+ A.setCoefficient(u2_id, 2 * row + 0, 1);
+ // Imaginary part
+ A.setCoefficient(u0_id, 2 * row + 1, sine);
+ A.setCoefficient(v0_id, 2 * row + 1, cosine - 1.0f);
+ A.setCoefficient(u1_id, 2 * row + 1, -sine);
+ A.setCoefficient(v1_id, 2 * row + 1, -cosine);
+ A.setCoefficient(v2_id, 2 * row + 1, 1);
+}
+
+static bool computeLeastSquaresConformalMap(Mesh *mesh)
+{
+ // For this to work properly, mesh should not have colocals that have the same
+ // attributes, unless you want the vertices to actually have different texcoords.
+ const uint32_t vertexCount = mesh->vertexCount();
+ const uint32_t D = 2 * vertexCount;
+ const uint32_t N = 2 * mesh->faceCount();
+ // N is the number of equations (one per triangle)
+ // D is the number of variables (one per vertex; there are 2 pinned vertices).
+ if (N < D - 4) {
+ return false;
+ }
+ sparse::Matrix A(D, N);
+ FullVector b(N);
+ FullVector x(D);
+ // Fill b:
+ b.fill(0.0f);
+ // Fill x:
+ uint32_t v0, v1;
+ if (!findApproximateDiameterVertices(mesh, &v0, &v1)) {
+ // Mesh has no boundaries.
+ return false;
+ }
+ if (mesh->texcoord(v0) == mesh->texcoord(v1)) {
+ // LSCM expects an existing parameterization.
+ return false;
+ }
+ for (uint32_t v = 0; v < vertexCount; v++) {
+ // Initial solution.
+ x[2 * v + 0] = mesh->texcoord(v).x;
+ x[2 * v + 1] = mesh->texcoord(v).y;
+ }
+ // Fill A:
+ const uint32_t faceCount = mesh->faceCount();
+ for (uint32_t f = 0, t = 0; f < faceCount; f++) {
+ const uint32_t vertex0 = mesh->vertexAt(f * 3 + 0);
+ const uint32_t vertex1 = mesh->vertexAt(f * 3 + 1);
+ const uint32_t vertex2 = mesh->vertexAt(f * 3 + 2);
+ setup_abf_relations(A, t, vertex0, vertex1, vertex2, mesh->position(vertex0), mesh->position(vertex1), mesh->position(vertex2));
+ t++;
+ }
+ const uint32_t lockedParameters[] = {
+ 2 * v0 + 0,
+ 2 * v0 + 1,
+ 2 * v1 + 0,
+ 2 * v1 + 1
+ };
+ // Solve
+ Solver::LeastSquaresSolver(A, b, x, lockedParameters, 4, 0.000001f);
+ // Map x back to texcoords:
+ for (uint32_t v = 0; v < vertexCount; v++)
+ mesh->texcoord(v) = Vector2(x[2 * v + 0], x[2 * v + 1]);
+ return true;
+}
+
+static bool computeOrthogonalProjectionMap(Mesh *mesh)
+{
+ uint32_t vertexCount = mesh->vertexCount();
+ // Avoid redundant computations.
+ float matrix[6];
+ Fit::computeCovariance(vertexCount, &mesh->position(0), matrix);
+ if (matrix[0] == 0 && matrix[3] == 0 && matrix[5] == 0)
+ return false;
+ float eigenValues[3];
+ Vector3 eigenVectors[3];
+ if (!Fit::eigenSolveSymmetric3(matrix, eigenValues, eigenVectors))
+ return false;
+ Vector3 axis[2];
+ axis[0] = normalize(eigenVectors[0], kEpsilon);
+ axis[1] = normalize(eigenVectors[1], kEpsilon);
+ // Project vertices to plane.
+ for (uint32_t i = 0; i < vertexCount; i++)
+ mesh->texcoord(i) = Vector2(dot(axis[0], mesh->position(i)), dot(axis[1], mesh->position(i)));
+ return true;
+}
+
// Estimate quality of existing parameterization.
struct ParameterizationQuality
{
@@ -5578,15 +5555,15 @@ struct ParameterizationQuality
bool boundaryIntersection = false;
};
-static ParameterizationQuality calculateParameterizationQuality(const Mesh *mesh, Array<uint32_t> *flippedFaces)
+static ParameterizationQuality calculateParameterizationQuality(const Mesh *mesh, uint32_t faceCount, Array<uint32_t> *flippedFaces)
{
XA_DEBUG_ASSERT(mesh != nullptr);
ParameterizationQuality quality;
- const uint32_t faceCount = mesh->faceCount();
uint32_t firstBoundaryEdge = UINT32_MAX;
for (uint32_t e = 0; e < mesh->edgeCount(); e++) {
if (mesh->isBoundaryEdge(e)) {
firstBoundaryEdge = e;
+ break;
}
}
XA_DEBUG_ASSERT(firstBoundaryEdge != UINT32_MAX);
@@ -5681,7 +5658,8 @@ static ParameterizationQuality calculateParameterizationQuality(const Mesh *mesh
// If more than half the triangles are flipped, reverse the flipped / not flipped classification.
quality.flippedTriangleCount = quality.totalTriangleCount - quality.flippedTriangleCount;
if (flippedFaces) {
- Array<uint32_t> temp(*flippedFaces);
+ Array<uint32_t> temp;
+ flippedFaces->copyTo(temp);
flippedFaces->clear();
for (uint32_t f = 0; f < faceCount; f++) {
bool match = false;
@@ -5732,28 +5710,36 @@ struct ChartWarningFlags
class Chart
{
public:
- Chart(const Mesh *originalMesh, const Array<uint32_t> &faceArray, const Basis &basis, uint32_t meshId, uint32_t chartGroupId, uint32_t chartId) : m_basis(basis), m_mesh(nullptr), m_unifiedMesh(nullptr), m_isDisk(false), m_isOrtho(false), m_isPlanar(false), m_warningFlags(0), m_closedHolesCount(0), m_fixedTJunctionsCount(0), m_faceArray(faceArray)
+ Chart(const segment::Atlas *atlas, const Mesh *originalMesh, uint32_t chartIndex, uint32_t meshId, uint32_t chartGroupId, uint32_t chartId) : m_mesh(nullptr), m_unifiedMesh(nullptr), m_isDisk(false), m_isOrtho(false), m_isPlanar(false), m_warningFlags(0), m_closedHolesCount(0), m_fixedTJunctionsCount(0)
{
XA_UNUSED(meshId);
XA_UNUSED(chartGroupId);
XA_UNUSED(chartId);
+ m_basis = atlas->chartBasis(chartIndex);
+ atlas->chartFaces(chartIndex).copyTo(m_faceArray);
// Copy face indices.
- m_mesh = XA_NEW(MemTag::Mesh, Mesh, originalMesh->epsilon(), faceArray.size() * 3, faceArray.size());
- m_unifiedMesh = XA_NEW(MemTag::Mesh, Mesh, originalMesh->epsilon(), faceArray.size() * 3, faceArray.size());
+ m_mesh = XA_NEW_ARGS(MemTag::Mesh, Mesh, originalMesh->epsilon(), m_faceArray.size() * 3, m_faceArray.size());
+ m_unifiedMesh = XA_NEW_ARGS(MemTag::Mesh, Mesh, originalMesh->epsilon(), m_faceArray.size() * 3, m_faceArray.size());
Array<uint32_t> chartMeshIndices;
- chartMeshIndices.resize(originalMesh->vertexCount(), (uint32_t)~0);
+ chartMeshIndices.resize(originalMesh->vertexCount());
+ chartMeshIndices.setAll(UINT32_MAX);
Array<uint32_t> unifiedMeshIndices;
- unifiedMeshIndices.resize(originalMesh->vertexCount(), (uint32_t)~0);
+ unifiedMeshIndices.resize(originalMesh->vertexCount());
+ unifiedMeshIndices.setAll(UINT32_MAX);
// Add vertices.
- const uint32_t faceCount = faceArray.size();
+ const uint32_t faceCount = m_initialFaceCount = m_faceArray.size();
for (uint32_t f = 0; f < faceCount; f++) {
for (uint32_t i = 0; i < 3; i++) {
- const uint32_t vertex = originalMesh->vertexAt(faceArray[f] * 3 + i);
+ const uint32_t vertex = originalMesh->vertexAt(m_faceArray[f] * 3 + i);
const uint32_t unifiedVertex = originalMesh->firstColocal(vertex);
if (unifiedMeshIndices[unifiedVertex] == (uint32_t)~0) {
unifiedMeshIndices[unifiedVertex] = m_unifiedMesh->vertexCount();
XA_DEBUG_ASSERT(equal(originalMesh->position(vertex), originalMesh->position(unifiedVertex), originalMesh->epsilon()));
+#if XA_SKIP_PARAMETERIZATION
+ m_unifiedMesh->addVertex(originalMesh->position(vertex), Vector3(0.0f), atlas->faceTexcoords(m_faceArray[f])[i]);
+#else
m_unifiedMesh->addVertex(originalMesh->position(vertex));
+#endif
}
if (chartMeshIndices[vertex] == (uint32_t)~0) {
chartMeshIndices[vertex] = m_mesh->vertexCount();
@@ -5767,7 +5753,7 @@ public:
for (uint32_t f = 0; f < faceCount; f++) {
uint32_t indices[3], unifiedIndices[3];
for (uint32_t i = 0; i < 3; i++) {
- const uint32_t vertex = originalMesh->vertexAt(faceArray[f] * 3 + i);
+ const uint32_t vertex = originalMesh->vertexAt(m_faceArray[f] * 3 + i);
indices[i] = chartMeshIndices[vertex];
unifiedIndices[i] = unifiedMeshIndices[originalMesh->firstColocal(vertex)];
}
@@ -5810,6 +5796,7 @@ public:
m_unifiedMesh = fixedUnifiedMesh;
m_unifiedMesh->createBoundaries();
m_unifiedMesh->linkBoundaries();
+ m_initialFaceCount = m_unifiedMesh->faceCount(); // Fixing t-junctions rewrites faces.
}
// See if there are any holes that need closing.
Array<uint32_t> boundaryLoops;
@@ -5825,7 +5812,7 @@ public:
// - Use minimal spanning trees or seamster.
Array<uint32_t> holeFaceCounts;
XA_PROFILE_START(closeChartMeshHoles)
- failed = !meshCloseHoles(m_unifiedMesh, boundaryLoops, basis.normal, holeFaceCounts);
+ failed = !meshCloseHoles(m_unifiedMesh, boundaryLoops, m_basis.normal, holeFaceCounts);
XA_PROFILE_END(closeChartMeshHoles)
m_unifiedMesh->createBoundaries();
m_unifiedMesh->linkBoundaries();
@@ -5907,7 +5894,7 @@ public:
void evaluateOrthoParameterizationQuality()
{
XA_PROFILE_START(parameterizeChartsEvaluateQuality)
- m_paramQuality = calculateParameterizationQuality(m_unifiedMesh, nullptr);
+ m_paramQuality = calculateParameterizationQuality(m_unifiedMesh, m_initialFaceCount, nullptr);
XA_PROFILE_END(parameterizeChartsEvaluateQuality)
// Use orthogonal parameterization if quality is acceptable.
if (!m_paramQuality.boundaryIntersection && m_paramQuality.geometricArea > 0.0f && m_paramQuality.stretchMetric <= 1.1f && m_paramQuality.maxStretchMetric <= 1.25f)
@@ -5918,9 +5905,9 @@ public:
{
XA_PROFILE_START(parameterizeChartsEvaluateQuality)
#if XA_DEBUG_EXPORT_OBJ_INVALID_PARAMETERIZATION
- m_paramQuality = calculateParameterizationQuality(m_unifiedMesh, &m_paramFlippedFaces);
+ m_paramQuality = calculateParameterizationQuality(m_unifiedMesh, m_initialFaceCount, &m_paramFlippedFaces);
#else
- m_paramQuality = calculateParameterizationQuality(m_unifiedMesh, nullptr);
+ m_paramQuality = calculateParameterizationQuality(m_unifiedMesh, m_initialFaceCount, nullptr);
#endif
XA_PROFILE_END(parameterizeChartsEvaluateQuality)
}
@@ -5961,6 +5948,7 @@ private:
Mesh *m_unifiedMesh;
bool m_isDisk, m_isOrtho, m_isPlanar;
uint32_t m_warningFlags;
+ uint32_t m_initialFaceCount; // Before fixing T-junctions and/or closing holes.
uint32_t m_closedHolesCount, m_fixedTJunctionsCount;
// List of faces of the original mesh that belong to this chart.
@@ -5979,9 +5967,9 @@ private:
struct CreateChartTaskArgs
{
+ const segment::Atlas *atlas;
const Mesh *mesh;
- const Array<uint32_t> *faceArray;
- const Basis *basis;
+ uint32_t chartIndex; // In the atlas.
uint32_t meshId;
uint32_t chartGroupId;
uint32_t chartId;
@@ -5992,7 +5980,7 @@ static void runCreateChartTask(void *userData)
{
XA_PROFILE_START(createChartMeshesThread)
auto args = (CreateChartTaskArgs *)userData;
- *(args->chart) = XA_NEW(MemTag::Default, Chart, args->mesh, *(args->faceArray), *(args->basis), args->meshId, args->chartGroupId, args->chartId);
+ *(args->chart) = XA_NEW_ARGS(MemTag::Default, Chart, args->atlas, args->mesh, args->chartIndex, args->meshId, args->chartGroupId, args->chartId);
XA_PROFILE_END(createChartMeshesThread)
}
@@ -6043,10 +6031,11 @@ public:
}
// Only initial meshes have face groups and ignored faces. The only flag we care about is HasNormals.
const uint32_t faceCount = m_faceToSourceFaceMap.size();
- m_mesh = XA_NEW(MemTag::Mesh, Mesh, sourceMesh->epsilon(), faceCount * 3, faceCount, sourceMesh->flags() & MeshFlags::HasNormals);
+ m_mesh = XA_NEW_ARGS(MemTag::Mesh, Mesh, sourceMesh->epsilon(), faceCount * 3, faceCount, sourceMesh->flags() & MeshFlags::HasNormals);
XA_DEBUG_ASSERT(faceCount > 0);
Array<uint32_t> meshIndices;
- meshIndices.resize(sourceMesh->vertexCount(), (uint32_t)~0);
+ meshIndices.resize(sourceMesh->vertexCount());
+ meshIndices.setAll((uint32_t)~0);
for (uint32_t f = 0; f < faceCount; f++) {
const uint32_t face = m_faceToSourceFaceMap[f];
for (uint32_t i = 0; i < 3; i++) {
@@ -6183,22 +6172,22 @@ public:
chartFaces.resize(m_mesh->faceCount());
for (uint32_t i = 0; i < chartFaces.size(); i++)
chartFaces[i] = i;
- Chart *chart = XA_NEW(MemTag::Default, Chart, m_mesh, chartFaces, m_sourceId, m_id, 0);
+ Chart *chart = XA_NEW_ARGS(MemTag::Default, Chart, m_mesh, chartFaces, m_sourceId, m_id, 0);
m_chartArray.push_back(chart);
#else
- XA_PROFILE_START(atlasBuilder)
- AtlasBuilder builder(m_mesh, nullptr, options);
- runAtlasBuilder(builder, options);
- XA_PROFILE_END(atlasBuilder)
- const uint32_t chartCount = builder.chartCount();
+ XA_PROFILE_START(buildAtlas)
+ segment::Atlas atlas(m_mesh, nullptr, options);
+ buildAtlas(atlas, options);
+ XA_PROFILE_END(buildAtlas)
+ const uint32_t chartCount = atlas.chartCount();
m_chartArray.resize(chartCount);
Array<CreateChartTaskArgs> taskArgs;
taskArgs.resize(chartCount);
for (uint32_t i = 0; i < chartCount; i++) {
CreateChartTaskArgs &args = taskArgs[i];
+ args.atlas = &atlas;
args.mesh = m_mesh;
- args.faceArray = &builder.chartFaces(i);
- args.basis = &builder.chartBasis(i);
+ args.chartIndex = i;
args.meshId = m_sourceId;
args.chartGroupId = m_id;
args.chartId = i;
@@ -6239,6 +6228,16 @@ public:
void parameterizeCharts(TaskScheduler *taskScheduler, ParameterizeFunc func)
{
const uint32_t chartCount = m_chartArray.size();
+#if XA_SKIP_PARAMETERIZATION
+ XA_UNUSED(taskScheduler);
+ XA_UNUSED(func);
+ for (uint32_t i = 0; i < chartCount; i++) {
+ Chart *chart = m_chartArray[i];
+ chart->evaluateOrthoParameterizationQuality();
+ chart->evaluateParameterizationQuality();
+ chart->transferParameterization();
+ }
+#else
Array<ParameterizeChartTaskArgs> taskArgs;
taskArgs.resize(chartCount);
TaskGroupHandle taskGroup = taskScheduler->createTaskGroup(chartCount);
@@ -6279,10 +6278,10 @@ public:
options.maxChartArea = invalidChartArea * 0.2f;
options.maxThreshold = 0.25f;
options.maxIterations = 3;
- AtlasBuilder builder(m_mesh, &meshFaces, options);
- runAtlasBuilder(builder, options);
- for (uint32_t j = 0; j < builder.chartCount(); j++) {
- Chart *chart = XA_NEW(MemTag::Default, Chart, m_mesh, builder.chartFaces(j), builder.chartBasis(j), m_sourceId, m_id, m_chartArray.size());
+ segment::Atlas atlas(m_mesh, &meshFaces, options);
+ buildAtlas(atlas, options);
+ for (uint32_t j = 0; j < atlas.chartCount(); j++) {
+ Chart *chart = XA_NEW_ARGS(MemTag::Default, Chart, &atlas, m_mesh, j, m_sourceId, m_id, m_chartArray.size());
m_chartArray.push_back(chart);
m_paramAddedChartsCount++;
}
@@ -6325,46 +6324,41 @@ public:
XA_FREE(chart);
m_paramDeletedChartsCount++;
}
-#endif
+#endif // XA_RECOMPUTE_CHARTS
+#endif // XA_SKIP_PARAMETERIZATION
}
private:
- void runAtlasBuilder(AtlasBuilder &builder, const ChartOptions &options)
+ void buildAtlas(segment::Atlas &atlas, const ChartOptions &options)
{
- if (builder.facesLeft() == 0)
+ if (atlas.facesLeft() == 0)
return;
- // This seems a reasonable estimate.
- XA_PROFILE_START(atlasBuilderCreateInitialCharts)
// Create initial charts greedely.
- builder.placeSeeds(options.maxThreshold * 0.5f);
+ atlas.placeSeeds(options.maxThreshold * 0.5f);
if (options.maxIterations == 0) {
- XA_DEBUG_ASSERT(builder.facesLeft() == 0);
- XA_PROFILE_END(atlasBuilderCreateInitialCharts)
+ XA_DEBUG_ASSERT(atlas.facesLeft() == 0);
return;
}
- builder.updateProxies();
- builder.relocateSeeds();
- builder.resetCharts();
- XA_PROFILE_END(atlasBuilderCreateInitialCharts)
+ atlas.relocateSeeds();
+ atlas.resetCharts();
// Restart process growing charts in parallel.
uint32_t iteration = 0;
while (true) {
- if (!builder.growCharts(options.maxThreshold, options.growFaceCount)) {
+ if (!atlas.growCharts(options.maxThreshold)) {
// If charts cannot grow more: fill holes, merge charts, relocate seeds and start new iteration.
- builder.fillHoles(options.maxThreshold * 0.5f);
- builder.updateProxies();
+ atlas.fillHoles(options.maxThreshold * 0.5f);
#if XA_MERGE_CHARTS
- builder.mergeCharts();
+ atlas.mergeCharts();
#endif
if (++iteration == options.maxIterations)
break;
- if (!builder.relocateSeeds())
+ if (!atlas.relocateSeeds())
break;
- builder.resetCharts();
+ atlas.resetCharts();
}
}
// Make sure no holes are left!
- XA_DEBUG_ASSERT(builder.facesLeft() == 0);
+ XA_DEBUG_ASSERT(atlas.facesLeft() == 0);
}
void removeChart(const Chart *chart)
@@ -6400,7 +6394,7 @@ static void runCreateChartGroupTask(void *userData)
{
XA_PROFILE_START(addMeshCreateChartGroupsThread)
auto args = (CreateChartGroupTaskArgs *)userData;
- *(args->chartGroup) = XA_NEW(MemTag::Default, ChartGroup, args->groupId, args->mesh, args->faceGroup);
+ *(args->chartGroup) = XA_NEW_ARGS(MemTag::Default, ChartGroup, args->groupId, args->mesh, args->faceGroup);
XA_PROFILE_END(addMeshCreateChartGroupsThread)
}
@@ -6448,7 +6442,7 @@ static void runParameterizeChartsJob(void *userData)
class Atlas
{
public:
- Atlas() : m_chartsComputed(false), m_chartsParameterized(false) {}
+ Atlas() : m_meshCount(0), m_chartsComputed(false), m_chartsParameterized(false) {}
~Atlas()
{
@@ -6460,6 +6454,8 @@ public:
bool chartsComputed() const { return m_chartsComputed; }
bool chartsParameterized() const { return m_chartsParameterized; }
+ uint32_t chartGroupCount() const { return m_chartGroups.size(); }
+ const ChartGroup *chartGroupAt(uint32_t index) const { return m_chartGroups[index]; }
uint32_t chartGroupCount(uint32_t mesh) const
{
@@ -6483,26 +6479,6 @@ public:
return nullptr;
}
- uint32_t chartCount() const
- {
- uint32_t count = 0;
- for (uint32_t i = 0; i < m_chartGroups.size(); i++)
- count += m_chartGroups[i]->chartCount();
- return count;
- }
-
- Chart *chartAt(uint32_t i)
- {
- for (uint32_t c = 0; c < m_chartGroups.size(); c++) {
- uint32_t count = m_chartGroups[c]->chartCount();
- if (i < count) {
- return m_chartGroups[c]->chartAt(i);
- }
- i -= count;
- }
- return nullptr;
- }
-
// This function is thread safe.
void addMesh(TaskScheduler *taskScheduler, const Mesh *mesh)
{
@@ -6548,9 +6524,32 @@ public:
m_chartGroups.push_back(chartGroups[g]);
m_chartGroupSourceMeshes.push_back(mesh->id());
}
+ m_meshCount++;
m_addMeshMutex.unlock();
}
+ // Chart id/index is determined by depth-first hierarchy of mesh -> chart group -> chart.
+ // For chart index to be consistent here, chart groups needs to sorted by mesh index. Since addMesh is called by multithreaded tasks, order is indeterminate, so chart groups need to be explicitly sorted after all meshes are added.
+ void sortChartGroups()
+ {
+ Array<ChartGroup *> oldChartGroups;
+ oldChartGroups.resize(m_chartGroups.size());
+ memcpy(oldChartGroups.data(), m_chartGroups.data(), sizeof(ChartGroup *) * m_chartGroups.size());
+ Array<uint32_t> oldChartGroupSourceMeshes;
+ oldChartGroupSourceMeshes.resize(m_chartGroupSourceMeshes.size());
+ memcpy(oldChartGroupSourceMeshes.data(), m_chartGroupSourceMeshes.data(), sizeof(uint32_t) * m_chartGroupSourceMeshes.size());
+ uint32_t current = 0;
+ for (uint32_t i = 0; i < m_meshCount; i++) {
+ for (uint32_t j = 0; j < oldChartGroups.size(); j++) {
+ if (oldChartGroupSourceMeshes[j] == i) {
+ m_chartGroups[current] = oldChartGroups[j];
+ m_chartGroupSourceMeshes[current] = oldChartGroupSourceMeshes[j];
+ current++;
+ }
+ }
+ }
+ }
+
bool computeCharts(TaskScheduler *taskScheduler, const ChartOptions &options, ProgressFunc progressFunc, void *progressUserData)
{
m_chartsComputed = false;
@@ -6629,37 +6628,18 @@ public:
taskScheduler->wait(&taskGroup);
if (progress.cancel)
return false;
- // Save original texcoords so PackCharts can be called multiple times (packing overwrites the texcoords).
- const uint32_t nCharts = chartCount();
- m_originalChartTexcoords.resize(nCharts);
- for (uint32_t i = 0; i < nCharts; i++) {
- const Mesh *mesh = chartAt(i)->mesh();
- m_originalChartTexcoords[i].resize(mesh->vertexCount());
- for (uint32_t j = 0; j < mesh->vertexCount(); j++)
- m_originalChartTexcoords[i][j] = mesh->texcoord(j);
- }
m_chartsParameterized = true;
return true;
}
- void restoreOriginalChartTexcoords()
- {
- const uint32_t nCharts = chartCount();
- for (uint32_t i = 0; i < nCharts; i++) {
- Mesh *mesh = chartAt(i)->mesh();
- for (uint32_t j = 0; j < mesh->vertexCount(); j++)
- mesh->texcoord(j) = m_originalChartTexcoords[i][j];
- }
- }
-
private:
std::mutex m_addMeshMutex;
+ uint32_t m_meshCount;
bool m_chartsComputed;
bool m_chartsParameterized;
Array<ChartGroup *> m_chartGroups;
RadixSort m_chartGroupsRadix; // By mesh indexCount.
Array<uint32_t> m_chartGroupSourceMeshes;
- Array<Array<Vector2> > m_originalChartTexcoords;
};
} // namespace param
@@ -6733,10 +6713,10 @@ public:
memcpy(&data[y * width], &m_data[y * m_width], min(m_width, width) * sizeof(uint32_t));
m_width = width;
m_height = height;
- swap(m_data, data);
+ data.moveTo(m_data);
}
- void addChart(uint32_t chartIndex, const BitImage *image, bool imageHasPadding, int atlas_w, int atlas_h, int offset_x, int offset_y)
+ void addChart(uint32_t chartIndex, const BitImage *image, const BitImage *imageBilinear, const BitImage *imagePadding, int atlas_w, int atlas_h, int offset_x, int offset_y)
{
const int w = image->width();
const int h = image->height();
@@ -6746,23 +6726,27 @@ public:
continue;
for (int x = 0; x < w; x++) {
const int xx = x + offset_x;
- if (xx >= 0 && xx < atlas_w && yy < atlas_h && image->bitAt(x, y)) {
+ if (xx >= 0 && xx < atlas_w && yy < atlas_h) {
const uint32_t dataOffset = xx + yy * m_width;
- if (m_data[dataOffset] != 0)
- continue;
- uint32_t value = chartIndex | kImageHasChartIndexBit;
- if (imageHasPadding)
- value |= kImageIsPaddingBit;
- m_data[dataOffset] = value;
+ if (image->bitAt(x, y)) {
+ XA_DEBUG_ASSERT(m_data[dataOffset] == 0);
+ m_data[dataOffset] = chartIndex | kImageHasChartIndexBit;
+ } else if (imageBilinear && imageBilinear->bitAt(x, y)) {
+ XA_DEBUG_ASSERT(m_data[dataOffset] == 0);
+ m_data[dataOffset] = chartIndex | kImageHasChartIndexBit | kImageIsBilinearBit;
+ } else if (imagePadding && imagePadding->bitAt(x, y)) {
+ XA_DEBUG_ASSERT(m_data[dataOffset] == 0);
+ m_data[dataOffset] = chartIndex | kImageHasChartIndexBit | kImageIsPaddingBit;
+ }
}
}
}
}
- void copyTo(uint32_t *dest, uint32_t destWidth, uint32_t destHeight) const
+ void copyTo(uint32_t *dest, uint32_t destWidth, uint32_t destHeight, int padding) const
{
for (uint32_t y = 0; y < destHeight; y++)
- memcpy(&dest[y * destWidth], &m_data[y * m_width], destWidth * sizeof(uint32_t));
+ memcpy(&dest[y * destWidth], &m_data[padding + (y + padding) * m_width], destWidth * sizeof(uint32_t));
}
#if XA_DEBUG_EXPORT_ATLAS_IMAGES
@@ -6777,20 +6761,26 @@ public:
if (x >= m_width)
continue;
const uint32_t data = m_data[x + y * m_width];
- if (!(data & kImageHasChartIndexBit))
+ uint8_t *bgr = &image[(x + y * width) * 3];
+ if (data == 0) {
+ bgr[0] = bgr[1] = bgr[2] = 0;
continue;
+ }
const uint32_t chartIndex = data & kImageChartIndexMask;
- uint8_t *color = &image[(x + y * width) * 3];
if (data & kImageIsPaddingBit) {
- color[0] = 255;
- color[1] = 0;
- color[2] = 255;
+ bgr[0] = 0;
+ bgr[1] = 0;
+ bgr[2] = 255;
+ } else if (data & kImageIsBilinearBit) {
+ bgr[0] = 0;
+ bgr[1] = 255;
+ bgr[2] = 0;
} else {
const int mix = 192;
srand((unsigned int)chartIndex);
- color[0] = uint8_t((rand() % 255 + mix) * 0.5f);
- color[1] = uint8_t((rand() % 255 + mix) * 0.5f);
- color[2] = uint8_t((rand() % 255 + mix) * 0.5f);
+ bgr[0] = uint8_t((rand() % 255 + mix) * 0.5f);
+ bgr[1] = uint8_t((rand() % 255 + mix) * 0.5f);
+ bgr[2] = uint8_t((rand() % 255 + mix) * 0.5f);
}
}
}
@@ -6817,11 +6807,61 @@ struct Chart
bool allowRotate;
// bounding box
Vector2 majorAxis, minorAxis, minCorner, maxCorner;
+ // UvMeshChart only
+ Array<uint32_t> faces;
Vector2 &uniqueVertexAt(uint32_t v) { return uniqueVertices.isEmpty() ? vertices[v] : vertices[uniqueVertices[v]]; }
uint32_t uniqueVertexCount() const { return uniqueVertices.isEmpty() ? vertexCount : uniqueVertices.size(); }
};
+struct AddChartTaskArgs
+{
+ param::Chart *paramChart;
+ Chart *chart; // out
+};
+
+static void runAddChartTask(void *userData)
+{
+ XA_PROFILE_START(packChartsAddChartsThread)
+ auto args = (AddChartTaskArgs *)userData;
+ param::Chart *paramChart = args->paramChart;
+ XA_PROFILE_START(packChartsAddChartsRestoreTexcoords)
+ paramChart->transferParameterization();
+ XA_PROFILE_END(packChartsAddChartsRestoreTexcoords)
+ Mesh *mesh = paramChart->mesh();
+ Chart *chart = args->chart = XA_NEW(MemTag::Default, Chart);
+ chart->atlasIndex = -1;
+ chart->material = 0;
+ chart->indexCount = mesh->indexCount();
+ chart->indices = mesh->indices();
+ chart->parametricArea = paramChart->computeParametricArea();
+ if (chart->parametricArea < kAreaEpsilon) {
+ // When the parametric area is too small we use a rough approximation to prevent divisions by very small numbers.
+ const Vector2 bounds = paramChart->computeParametricBounds();
+ chart->parametricArea = bounds.x * bounds.y;
+ }
+ chart->surfaceArea = paramChart->computeSurfaceArea();
+ chart->vertices = mesh->texcoords();
+ chart->vertexCount = mesh->vertexCount();
+ chart->allowRotate = true;
+ // Compute list of boundary vertices.
+ Array<Vector2> boundary;
+ boundary.reserve(16);
+ for (uint32_t v = 0; v < chart->vertexCount; v++) {
+ if (mesh->isBoundaryVertex(v))
+ boundary.push_back(mesh->texcoord(v));
+ }
+ XA_DEBUG_ASSERT(boundary.size() > 0);
+ // Compute bounding box of chart.
+ static thread_local BoundingBox2D boundingBox;
+ boundingBox.compute(boundary.data(), boundary.size(), mesh->texcoords(), mesh->vertexCount());
+ chart->majorAxis = boundingBox.majorAxis();
+ chart->minorAxis = boundingBox.minorAxis();
+ chart->minCorner = boundingBox.minCorner();
+ chart->maxCorner = boundingBox.maxCorner();
+ XA_PROFILE_END(packChartsAddChartsThread)
+}
+
struct FindChartLocationBruteForceTaskArgs
{
std::atomic<bool> *finished; // One of the tasks found a location that doesn't expand the atlas.
@@ -6830,7 +6870,8 @@ struct FindChartLocationBruteForceTaskArgs
const BitImage *chartBitImage;
const BitImage *chartBitImageRotated;
int w, h;
- bool blockAligned, resizableAtlas, allowRotate;
+ bool blockAligned, allowRotate;
+ uint32_t maxResolution;
// out
bool best_insideAtlas;
int best_metric, best_x, best_y, best_w, best_h, best_r;
@@ -6845,6 +6886,8 @@ static void runFindChartLocationBruteForceTask(void *userData)
return;
// Try two different orientations.
for (int r = 0; r < 2; r++) {
+ if (args->finished->load())
+ break;
int cw = args->chartBitImage->width();
int ch = args->chartBitImage->height();
if (r == 1) {
@@ -6855,8 +6898,8 @@ static void runFindChartLocationBruteForceTask(void *userData)
}
const int y = args->startPosition.y;
const int stepSize = args->blockAligned ? 4 : 1;
- for (int x = args->startPosition.x; x <= args->w + stepSize; x += stepSize) { // + 1 not really necessary here.
- if (!args->resizableAtlas && (x > (int)args->atlasBitImage->width() - cw || y > (int)args->atlasBitImage->height() - ch))
+ for (int x = args->startPosition.x; x <= args->w + stepSize; x += stepSize) {
+ if (args->maxResolution > 0 && (x > (int)args->maxResolution - cw || y > (int)args->maxResolution - ch))
continue;
if (args->finished->load())
break;
@@ -6891,6 +6934,10 @@ struct Atlas
{
~Atlas()
{
+ for (uint32_t i = 0; i < m_atlasImages.size(); i++) {
+ m_atlasImages[i]->~AtlasImage();
+ XA_FREE(m_atlasImages[i]);
+ }
for (uint32_t i = 0; i < m_bitImages.size(); i++) {
m_bitImages[i]->~BitImage();
XA_FREE(m_bitImages[i]);
@@ -6910,39 +6957,44 @@ struct Atlas
const Array<AtlasImage *> &getImages() const { return m_atlasImages; }
float getUtilization(uint32_t atlas) const { return m_utilization[atlas]; }
- void addChart(param::Chart *paramChart)
+ void addCharts(TaskScheduler *taskScheduler, param::Atlas *paramAtlas)
{
- Mesh *mesh = paramChart->mesh();
- Chart *chart = XA_NEW(MemTag::Default, Chart);
- chart->atlasIndex = -1;
- chart->material = 0;
- chart->indexCount = mesh->indexCount();
- chart->indices = mesh->indices();
- chart->parametricArea = paramChart->computeParametricArea();
- if (chart->parametricArea < kAreaEpsilon) {
- // When the parametric area is too small we use a rough approximation to prevent divisions by very small numbers.
- const Vector2 bounds = paramChart->computeParametricBounds();
- chart->parametricArea = bounds.x * bounds.y;
- }
- chart->surfaceArea = paramChart->computeSurfaceArea();
- chart->vertices = mesh->texcoords();
- chart->vertexCount = mesh->vertexCount();
- chart->allowRotate = true;
- // Compute list of boundary vertices.
- Array<Vector2> boundary;
- boundary.reserve(16);
- for (uint32_t v = 0; v < chart->vertexCount; v++) {
- if (mesh->isBoundaryVertex(v))
- boundary.push_back(mesh->texcoord(v));
+ // Count charts.
+ uint32_t chartCount = 0;
+ const uint32_t chartGroupsCount = paramAtlas->chartGroupCount();
+ for (uint32_t i = 0; i < chartGroupsCount; i++) {
+ const param::ChartGroup *chartGroup = paramAtlas->chartGroupAt(i);
+ if (chartGroup->isVertexMap())
+ continue;
+ chartCount += chartGroup->chartCount();
}
- XA_DEBUG_ASSERT(boundary.size() > 0);
- // Compute bounding box of chart.
- m_boundingBox.compute(boundary.data(), boundary.size(), mesh->texcoords(), mesh->vertexCount());
- chart->majorAxis = m_boundingBox.majorAxis();
- chart->minorAxis = m_boundingBox.minorAxis();
- chart->minCorner = m_boundingBox.minCorner();
- chart->maxCorner = m_boundingBox.maxCorner();
- m_charts.push_back(chart);
+ if (chartCount == 0)
+ return;
+ // Run one task per chart.
+ Array<AddChartTaskArgs> taskArgs;
+ taskArgs.resize(chartCount);
+ TaskGroupHandle taskGroup = taskScheduler->createTaskGroup(chartCount);
+ uint32_t chartIndex = 0;
+ for (uint32_t i = 0; i < chartGroupsCount; i++) {
+ const param::ChartGroup *chartGroup = paramAtlas->chartGroupAt(i);
+ if (chartGroup->isVertexMap())
+ continue;
+ const uint32_t count = chartGroup->chartCount();
+ for (uint32_t j = 0; j < count; j++) {
+ AddChartTaskArgs &args = taskArgs[chartIndex];
+ args.paramChart = chartGroup->chartAt(j);
+ Task task;
+ task.userData = &taskArgs[chartIndex];
+ task.func = runAddChartTask;
+ taskScheduler->run(taskGroup, task);
+ chartIndex++;
+ }
+ }
+ taskScheduler->wait(&taskGroup);
+ // Get task output.
+ m_charts.resize(chartCount);
+ for (uint32_t i = 0; i < chartCount; i++)
+ m_charts[i] = taskArgs[i].chart;
}
void addUvMeshCharts(UvMeshInstance *mesh)
@@ -6950,6 +7002,7 @@ struct Atlas
BitArray vertexUsed(mesh->texcoords.size());
Array<Vector2> boundary;
boundary.reserve(16);
+ BoundingBox2D boundingBox;
for (uint32_t c = 0; c < mesh->mesh->charts.size(); c++) {
UvMeshChart *uvChart = mesh->mesh->charts[c];
Chart *chart = XA_NEW(MemTag::Default, Chart);
@@ -6960,6 +7013,8 @@ struct Atlas
chart->vertices = mesh->texcoords.data();
chart->vertexCount = mesh->texcoords.size();
chart->allowRotate = mesh->rotateCharts;
+ chart->faces.resize(uvChart->faces.size());
+ memcpy(chart->faces.data(), uvChart->faces.data(), sizeof(uint32_t) * uvChart->faces.size());
// Find unique vertices.
vertexUsed.clearAll();
for (uint32_t i = 0; i < chart->indexCount; i++) {
@@ -6997,11 +7052,11 @@ struct Atlas
boundary.push_back(chart->uniqueVertexAt(v));
XA_DEBUG_ASSERT(boundary.size() > 0);
// Compute bounding box of chart.
- m_boundingBox.compute(boundary.data(), boundary.size(), boundary.data(), boundary.size());
- chart->majorAxis = m_boundingBox.majorAxis();
- chart->minorAxis = m_boundingBox.minorAxis();
- chart->minCorner = m_boundingBox.minCorner();
- chart->maxCorner = m_boundingBox.maxCorner();
+ boundingBox.compute(boundary.data(), boundary.size(), boundary.data(), boundary.size());
+ chart->majorAxis = boundingBox.majorAxis();
+ chart->minorAxis = boundingBox.minorAxis();
+ chart->minCorner = boundingBox.minCorner();
+ chart->maxCorner = boundingBox.maxCorner();
m_charts.push_back(chart);
}
}
@@ -7022,8 +7077,10 @@ struct Atlas
}
return true;
}
- uint32_t resolution = options.resolution;
+ // Estimate resolution and/or texels per unit if not specified.
m_texelsPerUnit = options.texelsPerUnit;
+ uint32_t resolution = options.resolution > 0 ? options.resolution + options.padding * 2 : 0;
+ const uint32_t maxResolution = m_texelsPerUnit > 0.0f ? resolution : 0;
if (resolution <= 0 || m_texelsPerUnit <= 0) {
if (resolution <= 0 && m_texelsPerUnit <= 0)
resolution = 1024;
@@ -7049,15 +7106,11 @@ struct Atlas
float minChartPerimeter = FLT_MAX, maxChartPerimeter = 0.0f;
for (uint32_t c = 0; c < chartCount; c++) {
Chart *chart = m_charts[c];
- //chartOrderArray[c] = chart.surfaceArea;
// Compute chart scale
float scale = (chart->surfaceArea / chart->parametricArea) * m_texelsPerUnit;
- if (chart->parametricArea == 0) { // < kAreaEpsilon)
+ if (chart->parametricArea == 0.0f)
scale = 0;
- }
XA_ASSERT(isFinite(scale));
- // Sort charts by perimeter. @@ This is sometimes producing somewhat unexpected results. Is this right?
- //chartOrderArray[c] = ((chart->maxCorner.x - chart->minCorner.x) + (chart->maxCorner.y - chart->minCorner.y)) * scale;
// Translate, rotate and scale vertices. Compute extents.
Vector2 minCorner(FLT_MAX, FLT_MAX);
if (!chart->allowRotate) {
@@ -7077,58 +7130,59 @@ struct Atlas
texcoord -= minCorner;
}
texcoord *= scale;
- XA_DEBUG_ASSERT(texcoord.x >= 0 && texcoord.y >= 0);
+ XA_DEBUG_ASSERT(texcoord.x >= 0.0f && texcoord.y >= 0.0f);
XA_DEBUG_ASSERT(isFinite(texcoord.x) && isFinite(texcoord.y));
extents = max(extents, texcoord);
}
XA_DEBUG_ASSERT(extents.x >= 0 && extents.y >= 0);
- // Limit chart size.
- const float maxChartSize = (float)options.maxChartSize;
- if (extents.x > maxChartSize || extents.y > maxChartSize) {
- const float limit = max(extents.x, extents.y);
- scale = maxChartSize / (limit + 1.0f);
- for (uint32_t i = 0; i < chart->uniqueVertexCount(); i++)
- chart->uniqueVertexAt(i) *= scale;
- extents *= scale;
- XA_DEBUG_ASSERT(extents.x <= maxChartSize && extents.y <= maxChartSize);
- }
- // Scale the charts to use the entire texel area available. So, if the width is 0.1 we could scale it to 1 without increasing the lightmap usage and making a better
- // use of it. In many cases this also improves the look of the seams, since vertices on the chart boundaries have more chances of being aligned with the texel centers.
- float scale_x = 1.0f;
- float scale_y = 1.0f;
- float divide_x = 1.0f;
- float divide_y = 1.0f;
- if (extents.x > 0) {
- int cw = ftoi_ceil(extents.x);
- if (options.blockAlign) {
- // Align all chart extents to 4x4 blocks, but taking padding into account.
- cw = align(cw + 2, 4) - 2;
+ // Scale the charts to use the entire texel area available. So, if the width is 0.1 we could scale it to 1 without increasing the lightmap usage and making a better use of it. In many cases this also improves the look of the seams, since vertices on the chart boundaries have more chances of being aligned with the texel centers.
+ if (extents.x > 0.0f && extents.y > 0.0f) {
+ // Block align: align all chart extents to 4x4 blocks, but taking padding and texel center offset into account.
+ const int blockAlignSizeOffset = options.padding * 2 + 1;
+ int width = ftoi_ceil(extents.x);
+ if (options.blockAlign)
+ width = align(width + blockAlignSizeOffset, 4) - blockAlignSizeOffset;
+ int height = ftoi_ceil(extents.y);
+ if (options.blockAlign)
+ height = align(height + blockAlignSizeOffset, 4) - blockAlignSizeOffset;
+ for (uint32_t v = 0; v < chart->uniqueVertexCount(); v++) {
+ Vector2 &texcoord = chart->uniqueVertexAt(v);
+ texcoord.x = texcoord.x / extents.x * (float)width;
+ texcoord.y = texcoord.y / extents.y * (float)height;
}
- scale_x = (float(cw) - kEpsilon);
- divide_x = extents.x;
- extents.x = float(cw);
- }
- if (extents.y > 0) {
- int ch = ftoi_ceil(extents.y);
- if (options.blockAlign) {
- // Align all chart extents to 4x4 blocks, but taking padding into account.
- ch = align(ch + 2, 4) - 2;
+ extents.x = (float)width;
+ extents.y = (float)height;
+ }
+ // Limit chart size, either to PackOptions::maxChartSize or maxResolution (if set), whichever is smaller.
+ // If limiting chart size to maxResolution, print a warning, since that may not be desirable to the user.
+ uint32_t maxChartSize = options.maxChartSize;
+ bool warnChartResized = false;
+ if (maxResolution > 0 && (maxChartSize == 0 || maxResolution < maxChartSize)) {
+ maxChartSize = maxResolution - options.padding * 2; // Don't include padding.
+ warnChartResized = true;
+ }
+ if (maxChartSize > 0) {
+ const float realMaxChartSize = (float)maxChartSize - 1.0f; // Aligning to texel centers increases texel footprint by 1.
+ if (extents.x > realMaxChartSize || extents.y > realMaxChartSize) {
+ if (warnChartResized)
+ XA_PRINT(" Resizing chart %u from %gx%g to %ux%u to fit atlas\n", c, extents.x, extents.y, maxChartSize, maxChartSize);
+ scale = realMaxChartSize / max(extents.x, extents.y);
+ for (uint32_t i = 0; i < chart->uniqueVertexCount(); i++) {
+ Vector2 &texcoord = chart->uniqueVertexAt(i);
+ texcoord = min(texcoord * scale, Vector2(realMaxChartSize));
+ }
}
- scale_y = (float(ch) - kEpsilon);
- divide_y = extents.y;
- extents.y = float(ch);
}
+ // Align to texel centers and add padding offset.
+ extents.x = extents.y = 0.0f;
for (uint32_t v = 0; v < chart->uniqueVertexCount(); v++) {
Vector2 &texcoord = chart->uniqueVertexAt(v);
- texcoord.x /= divide_x;
- texcoord.y /= divide_y;
- texcoord.x *= scale_x;
- texcoord.y *= scale_y;
- XA_ASSERT(isFinite(texcoord.x) && isFinite(texcoord.y));
+ texcoord.x += 0.5f + options.padding;
+ texcoord.y += 0.5f + options.padding;
+ extents = max(extents, texcoord);
}
chartExtents[c] = extents;
- // Sort charts by perimeter.
- chartOrderArray[c] = extents.x + extents.y;
+ chartOrderArray[c] = extents.x + extents.y; // Use perimeter for chart sort key.
minChartPerimeter = min(minChartPerimeter, chartOrderArray[c]);
maxChartPerimeter = max(maxChartPerimeter, chartOrderArray[c]);
}
@@ -7147,9 +7201,14 @@ struct Atlas
#else
const bool createImage = options.createImage;
#endif
- BitImage chartBitImage, chartBitImageRotated;
- int atlasWidth = 0, atlasHeight = 0;
- const bool resizableAtlas = !(options.resolution > 0 && options.texelsPerUnit > 0.0f);
+ // chartImage: result from conservative rasterization
+ // chartImageBilinear: chartImage plus any texels that would be sampled by bilinear filtering.
+ // chartImagePadding: either chartImage or chartImageBilinear depending on options, with a dilate filter applied options.padding times.
+ // Rotated versions swap x and y.
+ BitImage chartImage, chartImageBilinear, chartImagePadding;
+ BitImage chartImageRotated, chartImageBilinearRotated, chartImagePaddingRotated;
+ Array<Vector2i> atlasSizes;
+ atlasSizes.push_back(Vector2i(0, 0));
int progress = 0;
for (uint32_t i = 0; i < chartCount; i++) {
uint32_t c = ranks[chartCount - i - 1]; // largest chart first
@@ -7167,29 +7226,46 @@ struct Atlas
// V V V
// 0 1 2
XA_PROFILE_START(packChartsRasterize)
- // Leave room for padding.
- chartBitImage.resize(ftoi_ceil(chartExtents[c].x) + 1 + options.padding * 2, ftoi_ceil(chartExtents[c].y) + 1 + options.padding * 2, true);
+ // Resize and clear (discard = true) chart images.
+ // Leave room for padding at extents.
+ chartImage.resize(ftoi_ceil(chartExtents[c].x) + options.padding, ftoi_ceil(chartExtents[c].y) + options.padding, true);
if (chart->allowRotate)
- chartBitImageRotated.resize(chartBitImage.height(), chartBitImage.width(), true);
+ chartImageRotated.resize(chartImage.height(), chartImage.width(), true);
+ if (options.bilinear) {
+ chartImageBilinear.resize(chartImage.width(), chartImage.height(), true);
+ if (chart->allowRotate)
+ chartImageBilinearRotated.resize(chartImage.height(), chartImage.width(), true);
+ }
// Rasterize chart faces.
const uint32_t faceCount = chart->indexCount / 3;
for (uint32_t f = 0; f < faceCount; f++) {
- // Offset vertices by padding.
Vector2 vertices[3];
for (uint32_t v = 0; v < 3; v++)
- vertices[v] = chart->vertices[chart->indices[f * 3 + v]] + Vector2(0.5f) + Vector2(float(options.padding));
+ vertices[v] = chart->vertices[chart->indices[f * 3 + v]];
DrawTriangleCallbackArgs args;
- args.chartBitImage = &chartBitImage;
- args.chartBitImageRotated = chart->allowRotate ? &chartBitImageRotated : nullptr;
- raster::drawTriangle(Vector2((float)chartBitImage.width(), (float)chartBitImage.height()), vertices, drawTriangleCallback, &args);
- }
- // Expand chart by padding pixels. (dilation)
- BitImage chartBitImageNoPadding(chartBitImage), chartBitImageNoPaddingRotated(chartBitImageRotated);
+ args.chartBitImage = &chartImage;
+ args.chartBitImageRotated = chart->allowRotate ? &chartImageRotated : nullptr;
+ raster::drawTriangle(Vector2((float)chartImage.width(), (float)chartImage.height()), vertices, drawTriangleCallback, &args);
+ }
+ // Expand chart by pixels sampled by bilinear interpolation.
+ if (options.bilinear)
+ bilinearExpand(chart, &chartImage, &chartImageBilinear, chart->allowRotate ? &chartImageBilinearRotated : nullptr);
+ // Expand chart by padding pixels (dilation).
if (options.padding > 0) {
+ // Copy into the same BitImage instances for every chart to avoid reallocating BitImage buffers (largest chart is packed first).
XA_PROFILE_START(packChartsDilate)
- chartBitImage.dilate(options.padding);
- if (chart->allowRotate)
- chartBitImageRotated.dilate(options.padding);
+ if (options.bilinear)
+ chartImageBilinear.copyTo(chartImagePadding);
+ else
+ chartImage.copyTo(chartImagePadding);
+ chartImagePadding.dilate(options.padding);
+ if (chart->allowRotate) {
+ if (options.bilinear)
+ chartImageBilinearRotated.copyTo(chartImagePaddingRotated);
+ else
+ chartImageRotated.copyTo(chartImagePaddingRotated);
+ chartImagePaddingRotated.dilate(options.padding);
+ }
XA_PROFILE_END(packChartsDilate)
}
XA_PROFILE_END(packChartsRasterize)
@@ -7203,6 +7279,17 @@ struct Atlas
}
}
// Find a location to place the chart in the atlas.
+ BitImage *chartImageToPack, *chartImageToPackRotated;
+ if (options.padding > 0) {
+ chartImageToPack = &chartImagePadding;
+ chartImageToPackRotated = &chartImagePaddingRotated;
+ } else if (options.bilinear) {
+ chartImageToPack = &chartImageBilinear;
+ chartImageToPackRotated = &chartImageBilinearRotated;
+ } else {
+ chartImageToPack = &chartImage;
+ chartImageToPackRotated = &chartImageRotated;
+ }
uint32_t currentAtlas = 0;
int best_x = 0, best_y = 0;
int best_cw = 0, best_ch = 0;
@@ -7210,27 +7297,24 @@ struct Atlas
for (;;)
{
bool firstChartInBitImage = false;
+ XA_UNUSED(firstChartInBitImage);
if (currentAtlas + 1 > m_bitImages.size()) {
// Chart doesn't fit in the current bitImage, create a new one.
- BitImage *bi = XA_NEW(MemTag::Default, BitImage);
- bi->resize(resolution, resolution, true);
+ BitImage *bi = XA_NEW_ARGS(MemTag::Default, BitImage, resolution, resolution);
m_bitImages.push_back(bi);
+ atlasSizes.push_back(Vector2i(0, 0));
firstChartInBitImage = true;
if (createImage)
- m_atlasImages.push_back(XA_NEW(MemTag::Default, AtlasImage, resolution, resolution));
+ m_atlasImages.push_back(XA_NEW_ARGS(MemTag::Default, AtlasImage, resolution, resolution));
// Start positions are per-atlas, so create a new one of those too.
chartStartPositions.push_back(Vector2i(0, 0));
}
XA_PROFILE_START(packChartsFindLocation)
- const bool foundLocation = findChartLocation(taskScheduler, chartStartPositions[currentAtlas], options.bruteForce, m_bitImages[currentAtlas], &chartBitImage, &chartBitImageRotated, atlasWidth, atlasHeight, &best_x, &best_y, &best_cw, &best_ch, &best_r, options.blockAlign, resizableAtlas, chart->allowRotate);
+ const bool foundLocation = findChartLocation(taskScheduler, chartStartPositions[currentAtlas], options.bruteForce, m_bitImages[currentAtlas], chartImageToPack, chartImageToPackRotated, atlasSizes[currentAtlas].x, atlasSizes[currentAtlas].y, &best_x, &best_y, &best_cw, &best_ch, &best_r, options.blockAlign, maxResolution, chart->allowRotate);
XA_PROFILE_END(packChartsFindLocation)
- if (firstChartInBitImage && !foundLocation) {
- // Chart doesn't fit in an empty, newly allocated bitImage. texelsPerUnit must be too large for the resolution.
- XA_ASSERT(true && "chart doesn't fit");
- break;
- }
- if (resizableAtlas) {
- XA_DEBUG_ASSERT(foundLocation);
+ XA_DEBUG_ASSERT(!(firstChartInBitImage && !foundLocation)); // Chart doesn't fit in an empty, newly allocated bitImage. Shouldn't happen, since charts are resized if they are too big to fit in the atlas.
+ if (maxResolution == 0) {
+ XA_DEBUG_ASSERT(foundLocation); // The atlas isn't limited to a fixed resolution, a chart location should be found on the first attempt.
break;
}
if (foundLocation)
@@ -7241,7 +7325,7 @@ struct Atlas
// Update brute force start location.
if (options.bruteForce) {
// Reset start location if the chart expanded the atlas.
- if (best_x + best_cw > atlasWidth || best_y + best_ch > atlasHeight) {
+ if (best_x + best_cw > atlasSizes[currentAtlas].x || best_y + best_ch > atlasSizes[currentAtlas].y) {
for (uint32_t j = 0; j < chartStartPositions.size(); j++)
chartStartPositions[j] = Vector2i(0, 0);
}
@@ -7250,28 +7334,37 @@ struct Atlas
}
}
// Update parametric extents.
- atlasWidth = max(atlasWidth, best_x + best_cw);
- atlasHeight = max(atlasHeight, best_y + best_ch);
- if (resizableAtlas) {
- // Resize bitImage if necessary.
- if (uint32_t(atlasWidth) > m_bitImages[0]->width() || uint32_t(atlasHeight) > m_bitImages[0]->height()) {
- m_bitImages[0]->resize(nextPowerOfTwo(uint32_t(atlasWidth)), nextPowerOfTwo(uint32_t(atlasHeight)), false);
+ atlasSizes[currentAtlas].x = max(atlasSizes[currentAtlas].x, best_x + best_cw);
+ atlasSizes[currentAtlas].y = max(atlasSizes[currentAtlas].y, best_y + best_ch);
+ // Resize bitImage if necessary.
+ // If maxResolution > 0, the bitImage is always set to maxResolutionIncludingPadding on creation and doesn't need to be dynamically resized.
+ if (maxResolution == 0) {
+ const uint32_t w = (uint32_t)atlasSizes[currentAtlas].x;
+ const uint32_t h = (uint32_t)atlasSizes[currentAtlas].y;
+ if (w > m_bitImages[0]->width() || h > m_bitImages[0]->height()) {
+ m_bitImages[0]->resize(nextPowerOfTwo(w), nextPowerOfTwo(h), false);
if (createImage)
m_atlasImages[0]->resize(m_bitImages[0]->width(), m_bitImages[0]->height());
}
} else {
- atlasWidth = min((int)options.resolution, atlasWidth);
- atlasHeight = min((int)options.resolution, atlasHeight);
+ XA_DEBUG_ASSERT(atlasSizes[currentAtlas].x <= (int)maxResolution);
+ XA_DEBUG_ASSERT(atlasSizes[currentAtlas].y <= (int)maxResolution);
}
XA_PROFILE_START(packChartsBlit)
- addChart(m_bitImages[currentAtlas], &chartBitImage, &chartBitImageRotated, atlasWidth, atlasHeight, best_x, best_y, best_r);
+ addChart(m_bitImages[currentAtlas], chartImageToPack, chartImageToPackRotated, atlasSizes[currentAtlas].x, atlasSizes[currentAtlas].y, best_x, best_y, best_r);
XA_PROFILE_END(packChartsBlit)
if (createImage) {
- m_atlasImages[currentAtlas]->addChart(c, best_r == 0 ? &chartBitImageNoPadding : &chartBitImageNoPaddingRotated, false, atlasWidth, atlasHeight, best_x, best_y);
- m_atlasImages[currentAtlas]->addChart(c, best_r == 0 ? &chartBitImage : &chartBitImageRotated, true, atlasWidth, atlasHeight, best_x, best_y);
+ if (best_r == 0) {
+ m_atlasImages[currentAtlas]->addChart(c, &chartImage, options.bilinear ? &chartImageBilinear : nullptr, options.padding > 0 ? &chartImagePadding : nullptr, atlasSizes[currentAtlas].x, atlasSizes[currentAtlas].y, best_x, best_y);
+ } else {
+ m_atlasImages[currentAtlas]->addChart(c, &chartImageRotated, options.bilinear ? &chartImageBilinearRotated : nullptr, options.padding > 0 ? &chartImagePaddingRotated : nullptr, atlasSizes[currentAtlas].x, atlasSizes[currentAtlas].y, best_x, best_y);
+ }
}
chart->atlasIndex = (int32_t)currentAtlas;
- // Translate and rotate chart texture coordinates.
+ // Modify texture coordinates:
+ // - rotate if the chart should be rotated
+ // - translate to chart location
+ // - translate to remove padding from top and left atlas edges (unless block aligned)
for (uint32_t v = 0; v < chart->uniqueVertexCount(); v++) {
Vector2 &texcoord = chart->uniqueVertexAt(v);
Vector2 t = texcoord;
@@ -7279,8 +7372,12 @@ struct Atlas
XA_DEBUG_ASSERT(chart->allowRotate);
swap(t.x, t.y);
}
- texcoord.x = best_x + t.x + 0.5f;
- texcoord.y = best_y + t.y + 0.5f;
+ texcoord.x = best_x + t.x;
+ texcoord.y = best_y + t.y;
+ if (!options.blockAlign) {
+ texcoord.x -= (float)options.padding;
+ texcoord.y -= (float)options.padding;
+ }
XA_ASSERT(texcoord.x >= 0 && texcoord.y >= 0);
XA_ASSERT(isFinite(texcoord.x) && isFinite(texcoord.y));
}
@@ -7293,21 +7390,35 @@ struct Atlas
}
}
}
- if (resizableAtlas) {
- m_width = max(0, atlasWidth - (int)options.padding * 2);
- m_height = max(0, atlasHeight - (int)options.padding * 2);
+ if (options.blockAlign) {
+ if (maxResolution == 0) {
+ m_width = max(0, atlasSizes[0].x);
+ m_height = max(0, atlasSizes[0].y);
+ } else {
+ m_width = m_height = maxResolution;
+ }
} else {
- m_width = m_height = options.resolution;
+ // Remove padding from outer edges.
+ if (maxResolution == 0) {
+ m_width = max(0, atlasSizes[0].x - (int)options.padding * 2);
+ m_height = max(0, atlasSizes[0].y - (int)options.padding * 2);
+ } else {
+ m_width = m_height = maxResolution - (int)options.padding * 2;
+ }
}
XA_PRINT(" %dx%d resolution\n", m_width, m_height);
m_utilization.resize(m_bitImages.size());
for (uint32_t i = 0; i < m_utilization.size(); i++) {
- uint32_t count = 0;
- for (uint32_t y = 0; y < m_height; y++) {
- for (uint32_t x = 0; x < m_width; x++)
- count += m_bitImages[i]->bitAt(x, y);
+ if (m_width == 0 || m_height == 0)
+ m_utilization[i] = 0.0f;
+ else {
+ uint32_t count = 0;
+ for (uint32_t y = 0; y < m_height; y++) {
+ for (uint32_t x = 0; x < m_width; x++)
+ count += m_bitImages[i]->bitAt(x, y);
+ }
+ m_utilization[i] = float(count) / (m_width * m_height);
}
- m_utilization[i] = float(count) / (m_width * m_height);
if (m_utilization.size() > 1) {
XA_PRINT(" %u: %f%% utilization\n", i, m_utilization[i] * 100.0f);
}
@@ -7334,27 +7445,33 @@ private:
// is occupied at this point. At the end we have many small charts and a large atlas with sparse holes. Finding those holes randomly is slow. A better approach would be to
// start stacking large charts as if they were tetris pieces. Once charts get small try to place them randomly. It may be interesting to try a intermediate strategy, first try
// along one axis and then try exhaustively along that axis.
- bool findChartLocation(TaskScheduler *taskScheduler, const Vector2i &startPosition, bool bruteForce, const BitImage *atlasBitImage, const BitImage *chartBitImage, const BitImage *chartBitImageRotated, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, bool blockAligned, bool resizableAtlas, bool allowRotate)
+ bool findChartLocation(TaskScheduler *taskScheduler, const Vector2i &startPosition, bool bruteForce, const BitImage *atlasBitImage, const BitImage *chartBitImage, const BitImage *chartBitImageRotated, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, bool blockAligned, uint32_t maxResolution, bool allowRotate)
{
const int attempts = 4096;
if (bruteForce || attempts >= w * h)
- return findChartLocation_bruteForce(taskScheduler, startPosition, atlasBitImage, chartBitImage, chartBitImageRotated, w, h, best_x, best_y, best_w, best_h, best_r, blockAligned, resizableAtlas, allowRotate);
- return findChartLocation_random(atlasBitImage, chartBitImage, chartBitImageRotated, w, h, best_x, best_y, best_w, best_h, best_r, attempts, blockAligned, resizableAtlas, allowRotate);
+ return findChartLocation_bruteForce(taskScheduler, startPosition, atlasBitImage, chartBitImage, chartBitImageRotated, w, h, best_x, best_y, best_w, best_h, best_r, blockAligned, maxResolution, allowRotate);
+ return findChartLocation_random(atlasBitImage, chartBitImage, chartBitImageRotated, w, h, best_x, best_y, best_w, best_h, best_r, attempts, blockAligned, maxResolution, allowRotate);
}
- bool findChartLocation_bruteForce(TaskScheduler *taskScheduler, const Vector2i &startPosition, const BitImage *atlasBitImage, const BitImage *chartBitImage, const BitImage *chartBitImageRotated, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, bool blockAligned, bool resizableAtlas, bool allowRotate)
+ bool findChartLocation_bruteForce(TaskScheduler *taskScheduler, const Vector2i &startPosition, const BitImage *atlasBitImage, const BitImage *chartBitImage, const BitImage *chartBitImageRotated, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, bool blockAligned, uint32_t maxResolution, bool allowRotate)
{
const int stepSize = blockAligned ? 4 : 1;
+ const int chartMinHeight = min(chartBitImage->height(), chartBitImageRotated->height());
uint32_t taskCount = 0;
- for (int y = startPosition.y; y <= h + stepSize; y += stepSize)
+ for (int y = startPosition.y; y <= h + stepSize; y += stepSize) {
+ if (maxResolution > 0 && y > (int)maxResolution - chartMinHeight)
+ break;
taskCount++;
- Array<FindChartLocationBruteForceTaskArgs> taskArgs;
- taskArgs.resize(taskCount);
+ }
+ m_bruteForceTaskArgs.clear();
+ m_bruteForceTaskArgs.resize(taskCount);
TaskGroupHandle taskGroup = taskScheduler->createTaskGroup(taskCount);
std::atomic<bool> finished(false); // One of the tasks found a location that doesn't expand the atlas.
uint32_t i = 0;
for (int y = startPosition.y; y <= h + stepSize; y += stepSize) {
- FindChartLocationBruteForceTaskArgs &args = taskArgs[i];
+ if (maxResolution > 0 && y > (int)maxResolution - chartMinHeight)
+ break;
+ FindChartLocationBruteForceTaskArgs &args = m_bruteForceTaskArgs[i];
args.finished = &finished;
args.startPosition = Vector2i(y == startPosition.y ? startPosition.x : 0, y);
args.atlasBitImage = atlasBitImage;
@@ -7363,10 +7480,10 @@ private:
args.w = w;
args.h = h;
args.blockAligned = blockAligned;
- args.resizableAtlas = resizableAtlas;
args.allowRotate = allowRotate;
+ args.maxResolution = maxResolution;
Task task;
- task.userData = &taskArgs[i];
+ task.userData = &m_bruteForceTaskArgs[i];
task.func = runFindChartLocationBruteForceTask;
taskScheduler->run(taskGroup, task);
i++;
@@ -7376,7 +7493,7 @@ private:
int best_metric = INT_MAX;
bool best_insideAtlas = false;
for (i = 0; i < taskCount; i++) {
- FindChartLocationBruteForceTaskArgs &args = taskArgs[i];
+ FindChartLocationBruteForceTaskArgs &args = m_bruteForceTaskArgs[i];
if (args.best_metric > best_metric)
continue;
// A location that doesn't expand the atlas is always preferred.
@@ -7396,7 +7513,7 @@ private:
return best_metric != INT_MAX;
}
- bool findChartLocation_random(const BitImage *atlasBitImage, const BitImage *chartBitImage, const BitImage *chartBitImageRotated, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, int minTrialCount, bool blockAligned, bool resizableAtlas, bool allowRotate)
+ bool findChartLocation_random(const BitImage *atlasBitImage, const BitImage *chartBitImage, const BitImage *chartBitImageRotated, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, int minTrialCount, bool blockAligned, uint32_t maxResolution, bool allowRotate)
{
bool result = false;
const int BLOCK_SIZE = 4;
@@ -7410,16 +7527,17 @@ private:
// + 1 to extend atlas in case atlas full. We may want to use a higher number to increase probability of extending atlas.
int xRange = w + 1;
int yRange = h + 1;
- if (!resizableAtlas) {
- xRange = min(xRange, (int)atlasBitImage->width() - cw);
- yRange = min(yRange, (int)atlasBitImage->height() - ch);
+ // Clamp to max resolution.
+ if (maxResolution > 0) {
+ xRange = min(xRange, (int)maxResolution - cw);
+ yRange = min(yRange, (int)maxResolution - ch);
}
int x = m_rand.getRange(xRange);
int y = m_rand.getRange(yRange);
if (blockAligned) {
x = align(x, BLOCK_SIZE);
y = align(y, BLOCK_SIZE);
- if (!resizableAtlas && (x > (int)atlasBitImage->width() - cw || y > (int)atlasBitImage->height() - ch))
+ if (maxResolution > 0 && (x > (int)maxResolution - cw || y > (int)maxResolution - ch))
continue; // Block alignment pushed the chart outside the atlas.
}
// Early out.
@@ -7475,10 +7593,68 @@ private:
}
}
+ void bilinearExpand(const Chart *chart, BitImage *source, BitImage *dest, BitImage *destRotated) const
+ {
+ const int xOffsets[] = { -1, 0, 1, -1, 1, -1, 0, 1 };
+ const int yOffsets[] = { -1, -1, -1, 0, 0, 1, 1, 1 };
+ for (uint32_t y = 0; y < source->height(); y++) {
+ for (uint32_t x = 0; x < source->width(); x++) {
+ // Copy pixels from source.
+ if (source->bitAt(x, y))
+ goto setPixel;
+ // Empty pixel. If none of of the surrounding pixels are set, this pixel can't be sampled by bilinear interpolation.
+ {
+ uint32_t s = 0;
+ for (; s < 8; s++) {
+ const int sx = (int)x + xOffsets[s];
+ const int sy = (int)y + yOffsets[s];
+ if (sx < 0 || sy < 0 || sx >= (int)source->width() || sy >= (int)source->height())
+ continue;
+ if (source->bitAt((uint32_t)sx, (uint32_t)sy))
+ break;
+ }
+ if (s == 8)
+ continue;
+ }
+ // If a 2x2 square centered on the pixels centroid intersects the triangle, this pixel will be sampled by bilinear interpolation.
+ // See "Precomputed Global Illumination in Frostbite (GDC 2018)" page 95
+ for (uint32_t f = 0; f < chart->indexCount / 3; f++) {
+ const Vector2 centroid((float)x + 0.5f, (float)y + 0.5f);
+ Vector2 vertices[3];
+ for (uint32_t i = 0; i < 3; i++)
+ vertices[i] = chart->vertices[chart->indices[f * 3 + i]];
+ // Test for triangle vertex in square bounds.
+ for (uint32_t i = 0; i < 3; i++) {
+ const Vector2 &v = vertices[i];
+ if (v.x > centroid.x - 1.0f && v.x < centroid.x + 1.0f && v.y > centroid.y - 1.0f && v.y < centroid.y + 1.0f)
+ goto setPixel;
+ }
+ // Test for triangle edge intersection with square edge.
+ const Vector2 squareVertices[4] = {
+ Vector2(centroid.x - 1.0f, centroid.y - 1.0f),
+ Vector2(centroid.x + 1.0f, centroid.y - 1.0f),
+ Vector2(centroid.x + 1.0f, centroid.y + 1.0f),
+ Vector2(centroid.x - 1.0f, centroid.y + 1.0f)
+ };
+ for (uint32_t i = 0; i < 3; i++) {
+ for (uint32_t j = 0; j < 4; j++) {
+ if (linesIntersect(vertices[i], vertices[(i + 1) % 3], squareVertices[j], squareVertices[(j + 1) % 4], 0.0f))
+ goto setPixel;
+ }
+ }
+ }
+ continue;
+ setPixel:
+ dest->setBitAt(x, y);
+ if (destRotated)
+ destRotated->setBitAt(y, x);
+ }
+ }
+ }
+
struct DrawTriangleCallbackArgs
{
- BitImage *chartBitImage;
- BitImage *chartBitImageRotated;
+ BitImage *chartBitImage, *chartBitImageRotated;
};
static bool drawTriangleCallback(void *param, int x, int y)
@@ -7493,8 +7669,8 @@ private:
Array<AtlasImage *> m_atlasImages;
Array<float> m_utilization;
Array<BitImage *> m_bitImages;
- BoundingBox2D m_boundingBox;
Array<Chart *> m_charts;
+ Array<FindChartLocationBruteForceTaskArgs> m_bruteForceTaskArgs;
RadixSort m_radix;
uint32_t m_width = 0;
uint32_t m_height = 0;
@@ -7534,8 +7710,8 @@ static void DestroyOutputMeshes(Context *ctx)
for (int i = 0; i < (int)ctx->atlas.meshCount; i++) {
Mesh &mesh = ctx->atlas.meshes[i];
for (uint32_t j = 0; j < mesh.chartCount; j++) {
- if (mesh.chartArray[j].indexArray)
- XA_FREE(mesh.chartArray[j].indexArray);
+ if (mesh.chartArray[j].faceArray)
+ XA_FREE(mesh.chartArray[j].faceArray);
}
if (mesh.chartArray)
XA_FREE(mesh.chartArray);
@@ -7715,18 +7891,19 @@ AddMeshError::Enum AddMesh(Atlas *atlas, const MeshDecl &meshDecl, uint32_t mesh
#endif
// Don't know how many times AddMesh will be called, so progress needs to adjusted each time.
if (!ctx->addMeshProgress) {
- ctx->addMeshProgress = XA_NEW(internal::MemTag::Default, internal::Progress, ProgressCategory::AddMesh, ctx->progressFunc, ctx->progressUserData, 1);
+ ctx->addMeshProgress = XA_NEW_ARGS(internal::MemTag::Default, internal::Progress, ProgressCategory::AddMesh, ctx->progressFunc, ctx->progressUserData, 1);
}
else {
ctx->addMeshProgress->setMaxValue(internal::max(ctx->meshCount + 1, meshCountHint));
}
- bool decoded = (meshDecl.indexCount <= 0);
- uint32_t indexCount = decoded ? meshDecl.vertexCount : meshDecl.indexCount;
+ XA_PROFILE_START(addMeshCopyData)
+ const bool hasIndices = meshDecl.indexCount > 0;
+ const uint32_t indexCount = hasIndices ? meshDecl.indexCount : meshDecl.vertexCount;
XA_PRINT("Adding mesh %d: %u vertices, %u triangles\n", ctx->meshCount, meshDecl.vertexCount, indexCount / 3);
// Expecting triangle faces.
if ((indexCount % 3) != 0)
return AddMeshError::InvalidIndexCount;
- if (!decoded) {
+ if (hasIndices) {
// Check if any index is out of range.
for (uint32_t i = 0; i < indexCount; i++) {
const uint32_t index = DecodeIndex(meshDecl.indexFormat, meshDecl.indexData, meshDecl.indexOffset, i);
@@ -7737,7 +7914,7 @@ AddMeshError::Enum AddMesh(Atlas *atlas, const MeshDecl &meshDecl, uint32_t mesh
uint32_t meshFlags = internal::MeshFlags::HasFaceGroups | internal::MeshFlags::HasIgnoredFaces;
if (meshDecl.vertexNormalData)
meshFlags |= internal::MeshFlags::HasNormals;
- internal::Mesh *mesh = XA_NEW(internal::MemTag::Mesh, internal::Mesh, meshDecl.epsilon, meshDecl.vertexCount, indexCount / 3, meshFlags, ctx->meshCount);
+ internal::Mesh *mesh = XA_NEW_ARGS(internal::MemTag::Mesh, internal::Mesh, meshDecl.epsilon, meshDecl.vertexCount, indexCount / 3, meshFlags, ctx->meshCount);
for (uint32_t i = 0; i < meshDecl.vertexCount; i++) {
internal::Vector3 normal(0.0f);
internal::Vector2 texcoord(0.0f);
@@ -7750,7 +7927,7 @@ AddMeshError::Enum AddMesh(Atlas *atlas, const MeshDecl &meshDecl, uint32_t mesh
for (uint32_t i = 0; i < indexCount / 3; i++) {
uint32_t tri[3];
for (int j = 0; j < 3; j++)
- tri[j] = decoded ? i * 3 + j : DecodeIndex(meshDecl.indexFormat, meshDecl.indexData, meshDecl.indexOffset, i * 3 + j);
+ tri[j] = hasIndices ? DecodeIndex(meshDecl.indexFormat, meshDecl.indexData, meshDecl.indexOffset, i * 3 + j) : i * 3 + j;
bool ignore = false;
// Check for degenerate or zero length edges.
for (int j = 0; j < 3; j++) {
@@ -7769,10 +7946,37 @@ AddMeshError::Enum AddMesh(Atlas *atlas, const MeshDecl &meshDecl, uint32_t mesh
break;
}
}
+ // Ignore faces with any nan vertex attributes.
+ if (!ignore) {
+ for (int j = 0; j < 3; j++) {
+ const internal::Vector3 &pos = mesh->position(tri[j]);
+ if (internal::isNan(pos.x) || internal::isNan(pos.y) || internal::isNan(pos.z)) {
+ XA_PRINT(" NAN position in face: %d\n", i);
+ ignore = true;
+ break;
+ }
+ if (meshDecl.vertexNormalData) {
+ const internal::Vector3 &normal = mesh->normal(tri[j]);
+ if (internal::isNan(normal.x) || internal::isNan(normal.y) || internal::isNan(normal.z)) {
+ XA_PRINT(" NAN normal in face: %d\n", i);
+ ignore = true;
+ break;
+ }
+ }
+ if (meshDecl.vertexUvData) {
+ const internal::Vector2 &uv = mesh->texcoord(tri[j]);
+ if (internal::isNan(uv.x) || internal::isNan(uv.y)) {
+ XA_PRINT(" NAN texture coordinate in face: %d\n", i);
+ ignore = true;
+ break;
+ }
+ }
+ }
+ }
const internal::Vector3 &a = mesh->position(tri[0]);
const internal::Vector3 &b = mesh->position(tri[1]);
const internal::Vector3 &c = mesh->position(tri[2]);
- // Check for zero area faces. Don't bother if a degenerate or zero length edge was already detected.
+ // Check for zero area faces.
float area = 0.0f;
if (!ignore) {
area = internal::length(internal::cross(b - a, c - a)) * 0.5f;
@@ -7791,6 +7995,7 @@ AddMeshError::Enum AddMesh(Atlas *atlas, const MeshDecl &meshDecl, uint32_t mesh
ignore = true;
mesh->addFace(tri[0], tri[1], tri[2], ignore);
}
+ XA_PROFILE_END(addMeshCopyData)
if (ctx->addMeshTaskGroup.value == UINT32_MAX)
ctx->addMeshTaskGroup = ctx->taskScheduler->createTaskGroup();
AddMeshTaskArgs *taskArgs = XA_NEW(internal::MemTag::Default, AddMeshTaskArgs); // The task frees this.
@@ -7818,11 +8023,13 @@ void AddMeshJoin(Atlas *atlas)
ctx->addMeshProgress->~Progress();
XA_FREE(ctx->addMeshProgress);
ctx->addMeshProgress = nullptr;
+ ctx->paramAtlas.sortChartGroups();
#if XA_PROFILE
XA_PRINT("Added %u meshes\n", ctx->meshCount);
internal::s_profile.addMeshReal = clock() - internal::s_profile.addMeshReal;
#endif
XA_PROFILE_PRINT_AND_RESET(" Total (real): ", addMeshReal)
+ XA_PROFILE_PRINT_AND_RESET(" Copy data: ", addMeshCopyData)
XA_PROFILE_PRINT_AND_RESET(" Total (thread): ", addMeshThread)
XA_PROFILE_PRINT_AND_RESET(" Create colocals: ", addMeshCreateColocals)
XA_PROFILE_PRINT_AND_RESET(" Create face groups: ", addMeshCreateFaceGroups)
@@ -7880,8 +8087,13 @@ AddMeshError::Enum AddUvMesh(Atlas *atlas, const UvMeshDecl &decl)
}
internal::UvMeshInstance *meshInstance = XA_NEW(internal::MemTag::Default, internal::UvMeshInstance);
meshInstance->texcoords.resize(decl.vertexCount);
- for (uint32_t i = 0; i < decl.vertexCount; i++)
- meshInstance->texcoords[i] = *((const internal::Vector2 *)&((const uint8_t *)decl.vertexUvData)[decl.vertexStride * i]);
+ for (uint32_t i = 0; i < decl.vertexCount; i++) {
+ internal::Vector2 texcoord = *((const internal::Vector2 *)&((const uint8_t *)decl.vertexUvData)[decl.vertexStride * i]);
+ // Set nan values to 0.
+ if (internal::isNan(texcoord.x) || internal::isNan(texcoord.y))
+ texcoord.x = texcoord.y = 0.0f;
+ meshInstance->texcoords[i] = texcoord;
+ }
meshInstance->rotateCharts = decl.rotateCharts;
// See if this is an instance of an already existing mesh.
internal::UvMesh *mesh = nullptr;
@@ -7902,13 +8114,12 @@ AddMeshError::Enum AddUvMesh(Atlas *atlas, const UvMeshDecl &decl)
for (uint32_t i = 0; i < mesh->vertexToChartMap.size(); i++)
mesh->vertexToChartMap[i] = UINT32_MAX;
// Calculate charts (incident faces).
- internal::HashMap<internal::Vector2, uint32_t> vertexToFaceMap(internal::MemTag::Default, indexCount);
+ internal::HashMap<internal::Vector2> vertexToFaceMap(internal::MemTag::Default, indexCount); // Face is index / 3
const uint32_t faceCount = indexCount / 3;
for (uint32_t i = 0; i < indexCount; i++)
- vertexToFaceMap.add(meshInstance->texcoords[mesh->indices[i]], i / 3);
+ vertexToFaceMap.add(meshInstance->texcoords[mesh->indices[i]]);
internal::BitArray faceAssigned(faceCount);
faceAssigned.clearAll();
- internal::Array<uint32_t> chartFaces;
for (uint32_t f = 0; f < faceCount; f++) {
if (faceAssigned.bitAt(f))
continue;
@@ -7917,34 +8128,33 @@ AddMeshError::Enum AddUvMesh(Atlas *atlas, const UvMeshDecl &decl)
chart->material = decl.faceMaterialData ? decl.faceMaterialData[f] : 0;
// Walk incident faces and assign them to the chart.
faceAssigned.setBitAt(f);
- chartFaces.clear();
- chartFaces.push_back(f);
+ chart->faces.push_back(f);
for (;;) {
bool newFaceAssigned = false;
- const uint32_t faceCount2 = chartFaces.size();
+ const uint32_t faceCount2 = chart->faces.size();
for (uint32_t f2 = 0; f2 < faceCount2; f2++) {
- const uint32_t face = chartFaces[f2];
+ const uint32_t face = chart->faces[f2];
for (uint32_t i = 0; i < 3; i++) {
const internal::Vector2 &texcoord = meshInstance->texcoords[meshInstance->mesh->indices[face * 3 + i]];
- uint32_t mapFaceIndex = vertexToFaceMap.get(texcoord);
- while (mapFaceIndex != UINT32_MAX) {
- const uint32_t face2 = vertexToFaceMap.value(mapFaceIndex);
+ uint32_t mapIndex = vertexToFaceMap.get(texcoord);
+ while (mapIndex != UINT32_MAX) {
+ const uint32_t face2 = mapIndex / 3; // 3 vertices added per face.
// Materials must match.
if (!faceAssigned.bitAt(face2) && (!decl.faceMaterialData || decl.faceMaterialData[face] == decl.faceMaterialData[face2])) {
faceAssigned.setBitAt(face2);
- chartFaces.push_back(face2);
+ chart->faces.push_back(face2);
newFaceAssigned = true;
}
- mapFaceIndex = vertexToFaceMap.getNext(mapFaceIndex);
+ mapIndex = vertexToFaceMap.getNext(mapIndex);
}
}
}
if (!newFaceAssigned)
break;
}
- for (uint32_t i = 0; i < chartFaces.size(); i++) {
+ for (uint32_t i = 0; i < chart->faces.size(); i++) {
for (uint32_t j = 0; j < 3; j++) {
- const uint32_t vertex = meshInstance->mesh->indices[chartFaces[i] * 3 + j];
+ const uint32_t vertex = meshInstance->mesh->indices[chart->faces[i] * 3 + j];
chart->indices.push_back(vertex);
mesh->vertexToChartMap[vertex] = mesh->charts.size();
}
@@ -8019,11 +8229,14 @@ void ComputeCharts(Atlas *atlas, ChartOptions chartOptions)
XA_PRINT(" %u charts\n", chartCount);
XA_PROFILE_PRINT_AND_RESET(" Total (real): ", computeChartsReal)
XA_PROFILE_PRINT_AND_RESET(" Total (thread): ", computeChartsThread)
- XA_PROFILE_PRINT_AND_RESET(" Atlas builder: ", atlasBuilder)
- XA_PROFILE_PRINT_AND_RESET(" Init: ", atlasBuilderInit)
- XA_PROFILE_PRINT_AND_RESET(" Create initial charts: ", atlasBuilderCreateInitialCharts)
- XA_PROFILE_PRINT_AND_RESET(" Grow charts: ", atlasBuilderGrowCharts)
- XA_PROFILE_PRINT_AND_RESET(" Merge charts: ", atlasBuilderMergeCharts)
+ XA_PROFILE_PRINT_AND_RESET(" Build atlas: ", buildAtlas)
+ XA_PROFILE_PRINT_AND_RESET(" Init: ", buildAtlasInit)
+ XA_PROFILE_PRINT_AND_RESET(" Place seeds: ", buildAtlasPlaceSeeds)
+ XA_PROFILE_PRINT_AND_RESET(" Relocate seeds: ", buildAtlasRelocateSeeds)
+ XA_PROFILE_PRINT_AND_RESET(" Reset charts: ", buildAtlasResetCharts)
+ XA_PROFILE_PRINT_AND_RESET(" Grow charts: ", buildAtlasGrowCharts)
+ XA_PROFILE_PRINT_AND_RESET(" Merge charts: ", buildAtlasMergeCharts)
+ XA_PROFILE_PRINT_AND_RESET(" Fill holes: ", buildAtlasFillHoles)
XA_PROFILE_PRINT_AND_RESET(" Create chart meshes (real): ", createChartMeshesReal)
XA_PROFILE_PRINT_AND_RESET(" Create chart meshes (thread): ", createChartMeshesThread)
XA_PROFILE_PRINT_AND_RESET(" Fix t-junctions: ", fixChartMeshTJunctions)
@@ -8087,7 +8300,7 @@ void ParameterizeCharts(Atlas *atlas, ParameterizeFunc func)
XA_PRINT(" %u planar charts, %u ortho charts, %u other\n", planarChartsCount, orthoChartsCount, chartCount - (planarChartsCount + orthoChartsCount));
if (chartsDeletedCount > 0) {
XA_PRINT(" %u charts deleted due to invalid parameterizations, %u new charts added\n", chartsDeletedCount, chartsAddedCount);
- XA_PRINT(" %u charts\n", ctx->paramAtlas.chartCount());
+ XA_PRINT(" %u charts\n", chartCount);
}
uint32_t chartIndex = 0, invalidParamCount = 0;
for (uint32_t i = 0; i < ctx->meshCount; i++) {
@@ -8192,16 +8405,15 @@ void PackCharts(Atlas *atlas, PackOptions packOptions)
}
atlas->meshCount = 0;
// Pack charts.
+ XA_PROFILE_START(packChartsAddCharts)
internal::pack::Atlas packAtlas;
if (!ctx->uvMeshInstances.isEmpty()) {
for (uint32_t i = 0; i < ctx->uvMeshInstances.size(); i++)
packAtlas.addUvMeshCharts(ctx->uvMeshInstances[i]);
}
- else if (ctx->paramAtlas.chartCount() > 0) {
- ctx->paramAtlas.restoreOriginalChartTexcoords();
- for (uint32_t i = 0; i < ctx->paramAtlas.chartCount(); i++)
- packAtlas.addChart(ctx->paramAtlas.chartAt(i));
- }
+ else
+ packAtlas.addCharts(ctx->taskScheduler, &ctx->paramAtlas);
+ XA_PROFILE_END(packChartsAddCharts)
XA_PROFILE_START(packCharts)
if (!packAtlas.packCharts(ctx->taskScheduler, packOptions, ctx->progressFunc, ctx->progressUserData))
return;
@@ -8220,9 +8432,12 @@ void PackCharts(Atlas *atlas, PackOptions packOptions)
if (packOptions.createImage) {
atlas->image = XA_ALLOC_ARRAY(internal::MemTag::Default, uint32_t, atlas->atlasCount * atlas->width * atlas->height);
for (uint32_t i = 0; i < atlas->atlasCount; i++)
- packAtlas.getImages()[i]->copyTo(&atlas->image[atlas->width * atlas->height * i], atlas->width, atlas->height);
+ packAtlas.getImages()[i]->copyTo(&atlas->image[atlas->width * atlas->height * i], atlas->width, atlas->height, packOptions.blockAlign ? 0 : packOptions.padding);
}
XA_PROFILE_PRINT_AND_RESET(" Total: ", packCharts)
+ XA_PROFILE_PRINT_AND_RESET(" Add charts (real): ", packChartsAddCharts)
+ XA_PROFILE_PRINT_AND_RESET(" Add charts (thread): ", packChartsAddChartsThread)
+ XA_PROFILE_PRINT_AND_RESET(" Restore texcoords: ", packChartsAddChartsRestoreTexcoords)
XA_PROFILE_PRINT_AND_RESET(" Rasterize: ", packChartsRasterize)
XA_PROFILE_PRINT_AND_RESET(" Dilate (padding): ", packChartsDilate)
XA_PROFILE_PRINT_AND_RESET(" Find location (real): ", packChartsFindLocation)
@@ -8230,6 +8445,7 @@ void PackCharts(Atlas *atlas, PackOptions packOptions)
XA_PROFILE_PRINT_AND_RESET(" Blit: ", packChartsBlit)
XA_PRINT_MEM_USAGE
XA_PRINT("Building output meshes\n");
+ XA_PROFILE_START(buildOutputMeshes)
int progress = 0;
if (ctx->progressFunc) {
if (!ctx->progressFunc(ProgressCategory::BuildOutputMeshes, 0, ctx->progressUserData))
@@ -8265,8 +8481,7 @@ void PackCharts(Atlas *atlas, PackOptions packOptions)
outputMesh.chartArray = XA_ALLOC_ARRAY(internal::MemTag::Default, Chart, outputMesh.chartCount);
XA_PRINT(" mesh %u: %u vertices, %u triangles, %u charts\n", i, outputMesh.vertexCount, outputMesh.indexCount / 3, outputMesh.chartCount);
// Copy mesh data.
- uint32_t firstVertex = 0;
- uint32_t meshChartIndex = 0;
+ uint32_t firstVertex = 0, meshChartIndex = 0;
for (uint32_t cg = 0; cg < ctx->paramAtlas.chartGroupCount(i); cg++) {
const internal::param::ChartGroup *chartGroup = ctx->paramAtlas.chartGroupAt(i, cg);
if (chartGroup->isVertexMap()) {
@@ -8315,16 +8530,14 @@ void PackCharts(Atlas *atlas, PackOptions packOptions)
outputChart->flags = 0;
if (chart->paramQuality().boundaryIntersection || chart->paramQuality().flippedTriangleCount > 0)
outputChart->flags |= ChartFlags::Invalid;
- outputChart->indexCount = mesh->faceCount() * 3;
- outputChart->indexArray = XA_ALLOC_ARRAY(internal::MemTag::Default, uint32_t, outputChart->indexCount);
- for (uint32_t f = 0; f < mesh->faceCount(); f++) {
- for (uint32_t j = 0; j < 3; j++)
- outputChart->indexArray[3 * f + j] = firstVertex + mesh->vertexAt(f * 3 + j);
- }
+ outputChart->faceCount = mesh->faceCount();
+ outputChart->faceArray = XA_ALLOC_ARRAY(internal::MemTag::Default, uint32_t, outputChart->faceCount);
+ for (uint32_t f = 0; f < outputChart->faceCount; f++)
+ outputChart->faceArray[f] = chartGroup->mapFaceToSourceFace(chart->mapFaceToSourceFace(f));
outputChart->material = 0;
meshChartIndex++;
chartIndex++;
- firstVertex += chart->mesh()->vertexCount();
+ firstVertex += mesh->vertexCount();
}
}
}
@@ -8378,10 +8591,11 @@ void PackCharts(Atlas *atlas, PackOptions packOptions)
const internal::pack::Chart *chart = packAtlas.getChart(chartIndex);
XA_DEBUG_ASSERT(chart->atlasIndex >= 0);
outputChart->atlasIndex = (uint32_t)chart->atlasIndex;
- outputChart->indexCount = chart->indexCount;
- outputChart->indexArray = XA_ALLOC_ARRAY(internal::MemTag::Default, uint32_t, outputChart->indexCount);
+ outputChart->faceCount = chart->faces.size();
+ outputChart->faceArray = XA_ALLOC_ARRAY(internal::MemTag::Default, uint32_t, outputChart->faceCount);
outputChart->material = chart->material;
- memcpy(outputChart->indexArray, chart->indices, chart->indexCount * sizeof(uint32_t));
+ for (uint32_t f = 0; f < outputChart->faceCount; f++)
+ outputChart->faceArray[f] = chart->faces[f];
chartIndex++;
}
if (ctx->progressFunc) {
@@ -8396,6 +8610,8 @@ void PackCharts(Atlas *atlas, PackOptions packOptions)
}
if (ctx->progressFunc && progress != 100)
ctx->progressFunc(ProgressCategory::BuildOutputMeshes, 100, ctx->progressUserData);
+ XA_PROFILE_END(buildOutputMeshes)
+ XA_PROFILE_PRINT_AND_RESET(" Total: ", buildOutputMeshes)
XA_PRINT_MEM_USAGE
}
@@ -8430,9 +8646,10 @@ void SetProgressCallback(Atlas *atlas, ProgressFunc progressFunc, void *progress
ctx->progressUserData = progressUserData;
}
-void SetRealloc(ReallocFunc reallocFunc)
+void SetAlloc(ReallocFunc reallocFunc, FreeFunc freeFunc)
{
internal::s_realloc = reallocFunc;
+ internal::s_free = freeFunc;
}
void SetPrint(PrintFunc print, bool verbose)
diff --git a/thirdparty/xatlas/xatlas.h b/thirdparty/xatlas/xatlas.h
index c123e800b4..7be165e7e5 100644
--- a/thirdparty/xatlas/xatlas.h
+++ b/thirdparty/xatlas/xatlas.h
@@ -48,8 +48,8 @@ struct Chart
{
uint32_t atlasIndex; // Sub-atlas index.
uint32_t flags;
- uint32_t *indexArray;
- uint32_t indexCount;
+ uint32_t *faceArray;
+ uint32_t faceCount;
uint32_t material;
};
@@ -73,9 +73,10 @@ struct Mesh
uint32_t vertexCount;
};
-static const uint32_t kImageChartIndexMask = 0x3FFFFFFF;
-static const uint32_t kImageHasChartIndexBit = 0x40000000;
-static const uint32_t kImageIsPaddingBit = 0x80000000;
+static const uint32_t kImageChartIndexMask = 0x1FFFFFFF;
+static const uint32_t kImageHasChartIndexBit = 0x80000000;
+static const uint32_t kImageIsBilinearBit = 0x40000000;
+static const uint32_t kImageIsPaddingBit = 0x20000000;
// Empty on creation. Populated after charts are packed.
struct Atlas
@@ -173,7 +174,6 @@ struct ChartOptions
float textureSeamMetricWeight = 0.5f;
float maxThreshold = 2.0f; // If total of all metrics * weights > maxThreshold, don't grow chart. Lower values result in more charts.
- uint32_t growFaceCount = 32; // Grow this many faces at a time.
uint32_t maxIterations = 1; // Number of iterations of the chart growing and seeding phases. Higher values result in better charts.
};
@@ -188,12 +188,24 @@ void ParameterizeCharts(Atlas *atlas, ParameterizeFunc func = nullptr);
struct PackOptions
{
+ // Leave space around charts for texels that would be sampled by bilinear filtering.
+ bool bilinear = true;
+
+ // Align charts to 4x4 blocks. Also improves packing speed, since there are fewer possible chart locations to consider.
+ bool blockAlign = false;
+
// Slower, but gives the best result. If false, use random chart placement.
bool bruteForce = false;
// Create Atlas::image
bool createImage = false;
+ // Charts larger than this will be scaled down. 0 means no limit.
+ uint32_t maxChartSize = 0;
+
+ // Number of pixels to pad charts with.
+ uint32_t padding = 0;
+
// Unit to texel scale. e.g. a 1x1 quad with texelsPerUnit of 32 will take up approximately 32x32 texels in the atlas.
// If 0, an estimated value will be calculated to approximately match the given resolution.
// If resolution is also 0, the estimated value will approximately match a 1024x1024 atlas.
@@ -203,15 +215,6 @@ struct PackOptions
// If not 0, and texelsPerUnit is not 0, generate one or more atlases with that exact resolution.
// If not 0, and texelsPerUnit is 0, texelsPerUnit is estimated to approximately match the resolution.
uint32_t resolution = 0;
-
- // Charts larger than this will be scaled down.
- uint32_t maxChartSize = 1024;
-
- // Align charts to 4x4 blocks. Also improves packing speed, since there are fewer possible chart locations to consider.
- bool blockAlign = false;
-
- // Number of pixels to pad charts with.
- uint32_t padding = 0;
};
// Call after ParameterizeCharts. Can be called multiple times to re-pack charts with different options.
@@ -240,7 +243,8 @@ void SetProgressCallback(Atlas *atlas, ProgressFunc progressFunc = nullptr, void
// Custom memory allocation.
typedef void *(*ReallocFunc)(void *, size_t);
-void SetRealloc(ReallocFunc reallocFunc);
+typedef void (*FreeFunc)(void *);
+void SetAlloc(ReallocFunc reallocFunc, FreeFunc freeFunc = nullptr);
// Custom print function.
typedef int (*PrintFunc)(const char *, ...);
diff --git a/thirdparty/zstd/common/compiler.h b/thirdparty/zstd/common/compiler.h
index 0836e3ed27..87bf51ae8c 100644
--- a/thirdparty/zstd/common/compiler.h
+++ b/thirdparty/zstd/common/compiler.h
@@ -127,6 +127,13 @@
} \
}
+/* vectorization */
+#if !defined(__clang__) && defined(__GNUC__)
+# define DONT_VECTORIZE __attribute__((optimize("no-tree-vectorize")))
+#else
+# define DONT_VECTORIZE
+#endif
+
/* disable warnings */
#ifdef _MSC_VER /* Visual Studio */
# include <intrin.h> /* For Visual 2005 */
diff --git a/thirdparty/zstd/common/zstd_internal.h b/thirdparty/zstd/common/zstd_internal.h
index 31f756ab58..81b16eac2e 100644
--- a/thirdparty/zstd/common/zstd_internal.h
+++ b/thirdparty/zstd/common/zstd_internal.h
@@ -34,7 +34,6 @@
#endif
#include "xxhash.h" /* XXH_reset, update, digest */
-
#if defined (__cplusplus)
extern "C" {
#endif
@@ -193,19 +192,72 @@ static const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG;
* Shared functions to include for inlining
*********************************************/
static void ZSTD_copy8(void* dst, const void* src) { memcpy(dst, src, 8); }
+
#define COPY8(d,s) { ZSTD_copy8(d,s); d+=8; s+=8; }
+static void ZSTD_copy16(void* dst, const void* src) { memcpy(dst, src, 16); }
+#define COPY16(d,s) { ZSTD_copy16(d,s); d+=16; s+=16; }
+
+#define WILDCOPY_OVERLENGTH 8
+#define VECLEN 16
+
+typedef enum {
+ ZSTD_no_overlap,
+ ZSTD_overlap_src_before_dst,
+ /* ZSTD_overlap_dst_before_src, */
+} ZSTD_overlap_e;
/*! ZSTD_wildcopy() :
* custom version of memcpy(), can overwrite up to WILDCOPY_OVERLENGTH bytes (if length==0) */
-#define WILDCOPY_OVERLENGTH 8
-MEM_STATIC void ZSTD_wildcopy(void* dst, const void* src, ptrdiff_t length)
+MEM_STATIC FORCE_INLINE_ATTR DONT_VECTORIZE
+void ZSTD_wildcopy(void* dst, const void* src, ptrdiff_t length, ZSTD_overlap_e ovtype)
{
+ ptrdiff_t diff = (BYTE*)dst - (const BYTE*)src;
const BYTE* ip = (const BYTE*)src;
BYTE* op = (BYTE*)dst;
BYTE* const oend = op + length;
- do
- COPY8(op, ip)
- while (op < oend);
+
+ assert(diff >= 8 || (ovtype == ZSTD_no_overlap && diff < -8));
+ if (length < VECLEN || (ovtype == ZSTD_overlap_src_before_dst && diff < VECLEN)) {
+ do
+ COPY8(op, ip)
+ while (op < oend);
+ }
+ else {
+ if ((length & 8) == 0)
+ COPY8(op, ip);
+ do {
+ COPY16(op, ip);
+ }
+ while (op < oend);
+ }
+}
+
+/*! ZSTD_wildcopy_16min() :
+ * same semantics as ZSTD_wilcopy() except guaranteed to be able to copy 16 bytes at the start */
+MEM_STATIC FORCE_INLINE_ATTR DONT_VECTORIZE
+void ZSTD_wildcopy_16min(void* dst, const void* src, ptrdiff_t length, ZSTD_overlap_e ovtype)
+{
+ ptrdiff_t diff = (BYTE*)dst - (const BYTE*)src;
+ const BYTE* ip = (const BYTE*)src;
+ BYTE* op = (BYTE*)dst;
+ BYTE* const oend = op + length;
+
+ assert(length >= 8);
+ assert(diff >= 8 || (ovtype == ZSTD_no_overlap && diff < -8));
+
+ if (ovtype == ZSTD_overlap_src_before_dst && diff < VECLEN) {
+ do
+ COPY8(op, ip)
+ while (op < oend);
+ }
+ else {
+ if ((length & 8) == 0)
+ COPY8(op, ip);
+ do {
+ COPY16(op, ip);
+ }
+ while (op < oend);
+ }
}
MEM_STATIC void ZSTD_wildcopy_e(void* dst, const void* src, void* dstEnd) /* should be faster for decoding, but strangely, not verified on all platform */
diff --git a/thirdparty/zstd/compress/zstd_compress.c b/thirdparty/zstd/compress/zstd_compress.c
index 2e163c8bf3..1476512580 100644
--- a/thirdparty/zstd/compress/zstd_compress.c
+++ b/thirdparty/zstd/compress/zstd_compress.c
@@ -385,6 +385,11 @@ ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter param)
bounds.upperBound = ZSTD_lcm_uncompressed;
return bounds;
+ case ZSTD_c_targetCBlockSize:
+ bounds.lowerBound = ZSTD_TARGETCBLOCKSIZE_MIN;
+ bounds.upperBound = ZSTD_TARGETCBLOCKSIZE_MAX;
+ return bounds;
+
default:
{ ZSTD_bounds const boundError = { ERROR(parameter_unsupported), 0, 0 };
return boundError;
@@ -452,6 +457,7 @@ static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param)
case ZSTD_c_ldmHashRateLog:
case ZSTD_c_forceAttachDict:
case ZSTD_c_literalCompressionMode:
+ case ZSTD_c_targetCBlockSize:
default:
return 0;
}
@@ -497,6 +503,7 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value)
case ZSTD_c_ldmHashLog:
case ZSTD_c_ldmMinMatch:
case ZSTD_c_ldmBucketSizeLog:
+ case ZSTD_c_targetCBlockSize:
break;
default: RETURN_ERROR(parameter_unsupported);
@@ -671,6 +678,12 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams,
CCtxParams->ldmParams.hashRateLog = value;
return CCtxParams->ldmParams.hashRateLog;
+ case ZSTD_c_targetCBlockSize :
+ if (value!=0) /* 0 ==> default */
+ BOUNDCHECK(ZSTD_c_targetCBlockSize, value);
+ CCtxParams->targetCBlockSize = value;
+ return CCtxParams->targetCBlockSize;
+
default: RETURN_ERROR(parameter_unsupported, "unknown parameter");
}
}
@@ -692,13 +705,13 @@ size_t ZSTD_CCtxParams_getParameter(
*value = CCtxParams->compressionLevel;
break;
case ZSTD_c_windowLog :
- *value = CCtxParams->cParams.windowLog;
+ *value = (int)CCtxParams->cParams.windowLog;
break;
case ZSTD_c_hashLog :
- *value = CCtxParams->cParams.hashLog;
+ *value = (int)CCtxParams->cParams.hashLog;
break;
case ZSTD_c_chainLog :
- *value = CCtxParams->cParams.chainLog;
+ *value = (int)CCtxParams->cParams.chainLog;
break;
case ZSTD_c_searchLog :
*value = CCtxParams->cParams.searchLog;
@@ -773,6 +786,9 @@ size_t ZSTD_CCtxParams_getParameter(
case ZSTD_c_ldmHashRateLog :
*value = CCtxParams->ldmParams.hashRateLog;
break;
+ case ZSTD_c_targetCBlockSize :
+ *value = (int)CCtxParams->targetCBlockSize;
+ break;
default: RETURN_ERROR(parameter_unsupported, "unknown parameter");
}
return 0;
@@ -930,12 +946,12 @@ size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset)
@return : 0, or an error code if one value is beyond authorized range */
size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams)
{
- BOUNDCHECK(ZSTD_c_windowLog, cParams.windowLog);
- BOUNDCHECK(ZSTD_c_chainLog, cParams.chainLog);
- BOUNDCHECK(ZSTD_c_hashLog, cParams.hashLog);
- BOUNDCHECK(ZSTD_c_searchLog, cParams.searchLog);
- BOUNDCHECK(ZSTD_c_minMatch, cParams.minMatch);
- BOUNDCHECK(ZSTD_c_targetLength,cParams.targetLength);
+ BOUNDCHECK(ZSTD_c_windowLog, (int)cParams.windowLog);
+ BOUNDCHECK(ZSTD_c_chainLog, (int)cParams.chainLog);
+ BOUNDCHECK(ZSTD_c_hashLog, (int)cParams.hashLog);
+ BOUNDCHECK(ZSTD_c_searchLog, (int)cParams.searchLog);
+ BOUNDCHECK(ZSTD_c_minMatch, (int)cParams.minMatch);
+ BOUNDCHECK(ZSTD_c_targetLength,(int)cParams.targetLength);
BOUNDCHECK(ZSTD_c_strategy, cParams.strategy);
return 0;
}
@@ -951,7 +967,7 @@ ZSTD_clampCParams(ZSTD_compressionParameters cParams)
if ((int)val<bounds.lowerBound) val=(type)bounds.lowerBound; \
else if ((int)val>bounds.upperBound) val=(type)bounds.upperBound; \
}
-# define CLAMP(cParam, val) CLAMP_TYPE(cParam, val, int)
+# define CLAMP(cParam, val) CLAMP_TYPE(cParam, val, unsigned)
CLAMP(ZSTD_c_windowLog, cParams.windowLog);
CLAMP(ZSTD_c_chainLog, cParams.chainLog);
CLAMP(ZSTD_c_hashLog, cParams.hashLog);
@@ -1282,15 +1298,14 @@ static void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs)
}
/*! ZSTD_invalidateMatchState()
- * Invalidate all the matches in the match finder tables.
- * Requires nextSrc and base to be set (can be NULL).
+ * Invalidate all the matches in the match finder tables.
+ * Requires nextSrc and base to be set (can be NULL).
*/
static void ZSTD_invalidateMatchState(ZSTD_matchState_t* ms)
{
ZSTD_window_clear(&ms->window);
ms->nextToUpdate = ms->window.dictLimit;
- ms->nextToUpdate3 = ms->window.dictLimit;
ms->loadedDictEnd = 0;
ms->opt.litLengthSum = 0; /* force reset of btopt stats */
ms->dictMatchState = NULL;
@@ -1327,15 +1342,17 @@ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pl
typedef enum { ZSTDcrp_continue, ZSTDcrp_noMemset } ZSTD_compResetPolicy_e;
+typedef enum { ZSTD_resetTarget_CDict, ZSTD_resetTarget_CCtx } ZSTD_resetTarget_e;
+
static void*
ZSTD_reset_matchState(ZSTD_matchState_t* ms,
void* ptr,
const ZSTD_compressionParameters* cParams,
- ZSTD_compResetPolicy_e const crp, U32 const forCCtx)
+ ZSTD_compResetPolicy_e const crp, ZSTD_resetTarget_e const forWho)
{
size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);
size_t const hSize = ((size_t)1) << cParams->hashLog;
- U32 const hashLog3 = (forCCtx && cParams->minMatch==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;
+ U32 const hashLog3 = ((forWho == ZSTD_resetTarget_CCtx) && cParams->minMatch==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;
size_t const h3Size = ((size_t)1) << hashLog3;
size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
@@ -1349,7 +1366,7 @@ ZSTD_reset_matchState(ZSTD_matchState_t* ms,
ZSTD_invalidateMatchState(ms);
/* opt parser space */
- if (forCCtx && (cParams->strategy >= ZSTD_btopt)) {
+ if ((forWho == ZSTD_resetTarget_CCtx) && (cParams->strategy >= ZSTD_btopt)) {
DEBUGLOG(4, "reserving optimal parser space");
ms->opt.litFreq = (unsigned*)ptr;
ms->opt.litLengthFreq = ms->opt.litFreq + (1<<Litbits);
@@ -1377,6 +1394,19 @@ ZSTD_reset_matchState(ZSTD_matchState_t* ms,
return ptr;
}
+/* ZSTD_indexTooCloseToMax() :
+ * minor optimization : prefer memset() rather than reduceIndex()
+ * which is measurably slow in some circumstances (reported for Visual Studio).
+ * Works when re-using a context for a lot of smallish inputs :
+ * if all inputs are smaller than ZSTD_INDEXOVERFLOW_MARGIN,
+ * memset() will be triggered before reduceIndex().
+ */
+#define ZSTD_INDEXOVERFLOW_MARGIN (16 MB)
+static int ZSTD_indexTooCloseToMax(ZSTD_window_t w)
+{
+ return (size_t)(w.nextSrc - w.base) > (ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN);
+}
+
#define ZSTD_WORKSPACETOOLARGE_FACTOR 3 /* define "workspace is too large" as this number of times larger than needed */
#define ZSTD_WORKSPACETOOLARGE_MAXDURATION 128 /* when workspace is continuously too large
* during at least this number of times,
@@ -1388,7 +1418,7 @@ ZSTD_reset_matchState(ZSTD_matchState_t* ms,
note : `params` are assumed fully validated at this stage */
static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
ZSTD_CCtx_params params,
- U64 pledgedSrcSize,
+ U64 const pledgedSrcSize,
ZSTD_compResetPolicy_e const crp,
ZSTD_buffered_policy_e const zbuff)
{
@@ -1400,13 +1430,21 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
if (ZSTD_equivalentParams(zc->appliedParams, params,
zc->inBuffSize,
zc->seqStore.maxNbSeq, zc->seqStore.maxNbLit,
- zbuff, pledgedSrcSize)) {
- DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode (wLog1=%u, blockSize1=%zu)",
- zc->appliedParams.cParams.windowLog, zc->blockSize);
+ zbuff, pledgedSrcSize) ) {
+ DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> consider continue mode");
zc->workSpaceOversizedDuration += (zc->workSpaceOversizedDuration > 0); /* if it was too large, it still is */
- if (zc->workSpaceOversizedDuration <= ZSTD_WORKSPACETOOLARGE_MAXDURATION)
+ if (zc->workSpaceOversizedDuration <= ZSTD_WORKSPACETOOLARGE_MAXDURATION) {
+ DEBUGLOG(4, "continue mode confirmed (wLog1=%u, blockSize1=%zu)",
+ zc->appliedParams.cParams.windowLog, zc->blockSize);
+ if (ZSTD_indexTooCloseToMax(zc->blockState.matchState.window)) {
+ /* prefer a reset, faster than a rescale */
+ ZSTD_reset_matchState(&zc->blockState.matchState,
+ zc->entropyWorkspace + HUF_WORKSPACE_SIZE_U32,
+ &params.cParams,
+ crp, ZSTD_resetTarget_CCtx);
+ }
return ZSTD_continueCCtx(zc, params, pledgedSrcSize);
- } }
+ } } }
DEBUGLOG(4, "ZSTD_equivalentParams()==0 -> reset CCtx");
if (params.ldmParams.enableLdm) {
@@ -1449,7 +1487,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
DEBUGLOG(4, "windowSize: %zu - blockSize: %zu", windowSize, blockSize);
if (workSpaceTooSmall || workSpaceWasteful) {
- DEBUGLOG(4, "Need to resize workSpaceSize from %zuKB to %zuKB",
+ DEBUGLOG(4, "Resize workSpaceSize from %zuKB to %zuKB",
zc->workSpaceSize >> 10,
neededSpace >> 10);
@@ -1491,7 +1529,10 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
ZSTD_reset_compressedBlockState(zc->blockState.prevCBlock);
- ptr = zc->entropyWorkspace + HUF_WORKSPACE_SIZE_U32;
+ ptr = ZSTD_reset_matchState(&zc->blockState.matchState,
+ zc->entropyWorkspace + HUF_WORKSPACE_SIZE_U32,
+ &params.cParams,
+ crp, ZSTD_resetTarget_CCtx);
/* ldm hash table */
/* initialize bucketOffsets table later for pointer alignment */
@@ -1509,8 +1550,6 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
}
assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */
- ptr = ZSTD_reset_matchState(&zc->blockState.matchState, ptr, &params.cParams, crp, /* forCCtx */ 1);
-
/* sequences storage */
zc->seqStore.maxNbSeq = maxNbSeq;
zc->seqStore.sequencesStart = (seqDef*)ptr;
@@ -1587,15 +1626,14 @@ static int ZSTD_shouldAttachDict(const ZSTD_CDict* cdict,
* handled in _enforceMaxDist */
}
-static size_t ZSTD_resetCCtx_byAttachingCDict(
- ZSTD_CCtx* cctx,
- const ZSTD_CDict* cdict,
- ZSTD_CCtx_params params,
- U64 pledgedSrcSize,
- ZSTD_buffered_policy_e zbuff)
+static size_t
+ZSTD_resetCCtx_byAttachingCDict(ZSTD_CCtx* cctx,
+ const ZSTD_CDict* cdict,
+ ZSTD_CCtx_params params,
+ U64 pledgedSrcSize,
+ ZSTD_buffered_policy_e zbuff)
{
- {
- const ZSTD_compressionParameters *cdict_cParams = &cdict->matchState.cParams;
+ { const ZSTD_compressionParameters* const cdict_cParams = &cdict->matchState.cParams;
unsigned const windowLog = params.cParams.windowLog;
assert(windowLog != 0);
/* Resize working context table params for input only, since the dict
@@ -1607,8 +1645,7 @@ static size_t ZSTD_resetCCtx_byAttachingCDict(
assert(cctx->appliedParams.cParams.strategy == cdict_cParams->strategy);
}
- {
- const U32 cdictEnd = (U32)( cdict->matchState.window.nextSrc
+ { const U32 cdictEnd = (U32)( cdict->matchState.window.nextSrc
- cdict->matchState.window.base);
const U32 cdictLen = cdictEnd - cdict->matchState.window.dictLimit;
if (cdictLen == 0) {
@@ -1625,9 +1662,9 @@ static size_t ZSTD_resetCCtx_byAttachingCDict(
cctx->blockState.matchState.window.base + cdictEnd;
ZSTD_window_clear(&cctx->blockState.matchState.window);
}
+ /* loadedDictEnd is expressed within the referential of the active context */
cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit;
- }
- }
+ } }
cctx->dictID = cdict->dictID;
@@ -1681,7 +1718,6 @@ static size_t ZSTD_resetCCtx_byCopyingCDict(ZSTD_CCtx* cctx,
ZSTD_matchState_t* dstMatchState = &cctx->blockState.matchState;
dstMatchState->window = srcMatchState->window;
dstMatchState->nextToUpdate = srcMatchState->nextToUpdate;
- dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3;
dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd;
}
@@ -1761,7 +1797,6 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx,
ZSTD_matchState_t* dstMatchState = &dstCCtx->blockState.matchState;
dstMatchState->window = srcMatchState->window;
dstMatchState->nextToUpdate = srcMatchState->nextToUpdate;
- dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3;
dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd;
}
dstCCtx->dictID = srcCCtx->dictID;
@@ -1831,16 +1866,15 @@ static void ZSTD_reduceTable_btlazy2(U32* const table, U32 const size, U32 const
/*! ZSTD_reduceIndex() :
* rescale all indexes to avoid future overflow (indexes are U32) */
-static void ZSTD_reduceIndex (ZSTD_CCtx* zc, const U32 reducerValue)
+static void ZSTD_reduceIndex (ZSTD_matchState_t* ms, ZSTD_CCtx_params const* params, const U32 reducerValue)
{
- ZSTD_matchState_t* const ms = &zc->blockState.matchState;
- { U32 const hSize = (U32)1 << zc->appliedParams.cParams.hashLog;
+ { U32 const hSize = (U32)1 << params->cParams.hashLog;
ZSTD_reduceTable(ms->hashTable, hSize, reducerValue);
}
- if (zc->appliedParams.cParams.strategy != ZSTD_fast) {
- U32 const chainSize = (U32)1 << zc->appliedParams.cParams.chainLog;
- if (zc->appliedParams.cParams.strategy == ZSTD_btlazy2)
+ if (params->cParams.strategy != ZSTD_fast) {
+ U32 const chainSize = (U32)1 << params->cParams.chainLog;
+ if (params->cParams.strategy == ZSTD_btlazy2)
ZSTD_reduceTable_btlazy2(ms->chainTable, chainSize, reducerValue);
else
ZSTD_reduceTable(ms->chainTable, chainSize, reducerValue);
@@ -2524,6 +2558,7 @@ ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
op[0] = (BYTE)((nbSeq>>8) + 0x80), op[1] = (BYTE)nbSeq, op+=2;
else
op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3;
+ assert(op <= oend);
if (nbSeq==0) {
/* Copy the old tables over as if we repeated them */
memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse));
@@ -2532,6 +2567,7 @@ ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
/* seqHead : flags for FSE encoding type */
seqHead = op++;
+ assert(op <= oend);
/* convert length/distances into codes */
ZSTD_seqToCodes(seqStorePtr);
@@ -2555,6 +2591,7 @@ ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
if (LLtype == set_compressed)
lastNCount = op;
op += countSize;
+ assert(op <= oend);
} }
/* build CTable for Offsets */
{ unsigned max = MaxOff;
@@ -2577,6 +2614,7 @@ ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
if (Offtype == set_compressed)
lastNCount = op;
op += countSize;
+ assert(op <= oend);
} }
/* build CTable for MatchLengths */
{ unsigned max = MaxML;
@@ -2597,6 +2635,7 @@ ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
if (MLtype == set_compressed)
lastNCount = op;
op += countSize;
+ assert(op <= oend);
} }
*seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2));
@@ -2610,6 +2649,7 @@ ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
longOffsets, bmi2);
FORWARD_IF_ERROR(bitstreamSize);
op += bitstreamSize;
+ assert(op <= oend);
/* zstd versions <= 1.3.4 mistakenly report corruption when
* FSE_readNCount() receives a buffer < 4 bytes.
* Fixed by https://github.com/facebook/zstd/pull/1146.
@@ -2721,30 +2761,24 @@ void ZSTD_resetSeqStore(seqStore_t* ssPtr)
ssPtr->longLengthID = 0;
}
-static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
- void* dst, size_t dstCapacity,
- const void* src, size_t srcSize)
+typedef enum { ZSTDbss_compress, ZSTDbss_noCompress } ZSTD_buildSeqStore_e;
+
+static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)
{
ZSTD_matchState_t* const ms = &zc->blockState.matchState;
- size_t cSize;
- DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)",
- (unsigned)dstCapacity, (unsigned)ms->window.dictLimit, (unsigned)ms->nextToUpdate);
+ DEBUGLOG(5, "ZSTD_buildSeqStore (srcSize=%zu)", srcSize);
assert(srcSize <= ZSTD_BLOCKSIZE_MAX);
-
/* Assert that we have correctly flushed the ctx params into the ms's copy */
ZSTD_assertEqualCParams(zc->appliedParams.cParams, ms->cParams);
-
if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) {
ZSTD_ldm_skipSequences(&zc->externSeqStore, srcSize, zc->appliedParams.cParams.minMatch);
- cSize = 0;
- goto out; /* don't even attempt compression below a certain srcSize */
+ return ZSTDbss_noCompress; /* don't even attempt compression below a certain srcSize */
}
ZSTD_resetSeqStore(&(zc->seqStore));
/* required for optimal parser to read stats from dictionary */
ms->opt.symbolCosts = &zc->blockState.prevCBlock->entropy;
/* tell the optimal parser how we expect to compress literals */
ms->opt.literalCompressionMode = zc->appliedParams.literalCompressionMode;
-
/* a gap between an attached dict and the current window is not safe,
* they must remain adjacent,
* and when that stops being the case, the dict must be unset */
@@ -2798,6 +2832,21 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
{ const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize;
ZSTD_storeLastLiterals(&zc->seqStore, lastLiterals, lastLLSize);
} }
+ return ZSTDbss_compress;
+}
+
+static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize)
+{
+ size_t cSize;
+ DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)",
+ (unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit, (unsigned)zc->blockState.matchState.nextToUpdate);
+
+ { const size_t bss = ZSTD_buildSeqStore(zc, src, srcSize);
+ FORWARD_IF_ERROR(bss);
+ if (bss == ZSTDbss_noCompress) { cSize = 0; goto out; }
+ }
/* encode sequences and literals */
cSize = ZSTD_compressSequences(&zc->seqStore,
@@ -2826,6 +2875,25 @@ out:
}
+static void ZSTD_overflowCorrectIfNeeded(ZSTD_matchState_t* ms, ZSTD_CCtx_params const* params, void const* ip, void const* iend)
+{
+ if (ZSTD_window_needOverflowCorrection(ms->window, iend)) {
+ U32 const maxDist = (U32)1 << params->cParams.windowLog;
+ U32 const cycleLog = ZSTD_cycleLog(params->cParams.chainLog, params->cParams.strategy);
+ U32 const correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, maxDist, ip);
+ ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30);
+ ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX_32 <= 30);
+ ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31);
+ ZSTD_reduceIndex(ms, params, correction);
+ if (ms->nextToUpdate < correction) ms->nextToUpdate = 0;
+ else ms->nextToUpdate -= correction;
+ /* invalidate dictionaries on overflow correction */
+ ms->loadedDictEnd = 0;
+ ms->dictMatchState = NULL;
+ }
+}
+
+
/*! ZSTD_compress_frameChunk() :
* Compress a chunk of data into one or multiple blocks.
* All blocks will be terminated, all input will be consumed.
@@ -2844,7 +2912,7 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx,
BYTE* const ostart = (BYTE*)dst;
BYTE* op = ostart;
U32 const maxDist = (U32)1 << cctx->appliedParams.cParams.windowLog;
- assert(cctx->appliedParams.cParams.windowLog <= 31);
+ assert(cctx->appliedParams.cParams.windowLog <= ZSTD_WINDOWLOG_MAX);
DEBUGLOG(5, "ZSTD_compress_frameChunk (blockSize=%u)", (unsigned)blockSize);
if (cctx->appliedParams.fParams.checksumFlag && srcSize)
@@ -2859,19 +2927,10 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx,
"not enough space to store compressed block");
if (remaining < blockSize) blockSize = remaining;
- if (ZSTD_window_needOverflowCorrection(ms->window, ip + blockSize)) {
- U32 const cycleLog = ZSTD_cycleLog(cctx->appliedParams.cParams.chainLog, cctx->appliedParams.cParams.strategy);
- U32 const correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, maxDist, ip);
- ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30);
- ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX_32 <= 30);
- ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31);
- ZSTD_reduceIndex(cctx, correction);
- if (ms->nextToUpdate < correction) ms->nextToUpdate = 0;
- else ms->nextToUpdate -= correction;
- ms->loadedDictEnd = 0;
- ms->dictMatchState = NULL;
- }
- ZSTD_window_enforceMaxDist(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd, &ms->dictMatchState);
+ ZSTD_overflowCorrectIfNeeded(ms, &cctx->appliedParams, ip, ip + blockSize);
+ ZSTD_checkDictValidity(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd, &ms->dictMatchState);
+
+ /* Ensure hash/chain table insertion resumes no sooner than lowlimit */
if (ms->nextToUpdate < ms->window.lowLimit) ms->nextToUpdate = ms->window.lowLimit;
{ size_t cSize = ZSTD_compressBlock_internal(cctx,
@@ -2899,7 +2958,7 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx,
} }
if (lastFrameChunk && (op>ostart)) cctx->stage = ZSTDcs_ending;
- return op-ostart;
+ return (size_t)(op-ostart);
}
@@ -2991,6 +3050,7 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx,
fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, cctx->appliedParams,
cctx->pledgedSrcSizePlusOne-1, cctx->dictID);
FORWARD_IF_ERROR(fhSize);
+ assert(fhSize <= dstCapacity);
dstCapacity -= fhSize;
dst = (char*)dst + fhSize;
cctx->stage = ZSTDcs_ongoing;
@@ -3007,18 +3067,7 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx,
if (!frame) {
/* overflow check and correction for block mode */
- if (ZSTD_window_needOverflowCorrection(ms->window, (const char*)src + srcSize)) {
- U32 const cycleLog = ZSTD_cycleLog(cctx->appliedParams.cParams.chainLog, cctx->appliedParams.cParams.strategy);
- U32 const correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, 1 << cctx->appliedParams.cParams.windowLog, src);
- ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30);
- ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX_32 <= 30);
- ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31);
- ZSTD_reduceIndex(cctx, correction);
- if (ms->nextToUpdate < correction) ms->nextToUpdate = 0;
- else ms->nextToUpdate -= correction;
- ms->loadedDictEnd = 0;
- ms->dictMatchState = NULL;
- }
+ ZSTD_overflowCorrectIfNeeded(ms, &cctx->appliedParams, src, (BYTE const*)src + srcSize);
}
DEBUGLOG(5, "ZSTD_compressContinue_internal (blockSize=%u)", (unsigned)cctx->blockSize);
@@ -3074,7 +3123,7 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms,
const void* src, size_t srcSize,
ZSTD_dictTableLoadMethod_e dtlm)
{
- const BYTE* const ip = (const BYTE*) src;
+ const BYTE* ip = (const BYTE*) src;
const BYTE* const iend = ip + srcSize;
ZSTD_window_update(&ms->window, src, srcSize);
@@ -3085,32 +3134,42 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms,
if (srcSize <= HASH_READ_SIZE) return 0;
- switch(params->cParams.strategy)
- {
- case ZSTD_fast:
- ZSTD_fillHashTable(ms, iend, dtlm);
- break;
- case ZSTD_dfast:
- ZSTD_fillDoubleHashTable(ms, iend, dtlm);
- break;
+ while (iend - ip > HASH_READ_SIZE) {
+ size_t const remaining = iend - ip;
+ size_t const chunk = MIN(remaining, ZSTD_CHUNKSIZE_MAX);
+ const BYTE* const ichunk = ip + chunk;
- case ZSTD_greedy:
- case ZSTD_lazy:
- case ZSTD_lazy2:
- if (srcSize >= HASH_READ_SIZE)
- ZSTD_insertAndFindFirstIndex(ms, iend-HASH_READ_SIZE);
- break;
+ ZSTD_overflowCorrectIfNeeded(ms, params, ip, ichunk);
- case ZSTD_btlazy2: /* we want the dictionary table fully sorted */
- case ZSTD_btopt:
- case ZSTD_btultra:
- case ZSTD_btultra2:
- if (srcSize >= HASH_READ_SIZE)
- ZSTD_updateTree(ms, iend-HASH_READ_SIZE, iend);
- break;
+ switch(params->cParams.strategy)
+ {
+ case ZSTD_fast:
+ ZSTD_fillHashTable(ms, ichunk, dtlm);
+ break;
+ case ZSTD_dfast:
+ ZSTD_fillDoubleHashTable(ms, ichunk, dtlm);
+ break;
- default:
- assert(0); /* not possible : not a valid strategy id */
+ case ZSTD_greedy:
+ case ZSTD_lazy:
+ case ZSTD_lazy2:
+ if (chunk >= HASH_READ_SIZE)
+ ZSTD_insertAndFindFirstIndex(ms, ichunk-HASH_READ_SIZE);
+ break;
+
+ case ZSTD_btlazy2: /* we want the dictionary table fully sorted */
+ case ZSTD_btopt:
+ case ZSTD_btultra:
+ case ZSTD_btultra2:
+ if (chunk >= HASH_READ_SIZE)
+ ZSTD_updateTree(ms, ichunk-HASH_READ_SIZE, ichunk);
+ break;
+
+ default:
+ assert(0); /* not possible : not a valid strategy id */
+ }
+
+ ip = ichunk;
}
ms->nextToUpdate = (U32)(iend - ms->window.base);
@@ -3297,12 +3356,11 @@ static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx,
FORWARD_IF_ERROR( ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,
ZSTDcrp_continue, zbuff) );
- {
- size_t const dictID = ZSTD_compress_insertDictionary(
+ { size_t const dictID = ZSTD_compress_insertDictionary(
cctx->blockState.prevCBlock, &cctx->blockState.matchState,
&params, dict, dictSize, dictContentType, dtlm, cctx->entropyWorkspace);
FORWARD_IF_ERROR(dictID);
- assert(dictID <= (size_t)(U32)-1);
+ assert(dictID <= UINT_MAX);
cctx->dictID = (U32)dictID;
}
return 0;
@@ -3555,10 +3613,10 @@ static size_t ZSTD_initCDict_internal(
/* Reset the state to no dictionary */
ZSTD_reset_compressedBlockState(&cdict->cBlockState);
- { void* const end = ZSTD_reset_matchState(
- &cdict->matchState,
- (U32*)cdict->workspace + HUF_WORKSPACE_SIZE_U32,
- &cParams, ZSTDcrp_continue, /* forCCtx */ 0);
+ { void* const end = ZSTD_reset_matchState(&cdict->matchState,
+ (U32*)cdict->workspace + HUF_WORKSPACE_SIZE_U32,
+ &cParams,
+ ZSTDcrp_continue, ZSTD_resetTarget_CDict);
assert(end == (char*)cdict->workspace + cdict->workspaceSize);
(void)end;
}
@@ -4068,7 +4126,7 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
case zcss_flush:
DEBUGLOG(5, "flush stage");
{ size_t const toFlush = zcs->outBuffContentSize - zcs->outBuffFlushedSize;
- size_t const flushed = ZSTD_limitCopy(op, oend-op,
+ size_t const flushed = ZSTD_limitCopy(op, (size_t)(oend-op),
zcs->outBuff + zcs->outBuffFlushedSize, toFlush);
DEBUGLOG(5, "toFlush: %u into %u ==> flushed: %u",
(unsigned)toFlush, (unsigned)(oend-op), (unsigned)flushed);
@@ -4262,7 +4320,7 @@ size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
if (zcs->appliedParams.nbWorkers > 0) return remainingToFlush; /* minimal estimation */
/* single thread mode : attempt to calculate remaining to flush more precisely */
{ size_t const lastBlockSize = zcs->frameEnded ? 0 : ZSTD_BLOCKHEADERSIZE;
- size_t const checksumSize = zcs->frameEnded ? 0 : zcs->appliedParams.fParams.checksumFlag * 4;
+ size_t const checksumSize = (size_t)(zcs->frameEnded ? 0 : zcs->appliedParams.fParams.checksumFlag * 4);
size_t const toFlush = remainingToFlush + lastBlockSize + checksumSize;
DEBUGLOG(4, "ZSTD_endStream : remaining to flush : %u", (unsigned)toFlush);
return toFlush;
diff --git a/thirdparty/zstd/compress/zstd_compress_internal.h b/thirdparty/zstd/compress/zstd_compress_internal.h
index cc3cbb9da9..5495899be3 100644
--- a/thirdparty/zstd/compress/zstd_compress_internal.h
+++ b/thirdparty/zstd/compress/zstd_compress_internal.h
@@ -33,13 +33,13 @@ extern "C" {
***************************************/
#define kSearchStrength 8
#define HASH_READ_SIZE 8
-#define ZSTD_DUBT_UNSORTED_MARK 1 /* For btlazy2 strategy, index 1 now means "unsorted".
+#define ZSTD_DUBT_UNSORTED_MARK 1 /* For btlazy2 strategy, index ZSTD_DUBT_UNSORTED_MARK==1 means "unsorted".
It could be confused for a real successor at index "1", if sorted as larger than its predecessor.
It's not a big deal though : candidate will just be sorted again.
Additionally, candidate position 1 will be lost.
But candidate 1 cannot hide a large tree of candidates, so it's a minimal loss.
- The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be mishandled after table re-use with a different strategy
- Constant required by ZSTD_compressBlock_btlazy2() and ZSTD_reduceTable_internal() */
+ The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be mishandled after table re-use with a different strategy.
+ This constant is required by ZSTD_compressBlock_btlazy2() and ZSTD_reduceTable_internal() */
/*-*************************************
@@ -128,21 +128,20 @@ typedef struct {
BYTE const* base; /* All regular indexes relative to this position */
BYTE const* dictBase; /* extDict indexes relative to this position */
U32 dictLimit; /* below that point, need extDict */
- U32 lowLimit; /* below that point, no more data */
+ U32 lowLimit; /* below that point, no more valid data */
} ZSTD_window_t;
typedef struct ZSTD_matchState_t ZSTD_matchState_t;
struct ZSTD_matchState_t {
ZSTD_window_t window; /* State for window round buffer management */
- U32 loadedDictEnd; /* index of end of dictionary */
+ U32 loadedDictEnd; /* index of end of dictionary, within context's referential. When dict referential is copied into active context (i.e. not attached), effectively same value as dictSize, since referential starts from zero */
U32 nextToUpdate; /* index from which to continue table update */
- U32 nextToUpdate3; /* index from which to continue table update */
U32 hashLog3; /* dispatch table : larger == faster, more memory */
U32* hashTable;
U32* hashTable3;
U32* chainTable;
optState_t opt; /* optimal parser state */
- const ZSTD_matchState_t * dictMatchState;
+ const ZSTD_matchState_t* dictMatchState;
ZSTD_compressionParameters cParams;
};
@@ -195,6 +194,9 @@ struct ZSTD_CCtx_params_s {
int compressionLevel;
int forceWindow; /* force back-references to respect limit of
* 1<<wLog, even for dictionary */
+ size_t targetCBlockSize; /* Tries to fit compressed block size to be around targetCBlockSize.
+ * No target when targetCBlockSize == 0.
+ * There is no guarantee on compressed block size */
ZSTD_dictAttachPref_e attachDictPref;
ZSTD_literalCompressionMode_e literalCompressionMode;
@@ -324,7 +326,7 @@ MEM_STATIC void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const v
/* copy Literals */
assert(seqStorePtr->maxNbLit <= 128 KB);
assert(seqStorePtr->lit + litLength <= seqStorePtr->litStart + seqStorePtr->maxNbLit);
- ZSTD_wildcopy(seqStorePtr->lit, literals, litLength);
+ ZSTD_wildcopy(seqStorePtr->lit, literals, litLength, ZSTD_no_overlap);
seqStorePtr->lit += litLength;
/* literal Length */
@@ -564,6 +566,9 @@ MEM_STATIC U64 ZSTD_rollingHash_rotate(U64 hash, BYTE toRemove, BYTE toAdd, U64
/*-*************************************
* Round buffer management
***************************************/
+#if (ZSTD_WINDOWLOG_MAX_64 > 31)
+# error "ZSTD_WINDOWLOG_MAX is too large : would overflow ZSTD_CURRENT_MAX"
+#endif
/* Max current allowed */
#define ZSTD_CURRENT_MAX ((3U << 29) + (1U << ZSTD_WINDOWLOG_MAX))
/* Maximum chunk size before overflow correction needs to be called again */
@@ -675,31 +680,49 @@ MEM_STATIC U32 ZSTD_window_correctOverflow(ZSTD_window_t* window, U32 cycleLog,
* Updates lowLimit so that:
* (srcEnd - base) - lowLimit == maxDist + loadedDictEnd
*
- * This allows a simple check that index >= lowLimit to see if index is valid.
- * This must be called before a block compression call, with srcEnd as the block
- * source end.
+ * It ensures index is valid as long as index >= lowLimit.
+ * This must be called before a block compression call.
+ *
+ * loadedDictEnd is only defined if a dictionary is in use for current compression.
+ * As the name implies, loadedDictEnd represents the index at end of dictionary.
+ * The value lies within context's referential, it can be directly compared to blockEndIdx.
*
- * If loadedDictEndPtr is not NULL, we set it to zero once we update lowLimit.
- * This is because dictionaries are allowed to be referenced as long as the last
- * byte of the dictionary is in the window, but once they are out of range,
- * they cannot be referenced. If loadedDictEndPtr is NULL, we use
- * loadedDictEnd == 0.
+ * If loadedDictEndPtr is NULL, no dictionary is in use, and we use loadedDictEnd == 0.
+ * If loadedDictEndPtr is not NULL, we set it to zero after updating lowLimit.
+ * This is because dictionaries are allowed to be referenced fully
+ * as long as the last byte of the dictionary is in the window.
+ * Once input has progressed beyond window size, dictionary cannot be referenced anymore.
*
- * In normal dict mode, the dict is between lowLimit and dictLimit. In
- * dictMatchState mode, lowLimit and dictLimit are the same, and the dictionary
- * is below them. forceWindow and dictMatchState are therefore incompatible.
+ * In normal dict mode, the dictionary lies between lowLimit and dictLimit.
+ * In dictMatchState mode, lowLimit and dictLimit are the same,
+ * and the dictionary is below them.
+ * forceWindow and dictMatchState are therefore incompatible.
*/
MEM_STATIC void
ZSTD_window_enforceMaxDist(ZSTD_window_t* window,
- void const* srcEnd,
- U32 maxDist,
- U32* loadedDictEndPtr,
+ const void* blockEnd,
+ U32 maxDist,
+ U32* loadedDictEndPtr,
const ZSTD_matchState_t** dictMatchStatePtr)
{
- U32 const blockEndIdx = (U32)((BYTE const*)srcEnd - window->base);
- U32 loadedDictEnd = (loadedDictEndPtr != NULL) ? *loadedDictEndPtr : 0;
- DEBUGLOG(5, "ZSTD_window_enforceMaxDist: blockEndIdx=%u, maxDist=%u",
- (unsigned)blockEndIdx, (unsigned)maxDist);
+ U32 const blockEndIdx = (U32)((BYTE const*)blockEnd - window->base);
+ U32 const loadedDictEnd = (loadedDictEndPtr != NULL) ? *loadedDictEndPtr : 0;
+ DEBUGLOG(5, "ZSTD_window_enforceMaxDist: blockEndIdx=%u, maxDist=%u, loadedDictEnd=%u",
+ (unsigned)blockEndIdx, (unsigned)maxDist, (unsigned)loadedDictEnd);
+
+ /* - When there is no dictionary : loadedDictEnd == 0.
+ In which case, the test (blockEndIdx > maxDist) is merely to avoid
+ overflowing next operation `newLowLimit = blockEndIdx - maxDist`.
+ - When there is a standard dictionary :
+ Index referential is copied from the dictionary,
+ which means it starts from 0.
+ In which case, loadedDictEnd == dictSize,
+ and it makes sense to compare `blockEndIdx > maxDist + dictSize`
+ since `blockEndIdx` also starts from zero.
+ - When there is an attached dictionary :
+ loadedDictEnd is expressed within the referential of the context,
+ so it can be directly compared against blockEndIdx.
+ */
if (blockEndIdx > maxDist + loadedDictEnd) {
U32 const newLowLimit = blockEndIdx - maxDist;
if (window->lowLimit < newLowLimit) window->lowLimit = newLowLimit;
@@ -708,10 +731,31 @@ ZSTD_window_enforceMaxDist(ZSTD_window_t* window,
(unsigned)window->dictLimit, (unsigned)window->lowLimit);
window->dictLimit = window->lowLimit;
}
- if (loadedDictEndPtr)
- *loadedDictEndPtr = 0;
- if (dictMatchStatePtr)
- *dictMatchStatePtr = NULL;
+ /* On reaching window size, dictionaries are invalidated */
+ if (loadedDictEndPtr) *loadedDictEndPtr = 0;
+ if (dictMatchStatePtr) *dictMatchStatePtr = NULL;
+ }
+}
+
+/* Similar to ZSTD_window_enforceMaxDist(),
+ * but only invalidates dictionary
+ * when input progresses beyond window size. */
+MEM_STATIC void
+ZSTD_checkDictValidity(ZSTD_window_t* window,
+ const void* blockEnd,
+ U32 maxDist,
+ U32* loadedDictEndPtr,
+ const ZSTD_matchState_t** dictMatchStatePtr)
+{
+ U32 const blockEndIdx = (U32)((BYTE const*)blockEnd - window->base);
+ U32 const loadedDictEnd = (loadedDictEndPtr != NULL) ? *loadedDictEndPtr : 0;
+ DEBUGLOG(5, "ZSTD_checkDictValidity: blockEndIdx=%u, maxDist=%u, loadedDictEnd=%u",
+ (unsigned)blockEndIdx, (unsigned)maxDist, (unsigned)loadedDictEnd);
+
+ if (loadedDictEnd && (blockEndIdx > maxDist + loadedDictEnd)) {
+ /* On reaching window size, dictionaries are invalidated */
+ if (loadedDictEndPtr) *loadedDictEndPtr = 0;
+ if (dictMatchStatePtr) *dictMatchStatePtr = NULL;
}
}
diff --git a/thirdparty/zstd/compress/zstd_double_fast.c b/thirdparty/zstd/compress/zstd_double_fast.c
index 47faf6d641..5957255d90 100644
--- a/thirdparty/zstd/compress/zstd_double_fast.c
+++ b/thirdparty/zstd/compress/zstd_double_fast.c
@@ -43,8 +43,7 @@ void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms,
/* Only load extra positions for ZSTD_dtlm_full */
if (dtlm == ZSTD_dtlm_fast)
break;
- }
- }
+ } }
}
@@ -63,7 +62,10 @@ size_t ZSTD_compressBlock_doubleFast_generic(
const BYTE* const istart = (const BYTE*)src;
const BYTE* ip = istart;
const BYTE* anchor = istart;
- const U32 prefixLowestIndex = ms->window.dictLimit;
+ const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);
+ const U32 lowestValid = ms->window.dictLimit;
+ const U32 maxDistance = 1U << cParams->windowLog;
+ const U32 prefixLowestIndex = (endIndex - lowestValid > maxDistance) ? endIndex - maxDistance : lowestValid;
const BYTE* const prefixLowest = base + prefixLowestIndex;
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - HASH_READ_SIZE;
@@ -95,8 +97,15 @@ size_t ZSTD_compressBlock_doubleFast_generic(
dictCParams->chainLog : hBitsS;
const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictStart);
+ DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_generic");
+
assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState);
+ /* if a dictionary is attached, it must be within window range */
+ if (dictMode == ZSTD_dictMatchState) {
+ assert(lowestValid + maxDistance >= endIndex);
+ }
+
/* init */
ip += (dictAndPrefixLength == 0);
if (dictMode == ZSTD_noDict) {
@@ -138,7 +147,7 @@ size_t ZSTD_compressBlock_doubleFast_generic(
const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend;
mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4;
ip++;
- ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, 0, mLength-MINMATCH);
goto _match_stored;
}
@@ -147,7 +156,7 @@ size_t ZSTD_compressBlock_doubleFast_generic(
&& ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) {
mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4;
ip++;
- ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, 0, mLength-MINMATCH);
goto _match_stored;
}
@@ -170,8 +179,7 @@ size_t ZSTD_compressBlock_doubleFast_generic(
offset = (U32)(current - dictMatchIndexL - dictIndexDelta);
while (((ip>anchor) & (dictMatchL>dictStart)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */
goto _match_found;
- }
- }
+ } }
if (matchIndexS > prefixLowestIndex) {
/* check prefix short match */
@@ -186,16 +194,14 @@ size_t ZSTD_compressBlock_doubleFast_generic(
if (match > dictStart && MEM_read32(match) == MEM_read32(ip)) {
goto _search_next_long;
- }
- }
+ } }
ip += ((ip-anchor) >> kSearchStrength) + 1;
continue;
_search_next_long:
- {
- size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8);
+ { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8);
size_t const dictHLNext = ZSTD_hashPtr(ip+1, dictHBitsL, 8);
U32 const matchIndexL3 = hashLong[hl3];
const BYTE* matchL3 = base + matchIndexL3;
@@ -221,9 +227,7 @@ _search_next_long:
offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta);
while (((ip>anchor) & (dictMatchL3>dictStart)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */
goto _match_found;
- }
- }
- }
+ } } }
/* if no long +1 match, explore the short match we found */
if (dictMode == ZSTD_dictMatchState && matchIndexS < prefixLowestIndex) {
@@ -242,7 +246,7 @@ _match_found:
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
_match_stored:
/* match found */
@@ -250,11 +254,14 @@ _match_stored:
anchor = ip;
if (ip <= ilimit) {
- /* Fill Table */
- hashLong[ZSTD_hashPtr(base+current+2, hBitsL, 8)] =
- hashSmall[ZSTD_hashPtr(base+current+2, hBitsS, mls)] = current+2; /* here because current+2 could be > iend-8 */
- hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] =
- hashSmall[ZSTD_hashPtr(ip-2, hBitsS, mls)] = (U32)(ip-2-base);
+ /* Complementary insertion */
+ /* done after iLimit test, as candidates could be > iend-8 */
+ { U32 const indexToInsert = current+2;
+ hashLong[ZSTD_hashPtr(base+indexToInsert, hBitsL, 8)] = indexToInsert;
+ hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base);
+ hashSmall[ZSTD_hashPtr(base+indexToInsert, hBitsS, mls)] = indexToInsert;
+ hashSmall[ZSTD_hashPtr(ip-1, hBitsS, mls)] = (U32)(ip-1-base);
+ }
/* check immediate repcode */
if (dictMode == ZSTD_dictMatchState) {
@@ -278,8 +285,7 @@ _match_stored:
continue;
}
break;
- }
- }
+ } }
if (dictMode == ZSTD_noDict) {
while ( (ip <= ilimit)
@@ -294,14 +300,15 @@ _match_stored:
ip += rLength;
anchor = ip;
continue; /* faster when present ... (?) */
- } } } }
+ } } }
+ } /* while (ip < ilimit) */
/* save reps for next block */
rep[0] = offset_1 ? offset_1 : offsetSaved;
rep[1] = offset_2 ? offset_2 : offsetSaved;
/* Return the last literals size */
- return iend - anchor;
+ return (size_t)(iend - anchor);
}
@@ -360,10 +367,15 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(
const BYTE* anchor = istart;
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - 8;
- const U32 prefixStartIndex = ms->window.dictLimit;
const BYTE* const base = ms->window.base;
+ const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);
+ const U32 maxDistance = 1U << cParams->windowLog;
+ const U32 lowestValid = ms->window.lowLimit;
+ const U32 lowLimit = (endIndex - lowestValid > maxDistance) ? endIndex - maxDistance : lowestValid;
+ const U32 dictStartIndex = lowLimit;
+ const U32 dictLimit = ms->window.dictLimit;
+ const U32 prefixStartIndex = (dictLimit > lowLimit) ? dictLimit : lowLimit;
const BYTE* const prefixStart = base + prefixStartIndex;
- const U32 dictStartIndex = ms->window.lowLimit;
const BYTE* const dictBase = ms->window.dictBase;
const BYTE* const dictStart = dictBase + dictStartIndex;
const BYTE* const dictEnd = dictBase + prefixStartIndex;
@@ -371,6 +383,10 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(
DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_extDict_generic (srcSize=%zu)", srcSize);
+ /* if extDict is invalidated due to maxDistance, switch to "regular" variant */
+ if (prefixStartIndex == dictStartIndex)
+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, mls, ZSTD_noDict);
+
/* Search Loop */
while (ip < ilimit) { /* < instead of <=, because (ip+1) */
const size_t hSmall = ZSTD_hashPtr(ip, hBitsS, mls);
@@ -396,7 +412,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(
const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4;
ip++;
- ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, 0, mLength-MINMATCH);
} else {
if ((matchLongIndex > dictStartIndex) && (MEM_read64(matchLong) == MEM_read64(ip))) {
const BYTE* const matchEnd = matchLongIndex < prefixStartIndex ? dictEnd : iend;
@@ -407,7 +423,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(
while (((ip>anchor) & (matchLong>lowMatchPtr)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
} else if ((matchIndex > dictStartIndex) && (MEM_read32(match) == MEM_read32(ip))) {
size_t const h3 = ZSTD_hashPtr(ip+1, hBitsL, 8);
@@ -432,23 +448,27 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(
}
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
} else {
ip += ((ip-anchor) >> kSearchStrength) + 1;
continue;
} }
- /* found a match : store it */
+ /* move to next sequence start */
ip += mLength;
anchor = ip;
if (ip <= ilimit) {
- /* Fill Table */
- hashSmall[ZSTD_hashPtr(base+current+2, hBitsS, mls)] = current+2;
- hashLong[ZSTD_hashPtr(base+current+2, hBitsL, 8)] = current+2;
- hashSmall[ZSTD_hashPtr(ip-2, hBitsS, mls)] = (U32)(ip-2-base);
- hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base);
+ /* Complementary insertion */
+ /* done after iLimit test, as candidates could be > iend-8 */
+ { U32 const indexToInsert = current+2;
+ hashLong[ZSTD_hashPtr(base+indexToInsert, hBitsL, 8)] = indexToInsert;
+ hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base);
+ hashSmall[ZSTD_hashPtr(base+indexToInsert, hBitsS, mls)] = indexToInsert;
+ hashSmall[ZSTD_hashPtr(ip-1, hBitsS, mls)] = (U32)(ip-1-base);
+ }
+
/* check immediate repcode */
while (ip <= ilimit) {
U32 const current2 = (U32)(ip-base);
@@ -475,7 +495,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(
rep[1] = offset_2;
/* Return the last literals size */
- return iend - anchor;
+ return (size_t)(iend - anchor);
}
diff --git a/thirdparty/zstd/compress/zstd_fast.c b/thirdparty/zstd/compress/zstd_fast.c
index ed997b441c..a05b8a47f1 100644
--- a/thirdparty/zstd/compress/zstd_fast.c
+++ b/thirdparty/zstd/compress/zstd_fast.c
@@ -13,7 +13,8 @@
void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
- void const* end, ZSTD_dictTableLoadMethod_e dtlm)
+ const void* const end,
+ ZSTD_dictTableLoadMethod_e dtlm)
{
const ZSTD_compressionParameters* const cParams = &ms->cParams;
U32* const hashTable = ms->hashTable;
@@ -41,6 +42,7 @@ void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
} } } }
}
+
FORCE_INLINE_TEMPLATE
size_t ZSTD_compressBlock_fast_generic(
ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
@@ -58,7 +60,10 @@ size_t ZSTD_compressBlock_fast_generic(
const BYTE* ip0 = istart;
const BYTE* ip1;
const BYTE* anchor = istart;
- const U32 prefixStartIndex = ms->window.dictLimit;
+ const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);
+ const U32 maxDistance = 1U << cParams->windowLog;
+ const U32 validStartIndex = ms->window.dictLimit;
+ const U32 prefixStartIndex = (endIndex - validStartIndex > maxDistance) ? endIndex - maxDistance : validStartIndex;
const BYTE* const prefixStart = base + prefixStartIndex;
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - HASH_READ_SIZE;
@@ -165,7 +170,7 @@ _match: /* Requires: ip0, match0, offcode */
rep[1] = offset_2 ? offset_2 : offsetSaved;
/* Return the last literals size */
- return iend - anchor;
+ return (size_t)(iend - anchor);
}
@@ -222,8 +227,15 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
const U32 dictAndPrefixLength = (U32)(ip - prefixStart + dictEnd - dictStart);
const U32 dictHLog = dictCParams->hashLog;
- /* otherwise, we would get index underflow when translating a dict index
- * into a local index */
+ /* if a dictionary is still attached, it necessarily means that
+ * it is within window size. So we just check it. */
+ const U32 maxDistance = 1U << cParams->windowLog;
+ const U32 endIndex = (U32)((size_t)(ip - base) + srcSize);
+ assert(endIndex - prefixStartIndex <= maxDistance);
+ (void)maxDistance; (void)endIndex; /* these variables are not used when assert() is disabled */
+
+ /* ensure there will be no no underflow
+ * when translating a dict index into a local index */
assert(prefixStartIndex >= (U32)(dictEnd - dictBase));
/* init */
@@ -251,7 +263,7 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4;
ip++;
- ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, 0, mLength-MINMATCH);
} else if ( (matchIndex <= prefixStartIndex) ) {
size_t const dictHash = ZSTD_hashPtr(ip, dictHLog, mls);
U32 const dictMatchIndex = dictHashTable[dictHash];
@@ -271,7 +283,7 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
} /* catch up */
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
}
} else if (MEM_read32(match) != MEM_read32(ip)) {
/* it's not a match, and we're not going to check the dictionary */
@@ -286,7 +298,7 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
&& (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
}
/* match found */
@@ -327,7 +339,7 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
rep[1] = offset_2 ? offset_2 : offsetSaved;
/* Return the last literals size */
- return iend - anchor;
+ return (size_t)(iend - anchor);
}
size_t ZSTD_compressBlock_fast_dictMatchState(
@@ -366,15 +378,24 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
const BYTE* const istart = (const BYTE*)src;
const BYTE* ip = istart;
const BYTE* anchor = istart;
- const U32 dictStartIndex = ms->window.lowLimit;
+ const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);
+ const U32 maxDistance = 1U << cParams->windowLog;
+ const U32 validLow = ms->window.lowLimit;
+ const U32 lowLimit = (endIndex - validLow > maxDistance) ? endIndex - maxDistance : validLow;
+ const U32 dictStartIndex = lowLimit;
const BYTE* const dictStart = dictBase + dictStartIndex;
- const U32 prefixStartIndex = ms->window.dictLimit;
+ const U32 dictLimit = ms->window.dictLimit;
+ const U32 prefixStartIndex = dictLimit < lowLimit ? lowLimit : dictLimit;
const BYTE* const prefixStart = base + prefixStartIndex;
const BYTE* const dictEnd = dictBase + prefixStartIndex;
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - 8;
U32 offset_1=rep[0], offset_2=rep[1];
+ /* switch to "regular" variant if extDict is invalidated due to maxDistance */
+ if (prefixStartIndex == dictStartIndex)
+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, mls);
+
/* Search Loop */
while (ip < ilimit) { /* < instead of <=, because (ip+1) */
const size_t h = ZSTD_hashPtr(ip, hlog, mls);
@@ -394,7 +415,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4;
ip++;
- ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, 0, mLength-MINMATCH);
} else {
if ( (matchIndex < dictStartIndex) ||
(MEM_read32(match) != MEM_read32(ip)) ) {
@@ -410,7 +431,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
offset = current - matchIndex;
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
} }
/* found a match : store it */
@@ -445,7 +466,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
rep[1] = offset_2;
/* Return the last literals size */
- return iend - anchor;
+ return (size_t)(iend - anchor);
}
diff --git a/thirdparty/zstd/compress/zstd_lazy.c b/thirdparty/zstd/compress/zstd_lazy.c
index 53f998a437..94d906c01f 100644
--- a/thirdparty/zstd/compress/zstd_lazy.c
+++ b/thirdparty/zstd/compress/zstd_lazy.c
@@ -83,7 +83,10 @@ ZSTD_insertDUBT1(ZSTD_matchState_t* ms,
U32* largerPtr = smallerPtr + 1;
U32 matchIndex = *smallerPtr; /* this candidate is unsorted : next sorted candidate is reached through *smallerPtr, while *largerPtr contains previous unsorted candidate (which is already saved and can be overwritten) */
U32 dummy32; /* to be nullified at the end */
- U32 const windowLow = ms->window.lowLimit;
+ U32 const windowValid = ms->window.lowLimit;
+ U32 const maxDistance = 1U << cParams->windowLog;
+ U32 const windowLow = (current - windowValid > maxDistance) ? current - maxDistance : windowValid;
+
DEBUGLOG(8, "ZSTD_insertDUBT1(%u) (dictLimit=%u, lowLimit=%u)",
current, dictLimit, windowLow);
@@ -239,7 +242,9 @@ ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms,
const BYTE* const base = ms->window.base;
U32 const current = (U32)(ip-base);
- U32 const windowLow = ms->window.lowLimit;
+ U32 const maxDistance = 1U << cParams->windowLog;
+ U32 const windowValid = ms->window.lowLimit;
+ U32 const windowLow = (current - windowValid > maxDistance) ? current - maxDistance : windowValid;
U32* const bt = ms->chainTable;
U32 const btLog = cParams->chainLog - 1;
@@ -490,8 +495,10 @@ size_t ZSTD_HcFindBestMatch_generic (
const U32 dictLimit = ms->window.dictLimit;
const BYTE* const prefixStart = base + dictLimit;
const BYTE* const dictEnd = dictBase + dictLimit;
- const U32 lowLimit = ms->window.lowLimit;
const U32 current = (U32)(ip-base);
+ const U32 maxDistance = 1U << cParams->windowLog;
+ const U32 lowValid = ms->window.lowLimit;
+ const U32 lowLimit = (current - lowValid > maxDistance) ? current - maxDistance : lowValid;
const U32 minChain = current > chainSize ? current - chainSize : 0;
U32 nbAttempts = 1U << cParams->searchLog;
size_t ml=4-1;
@@ -653,7 +660,6 @@ size_t ZSTD_compressBlock_lazy_generic(
/* init */
ip += (dictAndPrefixLength == 0);
- ms->nextToUpdate3 = ms->nextToUpdate;
if (dictMode == ZSTD_noDict) {
U32 const maxRep = (U32)(ip - prefixLowest);
if (offset_2 > maxRep) savedOffset = offset_2, offset_2 = 0;
@@ -933,7 +939,6 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(
U32 offset_1 = rep[0], offset_2 = rep[1];
/* init */
- ms->nextToUpdate3 = ms->nextToUpdate;
ip += (ip == prefixStart);
/* Match Loop */
diff --git a/thirdparty/zstd/compress/zstd_ldm.c b/thirdparty/zstd/compress/zstd_ldm.c
index 784d20f3ab..3dcf86e6e8 100644
--- a/thirdparty/zstd/compress/zstd_ldm.c
+++ b/thirdparty/zstd/compress/zstd_ldm.c
@@ -447,7 +447,7 @@ size_t ZSTD_ldm_generateSequences(
if (ZSTD_window_needOverflowCorrection(ldmState->window, chunkEnd)) {
U32 const ldmHSize = 1U << params->hashLog;
U32 const correction = ZSTD_window_correctOverflow(
- &ldmState->window, /* cycleLog */ 0, maxDist, src);
+ &ldmState->window, /* cycleLog */ 0, maxDist, chunkStart);
ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction);
}
/* 2. We enforce the maximum offset allowed.
diff --git a/thirdparty/zstd/compress/zstd_opt.c b/thirdparty/zstd/compress/zstd_opt.c
index efb69d3267..e32e542e02 100644
--- a/thirdparty/zstd/compress/zstd_opt.c
+++ b/thirdparty/zstd/compress/zstd_opt.c
@@ -255,13 +255,13 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP
* to provide a cost which is directly comparable to a match ending at same position */
static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* const optPtr, int optLevel)
{
- if (optPtr->priceType >= zop_predef) return WEIGHT(litLength, optLevel);
+ if (optPtr->priceType >= zop_predef) return (int)WEIGHT(litLength, optLevel);
/* dynamic statistics */
{ U32 const llCode = ZSTD_LLcode(litLength);
- int const contribution = (LL_bits[llCode] * BITCOST_MULTIPLIER)
- + WEIGHT(optPtr->litLengthFreq[0], optLevel) /* note: log2litLengthSum cancel out */
- - WEIGHT(optPtr->litLengthFreq[llCode], optLevel);
+ int const contribution = (int)(LL_bits[llCode] * BITCOST_MULTIPLIER)
+ + (int)WEIGHT(optPtr->litLengthFreq[0], optLevel) /* note: log2litLengthSum cancel out */
+ - (int)WEIGHT(optPtr->litLengthFreq[llCode], optLevel);
#if 1
return contribution;
#else
@@ -278,7 +278,7 @@ static int ZSTD_literalsContribution(const BYTE* const literals, U32 const litLe
const optState_t* const optPtr,
int optLevel)
{
- int const contribution = ZSTD_rawLiteralsCost(literals, litLength, optPtr, optLevel)
+ int const contribution = (int)ZSTD_rawLiteralsCost(literals, litLength, optPtr, optLevel)
+ ZSTD_litLengthContribution(litLength, optPtr, optLevel);
return contribution;
}
@@ -372,13 +372,15 @@ MEM_STATIC U32 ZSTD_readMINMATCH(const void* memPtr, U32 length)
/* Update hashTable3 up to ip (excluded)
Assumption : always within prefix (i.e. not within extDict) */
-static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, const BYTE* const ip)
+static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms,
+ U32* nextToUpdate3,
+ const BYTE* const ip)
{
U32* const hashTable3 = ms->hashTable3;
U32 const hashLog3 = ms->hashLog3;
const BYTE* const base = ms->window.base;
- U32 idx = ms->nextToUpdate3;
- U32 const target = ms->nextToUpdate3 = (U32)(ip - base);
+ U32 idx = *nextToUpdate3;
+ U32 const target = (U32)(ip - base);
size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3);
assert(hashLog3 > 0);
@@ -387,6 +389,7 @@ static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, const BYTE*
idx++;
}
+ *nextToUpdate3 = target;
return hashTable3[hash3];
}
@@ -503,9 +506,11 @@ static U32 ZSTD_insertBt1(
} }
*smallerPtr = *largerPtr = 0;
- if (bestLength > 384) return MIN(192, (U32)(bestLength - 384)); /* speed optimization */
- assert(matchEndIdx > current + 8);
- return matchEndIdx - (current + 8);
+ { U32 positions = 0;
+ if (bestLength > 384) positions = MIN(192, (U32)(bestLength - 384)); /* speed optimization */
+ assert(matchEndIdx > current + 8);
+ return MAX(positions, matchEndIdx - (current + 8));
+ }
}
FORCE_INLINE_TEMPLATE
@@ -520,8 +525,13 @@ void ZSTD_updateTree_internal(
DEBUGLOG(6, "ZSTD_updateTree_internal, from %u to %u (dictMode:%u)",
idx, target, dictMode);
- while(idx < target)
- idx += ZSTD_insertBt1(ms, base+idx, iend, mls, dictMode == ZSTD_extDict);
+ while(idx < target) {
+ U32 const forward = ZSTD_insertBt1(ms, base+idx, iend, mls, dictMode == ZSTD_extDict);
+ assert(idx < (U32)(idx + forward));
+ idx += forward;
+ }
+ assert((size_t)(ip - base) <= (size_t)(U32)(-1));
+ assert((size_t)(iend - base) <= (size_t)(U32)(-1));
ms->nextToUpdate = target;
}
@@ -531,16 +541,18 @@ void ZSTD_updateTree(ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* iend) {
FORCE_INLINE_TEMPLATE
U32 ZSTD_insertBtAndGetAllMatches (
+ ZSTD_match_t* matches, /* store result (found matches) in this table (presumed large enough) */
ZSTD_matchState_t* ms,
+ U32* nextToUpdate3,
const BYTE* const ip, const BYTE* const iLimit, const ZSTD_dictMode_e dictMode,
- U32 rep[ZSTD_REP_NUM],
+ const U32 rep[ZSTD_REP_NUM],
U32 const ll0, /* tells if associated literal length is 0 or not. This value must be 0 or 1 */
- ZSTD_match_t* matches,
const U32 lengthToBeat,
U32 const mls /* template */)
{
const ZSTD_compressionParameters* const cParams = &ms->cParams;
U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1);
+ U32 const maxDistance = 1U << cParams->windowLog;
const BYTE* const base = ms->window.base;
U32 const current = (U32)(ip-base);
U32 const hashLog = cParams->hashLog;
@@ -556,8 +568,9 @@ U32 ZSTD_insertBtAndGetAllMatches (
U32 const dictLimit = ms->window.dictLimit;
const BYTE* const dictEnd = dictBase + dictLimit;
const BYTE* const prefixStart = base + dictLimit;
- U32 const btLow = btMask >= current ? 0 : current - btMask;
- U32 const windowLow = ms->window.lowLimit;
+ U32 const btLow = (btMask >= current) ? 0 : current - btMask;
+ U32 const windowValid = ms->window.lowLimit;
+ U32 const windowLow = ((current - windowValid) > maxDistance) ? current - maxDistance : windowValid;
U32 const matchLow = windowLow ? windowLow : 1;
U32* smallerPtr = bt + 2*(current&btMask);
U32* largerPtr = bt + 2*(current&btMask) + 1;
@@ -627,7 +640,7 @@ U32 ZSTD_insertBtAndGetAllMatches (
/* HC3 match finder */
if ((mls == 3) /*static*/ && (bestLength < mls)) {
- U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, ip);
+ U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, nextToUpdate3, ip);
if ((matchIndex3 >= matchLow)
& (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) {
size_t mlen;
@@ -653,9 +666,7 @@ U32 ZSTD_insertBtAndGetAllMatches (
(ip+mlen == iLimit) ) { /* best possible length */
ms->nextToUpdate = current+1; /* skip insertion */
return 1;
- }
- }
- }
+ } } }
/* no dictMatchState lookup: dicts don't have a populated HC3 table */
}
@@ -760,10 +771,13 @@ U32 ZSTD_insertBtAndGetAllMatches (
FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches (
+ ZSTD_match_t* matches, /* store result (match found, increasing size) in this table */
ZSTD_matchState_t* ms,
+ U32* nextToUpdate3,
const BYTE* ip, const BYTE* const iHighLimit, const ZSTD_dictMode_e dictMode,
- U32 rep[ZSTD_REP_NUM], U32 const ll0,
- ZSTD_match_t* matches, U32 const lengthToBeat)
+ const U32 rep[ZSTD_REP_NUM],
+ U32 const ll0,
+ U32 const lengthToBeat)
{
const ZSTD_compressionParameters* const cParams = &ms->cParams;
U32 const matchLengthSearch = cParams->minMatch;
@@ -772,12 +786,12 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches (
ZSTD_updateTree_internal(ms, ip, iHighLimit, matchLengthSearch, dictMode);
switch(matchLengthSearch)
{
- case 3 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 3);
+ case 3 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 3);
default :
- case 4 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 4);
- case 5 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 5);
+ case 4 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 4);
+ case 5 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 5);
case 7 :
- case 6 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 6);
+ case 6 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 6);
}
}
@@ -853,6 +867,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1);
U32 const minMatch = (cParams->minMatch == 3) ? 3 : 4;
+ U32 nextToUpdate3 = ms->nextToUpdate;
ZSTD_optimal_t* const opt = optStatePtr->priceTable;
ZSTD_match_t* const matches = optStatePtr->matchTable;
@@ -862,7 +877,6 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
DEBUGLOG(5, "ZSTD_compressBlock_opt_generic: current=%u, prefix=%u, nextToUpdate=%u",
(U32)(ip - base), ms->window.dictLimit, ms->nextToUpdate);
assert(optLevel <= 2);
- ms->nextToUpdate3 = ms->nextToUpdate;
ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize, optLevel);
ip += (ip==prefixStart);
@@ -873,7 +887,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
/* find first match */
{ U32 const litlen = (U32)(ip - anchor);
U32 const ll0 = !litlen;
- U32 const nbMatches = ZSTD_BtGetAllMatches(ms, ip, iend, dictMode, rep, ll0, matches, minMatch);
+ U32 const nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, ip, iend, dictMode, rep, ll0, minMatch);
if (!nbMatches) { ip++; continue; }
/* initialize opt[0] */
@@ -970,7 +984,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
U32 const litlen = (opt[cur].mlen == 0) ? opt[cur].litlen : 0;
U32 const previousPrice = opt[cur].price;
U32 const basePrice = previousPrice + ZSTD_litLengthPrice(0, optStatePtr, optLevel);
- U32 const nbMatches = ZSTD_BtGetAllMatches(ms, inr, iend, dictMode, opt[cur].rep, ll0, matches, minMatch);
+ U32 const nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, inr, iend, dictMode, opt[cur].rep, ll0, minMatch);
U32 matchNb;
if (!nbMatches) {
DEBUGLOG(7, "rPos:%u : no match found", cur);
@@ -1094,7 +1108,7 @@ _shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */
} /* while (ip < ilimit) */
/* Return the last literals size */
- return iend - anchor;
+ return (size_t)(iend - anchor);
}
@@ -1158,7 +1172,6 @@ ZSTD_initStats_ultra(ZSTD_matchState_t* ms,
ms->window.dictLimit += (U32)srcSize;
ms->window.lowLimit = ms->window.dictLimit;
ms->nextToUpdate = ms->window.dictLimit;
- ms->nextToUpdate3 = ms->window.dictLimit;
/* re-inforce weight of collected statistics */
ZSTD_upscaleStats(&ms->opt);
diff --git a/thirdparty/zstd/compress/zstdmt_compress.c b/thirdparty/zstd/compress/zstdmt_compress.c
index 38fbb90768..9e537b8848 100644
--- a/thirdparty/zstd/compress/zstdmt_compress.c
+++ b/thirdparty/zstd/compress/zstdmt_compress.c
@@ -1129,9 +1129,14 @@ size_t ZSTDMT_toFlushNow(ZSTDMT_CCtx* mtctx)
size_t const produced = ZSTD_isError(cResult) ? 0 : cResult;
size_t const flushed = ZSTD_isError(cResult) ? 0 : jobPtr->dstFlushed;
assert(flushed <= produced);
+ assert(jobPtr->consumed <= jobPtr->src.size);
toFlush = produced - flushed;
- if (toFlush==0 && (jobPtr->consumed >= jobPtr->src.size)) {
- /* doneJobID is not-fully-flushed, but toFlush==0 : doneJobID should be compressing some more data */
+ /* if toFlush==0, nothing is available to flush.
+ * However, jobID is expected to still be active:
+ * if jobID was already completed and fully flushed,
+ * ZSTDMT_flushProduced() should have already moved onto next job.
+ * Therefore, some input has not yet been consumed. */
+ if (toFlush==0) {
assert(jobPtr->consumed < jobPtr->src.size);
}
}
@@ -1148,12 +1153,16 @@ size_t ZSTDMT_toFlushNow(ZSTDMT_CCtx* mtctx)
static unsigned ZSTDMT_computeTargetJobLog(ZSTD_CCtx_params const params)
{
- if (params.ldmParams.enableLdm)
+ unsigned jobLog;
+ if (params.ldmParams.enableLdm) {
/* In Long Range Mode, the windowLog is typically oversized.
* In which case, it's preferable to determine the jobSize
* based on chainLog instead. */
- return MAX(21, params.cParams.chainLog + 4);
- return MAX(20, params.cParams.windowLog + 2);
+ jobLog = MAX(21, params.cParams.chainLog + 4);
+ } else {
+ jobLog = MAX(20, params.cParams.windowLog + 2);
+ }
+ return MIN(jobLog, (unsigned)ZSTDMT_JOBLOG_MAX);
}
static int ZSTDMT_overlapLog_default(ZSTD_strategy strat)
@@ -1197,7 +1206,7 @@ static size_t ZSTDMT_computeOverlapSize(ZSTD_CCtx_params const params)
ovLog = MIN(params.cParams.windowLog, ZSTDMT_computeTargetJobLog(params) - 2)
- overlapRLog;
}
- assert(0 <= ovLog && ovLog <= 30);
+ assert(0 <= ovLog && ovLog <= ZSTD_WINDOWLOG_MAX);
DEBUGLOG(4, "overlapLog : %i", params.overlapLog);
DEBUGLOG(4, "overlap size : %i", 1 << ovLog);
return (ovLog==0) ? 0 : (size_t)1 << ovLog;
@@ -1391,7 +1400,7 @@ size_t ZSTDMT_initCStream_internal(
FORWARD_IF_ERROR( ZSTDMT_resize(mtctx, params.nbWorkers) );
if (params.jobSize != 0 && params.jobSize < ZSTDMT_JOBSIZE_MIN) params.jobSize = ZSTDMT_JOBSIZE_MIN;
- if (params.jobSize > (size_t)ZSTDMT_JOBSIZE_MAX) params.jobSize = ZSTDMT_JOBSIZE_MAX;
+ if (params.jobSize > (size_t)ZSTDMT_JOBSIZE_MAX) params.jobSize = (size_t)ZSTDMT_JOBSIZE_MAX;
mtctx->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN); /* do not trigger multi-threading when srcSize is too small */
if (mtctx->singleBlockingThread) {
@@ -1432,6 +1441,8 @@ size_t ZSTDMT_initCStream_internal(
if (mtctx->targetSectionSize == 0) {
mtctx->targetSectionSize = 1ULL << ZSTDMT_computeTargetJobLog(params);
}
+ assert(mtctx->targetSectionSize <= (size_t)ZSTDMT_JOBSIZE_MAX);
+
if (params.rsyncable) {
/* Aim for the targetsectionSize as the average job size. */
U32 const jobSizeMB = (U32)(mtctx->targetSectionSize >> 20);
diff --git a/thirdparty/zstd/compress/zstdmt_compress.h b/thirdparty/zstd/compress/zstdmt_compress.h
index 12e6bcb3a3..12a526087d 100644
--- a/thirdparty/zstd/compress/zstdmt_compress.h
+++ b/thirdparty/zstd/compress/zstdmt_compress.h
@@ -50,6 +50,7 @@
#ifndef ZSTDMT_JOBSIZE_MIN
# define ZSTDMT_JOBSIZE_MIN (1 MB)
#endif
+#define ZSTDMT_JOBLOG_MAX (MEM_32bits() ? 29 : 30)
#define ZSTDMT_JOBSIZE_MAX (MEM_32bits() ? (512 MB) : (1024 MB))
diff --git a/thirdparty/zstd/decompress/zstd_decompress.c b/thirdparty/zstd/decompress/zstd_decompress.c
index 675596f5aa..e42872ad96 100644
--- a/thirdparty/zstd/decompress/zstd_decompress.c
+++ b/thirdparty/zstd/decompress/zstd_decompress.c
@@ -360,8 +360,11 @@ static size_t readSkippableFrameSize(void const* src, size_t srcSize)
sizeU32 = MEM_readLE32((BYTE const*)src + ZSTD_FRAMEIDSIZE);
RETURN_ERROR_IF((U32)(sizeU32 + ZSTD_SKIPPABLEHEADERSIZE) < sizeU32,
frameParameter_unsupported);
-
- return skippableHeaderSize + sizeU32;
+ {
+ size_t const skippableSize = skippableHeaderSize + sizeU32;
+ RETURN_ERROR_IF(skippableSize > srcSize, srcSize_wrong);
+ return skippableSize;
+ }
}
/** ZSTD_findDecompressedSize() :
@@ -378,11 +381,10 @@ unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize)
if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
size_t const skippableSize = readSkippableFrameSize(src, srcSize);
- if (ZSTD_isError(skippableSize))
- return skippableSize;
- if (srcSize < skippableSize) {
+ if (ZSTD_isError(skippableSize)) {
return ZSTD_CONTENTSIZE_ERROR;
}
+ assert(skippableSize <= srcSize);
src = (const BYTE *)src + skippableSize;
srcSize -= skippableSize;
@@ -467,6 +469,8 @@ static ZSTD_frameSizeInfo ZSTD_findFrameSizeInfo(const void* src, size_t srcSize
if ((srcSize >= ZSTD_SKIPPABLEHEADERSIZE)
&& (MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
frameSizeInfo.compressedSize = readSkippableFrameSize(src, srcSize);
+ assert(ZSTD_isError(frameSizeInfo.compressedSize) ||
+ frameSizeInfo.compressedSize <= srcSize);
return frameSizeInfo;
} else {
const BYTE* ip = (const BYTE*)src;
@@ -529,7 +533,6 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize)
return frameSizeInfo.compressedSize;
}
-
/** ZSTD_decompressBound() :
* compatible with legacy mode
* `src` must point to the start of a ZSTD frame or a skippeable frame
@@ -546,6 +549,7 @@ unsigned long long ZSTD_decompressBound(const void* src, size_t srcSize)
unsigned long long const decompressedBound = frameSizeInfo.decompressedBound;
if (ZSTD_isError(compressedSize) || decompressedBound == ZSTD_CONTENTSIZE_ERROR)
return ZSTD_CONTENTSIZE_ERROR;
+ assert(srcSize >= compressedSize);
src = (const BYTE*)src + compressedSize;
srcSize -= compressedSize;
bound += decompressedBound;
@@ -738,9 +742,8 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx,
(unsigned)magicNumber, ZSTD_MAGICNUMBER);
if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
size_t const skippableSize = readSkippableFrameSize(src, srcSize);
- if (ZSTD_isError(skippableSize))
- return skippableSize;
- RETURN_ERROR_IF(srcSize < skippableSize, srcSize_wrong);
+ FORWARD_IF_ERROR(skippableSize);
+ assert(skippableSize <= srcSize);
src = (const BYTE *)src + skippableSize;
srcSize -= skippableSize;
diff --git a/thirdparty/zstd/decompress/zstd_decompress_block.c b/thirdparty/zstd/decompress/zstd_decompress_block.c
index a2a7eedcf2..24f4859c56 100644
--- a/thirdparty/zstd/decompress/zstd_decompress_block.c
+++ b/thirdparty/zstd/decompress/zstd_decompress_block.c
@@ -505,7 +505,7 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr,
*nbSeqPtr = nbSeq;
/* FSE table descriptors */
- RETURN_ERROR_IF(ip+4 > iend, srcSize_wrong); /* minimum possible size */
+ RETURN_ERROR_IF(ip+1 > iend, srcSize_wrong); /* minimum possible size: 1 byte for symbol encoding types */
{ symbolEncodingType_e const LLtype = (symbolEncodingType_e)(*ip >> 6);
symbolEncodingType_e const OFtype = (symbolEncodingType_e)((*ip >> 4) & 3);
symbolEncodingType_e const MLtype = (symbolEncodingType_e)((*ip >> 2) & 3);
@@ -637,9 +637,10 @@ size_t ZSTD_execSequence(BYTE* op,
if (oLitEnd>oend_w) return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd);
/* copy Literals */
- ZSTD_copy8(op, *litPtr);
if (sequence.litLength > 8)
- ZSTD_wildcopy(op+8, (*litPtr)+8, sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */
+ ZSTD_wildcopy_16min(op, (*litPtr), sequence.litLength, ZSTD_no_overlap); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */
+ else
+ ZSTD_copy8(op, *litPtr);
op = oLitEnd;
*litPtr = iLitEnd; /* update for next sequence */
@@ -686,13 +687,13 @@ size_t ZSTD_execSequence(BYTE* op,
if (oMatchEnd > oend-(16-MINMATCH)) {
if (op < oend_w) {
- ZSTD_wildcopy(op, match, oend_w - op);
+ ZSTD_wildcopy(op, match, oend_w - op, ZSTD_overlap_src_before_dst);
match += oend_w - op;
op = oend_w;
}
while (op < oMatchEnd) *op++ = *match++;
} else {
- ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8); /* works even if matchLength < 8 */
+ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8, ZSTD_overlap_src_before_dst); /* works even if matchLength < 8 */
}
return sequenceLength;
}
@@ -717,9 +718,11 @@ size_t ZSTD_execSequenceLong(BYTE* op,
if (oLitEnd > oend_w) return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, prefixStart, dictStart, dictEnd);
/* copy Literals */
- ZSTD_copy8(op, *litPtr); /* note : op <= oLitEnd <= oend_w == oend - 8 */
if (sequence.litLength > 8)
- ZSTD_wildcopy(op+8, (*litPtr)+8, sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */
+ ZSTD_wildcopy_16min(op, *litPtr, sequence.litLength, ZSTD_no_overlap); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */
+ else
+ ZSTD_copy8(op, *litPtr); /* note : op <= oLitEnd <= oend_w == oend - 8 */
+
op = oLitEnd;
*litPtr = iLitEnd; /* update for next sequence */
@@ -766,13 +769,13 @@ size_t ZSTD_execSequenceLong(BYTE* op,
if (oMatchEnd > oend-(16-MINMATCH)) {
if (op < oend_w) {
- ZSTD_wildcopy(op, match, oend_w - op);
+ ZSTD_wildcopy(op, match, oend_w - op, ZSTD_overlap_src_before_dst);
match += oend_w - op;
op = oend_w;
}
while (op < oMatchEnd) *op++ = *match++;
} else {
- ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8); /* works even if matchLength < 8 */
+ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8, ZSTD_overlap_src_before_dst); /* works even if matchLength < 8 */
}
return sequenceLength;
}
@@ -889,6 +892,7 @@ ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets)
}
FORCE_INLINE_TEMPLATE size_t
+DONT_VECTORIZE
ZSTD_decompressSequences_body( ZSTD_DCtx* dctx,
void* dst, size_t maxDstSize,
const void* seqStart, size_t seqSize, int nbSeq,
@@ -918,6 +922,11 @@ ZSTD_decompressSequences_body( ZSTD_DCtx* dctx,
ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr);
ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);
+ ZSTD_STATIC_ASSERT(
+ BIT_DStream_unfinished < BIT_DStream_completed &&
+ BIT_DStream_endOfBuffer < BIT_DStream_completed &&
+ BIT_DStream_completed < BIT_DStream_overflow);
+
for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) {
nbSeq--;
{ seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset);
@@ -930,6 +939,7 @@ ZSTD_decompressSequences_body( ZSTD_DCtx* dctx,
/* check if reached exact end */
DEBUGLOG(5, "ZSTD_decompressSequences_body: after decode loop, remaining nbSeq : %i", nbSeq);
RETURN_ERROR_IF(nbSeq, corruption_detected);
+ RETURN_ERROR_IF(BIT_reloadDStream(&seqState.DStream) < BIT_DStream_completed, corruption_detected);
/* save reps for next block */
{ U32 i; for (i=0; i<ZSTD_REP_NUM; i++) dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); }
}
@@ -1131,6 +1141,7 @@ ZSTD_decompressSequencesLong_default(ZSTD_DCtx* dctx,
#ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG
static TARGET_ATTRIBUTE("bmi2") size_t
+DONT_VECTORIZE
ZSTD_decompressSequences_bmi2(ZSTD_DCtx* dctx,
void* dst, size_t maxDstSize,
const void* seqStart, size_t seqSize, int nbSeq,
diff --git a/thirdparty/zstd/zstd.h b/thirdparty/zstd/zstd.h
index 53470c18f3..a1910ee223 100644
--- a/thirdparty/zstd/zstd.h
+++ b/thirdparty/zstd/zstd.h
@@ -71,7 +71,7 @@ extern "C" {
/*------ Version ------*/
#define ZSTD_VERSION_MAJOR 1
#define ZSTD_VERSION_MINOR 4
-#define ZSTD_VERSION_RELEASE 0
+#define ZSTD_VERSION_RELEASE 1
#define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE)
ZSTDLIB_API unsigned ZSTD_versionNumber(void); /**< to check runtime library version */
@@ -82,16 +82,16 @@ ZSTDLIB_API unsigned ZSTD_versionNumber(void); /**< to check runtime library v
#define ZSTD_VERSION_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_LIB_VERSION)
ZSTDLIB_API const char* ZSTD_versionString(void); /* requires v1.3.0+ */
-/***************************************
-* Default constant
-***************************************/
+/* *************************************
+ * Default constant
+ ***************************************/
#ifndef ZSTD_CLEVEL_DEFAULT
# define ZSTD_CLEVEL_DEFAULT 3
#endif
-/***************************************
-* Constants
-***************************************/
+/* *************************************
+ * Constants
+ ***************************************/
/* All magic numbers are supposed read/written to/from files/memory using little-endian convention */
#define ZSTD_MAGICNUMBER 0xFD2FB528 /* valid since v0.8.0 */
@@ -183,9 +183,14 @@ ZSTDLIB_API int ZSTD_maxCLevel(void); /*!< maximum compres
***************************************/
/*= Compression context
* When compressing many times,
- * it is recommended to allocate a context just once, and re-use it for each successive compression operation.
+ * it is recommended to allocate a context just once,
+ * and re-use it for each successive compression operation.
* This will make workload friendlier for system's memory.
- * Use one context per thread for parallel execution in multi-threaded environments. */
+ * Note : re-using context is just a speed / resource optimization.
+ * It doesn't change the compression ratio, which remains identical.
+ * Note 2 : In multi-threaded environments,
+ * use one different context per thread for parallel execution.
+ */
typedef struct ZSTD_CCtx_s ZSTD_CCtx;
ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx(void);
ZSTDLIB_API size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx);
@@ -380,6 +385,7 @@ typedef enum {
* ZSTD_c_forceMaxWindow
* ZSTD_c_forceAttachDict
* ZSTD_c_literalCompressionMode
+ * ZSTD_c_targetCBlockSize
* Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them.
* note : never ever use experimentalParam? names directly;
* also, the enums values themselves are unstable and can still change.
@@ -389,6 +395,7 @@ typedef enum {
ZSTD_c_experimentalParam3=1000,
ZSTD_c_experimentalParam4=1001,
ZSTD_c_experimentalParam5=1002,
+ ZSTD_c_experimentalParam6=1003,
} ZSTD_cParameter;
typedef struct {
@@ -657,17 +664,33 @@ ZSTDLIB_API size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
ZSTD_inBuffer* input,
ZSTD_EndDirective endOp);
+
+/* These buffer sizes are softly recommended.
+ * They are not required : ZSTD_compressStream*() happily accepts any buffer size, for both input and output.
+ * Respecting the recommended size just makes it a bit easier for ZSTD_compressStream*(),
+ * reducing the amount of memory shuffling and buffering, resulting in minor performance savings.
+ *
+ * However, note that these recommendations are from the perspective of a C caller program.
+ * If the streaming interface is invoked from some other language,
+ * especially managed ones such as Java or Go, through a foreign function interface such as jni or cgo,
+ * a major performance rule is to reduce crossing such interface to an absolute minimum.
+ * It's not rare that performance ends being spent more into the interface, rather than compression itself.
+ * In which cases, prefer using large buffers, as large as practical,
+ * for both input and output, to reduce the nb of roundtrips.
+ */
ZSTDLIB_API size_t ZSTD_CStreamInSize(void); /**< recommended size for input buffer */
-ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output buffer. Guarantee to successfully flush at least one complete compressed block in all circumstances. */
+ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output buffer. Guarantee to successfully flush at least one complete compressed block. */
-/*******************************************************************************
- * This is a legacy streaming API, and can be replaced by ZSTD_CCtx_reset() and
- * ZSTD_compressStream2(). It is redundant, but is still fully supported.
+
+/* *****************************************************************************
+ * This following is a legacy streaming API.
+ * It can be replaced by ZSTD_CCtx_reset() and ZSTD_compressStream2().
+ * It is redundant, but remains fully supported.
* Advanced parameters and dictionary compression can only be used through the
* new API.
******************************************************************************/
-/**
+/*!
* Equivalent to:
*
* ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
@@ -675,16 +698,16 @@ ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output
* ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel);
*/
ZSTDLIB_API size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel);
-/**
+/*!
* Alternative for ZSTD_compressStream2(zcs, output, input, ZSTD_e_continue).
* NOTE: The return value is different. ZSTD_compressStream() returns a hint for
* the next read size (if non-zero and not an error). ZSTD_compressStream2()
- * returns the number of bytes left to flush (if non-zero and not an error).
+ * returns the minimum nb of bytes left to flush (if non-zero and not an error).
*/
ZSTDLIB_API size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
-/** Equivalent to ZSTD_compressStream2(zcs, output, &emptyInput, ZSTD_e_flush). */
+/*! Equivalent to ZSTD_compressStream2(zcs, output, &emptyInput, ZSTD_e_flush). */
ZSTDLIB_API size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output);
-/** Equivalent to ZSTD_compressStream2(zcs, output, &emptyInput, ZSTD_e_end). */
+/*! Equivalent to ZSTD_compressStream2(zcs, output, &emptyInput, ZSTD_e_end). */
ZSTDLIB_API size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output);
@@ -969,7 +992,7 @@ ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict);
#endif /* ZSTD_H_235446 */
-/****************************************************************************************
+/* **************************************************************************************
* ADVANCED AND EXPERIMENTAL FUNCTIONS
****************************************************************************************
* The definitions in the following section are considered experimental.
@@ -1037,6 +1060,10 @@ ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict);
#define ZSTD_LDM_HASHRATELOG_MIN 0
#define ZSTD_LDM_HASHRATELOG_MAX (ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN)
+/* Advanced parameter bounds */
+#define ZSTD_TARGETCBLOCKSIZE_MIN 64
+#define ZSTD_TARGETCBLOCKSIZE_MAX ZSTD_BLOCKSIZE_MAX
+
/* internal */
#define ZSTD_HASHLOG3_MAX 17
@@ -1162,7 +1189,7 @@ typedef enum {
* however it does mean that all frame data must be present and valid. */
ZSTDLIB_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize);
-/** ZSTD_decompressBound() :
+/*! ZSTD_decompressBound() :
* `src` should point to the start of a series of ZSTD encoded and/or skippable frames
* `srcSize` must be the _exact_ size of this series
* (i.e. there should be a frame boundary at `src + srcSize`)
@@ -1409,6 +1436,11 @@ ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* pre
*/
#define ZSTD_c_literalCompressionMode ZSTD_c_experimentalParam5
+/* Tries to fit compressed block size to be around targetCBlockSize.
+ * No target when targetCBlockSize == 0.
+ * There is no guarantee on compressed block size (default:0) */
+#define ZSTD_c_targetCBlockSize ZSTD_c_experimentalParam6
+
/*! ZSTD_CCtx_getParameter() :
* Get the requested compression parameter value, selected by enum ZSTD_cParameter,
* and store it into int* value.
@@ -1843,7 +1875,7 @@ typedef struct {
unsigned checksumFlag;
} ZSTD_frameHeader;
-/** ZSTD_getFrameHeader() :
+/*! ZSTD_getFrameHeader() :
* decode Frame Header, or requires larger `srcSize`.
* @return : 0, `zfhPtr` is correctly filled,
* >0, `srcSize` is too small, value is wanted `srcSize` amount,