From 87ac6fbfd94199c049037a5d76ab117944c26b3c Mon Sep 17 00:00:00 2001 From: "Andrea Baldacci, Ph.D" Date: Tue, 31 Oct 2017 02:05:01 +0100 Subject: [PATCH 001/183] Update CMakeLists.txt --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c30278b7f..270aad38a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -211,6 +211,11 @@ ELSEIF( CMAKE_COMPILER_IS_MINGW ) ADD_DEFINITIONS( -U__STRICT_ANSI__ ) ENDIF() +if(IOS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3") +endif(IOS) + if (ASSIMP_COVERALLS) MESSAGE(STATUS "Coveralls enabled") INCLUDE(Coveralls) From 978c156c2ad84d3b8afcabbc55ff966aafe3ecec Mon Sep 17 00:00:00 2001 From: Marco Di Benedetto Date: Wed, 10 Jan 2018 20:02:41 +0100 Subject: [PATCH 002/183] added import of material properties (double sided and transparency) in glTF 1.0 importer. --- code/glTFImporter.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/code/glTFImporter.cpp b/code/glTFImporter.cpp index 381e459fd..9e341c632 100644 --- a/code/glTFImporter.cpp +++ b/code/glTFImporter.cpp @@ -193,9 +193,16 @@ void glTFImporter::ImportMaterials(glTF::Asset& r) aimat->AddProperty(&str, AI_MATKEY_NAME); } - SetMaterialColorProperty(embeddedTexIdxs, r, mat.diffuse, aimat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE); + SetMaterialColorProperty(embeddedTexIdxs, r, mat.ambient, aimat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT ); + SetMaterialColorProperty(embeddedTexIdxs, r, mat.diffuse, aimat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE ); SetMaterialColorProperty(embeddedTexIdxs, r, mat.specular, aimat, aiTextureType_SPECULAR, AI_MATKEY_COLOR_SPECULAR); - SetMaterialColorProperty(embeddedTexIdxs, r, mat.ambient, aimat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT); + SetMaterialColorProperty(embeddedTexIdxs, r, mat.emission, aimat, aiTextureType_EMISSIVE, AI_MATKEY_COLOR_EMISSIVE); + + aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED); + + if (mat.transparent && (mat.transparency != 1.0f)) { + aimat->AddProperty(&mat.transparency, 1, AI_MATKEY_OPACITY); + } if (mat.shininess > 0.f) { aimat->AddProperty(&mat.shininess, 1, AI_MATKEY_SHININESS); From c749594e9de7b98ae63000aef19013dcc020d441 Mon Sep 17 00:00:00 2001 From: Marco Di Benedetto Date: Thu, 18 Jan 2018 19:12:51 +0100 Subject: [PATCH 003/183] gltf instant ok on valid extension. --- code/glTF2Importer.cpp | 4 ++-- code/glTFImporter.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 92328ec2d..8136af2dc 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -103,8 +103,8 @@ bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool { const std::string &extension = GetExtension(pFile); - if (extension != "gltf" && extension != "glb") - return false; + if (extension == "gltf" || extension == "glb") + return true; if (pIOHandler) { glTF2::Asset asset(pIOHandler); diff --git a/code/glTFImporter.cpp b/code/glTFImporter.cpp index b4d69e32f..4df9e1763 100644 --- a/code/glTFImporter.cpp +++ b/code/glTFImporter.cpp @@ -102,8 +102,8 @@ bool glTFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool { const std::string &extension = GetExtension(pFile); - if (extension != "gltf" && extension != "glb") - return false; + if (extension == "gltf" || extension == "glb") + return true; if (pIOHandler) { glTF::Asset asset(pIOHandler); From 5f38bd01ece06876250fd3bab4f72456170cf3e4 Mon Sep 17 00:00:00 2001 From: Marco Di Benedetto Date: Thu, 18 Jan 2018 22:28:44 +0100 Subject: [PATCH 004/183] restored gltf checks. --- code/glTF2Importer.cpp | 4 ++-- code/glTFImporter.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 8136af2dc..92328ec2d 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -103,8 +103,8 @@ bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool { const std::string &extension = GetExtension(pFile); - if (extension == "gltf" || extension == "glb") - return true; + if (extension != "gltf" && extension != "glb") + return false; if (pIOHandler) { glTF2::Asset asset(pIOHandler); diff --git a/code/glTFImporter.cpp b/code/glTFImporter.cpp index 4df9e1763..b4d69e32f 100644 --- a/code/glTFImporter.cpp +++ b/code/glTFImporter.cpp @@ -102,8 +102,8 @@ bool glTFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool { const std::string &extension = GetExtension(pFile); - if (extension == "gltf" || extension == "glb") - return true; + if (extension != "gltf" && extension != "glb") + return false; if (pIOHandler) { glTF::Asset asset(pIOHandler); From b28bcc365c1ca1063c5df4f906f73dfb49603477 Mon Sep 17 00:00:00 2001 From: Rohan Singh Date: Wed, 24 Jan 2018 09:45:20 -0500 Subject: [PATCH 005/183] Interpret IndexAToDirect as Direct when the index element is missing Paint3D exports FBX like this, which is wrong, but could still support it --- code/FBXMeshGeometry.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/code/FBXMeshGeometry.cpp b/code/FBXMeshGeometry.cpp index 4868b3e72..99e1b1c5f 100644 --- a/code/FBXMeshGeometry.cpp +++ b/code/FBXMeshGeometry.cpp @@ -427,16 +427,19 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, const std::vector& mapping_offsets, const std::vector& mappings) { + bool isDirect = ReferenceInformationType == "Direct"; + bool isIndexToDirect = ReferenceInformationType == "IndexToDirect"; + // fallback to direct data if there is no index data element + if ( isIndexToDirect && !HasElement( source, indexDataElementName ) ) { + isDirect = true; + isIndexToDirect = false; + } // handle permutations of Mapping and Reference type - it would be nice to // deal with this more elegantly and with less redundancy, but right // now it seems unavoidable. - if (MappingInformationType == "ByVertice" && ReferenceInformationType == "Direct") { - if ( !HasElement( source, indexDataElementName ) ) { - return; - } - + if (MappingInformationType == "ByVertice" && isDirect) { std::vector tempData; ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); @@ -449,14 +452,11 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, } } } - else if (MappingInformationType == "ByVertice" && ReferenceInformationType == "IndexToDirect") { + else if (MappingInformationType == "ByVertice" && isIndexToDirect) { std::vector tempData; ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); data_out.resize(vertex_count); - if ( !HasElement( source, indexDataElementName ) ) { - return; - } std::vector uvIndices; ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName)); @@ -471,7 +471,7 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, } } } - else if (MappingInformationType == "ByPolygonVertex" && ReferenceInformationType == "Direct") { + else if (MappingInformationType == "ByPolygonVertex" && isDirect) { std::vector tempData; ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); @@ -484,7 +484,7 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, data_out.swap(tempData); } - else if (MappingInformationType == "ByPolygonVertex" && ReferenceInformationType == "IndexToDirect") { + else if (MappingInformationType == "ByPolygonVertex" && isIndexToDirect) { std::vector tempData; ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); From 84957faa163ede7ab6da24f1b2a2a7d360c4351c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 2 Feb 2018 19:50:34 +0100 Subject: [PATCH 006/183] closes https://github.com/assimp/assimp/issues/1758: fix compiler warning. --- test/unit/utRemoveComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/utRemoveComponent.cpp b/test/unit/utRemoveComponent.cpp index 31e47b77c..4556e201e 100644 --- a/test/unit/utRemoveComponent.cpp +++ b/test/unit/utRemoveComponent.cpp @@ -110,7 +110,7 @@ void RemoveVCProcessTest::SetUp() char check[sizeof(aiMaterial) == sizeof(aiMaterial) ? 10 : -1]; check[0] = 0; // to remove compiler warning - EXPECT_TRUE( check ); + EXPECT_EQ( 0, check[0] ); } // ------------------------------------------------------------------------------------------------ From d47f34344e0c7b213a00e8d6359eb3fa2daf2030 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 3 Feb 2018 08:46:03 +0100 Subject: [PATCH 007/183] closes https://github.com/assimp/assimp/issues/1752: move guard over include statement. --- code/Importer/IFC/IFCReaderGen_4.cpp | 5 +- code/Importer/IFC/IFCReaderGen_4.h | 1147 +++++++++++++------------- 2 files changed, 578 insertions(+), 574 deletions(-) diff --git a/code/Importer/IFC/IFCReaderGen_4.cpp b/code/Importer/IFC/IFCReaderGen_4.cpp index eb6182674..7a312e691 100644 --- a/code/Importer/IFC/IFCReaderGen_4.cpp +++ b/code/Importer/IFC/IFCReaderGen_4.cpp @@ -40,13 +40,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** MACHINE-GENERATED by scripts/ICFImporter/CppGenerator.py */ -#include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER -#include "IFCReaderGen.h" +#include "AssimpPCH.h" +#include "IFCReaderGen4.h" namespace Assimp { using namespace IFC; +using namespace ::Assimp::IFC::Schema_4; namespace { diff --git a/code/Importer/IFC/IFCReaderGen_4.h b/code/Importer/IFC/IFCReaderGen_4.h index 6c0b5a283..ccfcb6ea0 100644 --- a/code/Importer/IFC/IFCReaderGen_4.h +++ b/code/Importer/IFC/IFCReaderGen_4.h @@ -47,6 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace IFC { +namespace Schema_4 { + using namespace STEP; using namespace STEP::EXPRESS; @@ -4866,583 +4868,584 @@ namespace IFC { } //! IFC namespace STEP { - // ****************************************************************************** - // Converter stubs - // ****************************************************************************** - + // ****************************************************************************** + // Converter stubs + // ****************************************************************************** + #define DECL_CONV_STUB(type) template <> size_t GenericFill(const STEP::DB& db, const EXPRESS::LIST& params, IFC::type* in) - - DECL_CONV_STUB(IfcRoot); - DECL_CONV_STUB(IfcObjectDefinition); - DECL_CONV_STUB(IfcObject); - DECL_CONV_STUB(IfcControl); - DECL_CONV_STUB(IfcActionRequest); - DECL_CONV_STUB(IfcActor); - DECL_CONV_STUB(IfcProduct); - DECL_CONV_STUB(IfcElement); - DECL_CONV_STUB(IfcDistributionElement); - DECL_CONV_STUB(IfcDistributionControlElement); - DECL_CONV_STUB(IfcActuator); - DECL_CONV_STUB(IfcTypeObject); - DECL_CONV_STUB(IfcTypeProduct); - DECL_CONV_STUB(IfcElementType); - DECL_CONV_STUB(IfcDistributionElementType); - DECL_CONV_STUB(IfcDistributionControlElementType); - DECL_CONV_STUB(IfcActuatorType); - DECL_CONV_STUB(IfcRepresentationItem); - DECL_CONV_STUB(IfcGeometricRepresentationItem); - DECL_CONV_STUB(IfcSolidModel); - DECL_CONV_STUB(IfcManifoldSolidBrep); - DECL_CONV_STUB(IfcAdvancedBrep); - DECL_CONV_STUB(IfcAdvancedBrepWithVoids); - DECL_CONV_STUB(IfcTopologicalRepresentationItem); - DECL_CONV_STUB(IfcFace); - DECL_CONV_STUB(IfcFaceSurface); - DECL_CONV_STUB(IfcAdvancedFace); - DECL_CONV_STUB(IfcDistributionFlowElement); - DECL_CONV_STUB(IfcFlowTerminal); - DECL_CONV_STUB(IfcAirTerminal); - DECL_CONV_STUB(IfcFlowController); - DECL_CONV_STUB(IfcAirTerminalBox); - DECL_CONV_STUB(IfcDistributionFlowElementType); - DECL_CONV_STUB(IfcFlowControllerType); - DECL_CONV_STUB(IfcAirTerminalBoxType); - DECL_CONV_STUB(IfcFlowTerminalType); - DECL_CONV_STUB(IfcAirTerminalType); - DECL_CONV_STUB(IfcEnergyConversionDevice); - DECL_CONV_STUB(IfcAirToAirHeatRecovery); - DECL_CONV_STUB(IfcEnergyConversionDeviceType); - DECL_CONV_STUB(IfcAirToAirHeatRecoveryType); - DECL_CONV_STUB(IfcAlarm); - DECL_CONV_STUB(IfcAlarmType); - DECL_CONV_STUB(IfcAnnotation); - DECL_CONV_STUB(IfcAnnotationFillArea); - DECL_CONV_STUB(IfcProfileDef); - DECL_CONV_STUB(IfcArbitraryClosedProfileDef); - DECL_CONV_STUB(IfcArbitraryOpenProfileDef); - DECL_CONV_STUB(IfcArbitraryProfileDefWithVoids); - DECL_CONV_STUB(IfcGroup); - DECL_CONV_STUB(IfcAsset); - DECL_CONV_STUB(IfcParameterizedProfileDef); - DECL_CONV_STUB(IfcAsymmetricIShapeProfileDef); - DECL_CONV_STUB(IfcAudioVisualAppliance); - DECL_CONV_STUB(IfcAudioVisualApplianceType); - DECL_CONV_STUB(IfcPlacement); - DECL_CONV_STUB(IfcAxis1Placement); - DECL_CONV_STUB(IfcAxis2Placement2D); - DECL_CONV_STUB(IfcAxis2Placement3D); - DECL_CONV_STUB(IfcCurve); - DECL_CONV_STUB(IfcBoundedCurve); - DECL_CONV_STUB(IfcBSplineCurve); - DECL_CONV_STUB(IfcBSplineCurveWithKnots); - DECL_CONV_STUB(IfcSurface); - DECL_CONV_STUB(IfcBoundedSurface); - DECL_CONV_STUB(IfcBSplineSurface); - DECL_CONV_STUB(IfcBSplineSurfaceWithKnots); - DECL_CONV_STUB(IfcBuildingElement); - DECL_CONV_STUB(IfcBeam); - DECL_CONV_STUB(IfcBeamStandardCase); - DECL_CONV_STUB(IfcBuildingElementType); - DECL_CONV_STUB(IfcBeamType); - DECL_CONV_STUB(IfcPresentationItem); - DECL_CONV_STUB(IfcCsgPrimitive3D); - DECL_CONV_STUB(IfcBlock); - DECL_CONV_STUB(IfcBoiler); - DECL_CONV_STUB(IfcBoilerType); - DECL_CONV_STUB(IfcBooleanResult); - DECL_CONV_STUB(IfcBooleanClippingResult); - DECL_CONV_STUB(IfcCompositeCurve); - DECL_CONV_STUB(IfcCompositeCurveOnSurface); - DECL_CONV_STUB(IfcBoundaryCurve); - DECL_CONV_STUB(IfcBoundingBox); - DECL_CONV_STUB(IfcHalfSpaceSolid); - DECL_CONV_STUB(IfcBoxedHalfSpace); - DECL_CONV_STUB(IfcSpatialElement); - DECL_CONV_STUB(IfcSpatialStructureElement); - DECL_CONV_STUB(IfcBuilding); - DECL_CONV_STUB(IfcElementComponent); - DECL_CONV_STUB(IfcBuildingElementPart); - DECL_CONV_STUB(IfcElementComponentType); - DECL_CONV_STUB(IfcBuildingElementPartType); - DECL_CONV_STUB(IfcBuildingElementProxy); - DECL_CONV_STUB(IfcBuildingElementProxyType); - DECL_CONV_STUB(IfcBuildingStorey); - DECL_CONV_STUB(IfcSystem); - DECL_CONV_STUB(IfcBuildingSystem); - DECL_CONV_STUB(IfcBurner); - DECL_CONV_STUB(IfcBurnerType); - DECL_CONV_STUB(IfcCShapeProfileDef); - DECL_CONV_STUB(IfcFlowFitting); - DECL_CONV_STUB(IfcCableCarrierFitting); - DECL_CONV_STUB(IfcFlowFittingType); - DECL_CONV_STUB(IfcCableCarrierFittingType); - DECL_CONV_STUB(IfcFlowSegment); - DECL_CONV_STUB(IfcCableCarrierSegment); - DECL_CONV_STUB(IfcFlowSegmentType); - DECL_CONV_STUB(IfcCableCarrierSegmentType); - DECL_CONV_STUB(IfcCableFitting); - DECL_CONV_STUB(IfcCableFittingType); - DECL_CONV_STUB(IfcCableSegment); - DECL_CONV_STUB(IfcCableSegmentType); - DECL_CONV_STUB(IfcPoint); - DECL_CONV_STUB(IfcCartesianPoint); - DECL_CONV_STUB(IfcCartesianPointList); - DECL_CONV_STUB(IfcCartesianPointList2D); - DECL_CONV_STUB(IfcCartesianPointList3D); - DECL_CONV_STUB(IfcCartesianTransformationOperator); - DECL_CONV_STUB(IfcCartesianTransformationOperator2D); - DECL_CONV_STUB(IfcCartesianTransformationOperator2DnonUniform); - DECL_CONV_STUB(IfcCartesianTransformationOperator3D); - DECL_CONV_STUB(IfcCartesianTransformationOperator3DnonUniform); - DECL_CONV_STUB(IfcCenterLineProfileDef); - DECL_CONV_STUB(IfcChiller); - DECL_CONV_STUB(IfcChillerType); - DECL_CONV_STUB(IfcChimney); - DECL_CONV_STUB(IfcChimneyType); - DECL_CONV_STUB(IfcConic); - DECL_CONV_STUB(IfcCircle); - DECL_CONV_STUB(IfcCircleProfileDef); - DECL_CONV_STUB(IfcCircleHollowProfileDef); - DECL_CONV_STUB(IfcCivilElement); - DECL_CONV_STUB(IfcCivilElementType); - DECL_CONV_STUB(IfcConnectedFaceSet); - DECL_CONV_STUB(IfcClosedShell); - DECL_CONV_STUB(IfcCoil); - DECL_CONV_STUB(IfcCoilType); - DECL_CONV_STUB(IfcColourSpecification); - DECL_CONV_STUB(IfcColourRgb); - DECL_CONV_STUB(IfcColumn); - DECL_CONV_STUB(IfcColumnStandardCase); - DECL_CONV_STUB(IfcColumnType); - DECL_CONV_STUB(IfcCommunicationsAppliance); - DECL_CONV_STUB(IfcCommunicationsApplianceType); - DECL_CONV_STUB(IfcPropertyAbstraction); - DECL_CONV_STUB(IfcProperty); - DECL_CONV_STUB(IfcComplexProperty); - DECL_CONV_STUB(IfcPropertyDefinition); - DECL_CONV_STUB(IfcCompositeCurveSegment); - DECL_CONV_STUB(IfcCompositeProfileDef); - DECL_CONV_STUB(IfcFlowMovingDevice); - DECL_CONV_STUB(IfcCompressor); - DECL_CONV_STUB(IfcFlowMovingDeviceType); - DECL_CONV_STUB(IfcCompressorType); - DECL_CONV_STUB(IfcCondenser); - DECL_CONV_STUB(IfcCondenserType); - DECL_CONV_STUB(IfcResource); - DECL_CONV_STUB(IfcConstructionResource); - DECL_CONV_STUB(IfcConstructionEquipmentResource); - DECL_CONV_STUB(IfcTypeResource); - DECL_CONV_STUB(IfcConstructionResourceType); - DECL_CONV_STUB(IfcConstructionEquipmentResourceType); - DECL_CONV_STUB(IfcConstructionMaterialResource); - DECL_CONV_STUB(IfcConstructionMaterialResourceType); - DECL_CONV_STUB(IfcConstructionProductResource); - DECL_CONV_STUB(IfcConstructionProductResourceType); - DECL_CONV_STUB(IfcContext); - DECL_CONV_STUB(IfcNamedUnit); - DECL_CONV_STUB(IfcContextDependentUnit); - DECL_CONV_STUB(IfcController); - DECL_CONV_STUB(IfcControllerType); - DECL_CONV_STUB(IfcConversionBasedUnit); - DECL_CONV_STUB(IfcConversionBasedUnitWithOffset); - DECL_CONV_STUB(IfcCooledBeam); - DECL_CONV_STUB(IfcCooledBeamType); - DECL_CONV_STUB(IfcCoolingTower); - DECL_CONV_STUB(IfcCoolingTowerType); - DECL_CONV_STUB(IfcCostItem); - DECL_CONV_STUB(IfcCostSchedule); - DECL_CONV_STUB(IfcCovering); - DECL_CONV_STUB(IfcCoveringType); - DECL_CONV_STUB(IfcCrewResource); - DECL_CONV_STUB(IfcCrewResourceType); - DECL_CONV_STUB(IfcCsgSolid); - DECL_CONV_STUB(IfcCurtainWall); - DECL_CONV_STUB(IfcCurtainWallType); - DECL_CONV_STUB(IfcCurveBoundedPlane); - DECL_CONV_STUB(IfcCurveBoundedSurface); - DECL_CONV_STUB(IfcPresentationStyle); - DECL_CONV_STUB(IfcElementarySurface); - DECL_CONV_STUB(IfcCylindricalSurface); - DECL_CONV_STUB(IfcDamper); - DECL_CONV_STUB(IfcDamperType); - DECL_CONV_STUB(IfcDerivedProfileDef); - DECL_CONV_STUB(IfcDirection); - DECL_CONV_STUB(IfcDiscreteAccessory); - DECL_CONV_STUB(IfcDiscreteAccessoryType); - DECL_CONV_STUB(IfcDistributionChamberElement); - DECL_CONV_STUB(IfcDistributionChamberElementType); - DECL_CONV_STUB(IfcDistributionSystem); - DECL_CONV_STUB(IfcDistributionCircuit); - DECL_CONV_STUB(IfcPort); - DECL_CONV_STUB(IfcDistributionPort); - DECL_CONV_STUB(IfcDoor); - DECL_CONV_STUB(IfcPropertySetDefinition); - DECL_CONV_STUB(IfcDoorStandardCase); - DECL_CONV_STUB(IfcDoorStyle); - DECL_CONV_STUB(IfcDoorType); - DECL_CONV_STUB(IfcDuctFitting); - DECL_CONV_STUB(IfcDuctFittingType); - DECL_CONV_STUB(IfcDuctSegment); - DECL_CONV_STUB(IfcDuctSegmentType); - DECL_CONV_STUB(IfcFlowTreatmentDevice); - DECL_CONV_STUB(IfcDuctSilencer); - DECL_CONV_STUB(IfcFlowTreatmentDeviceType); - DECL_CONV_STUB(IfcDuctSilencerType); - DECL_CONV_STUB(IfcEdge); - DECL_CONV_STUB(IfcEdgeCurve); - DECL_CONV_STUB(IfcLoop); - DECL_CONV_STUB(IfcEdgeLoop); - DECL_CONV_STUB(IfcElectricAppliance); - DECL_CONV_STUB(IfcElectricApplianceType); - DECL_CONV_STUB(IfcElectricDistributionBoard); - DECL_CONV_STUB(IfcElectricDistributionBoardType); - DECL_CONV_STUB(IfcFlowStorageDevice); - DECL_CONV_STUB(IfcElectricFlowStorageDevice); - DECL_CONV_STUB(IfcFlowStorageDeviceType); - DECL_CONV_STUB(IfcElectricFlowStorageDeviceType); - DECL_CONV_STUB(IfcElectricGenerator); - DECL_CONV_STUB(IfcElectricGeneratorType); - DECL_CONV_STUB(IfcElectricMotor); - DECL_CONV_STUB(IfcElectricMotorType); - DECL_CONV_STUB(IfcElectricTimeControl); - DECL_CONV_STUB(IfcElectricTimeControlType); - DECL_CONV_STUB(IfcElementAssembly); - DECL_CONV_STUB(IfcElementAssemblyType); - DECL_CONV_STUB(IfcQuantitySet); - DECL_CONV_STUB(IfcElementQuantity); - DECL_CONV_STUB(IfcEllipse); - DECL_CONV_STUB(IfcEllipseProfileDef); - DECL_CONV_STUB(IfcEngine); - DECL_CONV_STUB(IfcEngineType); - DECL_CONV_STUB(IfcEvaporativeCooler); - DECL_CONV_STUB(IfcEvaporativeCoolerType); - DECL_CONV_STUB(IfcEvaporator); - DECL_CONV_STUB(IfcEvaporatorType); - DECL_CONV_STUB(IfcProcess); - DECL_CONV_STUB(IfcEvent); - DECL_CONV_STUB(IfcTypeProcess); - DECL_CONV_STUB(IfcEventType); - DECL_CONV_STUB(IfcExternalSpatialStructureElement); - DECL_CONV_STUB(IfcExternalSpatialElement); - DECL_CONV_STUB(IfcSweptAreaSolid); - DECL_CONV_STUB(IfcExtrudedAreaSolid); - DECL_CONV_STUB(IfcExtrudedAreaSolidTapered); - DECL_CONV_STUB(IfcFaceBasedSurfaceModel); - DECL_CONV_STUB(IfcFaceBound); - DECL_CONV_STUB(IfcFaceOuterBound); - DECL_CONV_STUB(IfcFacetedBrep); - DECL_CONV_STUB(IfcFacetedBrepWithVoids); - DECL_CONV_STUB(IfcFan); - DECL_CONV_STUB(IfcFanType); - DECL_CONV_STUB(IfcFastener); - DECL_CONV_STUB(IfcFastenerType); - DECL_CONV_STUB(IfcFeatureElement); - DECL_CONV_STUB(IfcFeatureElementAddition); - DECL_CONV_STUB(IfcFeatureElementSubtraction); - DECL_CONV_STUB(IfcFillAreaStyleHatching); - DECL_CONV_STUB(IfcFillAreaStyleTiles); - DECL_CONV_STUB(IfcFilter); - DECL_CONV_STUB(IfcFilterType); - DECL_CONV_STUB(IfcFireSuppressionTerminal); - DECL_CONV_STUB(IfcFireSuppressionTerminalType); - DECL_CONV_STUB(IfcFixedReferenceSweptAreaSolid); - DECL_CONV_STUB(IfcFlowInstrument); - DECL_CONV_STUB(IfcFlowInstrumentType); - DECL_CONV_STUB(IfcFlowMeter); - DECL_CONV_STUB(IfcFlowMeterType); - DECL_CONV_STUB(IfcFooting); - DECL_CONV_STUB(IfcFootingType); - DECL_CONV_STUB(IfcFurnishingElement); - DECL_CONV_STUB(IfcFurnishingElementType); - DECL_CONV_STUB(IfcFurniture); - DECL_CONV_STUB(IfcFurnitureType); - DECL_CONV_STUB(IfcGeographicElement); - DECL_CONV_STUB(IfcGeographicElementType); - DECL_CONV_STUB(IfcGeometricSet); - DECL_CONV_STUB(IfcGeometricCurveSet); - DECL_CONV_STUB(IfcRepresentationContext); - DECL_CONV_STUB(IfcGeometricRepresentationContext); - DECL_CONV_STUB(IfcGeometricRepresentationSubContext); - DECL_CONV_STUB(IfcGrid); - DECL_CONV_STUB(IfcObjectPlacement); - DECL_CONV_STUB(IfcGridPlacement); - DECL_CONV_STUB(IfcHeatExchanger); - DECL_CONV_STUB(IfcHeatExchangerType); - DECL_CONV_STUB(IfcHumidifier); - DECL_CONV_STUB(IfcHumidifierType); - DECL_CONV_STUB(IfcIShapeProfileDef); - DECL_CONV_STUB(IfcIndexedPolyCurve); - DECL_CONV_STUB(IfcTessellatedItem); - DECL_CONV_STUB(IfcIndexedPolygonalFace); - DECL_CONV_STUB(IfcIndexedPolygonalFaceWithVoids); - DECL_CONV_STUB(IfcInterceptor); - DECL_CONV_STUB(IfcInterceptorType); - DECL_CONV_STUB(IfcSurfaceCurve); - DECL_CONV_STUB(IfcIntersectionCurve); - DECL_CONV_STUB(IfcInventory); - DECL_CONV_STUB(IfcJunctionBox); - DECL_CONV_STUB(IfcJunctionBoxType); - DECL_CONV_STUB(IfcLShapeProfileDef); - DECL_CONV_STUB(IfcLaborResource); - DECL_CONV_STUB(IfcLaborResourceType); - DECL_CONV_STUB(IfcLamp); - DECL_CONV_STUB(IfcLampType); - DECL_CONV_STUB(IfcLightFixture); - DECL_CONV_STUB(IfcLightFixtureType); - DECL_CONV_STUB(IfcLightSource); - DECL_CONV_STUB(IfcLightSourceAmbient); - DECL_CONV_STUB(IfcLightSourceDirectional); - DECL_CONV_STUB(IfcLightSourceGoniometric); - DECL_CONV_STUB(IfcLightSourcePositional); - DECL_CONV_STUB(IfcLightSourceSpot); - DECL_CONV_STUB(IfcLine); - DECL_CONV_STUB(IfcLocalPlacement); - DECL_CONV_STUB(IfcMappedItem); - DECL_CONV_STUB(IfcProductRepresentation); - DECL_CONV_STUB(IfcMaterialDefinitionRepresentation); - DECL_CONV_STUB(IfcMeasureWithUnit); - DECL_CONV_STUB(IfcMechanicalFastener); - DECL_CONV_STUB(IfcMechanicalFastenerType); - DECL_CONV_STUB(IfcMedicalDevice); - DECL_CONV_STUB(IfcMedicalDeviceType); - DECL_CONV_STUB(IfcMember); - DECL_CONV_STUB(IfcMemberStandardCase); - DECL_CONV_STUB(IfcMemberType); - DECL_CONV_STUB(IfcMirroredProfileDef); - DECL_CONV_STUB(IfcMotorConnection); - DECL_CONV_STUB(IfcMotorConnectionType); - DECL_CONV_STUB(IfcOccupant); - DECL_CONV_STUB(IfcOffsetCurve2D); - DECL_CONV_STUB(IfcOffsetCurve3D); - DECL_CONV_STUB(IfcOpenShell); - DECL_CONV_STUB(IfcOpeningElement); - DECL_CONV_STUB(IfcOpeningStandardCase); - DECL_CONV_STUB(IfcOrientedEdge); - DECL_CONV_STUB(IfcOuterBoundaryCurve); - DECL_CONV_STUB(IfcOutlet); - DECL_CONV_STUB(IfcOutletType); - DECL_CONV_STUB(IfcPath); - DECL_CONV_STUB(IfcPcurve); - DECL_CONV_STUB(IfcPerformanceHistory); - DECL_CONV_STUB(IfcPermit); - DECL_CONV_STUB(IfcPile); - DECL_CONV_STUB(IfcPileType); - DECL_CONV_STUB(IfcPipeFitting); - DECL_CONV_STUB(IfcPipeFittingType); - DECL_CONV_STUB(IfcPipeSegment); - DECL_CONV_STUB(IfcPipeSegmentType); - DECL_CONV_STUB(IfcPlanarExtent); - DECL_CONV_STUB(IfcPlanarBox); - DECL_CONV_STUB(IfcPlane); - DECL_CONV_STUB(IfcPlate); - DECL_CONV_STUB(IfcPlateStandardCase); - DECL_CONV_STUB(IfcPlateType); - DECL_CONV_STUB(IfcPointOnCurve); - DECL_CONV_STUB(IfcPointOnSurface); - DECL_CONV_STUB(IfcPolyLoop); - DECL_CONV_STUB(IfcPolygonalBoundedHalfSpace); - DECL_CONV_STUB(IfcTessellatedFaceSet); - DECL_CONV_STUB(IfcPolygonalFaceSet); - DECL_CONV_STUB(IfcPolyline); - DECL_CONV_STUB(IfcPresentationStyleAssignment); - DECL_CONV_STUB(IfcProcedure); - DECL_CONV_STUB(IfcProcedureType); - DECL_CONV_STUB(IfcProductDefinitionShape); - DECL_CONV_STUB(IfcProject); - DECL_CONV_STUB(IfcProjectLibrary); - DECL_CONV_STUB(IfcProjectOrder); - DECL_CONV_STUB(IfcProjectionElement); - DECL_CONV_STUB(IfcSimpleProperty); - DECL_CONV_STUB(IfcPropertyBoundedValue); - DECL_CONV_STUB(IfcPropertyEnumeratedValue); - DECL_CONV_STUB(IfcPropertyListValue); - DECL_CONV_STUB(IfcPropertyReferenceValue); - DECL_CONV_STUB(IfcPropertySet); - DECL_CONV_STUB(IfcPropertySingleValue); - DECL_CONV_STUB(IfcPropertyTableValue); - DECL_CONV_STUB(IfcProtectiveDevice); - DECL_CONV_STUB(IfcProtectiveDeviceTrippingUnit); - DECL_CONV_STUB(IfcProtectiveDeviceTrippingUnitType); - DECL_CONV_STUB(IfcProtectiveDeviceType); - DECL_CONV_STUB(IfcProxy); - DECL_CONV_STUB(IfcPump); - DECL_CONV_STUB(IfcPumpType); - DECL_CONV_STUB(IfcRailing); - DECL_CONV_STUB(IfcRailingType); - DECL_CONV_STUB(IfcRamp); - DECL_CONV_STUB(IfcRampFlight); - DECL_CONV_STUB(IfcRampFlightType); - DECL_CONV_STUB(IfcRampType); - DECL_CONV_STUB(IfcRationalBSplineCurveWithKnots); - DECL_CONV_STUB(IfcRationalBSplineSurfaceWithKnots); - DECL_CONV_STUB(IfcRectangleProfileDef); - DECL_CONV_STUB(IfcRectangleHollowProfileDef); - DECL_CONV_STUB(IfcRectangularPyramid); - DECL_CONV_STUB(IfcRectangularTrimmedSurface); - DECL_CONV_STUB(IfcReinforcingElement); - DECL_CONV_STUB(IfcReinforcingBar); - DECL_CONV_STUB(IfcReinforcingElementType); - DECL_CONV_STUB(IfcReinforcingBarType); - DECL_CONV_STUB(IfcReinforcingMesh); - DECL_CONV_STUB(IfcReinforcingMeshType); - DECL_CONV_STUB(IfcRelationship); - DECL_CONV_STUB(IfcRelDecomposes); - DECL_CONV_STUB(IfcRelAggregates); - DECL_CONV_STUB(IfcRelConnects); - DECL_CONV_STUB(IfcRelContainedInSpatialStructure); - DECL_CONV_STUB(IfcRelDefines); - DECL_CONV_STUB(IfcRelDefinesByProperties); - DECL_CONV_STUB(IfcRelFillsElement); - DECL_CONV_STUB(IfcRelVoidsElement); - DECL_CONV_STUB(IfcReparametrisedCompositeCurveSegment); - DECL_CONV_STUB(IfcRepresentation); - DECL_CONV_STUB(IfcRepresentationMap); - DECL_CONV_STUB(IfcRevolvedAreaSolid); - DECL_CONV_STUB(IfcRevolvedAreaSolidTapered); - DECL_CONV_STUB(IfcRightCircularCone); - DECL_CONV_STUB(IfcRightCircularCylinder); - DECL_CONV_STUB(IfcRoof); - DECL_CONV_STUB(IfcRoofType); - DECL_CONV_STUB(IfcRoundedRectangleProfileDef); - DECL_CONV_STUB(IfcSIUnit); - DECL_CONV_STUB(IfcSanitaryTerminal); - DECL_CONV_STUB(IfcSanitaryTerminalType); - DECL_CONV_STUB(IfcSeamCurve); - DECL_CONV_STUB(IfcSectionedSpine); - DECL_CONV_STUB(IfcSensor); - DECL_CONV_STUB(IfcSensorType); - DECL_CONV_STUB(IfcShadingDevice); - DECL_CONV_STUB(IfcShadingDeviceType); - DECL_CONV_STUB(IfcShapeModel); - DECL_CONV_STUB(IfcShapeRepresentation); - DECL_CONV_STUB(IfcShellBasedSurfaceModel); - DECL_CONV_STUB(IfcSite); - DECL_CONV_STUB(IfcSlab); - DECL_CONV_STUB(IfcSlabElementedCase); - DECL_CONV_STUB(IfcSlabStandardCase); - DECL_CONV_STUB(IfcSlabType); - DECL_CONV_STUB(IfcSolarDevice); - DECL_CONV_STUB(IfcSolarDeviceType); - DECL_CONV_STUB(IfcSpace); - DECL_CONV_STUB(IfcSpaceHeater); - DECL_CONV_STUB(IfcSpaceHeaterType); - DECL_CONV_STUB(IfcSpatialElementType); - DECL_CONV_STUB(IfcSpatialStructureElementType); - DECL_CONV_STUB(IfcSpaceType); - DECL_CONV_STUB(IfcSpatialZone); - DECL_CONV_STUB(IfcSpatialZoneType); - DECL_CONV_STUB(IfcSphere); - DECL_CONV_STUB(IfcSphericalSurface); - DECL_CONV_STUB(IfcStackTerminal); - DECL_CONV_STUB(IfcStackTerminalType); - DECL_CONV_STUB(IfcStair); - DECL_CONV_STUB(IfcStairFlight); - DECL_CONV_STUB(IfcStairFlightType); - DECL_CONV_STUB(IfcStairType); - DECL_CONV_STUB(IfcStructuralActivity); - DECL_CONV_STUB(IfcStructuralAction); - DECL_CONV_STUB(IfcStructuralAnalysisModel); - DECL_CONV_STUB(IfcStructuralItem); - DECL_CONV_STUB(IfcStructuralConnection); - DECL_CONV_STUB(IfcStructuralCurveAction); - DECL_CONV_STUB(IfcStructuralCurveConnection); - DECL_CONV_STUB(IfcStructuralMember); - DECL_CONV_STUB(IfcStructuralCurveMember); - DECL_CONV_STUB(IfcStructuralCurveMemberVarying); - DECL_CONV_STUB(IfcStructuralReaction); - DECL_CONV_STUB(IfcStructuralCurveReaction); - DECL_CONV_STUB(IfcStructuralLinearAction); - DECL_CONV_STUB(IfcStructuralLoadGroup); - DECL_CONV_STUB(IfcStructuralLoadCase); - DECL_CONV_STUB(IfcStructuralSurfaceAction); - DECL_CONV_STUB(IfcStructuralPlanarAction); - DECL_CONV_STUB(IfcStructuralPointAction); - DECL_CONV_STUB(IfcStructuralPointConnection); - DECL_CONV_STUB(IfcStructuralPointReaction); - DECL_CONV_STUB(IfcStructuralResultGroup); - DECL_CONV_STUB(IfcStructuralSurfaceConnection); - DECL_CONV_STUB(IfcStructuralSurfaceMember); - DECL_CONV_STUB(IfcStructuralSurfaceMemberVarying); - DECL_CONV_STUB(IfcStructuralSurfaceReaction); - DECL_CONV_STUB(IfcStyleModel); - DECL_CONV_STUB(IfcStyledItem); - DECL_CONV_STUB(IfcStyledRepresentation); - DECL_CONV_STUB(IfcSubContractResource); - DECL_CONV_STUB(IfcSubContractResourceType); - DECL_CONV_STUB(IfcSubedge); - DECL_CONV_STUB(IfcSurfaceCurveSweptAreaSolid); - DECL_CONV_STUB(IfcSurfaceFeature); - DECL_CONV_STUB(IfcSweptSurface); - DECL_CONV_STUB(IfcSurfaceOfLinearExtrusion); - DECL_CONV_STUB(IfcSurfaceOfRevolution); - DECL_CONV_STUB(IfcSurfaceStyle); - DECL_CONV_STUB(IfcSurfaceStyleShading); - DECL_CONV_STUB(IfcSurfaceStyleRendering); - DECL_CONV_STUB(IfcSurfaceStyleWithTextures); - DECL_CONV_STUB(IfcSweptDiskSolid); - DECL_CONV_STUB(IfcSweptDiskSolidPolygonal); - DECL_CONV_STUB(IfcSwitchingDevice); - DECL_CONV_STUB(IfcSwitchingDeviceType); - DECL_CONV_STUB(IfcSystemFurnitureElement); - DECL_CONV_STUB(IfcSystemFurnitureElementType); - DECL_CONV_STUB(IfcTShapeProfileDef); - DECL_CONV_STUB(IfcTank); - DECL_CONV_STUB(IfcTankType); - DECL_CONV_STUB(IfcTask); - DECL_CONV_STUB(IfcTaskType); - DECL_CONV_STUB(IfcTendon); - DECL_CONV_STUB(IfcTendonAnchor); - DECL_CONV_STUB(IfcTendonAnchorType); - DECL_CONV_STUB(IfcTendonType); - DECL_CONV_STUB(IfcTextLiteral); - DECL_CONV_STUB(IfcTextLiteralWithExtent); - DECL_CONV_STUB(IfcTopologyRepresentation); - DECL_CONV_STUB(IfcToroidalSurface); - DECL_CONV_STUB(IfcTransformer); - DECL_CONV_STUB(IfcTransformerType); - DECL_CONV_STUB(IfcTransportElement); - DECL_CONV_STUB(IfcTransportElementType); - DECL_CONV_STUB(IfcTrapeziumProfileDef); - DECL_CONV_STUB(IfcTriangulatedFaceSet); - DECL_CONV_STUB(IfcTrimmedCurve); - DECL_CONV_STUB(IfcTubeBundle); - DECL_CONV_STUB(IfcTubeBundleType); - DECL_CONV_STUB(IfcUShapeProfileDef); - DECL_CONV_STUB(IfcUnitAssignment); - DECL_CONV_STUB(IfcUnitaryControlElement); - DECL_CONV_STUB(IfcUnitaryControlElementType); - DECL_CONV_STUB(IfcUnitaryEquipment); - DECL_CONV_STUB(IfcUnitaryEquipmentType); - DECL_CONV_STUB(IfcValve); - DECL_CONV_STUB(IfcValveType); - DECL_CONV_STUB(IfcVector); - DECL_CONV_STUB(IfcVertex); - DECL_CONV_STUB(IfcVertexLoop); - DECL_CONV_STUB(IfcVertexPoint); - DECL_CONV_STUB(IfcVibrationIsolator); - DECL_CONV_STUB(IfcVibrationIsolatorType); - DECL_CONV_STUB(IfcVirtualElement); - DECL_CONV_STUB(IfcVoidingFeature); - DECL_CONV_STUB(IfcWall); - DECL_CONV_STUB(IfcWallElementedCase); - DECL_CONV_STUB(IfcWallStandardCase); - DECL_CONV_STUB(IfcWallType); - DECL_CONV_STUB(IfcWasteTerminal); - DECL_CONV_STUB(IfcWasteTerminalType); - DECL_CONV_STUB(IfcWindow); - DECL_CONV_STUB(IfcWindowStandardCase); - DECL_CONV_STUB(IfcWindowStyle); - DECL_CONV_STUB(IfcWindowType); - DECL_CONV_STUB(IfcWorkCalendar); - DECL_CONV_STUB(IfcWorkControl); - DECL_CONV_STUB(IfcWorkPlan); - DECL_CONV_STUB(IfcWorkSchedule); - DECL_CONV_STUB(IfcZShapeProfileDef); - DECL_CONV_STUB(IfcZone); + + DECL_CONV_STUB( IfcRoot ); + DECL_CONV_STUB( IfcObjectDefinition ); + DECL_CONV_STUB( IfcObject ); + DECL_CONV_STUB( IfcControl ); + DECL_CONV_STUB( IfcActionRequest ); + DECL_CONV_STUB( IfcActor ); + DECL_CONV_STUB( IfcProduct ); + DECL_CONV_STUB( IfcElement ); + DECL_CONV_STUB( IfcDistributionElement ); + DECL_CONV_STUB( IfcDistributionControlElement ); + DECL_CONV_STUB( IfcActuator ); + DECL_CONV_STUB( IfcTypeObject ); + DECL_CONV_STUB( IfcTypeProduct ); + DECL_CONV_STUB( IfcElementType ); + DECL_CONV_STUB( IfcDistributionElementType ); + DECL_CONV_STUB( IfcDistributionControlElementType ); + DECL_CONV_STUB( IfcActuatorType ); + DECL_CONV_STUB( IfcRepresentationItem ); + DECL_CONV_STUB( IfcGeometricRepresentationItem ); + DECL_CONV_STUB( IfcSolidModel ); + DECL_CONV_STUB( IfcManifoldSolidBrep ); + DECL_CONV_STUB( IfcAdvancedBrep ); + DECL_CONV_STUB( IfcAdvancedBrepWithVoids ); + DECL_CONV_STUB( IfcTopologicalRepresentationItem ); + DECL_CONV_STUB( IfcFace ); + DECL_CONV_STUB( IfcFaceSurface ); + DECL_CONV_STUB( IfcAdvancedFace ); + DECL_CONV_STUB( IfcDistributionFlowElement ); + DECL_CONV_STUB( IfcFlowTerminal ); + DECL_CONV_STUB( IfcAirTerminal ); + DECL_CONV_STUB( IfcFlowController ); + DECL_CONV_STUB( IfcAirTerminalBox ); + DECL_CONV_STUB( IfcDistributionFlowElementType ); + DECL_CONV_STUB( IfcFlowControllerType ); + DECL_CONV_STUB( IfcAirTerminalBoxType ); + DECL_CONV_STUB( IfcFlowTerminalType ); + DECL_CONV_STUB( IfcAirTerminalType ); + DECL_CONV_STUB( IfcEnergyConversionDevice ); + DECL_CONV_STUB( IfcAirToAirHeatRecovery ); + DECL_CONV_STUB( IfcEnergyConversionDeviceType ); + DECL_CONV_STUB( IfcAirToAirHeatRecoveryType ); + DECL_CONV_STUB( IfcAlarm ); + DECL_CONV_STUB( IfcAlarmType ); + DECL_CONV_STUB( IfcAnnotation ); + DECL_CONV_STUB( IfcAnnotationFillArea ); + DECL_CONV_STUB( IfcProfileDef ); + DECL_CONV_STUB( IfcArbitraryClosedProfileDef ); + DECL_CONV_STUB( IfcArbitraryOpenProfileDef ); + DECL_CONV_STUB( IfcArbitraryProfileDefWithVoids ); + DECL_CONV_STUB( IfcGroup ); + DECL_CONV_STUB( IfcAsset ); + DECL_CONV_STUB( IfcParameterizedProfileDef ); + DECL_CONV_STUB( IfcAsymmetricIShapeProfileDef ); + DECL_CONV_STUB( IfcAudioVisualAppliance ); + DECL_CONV_STUB( IfcAudioVisualApplianceType ); + DECL_CONV_STUB( IfcPlacement ); + DECL_CONV_STUB( IfcAxis1Placement ); + DECL_CONV_STUB( IfcAxis2Placement2D ); + DECL_CONV_STUB( IfcAxis2Placement3D ); + DECL_CONV_STUB( IfcCurve ); + DECL_CONV_STUB( IfcBoundedCurve ); + DECL_CONV_STUB( IfcBSplineCurve ); + DECL_CONV_STUB( IfcBSplineCurveWithKnots ); + DECL_CONV_STUB( IfcSurface ); + DECL_CONV_STUB( IfcBoundedSurface ); + DECL_CONV_STUB( IfcBSplineSurface ); + DECL_CONV_STUB( IfcBSplineSurfaceWithKnots ); + DECL_CONV_STUB( IfcBuildingElement ); + DECL_CONV_STUB( IfcBeam ); + DECL_CONV_STUB( IfcBeamStandardCase ); + DECL_CONV_STUB( IfcBuildingElementType ); + DECL_CONV_STUB( IfcBeamType ); + DECL_CONV_STUB( IfcPresentationItem ); + DECL_CONV_STUB( IfcCsgPrimitive3D ); + DECL_CONV_STUB( IfcBlock ); + DECL_CONV_STUB( IfcBoiler ); + DECL_CONV_STUB( IfcBoilerType ); + DECL_CONV_STUB( IfcBooleanResult ); + DECL_CONV_STUB( IfcBooleanClippingResult ); + DECL_CONV_STUB( IfcCompositeCurve ); + DECL_CONV_STUB( IfcCompositeCurveOnSurface ); + DECL_CONV_STUB( IfcBoundaryCurve ); + DECL_CONV_STUB( IfcBoundingBox ); + DECL_CONV_STUB( IfcHalfSpaceSolid ); + DECL_CONV_STUB( IfcBoxedHalfSpace ); + DECL_CONV_STUB( IfcSpatialElement ); + DECL_CONV_STUB( IfcSpatialStructureElement ); + DECL_CONV_STUB( IfcBuilding ); + DECL_CONV_STUB( IfcElementComponent ); + DECL_CONV_STUB( IfcBuildingElementPart ); + DECL_CONV_STUB( IfcElementComponentType ); + DECL_CONV_STUB( IfcBuildingElementPartType ); + DECL_CONV_STUB( IfcBuildingElementProxy ); + DECL_CONV_STUB( IfcBuildingElementProxyType ); + DECL_CONV_STUB( IfcBuildingStorey ); + DECL_CONV_STUB( IfcSystem ); + DECL_CONV_STUB( IfcBuildingSystem ); + DECL_CONV_STUB( IfcBurner ); + DECL_CONV_STUB( IfcBurnerType ); + DECL_CONV_STUB( IfcCShapeProfileDef ); + DECL_CONV_STUB( IfcFlowFitting ); + DECL_CONV_STUB( IfcCableCarrierFitting ); + DECL_CONV_STUB( IfcFlowFittingType ); + DECL_CONV_STUB( IfcCableCarrierFittingType ); + DECL_CONV_STUB( IfcFlowSegment ); + DECL_CONV_STUB( IfcCableCarrierSegment ); + DECL_CONV_STUB( IfcFlowSegmentType ); + DECL_CONV_STUB( IfcCableCarrierSegmentType ); + DECL_CONV_STUB( IfcCableFitting ); + DECL_CONV_STUB( IfcCableFittingType ); + DECL_CONV_STUB( IfcCableSegment ); + DECL_CONV_STUB( IfcCableSegmentType ); + DECL_CONV_STUB( IfcPoint ); + DECL_CONV_STUB( IfcCartesianPoint ); + DECL_CONV_STUB( IfcCartesianPointList ); + DECL_CONV_STUB( IfcCartesianPointList2D ); + DECL_CONV_STUB( IfcCartesianPointList3D ); + DECL_CONV_STUB( IfcCartesianTransformationOperator ); + DECL_CONV_STUB( IfcCartesianTransformationOperator2D ); + DECL_CONV_STUB( IfcCartesianTransformationOperator2DnonUniform ); + DECL_CONV_STUB( IfcCartesianTransformationOperator3D ); + DECL_CONV_STUB( IfcCartesianTransformationOperator3DnonUniform ); + DECL_CONV_STUB( IfcCenterLineProfileDef ); + DECL_CONV_STUB( IfcChiller ); + DECL_CONV_STUB( IfcChillerType ); + DECL_CONV_STUB( IfcChimney ); + DECL_CONV_STUB( IfcChimneyType ); + DECL_CONV_STUB( IfcConic ); + DECL_CONV_STUB( IfcCircle ); + DECL_CONV_STUB( IfcCircleProfileDef ); + DECL_CONV_STUB( IfcCircleHollowProfileDef ); + DECL_CONV_STUB( IfcCivilElement ); + DECL_CONV_STUB( IfcCivilElementType ); + DECL_CONV_STUB( IfcConnectedFaceSet ); + DECL_CONV_STUB( IfcClosedShell ); + DECL_CONV_STUB( IfcCoil ); + DECL_CONV_STUB( IfcCoilType ); + DECL_CONV_STUB( IfcColourSpecification ); + DECL_CONV_STUB( IfcColourRgb ); + DECL_CONV_STUB( IfcColumn ); + DECL_CONV_STUB( IfcColumnStandardCase ); + DECL_CONV_STUB( IfcColumnType ); + DECL_CONV_STUB( IfcCommunicationsAppliance ); + DECL_CONV_STUB( IfcCommunicationsApplianceType ); + DECL_CONV_STUB( IfcPropertyAbstraction ); + DECL_CONV_STUB( IfcProperty ); + DECL_CONV_STUB( IfcComplexProperty ); + DECL_CONV_STUB( IfcPropertyDefinition ); + DECL_CONV_STUB( IfcCompositeCurveSegment ); + DECL_CONV_STUB( IfcCompositeProfileDef ); + DECL_CONV_STUB( IfcFlowMovingDevice ); + DECL_CONV_STUB( IfcCompressor ); + DECL_CONV_STUB( IfcFlowMovingDeviceType ); + DECL_CONV_STUB( IfcCompressorType ); + DECL_CONV_STUB( IfcCondenser ); + DECL_CONV_STUB( IfcCondenserType ); + DECL_CONV_STUB( IfcResource ); + DECL_CONV_STUB( IfcConstructionResource ); + DECL_CONV_STUB( IfcConstructionEquipmentResource ); + DECL_CONV_STUB( IfcTypeResource ); + DECL_CONV_STUB( IfcConstructionResourceType ); + DECL_CONV_STUB( IfcConstructionEquipmentResourceType ); + DECL_CONV_STUB( IfcConstructionMaterialResource ); + DECL_CONV_STUB( IfcConstructionMaterialResourceType ); + DECL_CONV_STUB( IfcConstructionProductResource ); + DECL_CONV_STUB( IfcConstructionProductResourceType ); + DECL_CONV_STUB( IfcContext ); + DECL_CONV_STUB( IfcNamedUnit ); + DECL_CONV_STUB( IfcContextDependentUnit ); + DECL_CONV_STUB( IfcController ); + DECL_CONV_STUB( IfcControllerType ); + DECL_CONV_STUB( IfcConversionBasedUnit ); + DECL_CONV_STUB( IfcConversionBasedUnitWithOffset ); + DECL_CONV_STUB( IfcCooledBeam ); + DECL_CONV_STUB( IfcCooledBeamType ); + DECL_CONV_STUB( IfcCoolingTower ); + DECL_CONV_STUB( IfcCoolingTowerType ); + DECL_CONV_STUB( IfcCostItem ); + DECL_CONV_STUB( IfcCostSchedule ); + DECL_CONV_STUB( IfcCovering ); + DECL_CONV_STUB( IfcCoveringType ); + DECL_CONV_STUB( IfcCrewResource ); + DECL_CONV_STUB( IfcCrewResourceType ); + DECL_CONV_STUB( IfcCsgSolid ); + DECL_CONV_STUB( IfcCurtainWall ); + DECL_CONV_STUB( IfcCurtainWallType ); + DECL_CONV_STUB( IfcCurveBoundedPlane ); + DECL_CONV_STUB( IfcCurveBoundedSurface ); + DECL_CONV_STUB( IfcPresentationStyle ); + DECL_CONV_STUB( IfcElementarySurface ); + DECL_CONV_STUB( IfcCylindricalSurface ); + DECL_CONV_STUB( IfcDamper ); + DECL_CONV_STUB( IfcDamperType ); + DECL_CONV_STUB( IfcDerivedProfileDef ); + DECL_CONV_STUB( IfcDirection ); + DECL_CONV_STUB( IfcDiscreteAccessory ); + DECL_CONV_STUB( IfcDiscreteAccessoryType ); + DECL_CONV_STUB( IfcDistributionChamberElement ); + DECL_CONV_STUB( IfcDistributionChamberElementType ); + DECL_CONV_STUB( IfcDistributionSystem ); + DECL_CONV_STUB( IfcDistributionCircuit ); + DECL_CONV_STUB( IfcPort ); + DECL_CONV_STUB( IfcDistributionPort ); + DECL_CONV_STUB( IfcDoor ); + DECL_CONV_STUB( IfcPropertySetDefinition ); + DECL_CONV_STUB( IfcDoorStandardCase ); + DECL_CONV_STUB( IfcDoorStyle ); + DECL_CONV_STUB( IfcDoorType ); + DECL_CONV_STUB( IfcDuctFitting ); + DECL_CONV_STUB( IfcDuctFittingType ); + DECL_CONV_STUB( IfcDuctSegment ); + DECL_CONV_STUB( IfcDuctSegmentType ); + DECL_CONV_STUB( IfcFlowTreatmentDevice ); + DECL_CONV_STUB( IfcDuctSilencer ); + DECL_CONV_STUB( IfcFlowTreatmentDeviceType ); + DECL_CONV_STUB( IfcDuctSilencerType ); + DECL_CONV_STUB( IfcEdge ); + DECL_CONV_STUB( IfcEdgeCurve ); + DECL_CONV_STUB( IfcLoop ); + DECL_CONV_STUB( IfcEdgeLoop ); + DECL_CONV_STUB( IfcElectricAppliance ); + DECL_CONV_STUB( IfcElectricApplianceType ); + DECL_CONV_STUB( IfcElectricDistributionBoard ); + DECL_CONV_STUB( IfcElectricDistributionBoardType ); + DECL_CONV_STUB( IfcFlowStorageDevice ); + DECL_CONV_STUB( IfcElectricFlowStorageDevice ); + DECL_CONV_STUB( IfcFlowStorageDeviceType ); + DECL_CONV_STUB( IfcElectricFlowStorageDeviceType ); + DECL_CONV_STUB( IfcElectricGenerator ); + DECL_CONV_STUB( IfcElectricGeneratorType ); + DECL_CONV_STUB( IfcElectricMotor ); + DECL_CONV_STUB( IfcElectricMotorType ); + DECL_CONV_STUB( IfcElectricTimeControl ); + DECL_CONV_STUB( IfcElectricTimeControlType ); + DECL_CONV_STUB( IfcElementAssembly ); + DECL_CONV_STUB( IfcElementAssemblyType ); + DECL_CONV_STUB( IfcQuantitySet ); + DECL_CONV_STUB( IfcElementQuantity ); + DECL_CONV_STUB( IfcEllipse ); + DECL_CONV_STUB( IfcEllipseProfileDef ); + DECL_CONV_STUB( IfcEngine ); + DECL_CONV_STUB( IfcEngineType ); + DECL_CONV_STUB( IfcEvaporativeCooler ); + DECL_CONV_STUB( IfcEvaporativeCoolerType ); + DECL_CONV_STUB( IfcEvaporator ); + DECL_CONV_STUB( IfcEvaporatorType ); + DECL_CONV_STUB( IfcProcess ); + DECL_CONV_STUB( IfcEvent ); + DECL_CONV_STUB( IfcTypeProcess ); + DECL_CONV_STUB( IfcEventType ); + DECL_CONV_STUB( IfcExternalSpatialStructureElement ); + DECL_CONV_STUB( IfcExternalSpatialElement ); + DECL_CONV_STUB( IfcSweptAreaSolid ); + DECL_CONV_STUB( IfcExtrudedAreaSolid ); + DECL_CONV_STUB( IfcExtrudedAreaSolidTapered ); + DECL_CONV_STUB( IfcFaceBasedSurfaceModel ); + DECL_CONV_STUB( IfcFaceBound ); + DECL_CONV_STUB( IfcFaceOuterBound ); + DECL_CONV_STUB( IfcFacetedBrep ); + DECL_CONV_STUB( IfcFacetedBrepWithVoids ); + DECL_CONV_STUB( IfcFan ); + DECL_CONV_STUB( IfcFanType ); + DECL_CONV_STUB( IfcFastener ); + DECL_CONV_STUB( IfcFastenerType ); + DECL_CONV_STUB( IfcFeatureElement ); + DECL_CONV_STUB( IfcFeatureElementAddition ); + DECL_CONV_STUB( IfcFeatureElementSubtraction ); + DECL_CONV_STUB( IfcFillAreaStyleHatching ); + DECL_CONV_STUB( IfcFillAreaStyleTiles ); + DECL_CONV_STUB( IfcFilter ); + DECL_CONV_STUB( IfcFilterType ); + DECL_CONV_STUB( IfcFireSuppressionTerminal ); + DECL_CONV_STUB( IfcFireSuppressionTerminalType ); + DECL_CONV_STUB( IfcFixedReferenceSweptAreaSolid ); + DECL_CONV_STUB( IfcFlowInstrument ); + DECL_CONV_STUB( IfcFlowInstrumentType ); + DECL_CONV_STUB( IfcFlowMeter ); + DECL_CONV_STUB( IfcFlowMeterType ); + DECL_CONV_STUB( IfcFooting ); + DECL_CONV_STUB( IfcFootingType ); + DECL_CONV_STUB( IfcFurnishingElement ); + DECL_CONV_STUB( IfcFurnishingElementType ); + DECL_CONV_STUB( IfcFurniture ); + DECL_CONV_STUB( IfcFurnitureType ); + DECL_CONV_STUB( IfcGeographicElement ); + DECL_CONV_STUB( IfcGeographicElementType ); + DECL_CONV_STUB( IfcGeometricSet ); + DECL_CONV_STUB( IfcGeometricCurveSet ); + DECL_CONV_STUB( IfcRepresentationContext ); + DECL_CONV_STUB( IfcGeometricRepresentationContext ); + DECL_CONV_STUB( IfcGeometricRepresentationSubContext ); + DECL_CONV_STUB( IfcGrid ); + DECL_CONV_STUB( IfcObjectPlacement ); + DECL_CONV_STUB( IfcGridPlacement ); + DECL_CONV_STUB( IfcHeatExchanger ); + DECL_CONV_STUB( IfcHeatExchangerType ); + DECL_CONV_STUB( IfcHumidifier ); + DECL_CONV_STUB( IfcHumidifierType ); + DECL_CONV_STUB( IfcIShapeProfileDef ); + DECL_CONV_STUB( IfcIndexedPolyCurve ); + DECL_CONV_STUB( IfcTessellatedItem ); + DECL_CONV_STUB( IfcIndexedPolygonalFace ); + DECL_CONV_STUB( IfcIndexedPolygonalFaceWithVoids ); + DECL_CONV_STUB( IfcInterceptor ); + DECL_CONV_STUB( IfcInterceptorType ); + DECL_CONV_STUB( IfcSurfaceCurve ); + DECL_CONV_STUB( IfcIntersectionCurve ); + DECL_CONV_STUB( IfcInventory ); + DECL_CONV_STUB( IfcJunctionBox ); + DECL_CONV_STUB( IfcJunctionBoxType ); + DECL_CONV_STUB( IfcLShapeProfileDef ); + DECL_CONV_STUB( IfcLaborResource ); + DECL_CONV_STUB( IfcLaborResourceType ); + DECL_CONV_STUB( IfcLamp ); + DECL_CONV_STUB( IfcLampType ); + DECL_CONV_STUB( IfcLightFixture ); + DECL_CONV_STUB( IfcLightFixtureType ); + DECL_CONV_STUB( IfcLightSource ); + DECL_CONV_STUB( IfcLightSourceAmbient ); + DECL_CONV_STUB( IfcLightSourceDirectional ); + DECL_CONV_STUB( IfcLightSourceGoniometric ); + DECL_CONV_STUB( IfcLightSourcePositional ); + DECL_CONV_STUB( IfcLightSourceSpot ); + DECL_CONV_STUB( IfcLine ); + DECL_CONV_STUB( IfcLocalPlacement ); + DECL_CONV_STUB( IfcMappedItem ); + DECL_CONV_STUB( IfcProductRepresentation ); + DECL_CONV_STUB( IfcMaterialDefinitionRepresentation ); + DECL_CONV_STUB( IfcMeasureWithUnit ); + DECL_CONV_STUB( IfcMechanicalFastener ); + DECL_CONV_STUB( IfcMechanicalFastenerType ); + DECL_CONV_STUB( IfcMedicalDevice ); + DECL_CONV_STUB( IfcMedicalDeviceType ); + DECL_CONV_STUB( IfcMember ); + DECL_CONV_STUB( IfcMemberStandardCase ); + DECL_CONV_STUB( IfcMemberType ); + DECL_CONV_STUB( IfcMirroredProfileDef ); + DECL_CONV_STUB( IfcMotorConnection ); + DECL_CONV_STUB( IfcMotorConnectionType ); + DECL_CONV_STUB( IfcOccupant ); + DECL_CONV_STUB( IfcOffsetCurve2D ); + DECL_CONV_STUB( IfcOffsetCurve3D ); + DECL_CONV_STUB( IfcOpenShell ); + DECL_CONV_STUB( IfcOpeningElement ); + DECL_CONV_STUB( IfcOpeningStandardCase ); + DECL_CONV_STUB( IfcOrientedEdge ); + DECL_CONV_STUB( IfcOuterBoundaryCurve ); + DECL_CONV_STUB( IfcOutlet ); + DECL_CONV_STUB( IfcOutletType ); + DECL_CONV_STUB( IfcPath ); + DECL_CONV_STUB( IfcPcurve ); + DECL_CONV_STUB( IfcPerformanceHistory ); + DECL_CONV_STUB( IfcPermit ); + DECL_CONV_STUB( IfcPile ); + DECL_CONV_STUB( IfcPileType ); + DECL_CONV_STUB( IfcPipeFitting ); + DECL_CONV_STUB( IfcPipeFittingType ); + DECL_CONV_STUB( IfcPipeSegment ); + DECL_CONV_STUB( IfcPipeSegmentType ); + DECL_CONV_STUB( IfcPlanarExtent ); + DECL_CONV_STUB( IfcPlanarBox ); + DECL_CONV_STUB( IfcPlane ); + DECL_CONV_STUB( IfcPlate ); + DECL_CONV_STUB( IfcPlateStandardCase ); + DECL_CONV_STUB( IfcPlateType ); + DECL_CONV_STUB( IfcPointOnCurve ); + DECL_CONV_STUB( IfcPointOnSurface ); + DECL_CONV_STUB( IfcPolyLoop ); + DECL_CONV_STUB( IfcPolygonalBoundedHalfSpace ); + DECL_CONV_STUB( IfcTessellatedFaceSet ); + DECL_CONV_STUB( IfcPolygonalFaceSet ); + DECL_CONV_STUB( IfcPolyline ); + DECL_CONV_STUB( IfcPresentationStyleAssignment ); + DECL_CONV_STUB( IfcProcedure ); + DECL_CONV_STUB( IfcProcedureType ); + DECL_CONV_STUB( IfcProductDefinitionShape ); + DECL_CONV_STUB( IfcProject ); + DECL_CONV_STUB( IfcProjectLibrary ); + DECL_CONV_STUB( IfcProjectOrder ); + DECL_CONV_STUB( IfcProjectionElement ); + DECL_CONV_STUB( IfcSimpleProperty ); + DECL_CONV_STUB( IfcPropertyBoundedValue ); + DECL_CONV_STUB( IfcPropertyEnumeratedValue ); + DECL_CONV_STUB( IfcPropertyListValue ); + DECL_CONV_STUB( IfcPropertyReferenceValue ); + DECL_CONV_STUB( IfcPropertySet ); + DECL_CONV_STUB( IfcPropertySingleValue ); + DECL_CONV_STUB( IfcPropertyTableValue ); + DECL_CONV_STUB( IfcProtectiveDevice ); + DECL_CONV_STUB( IfcProtectiveDeviceTrippingUnit ); + DECL_CONV_STUB( IfcProtectiveDeviceTrippingUnitType ); + DECL_CONV_STUB( IfcProtectiveDeviceType ); + DECL_CONV_STUB( IfcProxy ); + DECL_CONV_STUB( IfcPump ); + DECL_CONV_STUB( IfcPumpType ); + DECL_CONV_STUB( IfcRailing ); + DECL_CONV_STUB( IfcRailingType ); + DECL_CONV_STUB( IfcRamp ); + DECL_CONV_STUB( IfcRampFlight ); + DECL_CONV_STUB( IfcRampFlightType ); + DECL_CONV_STUB( IfcRampType ); + DECL_CONV_STUB( IfcRationalBSplineCurveWithKnots ); + DECL_CONV_STUB( IfcRationalBSplineSurfaceWithKnots ); + DECL_CONV_STUB( IfcRectangleProfileDef ); + DECL_CONV_STUB( IfcRectangleHollowProfileDef ); + DECL_CONV_STUB( IfcRectangularPyramid ); + DECL_CONV_STUB( IfcRectangularTrimmedSurface ); + DECL_CONV_STUB( IfcReinforcingElement ); + DECL_CONV_STUB( IfcReinforcingBar ); + DECL_CONV_STUB( IfcReinforcingElementType ); + DECL_CONV_STUB( IfcReinforcingBarType ); + DECL_CONV_STUB( IfcReinforcingMesh ); + DECL_CONV_STUB( IfcReinforcingMeshType ); + DECL_CONV_STUB( IfcRelationship ); + DECL_CONV_STUB( IfcRelDecomposes ); + DECL_CONV_STUB( IfcRelAggregates ); + DECL_CONV_STUB( IfcRelConnects ); + DECL_CONV_STUB( IfcRelContainedInSpatialStructure ); + DECL_CONV_STUB( IfcRelDefines ); + DECL_CONV_STUB( IfcRelDefinesByProperties ); + DECL_CONV_STUB( IfcRelFillsElement ); + DECL_CONV_STUB( IfcRelVoidsElement ); + DECL_CONV_STUB( IfcReparametrisedCompositeCurveSegment ); + DECL_CONV_STUB( IfcRepresentation ); + DECL_CONV_STUB( IfcRepresentationMap ); + DECL_CONV_STUB( IfcRevolvedAreaSolid ); + DECL_CONV_STUB( IfcRevolvedAreaSolidTapered ); + DECL_CONV_STUB( IfcRightCircularCone ); + DECL_CONV_STUB( IfcRightCircularCylinder ); + DECL_CONV_STUB( IfcRoof ); + DECL_CONV_STUB( IfcRoofType ); + DECL_CONV_STUB( IfcRoundedRectangleProfileDef ); + DECL_CONV_STUB( IfcSIUnit ); + DECL_CONV_STUB( IfcSanitaryTerminal ); + DECL_CONV_STUB( IfcSanitaryTerminalType ); + DECL_CONV_STUB( IfcSeamCurve ); + DECL_CONV_STUB( IfcSectionedSpine ); + DECL_CONV_STUB( IfcSensor ); + DECL_CONV_STUB( IfcSensorType ); + DECL_CONV_STUB( IfcShadingDevice ); + DECL_CONV_STUB( IfcShadingDeviceType ); + DECL_CONV_STUB( IfcShapeModel ); + DECL_CONV_STUB( IfcShapeRepresentation ); + DECL_CONV_STUB( IfcShellBasedSurfaceModel ); + DECL_CONV_STUB( IfcSite ); + DECL_CONV_STUB( IfcSlab ); + DECL_CONV_STUB( IfcSlabElementedCase ); + DECL_CONV_STUB( IfcSlabStandardCase ); + DECL_CONV_STUB( IfcSlabType ); + DECL_CONV_STUB( IfcSolarDevice ); + DECL_CONV_STUB( IfcSolarDeviceType ); + DECL_CONV_STUB( IfcSpace ); + DECL_CONV_STUB( IfcSpaceHeater ); + DECL_CONV_STUB( IfcSpaceHeaterType ); + DECL_CONV_STUB( IfcSpatialElementType ); + DECL_CONV_STUB( IfcSpatialStructureElementType ); + DECL_CONV_STUB( IfcSpaceType ); + DECL_CONV_STUB( IfcSpatialZone ); + DECL_CONV_STUB( IfcSpatialZoneType ); + DECL_CONV_STUB( IfcSphere ); + DECL_CONV_STUB( IfcSphericalSurface ); + DECL_CONV_STUB( IfcStackTerminal ); + DECL_CONV_STUB( IfcStackTerminalType ); + DECL_CONV_STUB( IfcStair ); + DECL_CONV_STUB( IfcStairFlight ); + DECL_CONV_STUB( IfcStairFlightType ); + DECL_CONV_STUB( IfcStairType ); + DECL_CONV_STUB( IfcStructuralActivity ); + DECL_CONV_STUB( IfcStructuralAction ); + DECL_CONV_STUB( IfcStructuralAnalysisModel ); + DECL_CONV_STUB( IfcStructuralItem ); + DECL_CONV_STUB( IfcStructuralConnection ); + DECL_CONV_STUB( IfcStructuralCurveAction ); + DECL_CONV_STUB( IfcStructuralCurveConnection ); + DECL_CONV_STUB( IfcStructuralMember ); + DECL_CONV_STUB( IfcStructuralCurveMember ); + DECL_CONV_STUB( IfcStructuralCurveMemberVarying ); + DECL_CONV_STUB( IfcStructuralReaction ); + DECL_CONV_STUB( IfcStructuralCurveReaction ); + DECL_CONV_STUB( IfcStructuralLinearAction ); + DECL_CONV_STUB( IfcStructuralLoadGroup ); + DECL_CONV_STUB( IfcStructuralLoadCase ); + DECL_CONV_STUB( IfcStructuralSurfaceAction ); + DECL_CONV_STUB( IfcStructuralPlanarAction ); + DECL_CONV_STUB( IfcStructuralPointAction ); + DECL_CONV_STUB( IfcStructuralPointConnection ); + DECL_CONV_STUB( IfcStructuralPointReaction ); + DECL_CONV_STUB( IfcStructuralResultGroup ); + DECL_CONV_STUB( IfcStructuralSurfaceConnection ); + DECL_CONV_STUB( IfcStructuralSurfaceMember ); + DECL_CONV_STUB( IfcStructuralSurfaceMemberVarying ); + DECL_CONV_STUB( IfcStructuralSurfaceReaction ); + DECL_CONV_STUB( IfcStyleModel ); + DECL_CONV_STUB( IfcStyledItem ); + DECL_CONV_STUB( IfcStyledRepresentation ); + DECL_CONV_STUB( IfcSubContractResource ); + DECL_CONV_STUB( IfcSubContractResourceType ); + DECL_CONV_STUB( IfcSubedge ); + DECL_CONV_STUB( IfcSurfaceCurveSweptAreaSolid ); + DECL_CONV_STUB( IfcSurfaceFeature ); + DECL_CONV_STUB( IfcSweptSurface ); + DECL_CONV_STUB( IfcSurfaceOfLinearExtrusion ); + DECL_CONV_STUB( IfcSurfaceOfRevolution ); + DECL_CONV_STUB( IfcSurfaceStyle ); + DECL_CONV_STUB( IfcSurfaceStyleShading ); + DECL_CONV_STUB( IfcSurfaceStyleRendering ); + DECL_CONV_STUB( IfcSurfaceStyleWithTextures ); + DECL_CONV_STUB( IfcSweptDiskSolid ); + DECL_CONV_STUB( IfcSweptDiskSolidPolygonal ); + DECL_CONV_STUB( IfcSwitchingDevice ); + DECL_CONV_STUB( IfcSwitchingDeviceType ); + DECL_CONV_STUB( IfcSystemFurnitureElement ); + DECL_CONV_STUB( IfcSystemFurnitureElementType ); + DECL_CONV_STUB( IfcTShapeProfileDef ); + DECL_CONV_STUB( IfcTank ); + DECL_CONV_STUB( IfcTankType ); + DECL_CONV_STUB( IfcTask ); + DECL_CONV_STUB( IfcTaskType ); + DECL_CONV_STUB( IfcTendon ); + DECL_CONV_STUB( IfcTendonAnchor ); + DECL_CONV_STUB( IfcTendonAnchorType ); + DECL_CONV_STUB( IfcTendonType ); + DECL_CONV_STUB( IfcTextLiteral ); + DECL_CONV_STUB( IfcTextLiteralWithExtent ); + DECL_CONV_STUB( IfcTopologyRepresentation ); + DECL_CONV_STUB( IfcToroidalSurface ); + DECL_CONV_STUB( IfcTransformer ); + DECL_CONV_STUB( IfcTransformerType ); + DECL_CONV_STUB( IfcTransportElement ); + DECL_CONV_STUB( IfcTransportElementType ); + DECL_CONV_STUB( IfcTrapeziumProfileDef ); + DECL_CONV_STUB( IfcTriangulatedFaceSet ); + DECL_CONV_STUB( IfcTrimmedCurve ); + DECL_CONV_STUB( IfcTubeBundle ); + DECL_CONV_STUB( IfcTubeBundleType ); + DECL_CONV_STUB( IfcUShapeProfileDef ); + DECL_CONV_STUB( IfcUnitAssignment ); + DECL_CONV_STUB( IfcUnitaryControlElement ); + DECL_CONV_STUB( IfcUnitaryControlElementType ); + DECL_CONV_STUB( IfcUnitaryEquipment ); + DECL_CONV_STUB( IfcUnitaryEquipmentType ); + DECL_CONV_STUB( IfcValve ); + DECL_CONV_STUB( IfcValveType ); + DECL_CONV_STUB( IfcVector ); + DECL_CONV_STUB( IfcVertex ); + DECL_CONV_STUB( IfcVertexLoop ); + DECL_CONV_STUB( IfcVertexPoint ); + DECL_CONV_STUB( IfcVibrationIsolator ); + DECL_CONV_STUB( IfcVibrationIsolatorType ); + DECL_CONV_STUB( IfcVirtualElement ); + DECL_CONV_STUB( IfcVoidingFeature ); + DECL_CONV_STUB( IfcWall ); + DECL_CONV_STUB( IfcWallElementedCase ); + DECL_CONV_STUB( IfcWallStandardCase ); + DECL_CONV_STUB( IfcWallType ); + DECL_CONV_STUB( IfcWasteTerminal ); + DECL_CONV_STUB( IfcWasteTerminalType ); + DECL_CONV_STUB( IfcWindow ); + DECL_CONV_STUB( IfcWindowStandardCase ); + DECL_CONV_STUB( IfcWindowStyle ); + DECL_CONV_STUB( IfcWindowType ); + DECL_CONV_STUB( IfcWorkCalendar ); + DECL_CONV_STUB( IfcWorkControl ); + DECL_CONV_STUB( IfcWorkPlan ); + DECL_CONV_STUB( IfcWorkSchedule ); + DECL_CONV_STUB( IfcZShapeProfileDef ); + DECL_CONV_STUB( IfcZone ); #undef DECL_CONV_STUB +} //! Schema_4 } //! STEP } //! Assimp From 01e4d07c1eb56085323a0d84a0c782986edf33f6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 3 Feb 2018 09:42:07 +0100 Subject: [PATCH 008/183] closes https://github.com/assimp/assimp/issues/1583: update doc. --- doc/dox.h | 48 ++++-------------------------------------------- 1 file changed, 4 insertions(+), 44 deletions(-) diff --git a/doc/dox.h b/doc/dox.h index 5c6de51f1..ece3e31d9 100644 --- a/doc/dox.h +++ b/doc/dox.h @@ -206,56 +206,16 @@ the library files. Alternatively you can simply add the assimp project to your p your solution. -@section use_noboost Building without boost. - -The Boost-Workaround consists of dummy replacements for some boost utility templates. Currently there are replacements for - - - boost.scoped_ptr - - boost.scoped_array - - boost.format - - boost.random - - boost.common_factor - - boost.foreach - - boost.tuple - - boost.make_shared - -These implementations are very limited and are not intended for use outside assimp. A compiler -with full support for partial template specializations is required. To enable the workaround, put the following in -your compiler's list of predefined macros: -@code -#define ASSIMP_BUILD_BOOST_WORKAROUND -@endcode -
-If you're working with the provided solutions for Visual Studio use the -noboost build configs.
- -assimp_BUILD_BOOST_WORKAROUND implies assimp_BUILD_SINGLETHREADED.
-See the @ref assimp_st section -for more details. - - - @section assimp_dll Windows DLL Build -assimp can be built as DLL. You just need to select a -dll config from the list of project -configs and you're fine. +The Assimp-package can be built as DLL. You just need to run the default cmake run. -NOTE: Theoretically, assimp-dll can be used with multithreaded (non-dll) runtime libraries, -as long as you don't utilize any non-public stuff from the code folder. However, if you happen -to encounter *very* strange problems, try changing the runtime to Multithreaded (Debug) DLL. -@section assimp_stlport Building against STLport +@section assimp static lib -STLport is a free, fast and secure STL replacement that works with -all major compilers and platforms. To get it, download the latest release from -. -Usually you'll just need to run 'configure' + a makefile (see their README for more details). -Don't miss to add /stlport to your compiler's default include paths - prior -to the directory where your compiler vendor's headers lie. Do the same for /lib and -recompile assimp. To ensure you're really building against STLport see aiGetCompileFlags(). -
-In our testing, STLport builds tend to be a bit faster than builds against Microsoft's -C++ Standard Library. +The Assimp-package can be build as a static library as well. Do do so just set the configuration variable BUILD_SHARED_LIBS +to off during the cmake run. */ From 45e4a513defd3db1ed0b0746c00ff4453189160a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 3 Feb 2018 11:07:40 +0100 Subject: [PATCH 009/183] add cmake doc to doxygen-docu. --- doc/dox.h | 78 ++++++++++++++----------------------------------------- 1 file changed, 19 insertions(+), 59 deletions(-) diff --git a/doc/dox.h b/doc/dox.h index ece3e31d9..461c308e2 100644 --- a/doc/dox.h +++ b/doc/dox.h @@ -62,7 +62,7 @@ that it has not been implemented yet and some (most ...) formats lack proper spe See the @link importer_notes Importer Notes Page @endlink for information, what a specific importer can do and what not. Note that although this paper claims to be the official documentation, -http://assimp.sourceforge.net/main_features_formats.html +https://github.com/assimp/assimp/blob/master/Readme.md
is usually the most up-to-date list of file formats supported by the library.
1: Experimental loaders
@@ -90,9 +90,9 @@ but not all of them are *open-source*. If there's an accompagning '\source @section main_install Installation assimp can be used in two ways: linking against the pre-built libraries or building the library on your own. The former -option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2005 and 2008. For other -compilers you'll have to build assimp for yourself. Which is hopefully as hassle-free as the other way, but needs a bit -more work. Both ways are described at the @link install Installation page. @endlink +option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2012, 2013 and 2015. +For other compilers you'll have to build assimp for yourself. Which is hopefully as hassle-free as the other way, but +needs a bit more work. Both ways are described at the @link install Installation page. @endlink @section main_usage Usage @@ -133,78 +133,38 @@ assimp-discussions. @section install_prebuilt Using the pre-built libraries with Visual C++ 8/9 -If you develop at Visual Studio 2005 or 2008, you can simply use the pre-built linker libraries provided in the distribution. +If you develop at Visual Studio 2015 or 2017, you can simply use the pre-built linker libraries provided in the distribution. Extract all files to a place of your choice. A directory called "assimp" will be created there. Add the assimp/include path to your include paths (Menu->Extras->Options->Projects and Solutions->VC++ Directories->Include files) and the assimp/lib/<Compiler> path to your linker paths (Menu->Extras->Options->Projects and Solutions->VC++ Directories->Library files). This is necessary only once to setup all paths inside you IDE. -To use the library in your C++ project you have to include either <assimp/Importer.hpp> or <assimp/cimport.h> plus some others starting with <types.h>. -If you set up your IDE correctly the compiler should be able to find the files. Then you have to add the linker library to your -project dependencies. Link to /lib//assimp.lib. config-name is one of the predefined -project configs. For static linking, use release/debug. See the sections below on this page for more information on the -other build configs. -If done correctly you should now be able to compile, link, -run and use the application. If the linker complains about some integral functions being defined twice you probably have -mixed the runtimes. Recheck the project configuration (project properties -> C++ -> Code generation -> Runtime) if you use -static runtimes (Multithreaded / Multithreaded Debug) or dynamic runtimes (Multithreaded DLL / Multithreaded Debug DLL). -Choose the assimp linker lib accordingly. -

-Please don't forget to also read the @ref assimp_stl section on MSVC and the STL. - -@section assimp_stl Microsoft Compilers and the C++ Standard Library - -In VC8 and VC9 Microsoft introduced some Standard Library debugging features. A good example are improved iterator checks and -various useful debug checks. The problem is the performance penalty that incurs with those extra checks. - -Most of these security enhancements are active in release builds by default, rendering assimp several times -slower. However, it is possible to disable them by setting +To use the library in your C++ project you can simply generate a project file via cmake. One way is to add the assimp-folder as a subdirectory via the cmake-command @code -_HAS_ITERATOR_DEBUGGING=0 -_SECURE_SCL=0 +addsubdiectory(assimp) @endcode -in the preprocessor options (or alternatively in the source code, just before the STL is included for the first time). -assimp's vc8 and vc9 configs enable these flags by default. +Now just add the assimp-dependency to your application: -If you're linking statically against assimp: Make sure your applications uses the same STl settings! -If you do not, there are two binary incompatible STL versions mangled together and you'll crash. -Alternatively you can disable the fast STL settings for assimp by removing the 'FastSTL' property sheet from -the vc project file. +@code +TARGET_LINK_LIBRARIES(my_game assimp) +@endcode -If you're using assimp in a DLL/SO: It's ok. There's no STL used in the binary DLL/SO interface, so it doesn't care whether -your application uses the same STL settings or not. -

-Another option is to build against a different STL implementation, for example STlport. There's a special -@ref assimp_stlport section that has a description how to achieve this. +If done correctly you should now be able to compile, link, run and use the application. @section install_own Building the library from scratch -To build the library on your own you first have to get hold of the dependencies. Fortunately, special attention was paid to -keep the list of dependencies short. Unfortunately, the only dependency is boost which -can be a bit painful to set up for certain development environments. Boost is a widely used collection of classes and -functions for various purposes. Chances are that it was already installed along with your compiler. If not, you have to install -it for yourself. Read the "Getting Started" section of the Boost documentation for how to setup boost. VisualStudio users -can use a comfortable installer from -http://www.boost-consulting.com/products/free. Choose the appropriate version of boost for your runtime of choice. +First you need to install cmake. Now just get the code from github or download the latest version from the webside. +to buil the library just open a command-prompt / bash, navigate into the repo-folder and run cmake via: -If you don't want to use boost, you can build against our "Boost-Workaround". It consists of very small -implementations of the various boost utility classes used. However, you'll lose functionality (e.g. threading) by doing this. -So, if you can use boost, you should use boost. Otherwise, See the @link use_noboost NoBoost-Section @endlink -later on this page for the details of the workaround. - -Once boost is working, you have to set up a project for the assimp library in your favorite IDE. If you use VC2005 or -VC2008, you can simply load the solution or project files in the workspaces/ folder, otherwise you have to create a new -package and add all the headers and source files from the include/ and code/ directories. Set the temporary output folder -to obj/, for example, and redirect the output folder to bin/. Then build the library - it should compile and link fine. - -The last step is to integrate the library into your project. This is basically the same task as described in the -"Using the pre-built libraries" section above: add the include/ and bin/ directories to your IDE's paths so that the compiler can find -the library files. Alternatively you can simply add the assimp project to your project's overall solution and build it inside -your solution. +@code +cmake CMakeLists.txt +@endcode +A project-file of your default make-system ( like gnu-make on linux or Visual-Studio on Windows ) will be generated. +Run the build and you are done. You can find the libs at assimp/lib and the dll's / so's at bin. @section assimp_dll Windows DLL Build From 1fbfe6736e141480b54be49fe6a09ab2c64169e1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 3 Feb 2018 11:52:40 +0100 Subject: [PATCH 010/183] Update dox.h Update installation guide for linux and python. --- doc/dox.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/dox.h b/doc/dox.h index 461c308e2..d9462590b 100644 --- a/doc/dox.h +++ b/doc/dox.h @@ -90,9 +90,16 @@ but not all of them are *open-source*. If there's an accompagning '\source @section main_install Installation assimp can be used in two ways: linking against the pre-built libraries or building the library on your own. The former -option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2012, 2013 and 2015. +option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2013, 2015 and 2017. For other compilers you'll have to build assimp for yourself. Which is hopefully as hassle-free as the other way, but needs a bit more work. Both ways are described at the @link install Installation page. @endlink +If you want to use assimp on Ubuntu you can install it via the following command: + +@code +sudo apt-get install assimp +@endcode + +If you want to use the python-assimp-port just follow these instructions: https://github.com/assimp/assimp/tree/master/port/PyAssimp @section main_usage Usage @@ -115,7 +122,6 @@ assimp is considerably easy, as the whole postprocessing infrastructure is avail See the @link extend Extending the library @endlink page for more information. - @section main_support Support & Feedback If you have any questions/comments/suggestions/bug reports you're welcome to post them in our @@ -139,7 +145,8 @@ to your include paths (Menu->Extras->Options->Projects and Solutions-&g and the assimp/lib/<Compiler> path to your linker paths (Menu->Extras->Options->Projects and Solutions->VC++ Directories->Library files). This is necessary only once to setup all paths inside you IDE. -To use the library in your C++ project you can simply generate a project file via cmake. One way is to add the assimp-folder as a subdirectory via the cmake-command +To use the library in your C++ project you can simply generate a project file via cmake. One way is to add the assimp-folder +as a subdirectory via the cmake-command @code addsubdiectory(assimp) From 6aafc5879761c41e950df16355782ab7ab801f36 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 3 Feb 2018 15:51:20 +0200 Subject: [PATCH 011/183] Add missing assignment operator to aiString --- include/assimp/types.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/assimp/types.h b/include/assimp/types.h index f0d9b2428..9868f657c 100644 --- a/include/assimp/types.h +++ b/include/assimp/types.h @@ -304,6 +304,20 @@ struct aiString data[len] = 0; } + + /** Assigment operator */ + aiString& operator = (const aiString &rOther) { + if (this == &rOther) { + return *this; + } + + length = rOther.length;; + memcpy( data, rOther.data, length); + data[length] = '\0'; + return *this; + } + + /** Assign a const char* to the string */ aiString& operator = (const char* sz) { Set(sz); From a64d5155051a72cf4442af75facc4d71c22085a4 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 3 Feb 2018 16:04:14 +0200 Subject: [PATCH 012/183] Fix varible shadowing issue --- include/assimp/matrix3x3.inl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/assimp/matrix3x3.inl b/include/assimp/matrix3x3.inl index aaf62eb0f..ab2cc410b 100644 --- a/include/assimp/matrix3x3.inl +++ b/include/assimp/matrix3x3.inl @@ -313,16 +313,16 @@ inline aiMatrix3x3t& aiMatrix3x3t::FromToMatrix(const aiVector3t(2.0) / (u * u); - const TReal c2 = static_cast(2.0) / (v * v); - const TReal c3 = c1 * c2 * (u * v); + const TReal c1_ = static_cast(2.0) / (u * u); + const TReal c2_ = static_cast(2.0) / (v * v); + const TReal c3_ = c1_ * c2_ * (u * v); for (unsigned int i = 0; i < 3; i++) { for (unsigned int j = 0; j < 3; j++) { - mtx[i][j] = - c1 * u[i] * u[j] - c2 * v[i] * v[j] - + c3 * v[i] * u[j]; + mtx[i][j] = - c1_ * u[i] * u[j] - c2_ * v[i] * v[j] + + c3_ * v[i] * u[j]; } mtx[i][i] += static_cast(1.0); } From 82980c8a9cd604e59a04251d74e8b8caa4ad401c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 3 Feb 2018 16:24:45 +0200 Subject: [PATCH 013/183] Add missing assignment operator to aiBone --- include/assimp/mesh.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index a532e2b46..ea27d03a1 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -270,6 +270,32 @@ struct aiBone } } + + //! Assignment operator + aiBone &operator=(const aiBone& other) + { + if (this == &other) { + return *this; + } + + mName = other.mName; + mNumWeights = other.mNumWeights; + mOffsetMatrix = other.mOffsetMatrix; + + if (other.mWeights && other.mNumWeights) + { + if (mWeights) { + delete[] mWeights; + } + + mWeights = new aiVertexWeight[mNumWeights]; + ::memcpy(mWeights,other.mWeights,mNumWeights * sizeof(aiVertexWeight)); + } + + return *this; + } + + //! Destructor - deletes the array of vertex weights ~aiBone() { From b0b125dfe6fd24b5c63db1eb5ac1d9b2afc34a6e Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 3 Feb 2018 16:34:40 +0200 Subject: [PATCH 014/183] Initialize all members of aiVertexWeight in constructor --- include/assimp/mesh.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index ea27d03a1..5896284f6 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -212,7 +212,10 @@ struct aiVertexWeight #ifdef __cplusplus //! Default constructor - aiVertexWeight() { } + aiVertexWeight() + : mVertexId(0) + , mWeight(0.0f) + { } //! Initialisation from a given index and vertex weight factor //! \param pID ID From e21b79a8bf2fdfc70f8f4195b6aae3a5d9f34c8c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 3 Feb 2018 16:45:07 +0200 Subject: [PATCH 015/183] Mark Importer assignment operator deleted --- include/assimp/Importer.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/assimp/Importer.hpp b/include/assimp/Importer.hpp index 1263868bc..9ae8fccc6 100644 --- a/include/assimp/Importer.hpp +++ b/include/assimp/Importer.hpp @@ -139,6 +139,11 @@ public: */ Importer(const Importer& other); + // ------------------------------------------------------------------- + /** Assignment operator has been deleted + */ + Importer &operator=(const Importer &) = delete; + // ------------------------------------------------------------------- /** Destructor. The object kept ownership of the imported data, * which now will be destroyed along with the object. From 123b9ca71a299741f5addaa9752c3dbca3053712 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 3 Feb 2018 16:54:19 +0200 Subject: [PATCH 016/183] Initialize all members of aiMeshKey in constructor --- include/assimp/anim.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/assimp/anim.h b/include/assimp/anim.h index 9156aac06..1a2c11044 100644 --- a/include/assimp/anim.h +++ b/include/assimp/anim.h @@ -162,7 +162,10 @@ struct aiMeshKey #ifdef __cplusplus - aiMeshKey() { + aiMeshKey() + : mTime(0.0) + , mValue(0) + { } /** Construction from a given time and key value */ From 9397932e4f10d83954d5a4af0b0bd10a1b2894e6 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 3 Feb 2018 17:08:25 +0200 Subject: [PATCH 017/183] PretransformVertices: Rearrange some assignments to clarify things --- code/PretransformVertices.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/code/PretransformVertices.cpp b/code/PretransformVertices.cpp index 6c569c185..cc5c669e4 100644 --- a/code/PretransformVertices.cpp +++ b/code/PretransformVertices.cpp @@ -651,7 +651,8 @@ void PretransformVertices::Execute( aiScene* pScene) // generate mesh nodes for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes) { - aiNode* pcNode = *nodes = new aiNode(); + aiNode* pcNode = new aiNode(); + *nodes = pcNode; pcNode->mParent = pScene->mRootNode; pcNode->mName = pScene->mMeshes[i]->mName; @@ -663,7 +664,8 @@ void PretransformVertices::Execute( aiScene* pScene) // generate light nodes for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes) { - aiNode* pcNode = *nodes = new aiNode(); + aiNode* pcNode = new aiNode(); + *nodes = pcNode; pcNode->mParent = pScene->mRootNode; pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u",i); pScene->mLights[i]->mName = pcNode->mName; @@ -671,7 +673,8 @@ void PretransformVertices::Execute( aiScene* pScene) // generate camera nodes for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes) { - aiNode* pcNode = *nodes = new aiNode(); + aiNode* pcNode = new aiNode(); + *nodes = pcNode; pcNode->mParent = pScene->mRootNode; pcNode->mName.length = ::ai_snprintf(pcNode->mName.data,MAXLEN,"cam_%u",i); pScene->mCameras[i]->mName = pcNode->mName; From be865ae61380068fadd7e48f6ccc7d39287a46e8 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 3 Feb 2018 17:19:27 +0200 Subject: [PATCH 018/183] LimitBoneWeightsProcess: Initialize all members of Weight in constructor --- code/LimitBoneWeightsProcess.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/LimitBoneWeightsProcess.h b/code/LimitBoneWeightsProcess.h index 2fc6e02b1..090181982 100644 --- a/code/LimitBoneWeightsProcess.h +++ b/code/LimitBoneWeightsProcess.h @@ -120,7 +120,11 @@ public: { unsigned int mBone; ///< Index of the bone float mWeight; ///< Weight of that bone on this vertex - Weight() { } + Weight() + : mBone(0) + , mWeight(0.0f) + { } + Weight( unsigned int pBone, float pWeight) { mBone = pBone; From da19ed0b2a7a5e212bd1d843ded29bfc3412ccb7 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 3 Feb 2018 17:25:31 +0200 Subject: [PATCH 019/183] BaseImporter: Remove dead condition operator new never returns NULL, it throws exception on allocation failure --- code/BaseImporter.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/BaseImporter.cpp b/code/BaseImporter.cpp index f653d8d81..0892efd9f 100644 --- a/code/BaseImporter.cpp +++ b/code/BaseImporter.cpp @@ -157,9 +157,6 @@ void BaseImporter::GetExtensionList(std::set& extensions) // read 200 characters from the file std::unique_ptr _buffer (new char[searchBytes+1 /* for the '\0' */]); char* buffer = _buffer.get(); - if( NULL == buffer ) { - return false; - } const size_t read = pStream->Read(buffer,1,searchBytes); if( !read ) { From cfcaf3e97b382d4e30f2cdc2fd96af3bde632a9f Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 3 Feb 2018 17:38:30 +0200 Subject: [PATCH 020/183] BVH: Initialize all members of Node in constructor --- code/BVHLoader.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/BVHLoader.h b/code/BVHLoader.h index 0836488ff..a18ad81d9 100644 --- a/code/BVHLoader.h +++ b/code/BVHLoader.h @@ -84,7 +84,10 @@ class BVHLoader : public BaseImporter std::vector mChannels; std::vector mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames - Node() { } + Node() + : mNode(nullptr) + { } + explicit Node( const aiNode* pNode) : mNode( pNode) { } }; From 5278e1a5f85dc51f5e0f4c2be28e5b4fffce0227 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 3 Feb 2018 17:52:25 +0200 Subject: [PATCH 021/183] CSM: Fix a possible memory leak by using std::unique_ptr --- code/CSMLoader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/CSMLoader.cpp b/code/CSMLoader.cpp index 7b0064164..7160740c1 100644 --- a/code/CSMLoader.cpp +++ b/code/CSMLoader.cpp @@ -136,7 +136,7 @@ void CSMImporter::InternReadFile( const std::string& pFile, TextFileToBuffer(file.get(),mBuffer2); const char* buffer = &mBuffer2[0]; - aiAnimation* anim = new aiAnimation(); + std::unique_ptr anim(new aiAnimation()); int first = 0, last = 0x00ffffff; // now process the file and look out for '$' sections @@ -294,8 +294,8 @@ void CSMImporter::InternReadFile( const std::string& pFile, // Store the one and only animation in the scene pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations=1]; - pScene->mAnimations[0] = anim; anim->mName.Set("$CSM_MasterAnim"); + pScene->mAnimations[0] = anim.release(); // mark the scene as incomplete and run SkeletonMeshBuilder on it pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; From 37d352622bd3556d25ea744eded79d818391b3df Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 4 Feb 2018 16:42:36 +0100 Subject: [PATCH 022/183] closes https://github.com/assimp/assimp/issues/774: use correct type for unitscale in fbx. --- code/FBXDocument.cpp | 4 +++- test/unit/utFBXImporterExporter.cpp | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/code/FBXDocument.cpp b/code/FBXDocument.cpp index 3714cd617..a1fbd2cf0 100644 --- a/code/FBXDocument.cpp +++ b/code/FBXDocument.cpp @@ -351,7 +351,9 @@ void Document::ReadGlobalSettings() return; } - std::shared_ptr props = GetPropertyTable(*this, "", *ehead, *ehead->Compound(), true); + std::shared_ptr props = GetPropertyTable( *this, "", *ehead, *ehead->Compound(), true ); + + //double v = PropertyGet( *props.get(), std::string("UnitScaleFactor"), 1.0 ); if(!props) { DOMError("GlobalSettings dictionary contains no property table"); diff --git a/test/unit/utFBXImporterExporter.cpp b/test/unit/utFBXImporterExporter.cpp index d5a6e4354..d2576cfa1 100644 --- a/test/unit/utFBXImporterExporter.cpp +++ b/test/unit/utFBXImporterExporter.cpp @@ -101,12 +101,12 @@ TEST_F( utFBXImporterExporter, importPhongMaterial ) { TEST_F(utFBXImporterExporter, importUnitScaleFactor) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/spider.fbx", aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/global_settings.fbx", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); EXPECT_NE(nullptr, scene->mMetaData); double factor(0.0); scene->mMetaData->Get("UnitScaleFactor", factor); - EXPECT_DOUBLE_EQ(1.0, factor); + EXPECT_DOUBLE_EQ(500.0, factor); } From c5157bc471216c6efc90126539ef44296c23d66e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 4 Feb 2018 17:13:58 +0100 Subject: [PATCH 023/183] fbx: global settings use float instead of double. --- code/FBXDocument.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/FBXDocument.h b/code/FBXDocument.h index 386a660d9..654f5bfa8 100644 --- a/code/FBXDocument.h +++ b/code/FBXDocument.h @@ -1022,8 +1022,8 @@ public: fbx_simple_property(CoordAxisSign, int, 1) fbx_simple_property(OriginalUpAxis, int, 0) fbx_simple_property(OriginalUpAxisSign, int, 1) - fbx_simple_property(UnitScaleFactor, double, 1) - fbx_simple_property(OriginalUnitScaleFactor, double, 1) + fbx_simple_property(UnitScaleFactor, float, 1) + fbx_simple_property(OriginalUnitScaleFactor, float, 1) fbx_simple_property(AmbientColor, aiVector3D, aiVector3D(0,0,0)) fbx_simple_property(DefaultCamera, std::string, "") From 8284226efdccdfcba303f5ef9ddd74365055d616 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 4 Feb 2018 17:19:49 +0100 Subject: [PATCH 024/183] fbx: add missing test file. --- test/models/FBX/global_settings.fbx | Bin 0 -> 57596 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/models/FBX/global_settings.fbx diff --git a/test/models/FBX/global_settings.fbx b/test/models/FBX/global_settings.fbx new file mode 100644 index 0000000000000000000000000000000000000000..3fc616bb034a81d3cdc62139b6839cb389112294 GIT binary patch literal 57596 zcmb?k2V4`$+YX`>3#h1wif}gUpeUULlp=!QiHaNuA_PQAkPteF9Xl%L0a9Y;uz-q+ z(t?1XCq)kqq=urj0146pY2Qq;Xi(bS|J&ciEW7hOJ8zlyeP?zzXX+5(U^JbezCOag~y0g53OQ3HnnlgZ$q zlF-4)+i(PLlbPTsM(C*64p&ct57tB;9JdlaZi#ioc{-V_0>^!Xj!)a+>hA7}7n~FU zCx&hBkd~XkQE|SYIB!d3@W&Q+tS8X7;!3=a!$Ef}-$Eu9s}$EMZd|Xf2@VquDyqOg zRoAPp-=Mk+$O(l~0v}0BN318-#M2pPIT!qC?2bF=;12&piF`jJ0nY5fW4#R=d>obm zB<$D>@R*D~q+(v7CT*&Kjkt}pVOrPL$2q*>u3WbhKm|_F`aNbUM{+VXsGrO?9uFw_vXHcN0P(Z#2 zTX7y9SWm%4AmO*oP{`w_@ASrbVZD7^v3PYA4*-WyP4E%fX*msi8Q>fVkY$gzyCsB~ zTKagqdb(IbmB3@*3&n4!?2H5I_g?RO&_4yC(E;ZGjTCI+?f74qS^*RE)@1@FXe|qG zR~ImV-T9^iB6{Hno=$>pfU~Hrpg9!541B<~7l5xJM6kY>m%FPYe~9e`P6zZXED>ml z67b9hdqZw%1nK#Z8wj%nI18BPgRha8%^kqt9hp~Fi1!C~??uRs1>_4yAxC1sDE!j_ zN_f!h1d4#V*dZW=W8_8g78n+_$4lSe70-{1t|06JQBfwIK3Es5AfB5*8FCQVIst6zcbKb-=h#pV z0$nSB!hvGs4U+uW7X_&Tr48D`zN!e2F#CN+j(c4Kf)={&RvgGhV_z3s1C{*qb-yFW zz3x{8y+P1xm_>%7*La%dfo(I;EfjjQ01jjN5Abn&5QI=@2q>h%*B(z-A4^9EcdU_v zqYp?R(C$t+0w`SgyXFLsgNV5ez!?-ukEHhwVbKGB0#AXC{;ebbo=pLSPv}|O1Z>bf z^*s){f(*SC=MFjqoCL+++hx4P63Q<)40ce09*wx;30PrceeeN&gdSpmb#@@Q`)md2 z%-dmi9XKbfC5-i0PlCtyJYxbg zLIVO(6+mDQAjHzk!4s6i-adR;O!3KLdM=o!294ljJ$ygc7b#`uCY-XdVpw@?HLpa*(8T*A$$_JDvS%T=To+V zTEmAQCgMS3SYg51TVklOnX3BrRp0=MFAKlI{k|70L_aK0tp_Kd##Vq2q8Ce0?mJ>F z1SKf+VBGg=ly4Rsz`!0M#*hKK`h3Icd%Ai6b2z$;)qqnYdU&(e(|$Km6^-9h0nzpSkvUAq3DJ$K_&R zzLBm2Wd;-z`T-CV79B9@dSmfE|FM54lmlRa`dT62gcjL_b9d*5F8&TUfcino1O%fc zz(C7cfT=!66CwPyB+YH7Z>@kuO zU~vj?yRZ)KFf)vehdvyP;F0Mt#0x-*@T!W?&%hFqPr#VcGcYd@nzaCrm_nbQDM3K#WY z5G}wFw$s%g>nZ7GTfE*PwFEGO=UV8ZkKtflz4y2_~q(J^ZZ~GZpwB z6hp`kkj3%{0gDZU1&S+fB6cu9XoS2G(iq{qJsf;Yz*3z5NF^G$E{ZrD$xi{^SbjA; zrUe+7OazGxL>L_5HFtYEc;aD78_)dQaq#1L02<7Rg*;&B`Y@L7boIrJ>wd&>@Z()S z1HmKl4+bzOKfL~42Y14F;j=ZI~~nnYr$LtioFVaz>wn)ez?|0@E)}k zhtown1hz~7gIUBF7|96Cd|)d4p?C*JP&W{SP@FPy#kxCLIPZ1E`tjqI@ZS*fJwx2^ zlGoJxj4-HyQ%F~Z5c~LjA9i3k2l&fCZzWiggW`_NVf`b=2x-$m4~uXJM=2ag;$~P+ z7bM~e&G8T+R$Q4}x7S-=F9rb%>03~w2?p0_+ABq1Lgoc&ia+fTBxTo&u06V}jZg~jj$1nFs=myM^e;^Q~E`a#~Lf|^Q zh35{OFI+7i1^RB}s~LN{I*kUI0#R!M+`3WGh> z4|Me)2z>hoEkp5+ARr;5LJ+JVwhql_e}DlL>MjC9YDk!L_}3yhfiOLSIv{2cVECgA z>;u_g|MX$|?*#U8$dYRkR71{Sqm(WLWLo*7k0!P+%Ved?%+9r8SOH? z00fW3F6a?P6NT#~>Af5d2bmDaaE@-@AR~-Jt;GV_g{}!9 zLvuuP2VYkgSUQb5`S3spgsurPp^1lsi(oBlglonikY5Ddgub;E++`ch#|x3>O9jp0 z3|KH{90>sJDWV{dNhr3MVzFL=5^w}hh8PP3qX+SVRmku2L49|xLk_#aocR#W-D&(H zvJMl9unDCs7~LQvnAL$bicxy;4#6QK66T6;gJEJbfoY1v#`Fjb;}v7(2m>;vhYiSG zI3IyCKqLH|=)pn$7N$7NsieWdZ({K4;js%^2Ij+f1Y--qz)S_c1$B&6@o+ePH>cum ztUoOB_?G-}ZO<4WVK5Mis6zvpU*nDH+0cuQBH%S(A9xYJkcRglK==T`d<-8?Lx4g> z!uP9=?d<~yR7G%2FcbV1HGHt+`u&zA0&%qkXnx$nd1D3ZAc_`V4vs?;__3DpQ4CBF zLL?+Qba}oMWBPn10u=JMA<;dt6LWeI5E`J(#-JG6VNfVTgaXL{!{he^-98lI5E{MT zC7F=lzZ;ifY&jSh(Pck5KB%sT>^YX>>jETHqR_+_S%k85aK`F`XKzM|S^$6|AxNP* z;s>OK=Pv$JETb`GA`D0oH3yImU37@aCjcy?O$H}^m|wwRCjpWV?79y4fMExRz!3aj zE+QBrOhTg{%r!O|>~L`Lbai&cdjE#Qd5k8-83Zikh8qnOzd;rG*s&s~wccBVVxvG3 z7(j+mj2$>l^dvyegPIr(Keqdr=ouq;AwvjiNwCTepU44)0Ds+o1VezUqC$sX9v0IU z-r$LyZ<3D_MBX5Hp;#ugB57b@r0?mZuRogpc?ep_g8|wA>*aIE!kIri9u2t}OnIQ% zR)P<3Ah?id1Rm_c3+@Ds?OGOBfpu=DHfR`Os zFOJFKs@H>X3>RA8)5q1p-PHj<%Ip$$Uk^wNg+_x95PB~75{3<5z8$3(HEo)$A&o7V z5QIx;+z4}lhr$lw1asCOhA+4%Dgyx{P76B%?56Jso~$B(#0gii3C`g5)@as)3ph{^ zWZVg2H+;+pJR;~06u`f4IQqi_;sC0HPzX(%`3np~6r(vkj6M_G5Yy+i1?!Lh}QVar}4)%Y(-v#>*?sx3~)UOwV+fsrfDSTm*@E*2Pek&UL?${7|27{-p-M(!bnySL zj5C2QpqxT3xzpR#13uci7d#Po(A6DQHZYL=Fhzj|*oJ^Xub{r5&9;K4uJDSMha8-6 zek0DuA?`SW6B>=Xu#Q-8&wo5508eBy!w5WNDrhY1MNm>W&vUN_J@~A@OzrF|^eXF@N>?zw)~O%nOS+5K#WN{^lUOCW8J#iy*xJQ+IiQ7TQNB zgbm$500t5~pD>cU0|;s#0R3PaA?*J!te=cc{NG164G=?Vfm1f!2R;pskN-~~2B{f%%4%}2j+__#VdgSAk+pa>qNB7r1uTL?;6 zDE?SPf@*jiBBu!u{Wkuh5DIk*Ap#OYmxbdEJ|hTjos5rOrpE+m2-zk;PXk{&y|LeP z1FMMtkajIVG>5E0u?RdC;`6`Ko&mH_`5+hl80*IlM72nNgrpqm%l{CCHh~KL2i<*R z{qG^j4X{EXLCpq*gmL^EtE=azBl^~uuu+F|#h9>B$MQ@tDIcwqV}u>!F=3;2k~k)8 z)J{G_U@HZhguZR!c?j$6>N8s14O*bu5tLAvLh|U?KBf=m3D6si!3Wq4R#FJ=e{ius zMkk$^hkd&xCA3ojNTKnB^zyx@qdM3m5PwHSjcPiLu~4I0&U19s7~XYfEYzqytQ-q9 zY7aHl1=jvmVC^~J3z==XIto<`|FuZGhR~Y}=w}Jj4@HCj%FRHycL=y=3UecVFcE4Y zhl2ApurV;3Gz2~ZR0_wy4Dbb<-vCUQcR6^vAP=03lNkNS=9o}&O&^U|6OyGE;Q(LIF3{wNUBG)~{AVmCRQ$}-5~yT!nh8m?e_Wag z33X=#8i9fSl~A;CX(lApMoGCfC z0F(zW3~v^VG<||=8K05nAXvcW3&EE;7T%Wj#e%mgz)P+^U|9ydtxE9n!hu&IMp@HY ztt-$I!ZH`IATM!zd){2|e4i*d<2uqCO7OiI8-xYiseyfgYb!?H@Cn0_E(=bgF8mBI zvoHXwWupOF007n}NJ^0gyJn8=io(fiq+{?}-=7+Te+@nDBD8}Bi@idXFsxZ2_qR;- z(*Z~`F6&i<)d(~OI)U{YWF5*;KmPp_=SUgqZ@>w?8d+|FRhrPU-4B0`z_=W&V?n$j z_<%mm8-@qZCiBZn{=+3>oZ|DsfB0{mz?}(f*+31@Yhh9qUZRr#fA4?`W?*GL6j_dg zJwz6IA>=s%g;mPW3%$_lV4>HN;Oz`%(>o90JYD{TDo3_qt93K=1mc5PBNQLn6)s(Z z#Vh_Q;V3UG!S3*R4H3AK01nl?1HA3zYUU6y`a1kI0D{i7MF4{G8+y3n@nDz=7Mmfx zU=Qq!?^gfl4RZk)lZN<&UN0N%F$4x?nZBSu;A=<+!`=lOu{(lA?{`D5cnIw{0v-rK zR0_pI=qA*gr$6_`04!(_@M9@w^dJQO{g~pz(~+mh63Hd$juKhUJjtlK;D%b zQ9qy|e?v-1q1bW;P(lb&hs_4N-zgmfCeSSu>NUcFEF8cd3O|D|x~=DK7082xAV~l; zhmUN#?{M&%NRccg$2ZBG5KhE@U|QKi@c7SkcL5#L517b9cZ6dK(h2Eh%z~SNf$>H>@VBetagsCUq$NNYhEd@lf;7hP9 zB~16f^mG8}DPolQBVJ(Q{WUQZ=u{|XS%SAT!F@;XF=E!}LE={Ewjz(2l=B-0+=fq*Gw61~S4p^PO=*8W9lwyMNnjzPA?_ zysn6k1#6t5;IK0I%oz5Tq1m<-9&k{i_%9!ufk`m<35N|RkztMa_e{;;OF=t~fheFg z)V1``o!g;bp9uyBFM}%$ZNrlCb%2DZ72tyym(MuXDp%3Zn62PWBp)|Y1qpjLZ=1l3 zV8R5i11~wQ@kXk_eg!-bEaZ&Cp9&bwldSblAy6Sv4X}>jg*N!BGLr%44|fPBfkSx+ zMkqk?F@hJI#XyTu0%RRZZw8NYP>+oetOQMg+_3J1x*F%6?RH=;==6HuQ3AxcIvXiz zoB(W4XGjKv0RTQbC9oHNmNJ6$pwSqcrJ2BUA>|LAYiKbGmUbxqGf&WSNA+6K?c;c@ z5xC0GHq2{x0_4B-+LJ$H#`W3)fPr@SU%gh`^gny8HUj-OUK@;H{V|T=(Xzuinc%WPfy~Oo?aiq39kk? zVRHWdO!I$=Rhz*Q1GG9aqk*$dL76uGd&e-%;1Kv)u#a>|=yA($Zyk&f>7O7-q$-#O z2n5J+UpfUX!4{|2gD@b)&UA24z%c%6r+kLG%X*tE1kxj=nj|<5UmF~^3sX;Rxw&&? z4~Br?g$6#vkKcg@{~Uh)+oL8-8bW|9NI`=_0wEpp{ce<`C!c?0QT`D&hKsNunt-~5 zEW%Gt|I7f*_x&db^mht0F(SoiYmmb!{1J*(fEf4!-yY+JCdb(CQckL=zKko^ z(Mv%%gth*|yOd)Z?x?<=$S#2>V0{#7T?Typ%Lku<3-P^3A7K(23=@5FSFu!IUhmk? z_zAs6r-(vj@px2B7J^4Y5e($tUOpS6^3y|Q-QZzJdXgYPz(|J7UuS{cZ*QD{koNs8 zve7g;L!m3sm*Zo4&P% z&~MuVTfsHfgCjpw3$K#(VV!{DKcOrj`?1RXUMc9=e4`xxdoSzv{D(>*>ruPBpg?G~ zpDWn;TYQ0D;Llv+R|=@xNTrZ92dNb5L_wucgmC?*N+HX1{lXvm*T-0-r?^)jD^&IW zPo-cF82^V#Aqs*2`%0k-L2eR=@*gUN#a4o@BVqQxR0?(og-~#WDR#n@0vW-p3WS&n zz94?$mBJT<;D4(WR_+_7iT_e5xQ#_Iu}a|y?1j!>ST zNTu)_!k{L&=6|Xb;>P5dSfx;pa3C(sH{8Fh6xRGEP{#jWDYzj_LWAL3Y#*y$1v=zc z3J-q9PpDEj;Ou-lD(V%c4Z;7*N&#HHnaA6wrwS6p|5Paq5rOaTn*(4Svr;&YG#Ir~ zcnTT}CD~z>!YDbYTBtozDU6a~rXs0zA?WT%m4aU4<%9f6Axi;N3Tr{@u`2~*p%y<> z3IRfwnpmX}hmcLYQh1poP}B+_j>ORNtJ!R8EoEp9{Hal*rIY{(2*PJ!blT z*hH`mTSgc<$p2mkS`BGn#G-D-VL^lUARtJ9g~ya9SBJfmI;pfi(I?D;gW~( ztkX&dBn>X7|Nd#3hSlWB25alDYA}|pEz@f%Sg3b=vVl+1smV{>mzg??EOoNpHvibH zX*Vc=y}c}Y)PkieB8i-SDrt|^o^s9pqM*RQ)Jz_?JijEZt2La;y*6;SD~wLnjYxT+ z6JMmoOqr}>gjGckKqh-gAYv^0sauCE|mE05GjV%I6RJPhb6Ownri`i6|*^qxS|z7)%1xaz*W zE{%n<`R0{-s9gac^sUk?-s76Smha;(iyL%J>G$A8_t#-k8yLODwz`~;VUos5>`LE{ zv5W@g@Q0MQ`8jYHM)Ju+Zi*)9G^}*)@qEb|R!)pA1Of7A0{u%oWj2t!hJ-H}6z>*rc zJ)OBVWRZq?oeMhskwS1+Lnx-PT7W^C6abN*%)~RnB`B?LSD?k_-LpQhk-&B~JNG zX%?^a9s8bnlll?bUxdb0nZ3^z_C{%0ZP`Fyx5d+%M;C?91QE zvTJ0k6zIvlcclVCG5?6Z?5zukion!_mOAiGvR+jNRuB^dPg`JKImC0C*>$w|wznTM z+if$+L9uiK#rV+vaym!zc;>O1w{&jIUjM2TijA@ihO-0BbW-mEt3~VUNncgb?~0kr z;)aAO<_p#JkX$;88d$l1;B+(oGO4yK>0xoHO9qo;s}|QFC+)K@RC&F9wSTe#I^Yzp z_H%QFv+mbS7V2d%p=F^9`-Oi)f@o%+E+d(8Gnx38MTh_TBebvW6n1w&jZ22V@|#Dp z^e_LU6Ec`Fm&t_J0nglOm1~rAA_7t{trR*glYI8Qv2yPtWkPBG;v*%+k4k#O2N|Un z#QH)jQvOX^f3@)=d)A*_A1&C~yzc&!m4qIa3qdW@_Kk9fbFGf}U_?uNUEU7)ui;t^ znZ_N>XSsPCJrYN!I@**{eD(pR^)XpXmsXli)h!BtSzpBQab*VLVk-zpa zFprn}DAbJ{O8hHTgOJVso3olWIY!HbTh`C2Omt~nL1lFgyph9wawFa2KGZd06%V?^ zc0XL|$tw=ZC9-ZZ<5J=V+wRAc7lu*t>v;q8BYEN6qcu7T!Gzpi*tx5r-)uKgBRb%kNHIGU{qSv)AC3RvV$%CuACaKpBM*DXx^vP06 zt81Gn-2?{J8@7gDOi}L*DyySkPQgDn^kYz4Kxr|NU~|L2PtJ?(HW)vo@8g|AGxG;K>}-MDp>%cv*{5|5c3tOf*_!x~;wza_;ldv?g_Ue5dGOM~zHLf|K7~K@m z=~!59Q6J#x#ooc~qa11ci^yax9t^2SOUI;>9yIDEW6nHi48oV@PaqCE5OL(7Mf(?0P*Bk>5~1+^n0n9$EToYv5Gs9fi)B_^gg^<&&i z!rH`|v!oNH{mXh^E0NC8gA8J(1wW0}3aI`2EGysu?$y=crn*|U#(?JvYgvp(R>cXI zf@v*J+r%@QH`~Q5tY)5hkle(oZIJGl;p%o`H2O@H1`Qj&_LgK`p*Oy3W|5i(bIxQ2 z6V%X*3QU(xO3_@7Gp2u^Ma;lwQfp{URl^SM8CeRCxTYB2qNJhIwVB!l>llsTF4kq) zvUv7!i(X6iFJX0M|7>o1%t&GE*Y!Oi6z#{=#o3VN?e4eNVVSg9Xkp{YX$hi4Tw1F|*HTJb zxql$WiyX2wB!87>%O}j29U;@>=&E_gy34tjLVe$KSXoBu0k16+I~5IE3c-HJlR;s>yJ!k^<$@8*LKxGB-=zvla7_YZF?g zV6)GDpo){s%)!s7?#7bhyaLkJH|YK{5Lf=P#Uj&wxjpX-tJ;b9urSWDI#6xB3+Z9d z5rQu_6}Lm{)7F&szW#_76)Po{xNdWl@G~o*_mj;-zELv@_ zf0KMrguX(Ij>>(yZDyZ_tWHB#Wb;8!T{Cv$T=YxdC;8Q>FMSohzFM7#-pw3{%daL- zPoxlOv&fB<45^gZsUd=yOzmv&6Wu#;?g1`1PdLrN`tzTUcKrYrAmB{rBRKCbviZ9B z%P_*i{43Wo)a9ce&i+fH`OCg2?8%KApN2*4u>K{8{_7Ilt(~*B|GIMd_N`}px1B1B z{z^nAvV8VaY1*Bv{+MRo1!{Lt+#Qo^tM?rCI>Gz49D=6+PNt`2IVQ*oFIF;u+&+yZf)uu+u6TMkWtars>-64WszyX$G< z$4~id%1xh|w0M%(iGk98aL@?g7k{U zFJ}*I@weukG~2&kY{e<^B+O#sqTYl zNoO>j=$dvQ`PG4$#9!R+*-fW4pWrg@KCW2uL6$OI%`)j|uc;o6z50{8X3JT7(Il~^ zK|+0DT0Qx6`3XY3`bl-tnL#6_^?^i^RP7vVW480Ns7l+K^@A^NkfoD|BG2x=Soh3& zA^ZH}s6cJ@F6LflY>WJr=UJ55wdrz1S@vp`a>~5g^jSpZbn!@*S~ zRR8GQCd#m>r!`QG6fQiJ;>tgZ8dM2h8N7p?R$u5=7nV&I!E?mx3m-IKPKJTqvy7%| zT?#qQ`TB-zrN#!5o3`VEOnGmiYqlROdbV(50wTkTIOqf|3k-m@cV^3BZZmiLdjv)`WLP2tK# z(vq?$lkgd@2>tX0?uBt{>yvJ&-sGL+p(vs1>dDT|9VA6vJFd8`xN8^bwew+BH+8+k zY|2d>`@DX<$quQW*1BhP^BBst*R9VLohd?b&)LyHBg$>noT#Hw=xOfvio{%Ikmiz? zrVl7HW1twklSQJk`jiWlpwT^a(GeOf#L7vR`cdjP1ut{N1p}Ue#6y1~67-+O~wZY;QStW73Bz zOCVC=BdQ2G*Xjg)(w^7i%+-%K<3nbAx~@wQxP&-siIZ1$dC;$^jnO)W*?egTV0U3;))IR6mDvC0+#c>rHK)eWk#K( zW|?x_<}~#ZPNp_^VXfO6GY?sv%R=zH4KcX$49gWLl^>d>8f?^a-7M99y zjh4^V&I|G3X}#_^7FA1iy4TnLaz;M=tR0O@q_1Kv5T$E|Mdf>nRtD{*%ITblNIMz| z2A}5vdBXGN!|PJD-D|1`_a+j^lY-he_p^7O zt>&IPptx`T=Hh@Ubs3*S)Ffi`q)&(EDCAc;(oLR~pDW+_iqYg;_E*l`V~z4I)R}d< z^bAi=4*nTF`@@H_4m?psS*qwpXim;i{3ZOw?#ofB%i8BRYd6Q<@^aXTD#(>ReqO{a zz#%ClDTpOsF2+8iAfmlO+KVVJi4iO4)pStpUHR!cS`?3!(ok>CY3j*6%%OaZe7pCszHUN zo$+qRfA2loSlX9%iDBz6Rc&8BuP>EaW{dYh`{0!roG>DAy1-(>&8W)EfI8oFH>>=}gf9b% zQPhBQY|FaTCzGFB)J-W}ePh2ukhIcK9si($(tHQHA1yIqzZ-2oE^VIunM@fDy7E=d ztL%X6#X*I=i8R?Od*b^gwB$Of8`B##%9)^c{546Ru6Vfb`asd+`5i1Hk7x9`ri?bf z58_Ah3Eg)a3rblI`5G^zYZnJmCl{r$>hC)BRupaYSFrQo5I&F#UIn4t@A(#~r*vvx zB`T-R3e+6j<1@R`<=uK8)t;$6Qa+YF?-}Mh$?d4B2UDut5%GtQ>KUn-C6@9HZQFA69km9@*g^^H5$T>npB!IUC} zqPcz13C&qWtIF7WZ>n``=ZW=9?@{ToWSG_2Hs~}|d*jzHF^zJ3$O_2~`X{JwEpZWX zHqeA!8n*8zvvoAT<4sJfI{EtOdV9m@1iw%fVcPTZC>8%X?oR#IrYLm7-swoMF7 zuhsqL}`46TYqkk~0Uvk6CAFt|`V(7`yL5slJ zExG0D71_|@I@8zAZ5wYEn%BS2c3zdLle)-@Qt;?O;lo%1tK(~qPlXqcvVJh>QsbBV zb>?2v?d8-gtw6R)iY5^;05n|)+_91nzTPI0)GI3vhL;%Stc##W+?c=I1+pTHwD(9#`1-px@R%M)!qH zSAULkoA`l{b44dSr|H;(P$E~qw!Wj69(L-RAzBpTX!pr4@q^1Q65vs}rkwf+G`j^RFEX>DxWI zvpnWc-P3)weQ__Eo^i}#wD*~tkil>?8LF=L1{$*hh!r>2QCyOK>j%PGBc zGZ)232ZybfNQ>QA-<@67-jy=2u&}gt>EYz&F3o!Px&>O(V&;BIotpq9nN$;u(nt3ac$-m|L0L|XIigl7j+a)Zj0X%J)mBb=_X2M zbv|%Ih275oz)`bIEAEUA;f9pYzl6suYD`@sJAh5!9AxVk8I-BZiwgpQ-pL)!?^Y9la)aSK}K3%8jCBMsZ{B<^O z`Kzc(LZj`oki<0|UHh)(k+UDXzmcC4XJ39luv_WqtJ>Z+oo8;lgW5CXV<)SNB_8W- zvRl&>SpK^EV`50hla#5Io1djhz7$n17w^rAWjdA?mV(*q4Uli_{AxNuus*#1tHFoa zrFm(6>XynU_tVq*`sH88?Y$voS8H)EQ@Uo-+DW}#O=X3>O=YpXc{!Ou%ZxH*J&Gl~ zo8Q*o_@%b`NYmhQ26`j)NPERaH7n^UNp%bBL*joshdbRVp>#stX-5}Gz7el8Ei0NM zI}T`CEA)Nba3agpDU%#!(_iywO(5#iY24YAVzZ2E?o%q!L1L4rvuql%AvQkBFBMC2 zNbh=@bdSEul-Nu))xM?vy6?-DOdjMOMvGzOGUPlw zLl)Hb?*=8zMpk=&O^{lpSqJVZxDTLJOLe(NTszoqk;j^mWu`mnMLD{=-k(?aX6HVS zrj+CH4ywfpPKvX4~a;+!3|3qPF(v>O~-t6@9dr_&G_eh*8Pu-MYZv*$@y%#o>`v)vOV_E%nHpoJ9`FZ6q3?DN~&4< zo#b3PKo?_oGsQtTvbvfQRzRw%PtjH-Xg|YO;y;$pqsVd41U$`#k>BOq#(LL|Uq}=U zmh`yidx?sHDNR&?O$DW`<@O;wjegteicQ-2c2V|W zIL++vyM2E{(|p^rmGWSExlY&ZsH42qscN%z5&3@20ej_qt0n7rDf_a}kqI66YRP9& z&%-{<-AbCo!iB8Pa&D_~;JDnFo;k->D#l1Q)D#tTwSqU`PFH*OsRSmIpv0xns>a-3RZf zy+`SU1`-1621t{oYuK0kV>|8`#*3yLU&A~yM{`aB*QkoFLD3GZ(Nm|)u)Ft~enIv? z$`?{#z^2#q&rvyP>rz^^YZT^}w`FQGYwOhCKFG4ASC;k5P(9A&NFG@0MPb))iTDtr zhW1+#TdM5w!qV2do;t-!eEUFmI^meODAnOX&LjPxx&=?CR4 zqe!H6%IT|3qX&XwP%B;frIgo;kzki!9L z6A2NC4Lu_N;g!DKUg$e2-=nq|vdRuw5#bnKQp&Bb48GoBOP5>>aL zmS?Xcr~5|hL;vaWqwm~lypP_*_{dX&#jRlQ2y05DXHnmouOJ&b74av<7^3G(9F-TGqN*sf!eE*@SOR+gF$QtdkT)S z;b88WS>eSJifTtBmaXqH*|ubM`0q}^=2MSvGdcYCs$@pDnI3H@{$J_WV3ABbBSaUT|L2DCx2! zJhEW-y;-j<3J$DDjx1c%lzjBmT4za9>pbthkFRfjh}B*A>Fo-&HzXeeN`y z9)G8|Vq^HJhfYhiHQyO7{(3cR(wsXp4OebIvVG^(zkk2E_RC5O!z}~X|Jq%@@cyog zZKZeSJx|!G+g5VfXwyB(!b9fMe*1X+#j|)(>c=bQKKG{WZ#9<={5u-ma~G;XPi(_U zmD3w9%(~#T$#MRBjqIo_gB7Z$5)4#gvh+ zhXz$Q3v}>Ry4b&Hn%&oPhiVyr_yyN3B`*jzn{>v|ebQOu;MteYuAu<>%#TBqd;HQ$;Dxx%k}SGc$Km)~#e%$2-5`&z=5J(+j4Hvehj(Y7mW zaMO)h7hSijUN5=2_u0*L9fvH$wLV6z_qn^Urd8h}up~6I=Qc)k{@VkmXXfoZy&(o@ zU{2$F3k{j5T&H^~*Ia=HtP7l_@KO1}^B$jBz80&wX5X8`Wsm(QHq@Iv`i-I-FCI7;U{7~uTUO% zYaufA*T^Mv?rPj!xz&2hN{cD0XK!DAe^>1F(!0N0JY;5h{o^0LK6hmHw;1^a{(TtPQa?#tW0_sOMo?#r)w zxVtE&eJa$KI`X@{<~vtnWbkUdj`I@gy%<-q;U?+JUJ8#=G)#_}+0=}bn%lf@47G3=H zOg=F6kfYmmpH4|{UP_jF8##IAc|@1LoZYr%7g|Yk)i#sWnTqRLC02;ay(_SC`ejW8 z@bj;iBZBALP&~ELY}UD57q0$(Z#q`;=Atjd{CwSE%h1%)+tB_pd_TW8qvKGqA8-R| z$tgj++o3S{bt$K%7!35tUruhgP=DdTvfF(5vz9?OH)~dMRyki$fL+{e{dUQ`;UI_I~tCvh{}9oArE-l@Ilo(2qL*Y|{cVo`&Nu}0nzbFXtHG4<*~k3u`-3<)?f3V&E2NI)E=NmlGdOrP{J7%Zm*bQrS1gTU zo`Yk%;+>)A*UK=tses@nxqa6;7~Fo@ePhYZ-M2RGVV=If`?lET1VfLuU&&&d?trnq z#qzfgSIgko9=LSLr%62Z?~8Ul_mnyF-zti!p1e8T1sK{`LniX=?j?~$?oCfG1JRb# zDsty7sVGGE;g7<=ov%?OY3_ZFs_|ZPz1I~aS}o-^EytZ{l$#fpyX==$?@WNhnV4!F zf45C25P$EhIq2*4nSad!qtP-{Y}4I!9WC3Gh&L9(TqCA(=Jj+(joWhhlUHmwC2>zR zT$AtoMTgZMpL*1OP@SducGjgMS1n_j{J3?KB3>43PUOdmSr-0(;q7J?F0Ac}o8MLX z%dy<0v(B0rA)dbtE@J2hm4K2N-lQx)EkR! zJX3$+;3ApG?A^e`z`Z5k?|TjGdTN{R<8a;f=9T%Hg>u}w*%4vS3(_~%S-3qf zp=S|i^M8EB(4GHd4}u@J;J=>n4^pv!D&_nCoE;1Ppb4Fr%>Q4MfrF?erv;RA!2Ue& z>lj|(H(}u0An^ZhIP~-W@Kv-e0mE+n?LuJi;p6$={%^xS0>!{t16ObGg8}{7g3Dad z0lgCR7VqS>RB-SlI0&ARj1BsvwC6~3kV4}O@rw&K+4;ZNd{70wdWHF|`5~;=gw0|} zum4p1bo@wq`>oS&*JdAn(50rYo`*{N^@+@sIpU|7mvfElJmjyK{%&({e#7#MH>MYO z?YZi%63`b++nq5-w>yf!)v_q3vorU^r?PxvwFvjG-;2t#C|t9n#77y^ruDppf-bK7 zC6Uu#+uArc=^6EQtX&VKgO@|K>lfvah`q7AKVNXpvpCz<)z75%dGRQX7cZ5tnfJ~G zwP-b#dj_|uh4()W)TK61v|~U2!j|+pgY&D(Tw#%%Q^i>v6#lL|OV-Y<<3;)Ygclk0 zFBkeLDK)qgJN$YmUET5fZ@!@NOsoF!%@4j;O9oFUq$Wl`$lTkj7c3HA!#qP@P>WWN zdEl$XE0sUppDkMz64O^hTf4tFiS+d1dFJe*$zK(VL|FTA-oaQ>iA}e%GDW{U$F5i{ zAg^Ao`Qi2=8nN)RtDgB)7h;=?M>^-W8hK4j06DD6zj=mIG&(ID$4PBzyp(hueO9^N zr(Zj-vpo2diIqfrNSCHBB`-exQ(S0$m$Ch-K@|nJV$rVUi`p!hCusP!gL9~@U@at-5J6wr?mVQ-h9cgGDImjyVWk>Tn4X( znMt2tME7{nJe8G$*XYow{49q{EOtoyBC4LWje69AQx)b>d5d?kIArk4)%XgZ%5|xo zr24z|8`Ec`urpdZ48v3h+0WD5iE^DfjI>)8Ym};jQsRRpLz!1&YSf9n?%~}TIQG5I zt(4wRHUwR&Pikv`|9d&vb7%^!a0QQ1N1*;qFOkfzWS0=T9LgvIW%2H|c-Kcfy4@k4 zY^8uR%Jr9ZY6epxV{+&*FDpLlvg--97;BvOCC;?_>7jPdYzC^zYrgjP@x&=TrVGkj z`j1pHa&hO;q@eJc=K8H+Dx8`2SHrzdbCj9d^ja1Vz_B~|RE=SR1N9NNOm@;k~sL+LmiguYJKy-8F`;;9B% zgof%C_NdV_YFlzb%oBWCs2;>7&3jZ5@sE&{v<_|tH|2Km6=D-!dQ%`NFOYRf2HnuE zL1lz?C=c9Z`YcR|jQubtmEm_w)V^JsS%Qgi2~MNi9cDKr$Klw~kDBGEH!RHKx!u`` z*QFEt%KS?Q&*4A6z8As8Ep`oD5)2%yBd7m)b0&#FuHw+7E2=tO(!Q*ZrihUvnm1aQ zCT$as=~_pB65#L~g{+%YBI^09Ib{uXN=*d!AaC&^yJp!o3?;qDzIzsddwnsjyK)NU zFumO0IyNa@o$|3(HCy2ZroKPvQMV(0U#vpBHZ5I~m98?tM(^+JMt_iW*;k;wL172R z4mT*B*>OervD+;fb5(iw?mVv@v3^aBVbAGR?5FSV*Lpta-p%Xlvq@DjP>LbC#rCsy z5NgSnf*Vp!siNH!(kM00iB#$n4!wi8mDgEsk=(4(bfxJS?ZJM=ZJ7s)YwNm;=$JPx zCo%e@Y+NVn5B%P9=_T>B^pcP-ddX{8c(NXQ+?Y2x&PozilR0{8F(kJ%cu?!dOld3vT6Z6IF$*%iXX!S}5C9U$k$8d7F&r7$6 zbxEBP%Sgb~tA~~cBn0@zhE*Q4Ka1<`JS?jgcHg&waVoSUPD&%Z7*isTrq9tXWYlE- z9&)Rz2)npaKGtg!wT1VT)WduA33J$Saiz>)qStlPHg?HemhsD{r0JLnr6_Ywa>zl> zd5&X8GEGO7>kw<7N2)Atspw16>)AQ!b)o z`jyh|svj+3=YJ${SM`N`Za>Fy{JMf=Q2zA%1#?b#$UzOXv{O*iVxMe_zQ1(`MTsw5 zO6~i?D&5H~T1~ob)VbO&cYA2(VsCSw;!B-_R+LDRz3wF~I?(^5YVd3znd@VphU*9r z|AeR9k+BDgt%$qAeo89!#f5~0C}3qPG-Ay;*p!Fc6y6kVh&kB&XJMb&JsEVp89M9& zI&AuzxLLI^?Q%JOT3yRyo_tbWlifY=K=*#-o{G3xneB^c;=z)}zUR5>^MZ^t@K0Q( zD^+OTdS5h4eqakJySOimf#pe1*7;uQTH|}A@?$G7^5?w^{qTJWeZiN+Un#u!(984H z2jMa5obJ+B*FAc*b*X#G9V&`uk?(!dY%3QLba)o=;fW?Eo|9H@ao^d&{K55As<+;A ztyit9wxt-n=OJj%AagZ``^`+vQC3tWuZ`#5WD5(XiZMk*m{38mX0 zglHtRy3Hm@H|a9f)Ld8*QbR@PGDIq=kaVA+t4fVhP3bb4nVM>*rkQDO&F%kAWtaWh z-*IIp?{Y^E~f+-sd`M@#koh(lA&g@JyO?u|;ofFIn)Uj8z)% z`IF=>_{mo6$O0U#*)lRC@wUeYqpq9~cF!c%3XeI28mZ(3fUEa>jqTt%$%uBEF{+)@^BhA1R&y#hBtVdUl z!CV9^cd)BiSXya~=(cRe^A9e9@~@o_FZ7%^6?(0AX{pWl0$}wQq10lTnLT~_vDLzj zv^xHc^RI}x=uXQ;F8nwtq!SPr*TsrmoiA>=?Tf^aC@AMNAhItLBjX8b`vfdo%!z`; zEAE8nJ%gU-o^fF>$$R`=G^;QzP7J|VIc#h4nY@pFmaE*+`rP~H?}JIK&1~HrNl|U0 zoJ~N%6VilvD0@5A2sL{45w;K~ zZajmn1L=oX&3q-u=Bma-(}s|iL*}jz*(O)erHn4YkoXa?=t%e)ypAVE?#{G~HGIlQ zdtDZF1#tO$BETN><+k@S9xFB4muMc;s2v3)VMnLU1c9krITIPD*JU(;d1m+$wPqp^ zr4w-=xYNe_fMx}`O|m-K7wa+EV3@E>ErF@q>~3*)M7P<)a0M6^RgeA%e`cjjDCN%L zNzs%fri{n3B#r9zc}A>nP~{&=#G6f&NApNxzOtS83r0m4WVkmkF|-$8`$$7=7UMtLTm<9kL(y`FehJd{O} zWxrMrbPYIVbcDY|?2F0pwnUsNXBoG{oy0dy$>R_oM=G z%~5*{9fnMloOgVWY0UO1dq6?MVHA`#au6!?yB7aSJi?9-s)A0KZW_3@ouYZ53EtJ1 zyJCUpaD-;bke0DmStNA)r}G0&FpgE-4oOfCcr-Q(-Ry>MWxgE^Bx7XGF%B!hVy^K? zdD4PsMEa`2j?l-QC!{bciZSuB=L8)Q7d3W3GY-PXgkv!)%w@WKI$hj~5;qHgQ$-u8 zw>90%cEN@(jtyC)A+FK1kS%&c1Vi@25<_;fu?z*Sgbm4Vv`g zhyt<&+q8;#5S^m(oLxfp7?GknZAMTlV!KJbVzzQ#A&)UHu%+lexRuu}d_3%cJJ>Ba z*vy(EY{h&EC>Zi9q0EA7IDrN5zE?2IhW1BPF(VX>tjS=SL42R0YuHw8kWZ_|Hs@C+ zF&LIdoG62b!7S7j;EpMjvk+D_6Tgn$l?@5CEU%G{6J$~JgS{bEF;**Vl9TJmqwqVQ zewu(nC(?#IO~?KC7EDkUBQm5b0G?7VQ1P=o*C3kLHxJzD9j*xpiUW4|$5S!{35Jvz zu(+T`%z*6LTWWnqbwq2q;Nf;lz=+{kcx(PbYG}m>2h4r zhQp<{LTS0IY=UUeMQp$K5yR(sUIP_~Mb#^#wj~zXl@cpY3p_f6$DPQ0c|SD-bJei! z08+5LR+^BLN7oZ%gXAN;HgJeM(RE#MEvfqmDPk1si|nx+u_tkLK=mM#cWN}I<@pyq{KVN9bshXw`Tdo8=_a;)8)PkG zfRH7-^skV8G_CX^!+ld~&12p0X*HYoIR6ld~@)YLjZ2AU@@5H zMV2W&p^p|pa>LQX?(YpZ+Y;QyMN1w07WE1r$LB7#oH77iuBoaK`2If06-+jjqkk=z zZXfu*Uvw;qRhnBHNN!sUs~{4uNeN|YrImUJO8H>D^! z45oqE*3?5U*Ckt2+eySv&o@ZcA{+g^Gqw_j9Jxu&?((Ad*(Ia(Wlxv!K68hU$^hfq zux>?&?3eaMYcuk+uiBbi&-i5%_3VPYfKk6|`OgF1ntF`!NKE=ns>Jbi1W(>XTEEpG z&Zu3b0bX@}*os`uIS_@&R(~%jcK`I%D!v$UWtJZ|Wn5tCOfO#r-9r|5n zh!k^``))NTR=?BcBp|O{^kGP`6gRT3@>)=NU2a2gMIHa)L_^cDt|AnfjD(|_x|>+B z6Puh!p=3PgC!V@-Z^)fbqU-v8DJ}TELUblWMmwm7jLV3D+n!8Zo{LQcL7o=%?+cRsv=VRegNE*4Sg+7)&#*xbgIPQ#i}y>rI8JFOT3|=_J}j#?UrUG_ zY!vu($4TbJ1sQN}3~q+Jss{Z!PElHzLeoRc!R)|Ans(6K9$RbDcdJ2q2C-o$F<3L_ z;i$0;LQVi$syDo&vrnE2={gS>*n{2oWh4zkc`oJze{*?A9YJSD6b1Wo8L+xGOYN-K z)3e-aG`P{5r7-b70zshp=a>?ciOI0>&Y`I{8=a>7I2d<09z8mMOp%EcOZoe zB`zIQ8kvvPi<6&3s$-4U;3HLKZ<@JIH%6m$l2-^@LczSBSlh}8orVwD_5Rux@iJi3 z^sx-s?nVt92bK*SUQGv72jyb;^1GUqtVD_XNn+$jJ$YPTkvtxsG@cT}E8uUgE;N)W z^?5)p<&q>dqn^KfniZ)UEHaK%74Cv@{LW>Ib2|Nhm7u^Ug5&d;l~3m<>!SG(nk*75 zzR?imkANLD`J{K;#^kipVl+Q$j)QEWk6RhAAGJrWttJCS@%t@B(a&OnR%sS?lv?1V zdxzD;DiPs?{Scs0?L=Dn%T#+Wq{X(2l1QA?cBR8Jp9FB-_$_`A{4i2kNrZn=^9ZX6 z=?!x(;aFaSHi12m@t8v-Exn3%aCcz`jwMXDbcIJNI zF_3aK$!-(Zm(Ui=Qd)A4;U4N5iwCedOkcb-+ngYFpD^SS-vC%R?gR=Wqyb1g6$zzLY!t2PuPp*_a5=*1Whl#lXP>gAXyQB}|pEucCpP82M_ zk?HdRUpdG{(zz0eyc2s>WY4x;31wdT5auQSq5q`N#a+G*lUE(ODYuT0WG^y^&SkwU z&;)gMhZD=PfW2=11cq7L&ng&v0b539=)x+rEN=Fmg)9+RxXCx?X&*w9ap|bOF@1h@ zUXf!>*D^xd&@6sXB{8e$AkV2Q?=h-y2{cW1iIo(Oi<12O7~G;)o>7%JzBBw0io1ZK z$_eircb23-@f0NTt1_Z^48Xz@zV+r$WIo*G?+#GC<6*5eO9N0| zJ)6R-(rNIC>*JpTR?-OdvW0!ms_S-Y*zmDg1W7hURTkrcREdcmKV+gqCvGvQQ$ugR zE`Jzs8FqFBEJ1p`#j48CgOE0On62)l5^xydeA1TFMHB0HUnT6u*FmymK7qTt)h7IY zC2RFELKYFM(Wx3y+K4p5UfB}4uVYBmpS(~poXW5|=w9RjTSo$LTB~_c?a0M+ z@@jm>BG#&a)K1w8dc6RXU#?R^F=Yx)V~0#Bw#qn(vV{=5UlTC)Q)#!P{((=5mbgSo zBEgKJ=a?iILV9}w<}AR2APGfRd-~$dcf{_NgHO5KqnO@+U1J5AO=_I z#U}$RoL-*F4O}gR4o@F+ba?90vXt;5jz%hhN|z9B4VlI0=P=q}jBuWwMAw2=$8fG( z&u}hikZF0k%Fp1r89ovLZQ?}oHJa!o+e)*=s3);ZcH2CkMOtXYzZ>k zB|*NP1u%(h1*Njm;QpNe=2&VAH<^T@ln%gt19qMn+#kj*Mn0Dw2w#nlv|>fLl9E!T zWwXjN6cq2fi9Q7mLF7g<*R^=w2;BksAX?I5^_jU9CzK5xwSATSw#mA5>xj)TKGNdQ zgqFri{~JD8I40;7{VjDCozxy=e_ct{Lh@@LY1JM2)6WvS_n>^aWrW7Be9T-JtLLCs zKFlGw5dv^d#C53D3w;N3GH@d?v09SvoY>6WNl)F6+uFNXB+?~)LX^CMtuEoKJT7!o z3%NBIX$6Su#*uZ^RAmT%+a3CRck=0C{64%USv};;=O}5}4Z#yQuuAN3Cxrcy?7kO& z9>0a}mPp@sDOG?NK_kja_(6xU(P3dDYI)0~A+~ym{jy&O8v`6ou*p&zN>{i!vZJq( z(mi;SvNVTKoBk0o{7yapeZDpQSyy~h*^wftB{@@eFX~*8N4oPzM35=Omi}@m*(8M? zgQE9~C}ki61l4&vuuQG=Z&KNW?7w_R^H||-Qcmrx#(w0jW_$cvsU;$HW{Wy!w#G0y zu}n%KA&^%j!E2%QJ~?D7biK_^#L_|YZ9Rtd@(&tz#KSyj9{ov;3c8Zk{ZX=XF!4$d zct`Kf@M}SXTXPq1&MdSoj5a^^1j8X-g5ZQlHRHm_i84%F6eoErM8qWVkneFwYiVkE zX=>s_q~R^&hx*)`Lq<*2Z!A?tFFh8{?5jd60fKjdGsCh!`yToTcl{y4IM?_LmbxLLXfIfu4c z8?ohG?USqhkGxTqt3rdwi`63fzX{=w4Zcf3J6de&uC~A)=kaXbHC;FW?^WLTw1I z%D-g}m_j!tFJcFeT5cV>*tB_(W#18WQzn-*l1blO6KDNFxI6V|z2U*Y#jxw4F}&(6 zc;h_#U>K!)8Nfgk8p{nB-WHv>smkt=`2&|^*YTg`YGXFC=a-_c#gBwk#vq&P&Z(i+g_M3W zHykqM)SodsIeu60GG%=UxgROfuJ~CjXF88 zm-4?FIjWZZF+|1>>i*2=boj-=hu{RgPWdo^hj!Uqo}dTe+zw3*)!_^a$eD*5#PEs)t_376gc7TZZO~ez1KVKcCPsqJJ6Ew(Il6?9e>IyjmWV znjSv46Tj4!sYZuKHoHJ{^kSli?cak%cRS_OD`x)NMG!1 z3+|Msd5Xz{t~ie$B);-&0$vSZvDwY;V9!Vt_>P0tpxm5h~#^DuZslC_mvyJd@7F0))iD?a&YB%bo6-I|20QPFo^weAD z(^Bo|yc=_Hv=t44-6rK2!!vn#TNA_grCD1%(i&a?I8vKKtxX=09PXFnj|S~)#C`~0 z)yT(rYZy-L%U>$_I@?(f)oFsAhSyx{-Q)6DudQVNOXwa}d8!?QcjL4k^C<+l6h3ak z8kPQ*p}#Ht+^xQiBgUU?OsJINm1(&@eqKn_{Wd3h+w` zkoxh!wY6E-2{1N_S%$9Vpfq~OdjSWux~K`6Qux}|xr}v@e8JqFhH`3mnD9*@r^%X^ z(+}CmFhfQdwG;69oP~J2O2s`3@~$e2_ZP?)&2#8v77FrQA0gBw(&)?Ydy>L^a)?TP ztxLy{a;q%E2QnQH#Xl3ip6*XVVy$S=>*24r<-TquFB=WR*@tB60_J4cuRWL%SU+N8 zgXOdHYk|SE;>K#R8sfusglExP(H|H{d{O)CRp$O#631c4rr|YOus65`WkqP%IdtFU z8ab}_G*uU{9T7PC+s^Q=;}=yT6iLkW1WS0XJptU=Jh3R}OXKkG&Ekx@jQFJ((tgbX^W|){1-^ej9*N5UTozt7E*sUcq`KjQ78^=!LlXK*aTfs#FU%5ff=;`J=WPZh zh=3a%n#brFzPPIf5p`o~tWxHRX26RI+=A)bXFl(9yg6_S$hOt!es2hCrP_J=(;-hu zKk+^;^8Aq|O6bzuCgbe+a_DQDy@?jj?u_r0I=x|mKf{CNY+*PInR0-r4D8{WxPkjD zFTV54HaV?sU2pi*ry@hvco_U_0$sNaZYk$sde&%Avcue^mL<+PbkcEiOu9qy8ptMv z{?`XgIYVOgEWigt*KQ&(%I=4|<{X#uTu(%V|t|e0W_}*^g-r zpqvwkp1q{vV~-+aB4;(L@nmCHDZHt5u=a!;M$1{F=;i2UTXx1>NxH$6PX$44y4Z_# zCx=gE#&8Lybf4fnvzv@lS97`5-d3P>kk5nNFZv8cza^XE(q%xW{eVwknC3eL=Hk}s z=VWb&=Gb0Iplg>5HhgNiD^NW?n`}|Do<4E;5tr#5R6dXDpqP%uQMo7R{UhOTLpRb> z_Ye^$av+}>Wo3OEYtn<<_=E&c_;KXSDB-a4?elBMzI5KU{pgfZ2TYriojL-P0NqKd z=N3DDddIydj@A_YxW$4xD8c6*H{RpdMEsSMg}&}G3zia1K8~p%iBLQu;sU&U8G!}& zOv_d3TL-z+_qgosm07}i?gMgAs;dh_5-T?`*1?Y=(&_B2lyf`kRb&o0Tf4b1GNT%2e;e;1>Fmp98NOe=9(uyiFm9FrD>5w z9(CEQ`*rs?QPFrNIh}WTf?n1?47pP_niiT$vcP2A7)3NI{J%m#2Y6kFzK^#7_U+Hz zsmZ=HM0>GKjL%hJK?Y*g4}j|JMBhRB@K(2!ot`*=5LY`O#C!dZ_M)2;sk%G3lJ&`% z+BfM=&844hsIzw{Lk27wi4{aqq%L8=s$iFcs80oC5=#NMBup`_cq^b(HdnO&CA9-r*4S-DxR*NY8iOY#WT0-6xR;ZamO!C zmAy-o=0u6Cc7Ufub|K32>#W4v9xGK{L=}GE9+hu(ZGpm?X8Sn5z%4{z;Re5qZ|_g} zSi}_^Bh*gZ0=CrEouB0Hm^Q)5;L%^kJMzvV!I3~|zslk!=O*3dxU1lwfF*-xV%Vu% z;bmdDm;NI>g$T*LV^g8EV+apyd6lJxWV8F8js${BF(Iy$i^^=rDTyxa#aVztREUsr zLttT3h21oQ=*8vp=sM$ugreq9wsXI%9{2pH3{ow+9cC*LIaAg(bx&lJ^i?qGWp|q! z@b_E*A8Sk^ZI8wWNZ&_kI{hO4(zwe4h#v!fPd!i1hU`-U)6uK55}q!sD)G%=;q&wp zLw37@hAjssOiBn(vF_u?z}DmG<$FvJt2QMUgsb9XT`8>1wj77M$*Fp)B|1+d)`YiO z>=G}eX^E7hB=Am{mUJ(AcNLU@QcompN~5ce94A}fl_dKx0&wHzQDk%{gm3>7k^7{~ zI8LAC(*dU?24OfHl$Hi1NiaJyPpb0K`V;A0aQ|$_Th6Htevg@Cwq~&Hg-8{bCu+wWekNY@H+wz7ni#o@1 zgV?0P&gG{9vpT9c!A4YvGeRJ5aDG;S@#D^WIajImk4v-4VtaEWH2;~vS|ahMsM3E9 z$GSg|3C)F;{_mrFSc^V?(2 zsZ*t28AN3W5^0YE7B$8BzmKp9e-F`-j|CODemXX6cGlS?Ibs7`!?!Fw_R^po^ss1r z54ZuJGiZm287S1X80bq+)`HYokyYA~I&K3Zj<+^tt<`A5ekfXlIYZ~l69zC+Th0;S z!3nlbMNH7uMX-m0_p|`u13h5y8H}bD`bGazEK{hHgf*CUfnqz@qFovG4l^7 zJ@IwnnphUZV8omAu_esQnU?rOaI074rJSxkjP~K$Y$G0Q_0)mv0B*JF$juvxTR|Vg z0FUX1q9dS)BJd*iPYu#UmmK>O_2S(VfTo0osB~&|VlxCM(B^fQLKci9zD1wnt0))h z^O&t?sY1S@F)`QaVD zlNT(mCP`|y7M4YNc4yQ93A@9d^@@d~JH?Z!7N1^$^(F?{XrcL=p#7W=cyE5hvt@Wr z#8t=$@r)chQ3p;xfjE-mQnmM{Oc_V{Wk29-9w}@oJBU=)t0g`=2)ZR-OmVwvh*IK) z?h1FwuHD7nKUDfa_9^3z^GW}-C>X=@nqiCF9WYIz#rScj0r5GcjdSGKJNX@)K9wPR zg~VJ|4G7(8P?3Q;kf17gfsoxwenGxZfW;Z{AD&9}`dH>oy(By^4CW38f;S9^Jk04o z3W1%k7uox2k+=VCHAiYa=@whhX&|BU}p3Si1E@6?39e^~28% ziRu{J*tZG$16zXja(B9tmGzp|e-M~fmG+;>n(FtAG$}Ihqyznm#OyE7OTfaF@s(f)-{3MoZqQY zD!nY0ledXR4*R=CCD1^Gk3}Z6d+7SsFfj8p=54W!DeMY&>HF;9)LDQE}+~xM0gr^Q-}-vxSyVYIySgw;|`$`jyI?C6Ko}oGCVe zEeJP?4SDu``H;reIw;_}C)&$Lk2{Ua-qT6kAEosN9CC(NHl0+pjETly3O<)I7^zA> zUfGKc5}XFbOEnlV=WZ!o0B(#=xB}i{sAz5pDaDQDk2zyOH!i27kUFIs*w7NJot_Ej zrw>K@L3af+C_k;&#B#Q;wtVJue$|hFiKj-dN)+jj29Zs{n%ounWN|kBj2O2lvG~3A zE`?)Ab6%S2f@Jf_@t>g8X4POh#EOww#YOA6ORAq=lW^ zANA0DlMZxN0AY#VA@;rH&^e;t_;(FIk0+8F2DtpB2p0nsPZxT{v7Ts!K(8Y#jESZg zDb+Xp$W9Kb*V8W6#%$?3Vou-5PWMV(gr?aLMH+`-nuLR#4`B);f_wgsQa@C-F8-s` z&c05i{AkUiaDy~~rXCcL8`!JNfbq5GfyPzE>n1E6Ay{#Bz{TWOd56UuHY)kL{1B4F zd75snep0iAJ_i`P({RPJC20376>SNhe_zd%PF`ridG6*~kN~!lR)TJFy{>AC29IdI zd1wOk&>ETFbB^lRAa)>XWiN=X8$w)IPt>gTI+Cp1udE~TDg)eW_v7S<^g#;_#2At* z!f{RC)i^aNKdojT#qW7X&NQ!~WS4BX!V=v(9<_HoJ@tV95?G{2QwywJKZ|3Zdf-Nq zV*@mRDBN66JiwV$vo!e4al##9J}vr=OdB@N6s38UErHx2&91DxV_Gf&DyTPj_1KX639OP#gUia$$`A;O3 zI#Ae{E~d(&Cj1iZaTy-C!3J>&WEAVSbaiHoE|jw$Sl^WwT$EG-Gg$JQVRy(yC{zY0R|DteHeD9Q3m4lu zjcsds173xzv6)!0nh0r|y&8X&`|`f^nX;Tud%0ac=GVp?u1m~N)*+9KV~i(2vYvY? z_~uoFP-I-tL_lLyT9}eF&w?|X->te}XcyT`iWx(;qG*q7d7qhrO#PY2MM_MKmsT~u z5suRAy06DDGEH;eDH+`fa#&03FR)}V+3T-6EkXsF4p}4H{2lmr`(!sl3_JaG=;wDe zni;wBJ2udh$il;H?a~A$v|^DJE0h_q9J^;l zlpdwsYiBvoaZ264aCg25JJ2+mQ-D7A`9`c2qS(u2CnrAnqr-yd<|ljFp&f!u@kf*o zTIHR-t%(jKwvc95DE@^L-67?{55t7RC2ZtCJl)8jX!eGaKdPfQ7<2xt%c1anQjG$c zMvq|h-6cN@OC}5&+CBX$Aq557>pZ9ts0%0 zi(O3BN{>->fak69;hy65RP_g{wjD3(BYfoobKn+%ZFCvC*!IOlUYl8$-N^9U5%0F1 zjub#yq4S@7&bvxpEQiEypEyQ2S(T^H-dK}Jdl>E~KS70^sU2Ds#i@J*21(HK>kw}q z`K01s8g3{S$6-%yV(-;YU}L?PwqGLCSz<1g20&c&bCS;Ocza~Nvs;5 zuZ6#0B|ASJVI`CQG`_v(1S4=fgM{1@Lt+58XFldKOL|XnnT{BDN#zJXMg5V1(vZ^l zPPqCl!_NwJ*{G|8D^C+VW^%hCj`Br0mjiB@_w3zf@BsGtSJceRP{qvm*hO`UTL_~+ zi#3~w;-`QTU!+8Vx(5;ZBGGgsW=}v03m;&$9%d2e;vxpsS z(`(~3Nn>9GF-M{qf~&$)D7UNn4@`b+0S)(M2nvgO21LB%ap9A2+wdRoS7-6^f>2v6 zAeV3sYk_;CnX%j3de$ayZaW!o?#5~kQ~Cg6H^M1QlyXGiBhpA&s72wb@Hvn%Tej8% zOdLdPSM7kN9*+T0kT1bq6A`j?fSW9zyp5|1;Ckt@DLK)LOPV&>es?MGQ9nsxh{SW@bFZ{J#=td zV??9+u*j&|dTvyWO?Z>ctxg+~- zOr5b+*&1NVhT8mJvCfE-9U2=UiNnJMffI2aX}p}kMY5YA0MqrZIro+F>Lp2g%^~J4oAs*`&rax&koCj`CcFI z9PdL7zH*vZ*g^INcJxQOBWNyHgReKsZu+i>Dsa|fDk=#1Ql5gNh5wSt7fcb6YTlxvF-Y7`@mAe~* zDuC9S`M6MJiP(Gr!t^tjj|Lw9){+TkwYRX<)VMD%wcOU7!86}a%p}tC z+QVphCv1QXqL_twOIG$br@dT>m6T%&MX5A!ZWdef5uJ3_oV;!*e!P8eSuGq~KsO!# zsA0#g;Y0Iy{6%W~G2%+tC;oLl<@mfu_z-SWhB0Da*6XPR9+r|z?ess1Y z&U&v#;?7JV5#OVamHJ=AN$S$_I;nNolzy#bCd_4NEWK`isjF=G2G*nwyFo(~GlDU0 zGriqMPwGLxr(3pSHp-wpu%@$=@KL!9+>L(Gn>ec82JR^@oXCi%O_bEsN~^RWG5TVM zY6o`N1O`S~7ThypPhGmk@Q~21KckJs`S=u4A1~<`1`Cab`rMOV&+$i&el&EDpRq4^ zlo1@eZ@hivrN}3=Gc4*9{hDQQk2UbTo zZqinOcwsa0W-SsOTxXB~_*rSBbpbEeVry#yyM1#ii*ER&hdEZ<(;7_}yo5^}Iw%2YD(4rlyMse3Rtlt7<{wQ*YI!4%uJPv$9;j+EK^+x*;bp!MDU>S0_9@(2Q5>V^l z=M4DY-iy3-JZVnah&hlZ@x1l&vKYbo)LM?>5Shd+m$$`?47m%+7At?UJ|YiMqd(k} z!Hz>;iPLKpg4!UfL`t4ywIT3v!GTwhQ%HLkUR#CXfYp8Dv*-r_3IYb$$w~$;yM_k6 zSfFk3O5G>fzmPb8JMJ(Y=Npz($(Yb{^%QxR&@9!(9&Z~X*kqJ=EN|>PM zmPG{aa6jfI#c6Y%SBE9eJdk%s5gdPA~gyA6cx{7#k}nv|ll-H-u7aeszbzQ~rFi}r5^L#n1$>IR?5ia7sv|BZW-eUQZ z2TPWxaSsOLuQjQu+M)?td#@JOu}X*DQvDp?x!)vP^RPiPmk4YFIDm!~SduThsL`XX z1^K~j)OoQp8l+CFFc`t)w%2_?v@9KZ*xGzV->h~bapGW!rg6LE&f%;)oswbwYnBNU z2fZ^o>8L{S8qBfx4JU1-m^qjuG&Q!!nSTw9s~pwmKhK3PaP$N7f+P`}Z-?sxvsk^) zbq+8UQAcr2!i0&t`V>ejNpGM=igYHZWRNS+% z2xhPl?1a2u#-FqvUWOW*s6Jdr+!!t&Q(>Zp%WPi^H$-cS89v;XI7Zhs!_V(x7dJ7{ z8*`F^d!BWnb3XOmbc8)k(hQ7mj!t>9o$+?!wnXGz+Q9O{y#rnXL>L;K_*`$m;D0S08Rj&TKdGo=eCv*2W99zsfbSXG@q2mA$}># zN$gSr446wI&N7PGAeQqGL%T;6XJP?@W(%(6b$NFy() zJsBBlv8DOZImgycU`2xQGuZP{kro4_`!IOpyD>x_#a{!YqUxtz6Y>q2A)D7{&ZH!-uY7;$+NEx#e9+?s z8yC!AFWCOlta=K+AAtXRZ>XLeJy7>6@KGd1UlhSZC33m^N8ncxJxp;_eFFFo{LE04 z%?3U);QyE5D(a=ce+8T(uYmai4`e5UH3@(hO)0LrFc7^Dakrbs_e{rrCUS-}4< zm->QI5$RG95KciV1)H?g{4wKyb>hEv`jUz+|IU&gz@MJ4D*ia}zhoNa|MJBDo@ubZ zd*Z)(y1jxZzn}Qu5ByQg=&!&>!3l4EvSik-t$(vKH|)1nPZ40I7Sj7#<@=3kYtSlJo;PpRYRiJEi~HVIz<+qx1Cq)8_q+zJ+mmJT3*?>Vks|${ ze|~k5IrE45>Y1U#PQY8sze;aw|Cq-mU(4QMw_uMzFE`&wFz zse-_N_3i{Epl+A1uRRdfI%c}Lg06r4&V>0u?WEuz^PhTk!mDX8MHExzFWn9DQcR;F zDLCjqQY)YczHshW%8NaX>Kjfu4L~~q?Z2dNl-HJNME^~B*?x7w|516>{?(OIr!lMm z`Z1*@6%6`4QkDGOYke8ffBq8QPf6?splY@vG_~)ch35vRw4`F5r{aoEMIoQ8o(yF- zRl2?IkGcJwNd^I+-2l=r=^GuI4L~UP00>h3B>?9kci+{=0^QC9`A)uOV=_+JpUV`u zz6ve={V?S->!)z>0jA0Q0r zQs8Hg#~C-cZ}6_*;6Sfa@L-QgzPDHCMXz(g_KGP}gh}`G2n3?r0(r$(^E8%qKpw!N z=-GjDX8{ooo?KcL((tDU;|e*L+c0%){V6Dgc0KA5ak*|f5e6VpN6^(U|$7&woCyk=(Ep1(8Dv(AAauiB(I)#3-kc!qL}0F zTo@?*Be?>|z9^u%hXERUO-k}42@zk_DNoiNbMx_-tin!LDOOn}`?GGR0dBLJ`Et5c z@g5o_E4c3@56%VtOy_~%=6_rn$ZaQ1Nw*%5pO)?mUL87$HdBJ;=6C*U*;b7DkeBD# z;6;sp^!gjoE^bl)Po-}}`w9T@uSI*tUAL{10&{^KX)&33z*JxIrh6LqKM`%w;jD{? zs;qbJvAEtePpips=bnwUr$1LBb$9*@3`No7er%+}MJapA<9NvsOYzh@pzWBo@g2O#?1YBB`i{P!#e1}0g70)Y zG>T{av`RsRscjapR?V0>MM1@EO|DbLU-jx661oA{zNBwRXwdq9pfq->+Af@2o@T_R zs7+=*pIA0Vh@8g#PuRmrx2DbJn{}$~wPo2`V4doi#`;f{#@M7*`wTc4rqtVv8DkJl zV0!*N%N?7>@-J8p1)%sVmb?2^-Je)4ce?6ZmTR6a{ktqz)H%g+YXMG~RvK0|;Xf(( z#$I84PMJAhm`+hXRbtrwN2kBxIx_&_m-G$SJq95D^-}d05w9nvasLyp^V-34`S6Y5 z^C`dslLQz(wxp>Z|0AyZmf5D4zcEMxfb-vTUHvqc{~g!O>X^FrSJuGaGkkVU1O79^ z2lH3Zf6MSGn1=ppsDH!o8U4?p{yoFT0AODQeYOCfX~XoN8$J#IvcHn-z_055B-vM| ztG<=&?CH|KE7|0pDaqCaMl$D%tLRV%TSGzq8TN~TuP>#5cu#x2>=kt?pZ@6f@3MUc zs4$qWP?%furY*8RR-+?l{m*$G{0jEZwq6U+qc7%06;c>(tB|J)q~>>oSb-?zNC{uA9EuQO-hLH}%Y9{_ZU z(QO7k)3Y^=?!OIga+b8arv|ro3Vb2(^OZsWUk5m~lmb9sWrC(renmg~^B(SSU$?-2 zgfAb!_b2*&!S~;W2z2ZNWON#S)6@`^fgk&`Ugx~dc?1Rh1?f$U6-j&NW`IW(m)z}i+TKevj^X8_Qj<6y;<#dn|;|g|K4o$yUo7rb$)NQl>EJs-q+r(s~F_(LpJ|z zv;AxRj3%3X8M6C#n=L+RYB<^K%aE^sx7n9lb%(wT`Neme?S(FyH;L`bkUPHHtT=y{ zf_gI!eHpU+yUo6=55Hej0rPv>^ZEDejM)lDU63(Q@@0tw+;M@EPQgDKeJ?jrmaY8Z qUF(?_A>vCKg0<*BwHeg-Q^Ehoze2|w-o3m|G;?Sk6^>o6^8WxG3$}Ow literal 0 HcmV?d00001 From 3685791e0d05623c68d990ba3878f17d99888211 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 4 Feb 2018 22:15:18 +0100 Subject: [PATCH 025/183] closes https://github.com/assimp/assimp/issues/1729: check for bit flip when unsigned int overflow happens in x-file parsing. --- code/XFileParser.cpp | 241 ++++++++++++++++++++++--------------------- code/XFileParser.h | 59 +++++------ 2 files changed, 151 insertions(+), 149 deletions(-) diff --git a/code/XFileParser.cpp b/code/XFileParser.cpp index c7efb83c4..34d8c89f9 100644 --- a/code/XFileParser.cpp +++ b/code/XFileParser.cpp @@ -87,59 +87,60 @@ static void dummy_free (void* /*opaque*/, void* address) { // ------------------------------------------------------------------------------------------------ // Constructor. Creates a data structure out of the XFile given in the memory block. XFileParser::XFileParser( const std::vector& pBuffer) -{ - mMajorVersion = mMinorVersion = 0; - mIsBinaryFormat = false; - mBinaryNumCount = 0; - P = End = NULL; - mLineNumber = 0; - mScene = NULL; - +: mMajorVersion( 0 ) +, mMinorVersion( 0 ) +, mIsBinaryFormat( false ) +, mBinaryNumCount( 0 ) +, mP( nullptr ) +, mEnd( nullptr ) +, mLineNumber( 0 ) +, mScene( nullptr ) { // vector to store uncompressed file for INFLATE'd X files std::vector uncompressed; // set up memory pointers - P = &pBuffer.front(); - End = P + pBuffer.size() - 1; + mP = &pBuffer.front(); + mEnd = mP + pBuffer.size() - 1; // check header - if( strncmp( P, "xof ", 4) != 0) - throw DeadlyImportError( "Header mismatch, file is not an XFile."); + if ( 0 != strncmp( mP, "xof ", 4 ) ) { + throw DeadlyImportError( "Header mismatch, file is not an XFile." ); + } // read version. It comes in a four byte format such as "0302" - mMajorVersion = (unsigned int)(P[4] - 48) * 10 + (unsigned int)(P[5] - 48); - mMinorVersion = (unsigned int)(P[6] - 48) * 10 + (unsigned int)(P[7] - 48); + mMajorVersion = (unsigned int)(mP[4] - 48) * 10 + (unsigned int)(mP[5] - 48); + mMinorVersion = (unsigned int)(mP[6] - 48) * 10 + (unsigned int)(mP[7] - 48); bool compressed = false; // txt - pure ASCII text format - if( strncmp( P + 8, "txt ", 4) == 0) + if( strncmp( mP + 8, "txt ", 4) == 0) mIsBinaryFormat = false; // bin - Binary format - else if( strncmp( P + 8, "bin ", 4) == 0) + else if( strncmp( mP + 8, "bin ", 4) == 0) mIsBinaryFormat = true; // tzip - Inflate compressed text format - else if( strncmp( P + 8, "tzip", 4) == 0) + else if( strncmp( mP + 8, "tzip", 4) == 0) { mIsBinaryFormat = false; compressed = true; } // bzip - Inflate compressed binary format - else if( strncmp( P + 8, "bzip", 4) == 0) + else if( strncmp( mP + 8, "bzip", 4) == 0) { mIsBinaryFormat = true; compressed = true; } else ThrowException( format() << "Unsupported xfile format '" << - P[8] << P[9] << P[10] << P[11] << "'"); + mP[8] << mP[9] << mP[10] << mP[11] << "'"); // float size - mBinaryFloatSize = (unsigned int)(P[12] - 48) * 1000 - + (unsigned int)(P[13] - 48) * 100 - + (unsigned int)(P[14] - 48) * 10 - + (unsigned int)(P[15] - 48); + mBinaryFloatSize = (unsigned int)(mP[12] - 48) * 1000 + + (unsigned int)(mP[13] - 48) * 100 + + (unsigned int)(mP[14] - 48) * 10 + + (unsigned int)(mP[15] - 48); if( mBinaryFloatSize != 32 && mBinaryFloatSize != 64) ThrowException( format() << "Unknown float size " << mBinaryFloatSize << " specified in xfile header." ); @@ -147,7 +148,7 @@ XFileParser::XFileParser( const std::vector& pBuffer) // The x format specifies size in bits, but we work in bytes mBinaryFloatSize /= 8; - P += 16; + mP += 16; // If this is a compressed X file, apply the inflate algorithm to it if (compressed) @@ -186,13 +187,13 @@ XFileParser::XFileParser( const std::vector& pBuffer) ::inflateInit2(&stream, -MAX_WBITS); // skip unknown data (checksum, flags?) - P += 6; + mP += 6; // First find out how much storage we'll need. Count sections. - const char* P1 = P; + const char* P1 = mP; unsigned int est_out = 0; - while (P1 + 3 < End) + while (P1 + 3 < mEnd) { // read next offset uint16_t ofs = *((uint16_t*)P1); @@ -216,18 +217,18 @@ XFileParser::XFileParser( const std::vector& pBuffer) // Allocate storage and terminating zero and do the actual uncompressing uncompressed.resize(est_out + 1); char* out = &uncompressed.front(); - while (P + 3 < End) + while (mP + 3 < mEnd) { - uint16_t ofs = *((uint16_t*)P); + uint16_t ofs = *((uint16_t*)mP); AI_SWAP2(ofs); - P += 4; + mP += 4; - if (P + ofs > End + 2) { + if (mP + ofs > mEnd + 2) { throw DeadlyImportError("X: Unexpected EOF in compressed chunk"); } // push data to the stream - stream.next_in = (Bytef*)P; + stream.next_in = (Bytef*)mP; stream.avail_in = ofs; stream.next_out = (Bytef*)out; stream.avail_out = MSZIP_BLOCK; @@ -242,15 +243,15 @@ XFileParser::XFileParser( const std::vector& pBuffer) // and advance to the next offset out += MSZIP_BLOCK - stream.avail_out; - P += ofs; + mP += ofs; } // terminate zlib ::inflateEnd(&stream); // ok, update pointers to point to the uncompressed file data - P = &uncompressed[0]; - End = out; + mP = &uncompressed[0]; + mEnd = out; // FIXME: we don't need the compressed data anymore, could release // it already for better memory usage. Consider breaking const-co. @@ -647,8 +648,8 @@ void XFileParser::ParseDataObjectMeshVertexColors( Mesh* pMesh) if( !mIsBinaryFormat) { FindNextNoneWhiteSpace(); - if( *P == ';' || *P == ',') - P++; + if( *mP == ';' || *mP == ',') + mP++; } } @@ -678,8 +679,8 @@ void XFileParser::ParseDataObjectMeshMaterialList( Mesh* pMesh) // commented out version check, as version 03.03 exported from blender also has 2 semicolons if( !mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2) { - if(P < End && *P == ';') - ++P; + if(mP < mEnd && *mP == ';') + ++mP; } // if there was only a single material index, replicate it on all faces @@ -1029,12 +1030,12 @@ void XFileParser::TestForSeparator() return; FindNextNoneWhiteSpace(); - if( P >= End) + if( mP >= mEnd) return; // test and skip - if( *P == ';' || *P == ',') - P++; + if( *mP == ';' || *mP == ',') + mP++; } // ------------------------------------------------------------------------------------------------ @@ -1062,46 +1063,54 @@ std::string XFileParser::GetNextToken() // in binary mode it will only return NAME and STRING token // and (correctly) skip over other tokens. - if( End - P < 2) return s; + if( mEnd - mP < 2) return s; unsigned int tok = ReadBinWord(); unsigned int len; // standalone tokens switch( tok) { - case 1: - // name token - if( End - P < 4) return s; - len = ReadBinDWord(); - if( End - P < int(len)) return s; - s = std::string(P, len); - P += len; + case 1: { + // name token + if ( mEnd - mP < 4 ) return s; + len = ReadBinDWord(); + const int bounds( mEnd - mP ); + const int iLen( len ); + if ( iLen < 0 ) { + return s; + } + if ( bounds < iLen ) { + return s; + } + s = std::string( mP, len ); + mP += len; + } return s; case 2: // string token - if( End - P < 4) return s; + if( mEnd - mP < 4) return s; len = ReadBinDWord(); - if( End - P < int(len)) return s; - s = std::string(P, len); - P += (len + 2); + if( mEnd - mP < int(len)) return s; + s = std::string(mP, len); + mP += (len + 2); return s; case 3: // integer token - P += 4; + mP += 4; return ""; case 5: // GUID token - P += 16; + mP += 16; return ""; case 6: - if( End - P < 4) return s; + if( mEnd - mP < 4) return s; len = ReadBinDWord(); - P += (len * 4); + mP += (len * 4); return ""; case 7: - if( End - P < 4) return s; + if( mEnd - mP < 4) return s; len = ReadBinDWord(); - P += (len * mBinaryFloatSize); + mP += (len * mBinaryFloatSize); return ""; case 0x0a: return "{"; @@ -1159,19 +1168,19 @@ std::string XFileParser::GetNextToken() else { FindNextNoneWhiteSpace(); - if( P >= End) + if( mP >= mEnd) return s; - while( (P < End) && !isspace( (unsigned char) *P)) + while( (mP < mEnd) && !isspace( (unsigned char) *mP)) { // either keep token delimiters when already holding a token, or return if first valid char - if( *P == ';' || *P == '}' || *P == '{' || *P == ',') + if( *mP == ';' || *mP == '}' || *mP == '{' || *mP == ',') { if( !s.size()) - s.append( P++, 1); + s.append( mP++, 1); break; // stop for delimiter } - s.append( P++, 1); + s.append( mP++, 1); } } return s; @@ -1186,18 +1195,18 @@ void XFileParser::FindNextNoneWhiteSpace() bool running = true; while( running ) { - while( P < End && isspace( (unsigned char) *P)) + while( mP < mEnd && isspace( (unsigned char) *mP)) { - if( *P == '\n') + if( *mP == '\n') mLineNumber++; - ++P; + ++mP; } - if( P >= End) + if( mP >= mEnd) return; // check if this is a comment - if( (P[0] == '/' && P[1] == '/') || P[0] == '#') + if( (mP[0] == '/' && mP[1] == '/') || mP[0] == '#') ReadUntilEndOfLine(); else break; @@ -1214,22 +1223,22 @@ void XFileParser::GetNextTokenAsString( std::string& poString) } FindNextNoneWhiteSpace(); - if( P >= End) + if( mP >= mEnd) ThrowException( "Unexpected end of file while parsing string"); - if( *P != '"') + if( *mP != '"') ThrowException( "Expected quotation mark."); - ++P; + ++mP; - while( P < End && *P != '"') - poString.append( P++, 1); + while( mP < mEnd && *mP != '"') + poString.append( mP++, 1); - if( P >= End-1) + if( mP >= mEnd-1) ThrowException( "Unexpected end of file while parsing string"); - if( P[1] != ';' || P[0] != '"') + if( mP[1] != ';' || mP[0] != '"') ThrowException( "Expected quotation mark and semicolon at the end of a string."); - P+=2; + mP+=2; } // ------------------------------------------------------------------------------------------------ @@ -1238,35 +1247,35 @@ void XFileParser::ReadUntilEndOfLine() if( mIsBinaryFormat) return; - while( P < End) + while( mP < mEnd) { - if( *P == '\n' || *P == '\r') + if( *mP == '\n' || *mP == '\r') { - ++P; mLineNumber++; + ++mP; mLineNumber++; return; } - ++P; + ++mP; } } // ------------------------------------------------------------------------------------------------ unsigned short XFileParser::ReadBinWord() { - ai_assert(End - P >= 2); - const unsigned char* q = (const unsigned char*) P; + ai_assert(mEnd - mP >= 2); + const unsigned char* q = (const unsigned char*) mP; unsigned short tmp = q[0] | (q[1] << 8); - P += 2; + mP += 2; return tmp; } // ------------------------------------------------------------------------------------------------ -unsigned int XFileParser::ReadBinDWord() -{ - ai_assert(End - P >= 4); - const unsigned char* q = (const unsigned char*) P; +unsigned int XFileParser::ReadBinDWord() { + ai_assert(mEnd - mP >= 4); + + const unsigned char* q = (const unsigned char*) mP; unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24); - P += 4; + mP += 4; return tmp; } @@ -1275,20 +1284,20 @@ unsigned int XFileParser::ReadInt() { if( mIsBinaryFormat) { - if( mBinaryNumCount == 0 && End - P >= 2) + if( mBinaryNumCount == 0 && mEnd - mP >= 2) { unsigned short tmp = ReadBinWord(); // 0x06 or 0x03 - if( tmp == 0x06 && End - P >= 4) // array of ints follows + if( tmp == 0x06 && mEnd - mP >= 4) // array of ints follows mBinaryNumCount = ReadBinDWord(); else // single int follows mBinaryNumCount = 1; } --mBinaryNumCount; - if ( End - P >= 4) { + if ( mEnd - mP >= 4) { return ReadBinDWord(); } else { - P = End; + mP = mEnd; return 0; } } else @@ -1299,24 +1308,24 @@ unsigned int XFileParser::ReadInt() // check preceding minus sign bool isNegative = false; - if( *P == '-') + if( *mP == '-') { isNegative = true; - P++; + mP++; } // at least one digit expected - if( !isdigit( *P)) + if( !isdigit( *mP)) ThrowException( "Number expected."); // read digits unsigned int number = 0; - while( P < End) + while( mP < mEnd) { - if( !isdigit( *P)) + if( !isdigit( *mP)) break; - number = number * 10 + (*P - 48); - P++; + number = number * 10 + (*mP - 48); + mP++; } CheckForSeparator(); @@ -1329,10 +1338,10 @@ ai_real XFileParser::ReadFloat() { if( mIsBinaryFormat) { - if( mBinaryNumCount == 0 && End - P >= 2) + if( mBinaryNumCount == 0 && mEnd - mP >= 2) { unsigned short tmp = ReadBinWord(); // 0x07 or 0x42 - if( tmp == 0x07 && End - P >= 4) // array of floats following + if( tmp == 0x07 && mEnd - mP >= 4) // array of floats following mBinaryNumCount = ReadBinDWord(); else // single float following mBinaryNumCount = 1; @@ -1341,22 +1350,22 @@ ai_real XFileParser::ReadFloat() --mBinaryNumCount; if( mBinaryFloatSize == 8) { - if( End - P >= 8) { - ai_real result = (ai_real) (*(double*) P); - P += 8; + if( mEnd - mP >= 8) { + ai_real result = (ai_real) (*(double*) mP); + mP += 8; return result; } else { - P = End; + mP = mEnd; return 0; } } else { - if( End - P >= 4) { - ai_real result = *(ai_real*) P; - P += 4; + if( mEnd - mP >= 4) { + ai_real result = *(ai_real*) mP; + mP += 4; return result; } else { - P = End; + mP = mEnd; return 0; } } @@ -1367,21 +1376,21 @@ ai_real XFileParser::ReadFloat() // check for various special strings to allow reading files from faulty exporters // I mean you, Blender! // Reading is safe because of the terminating zero - if( strncmp( P, "-1.#IND00", 9) == 0 || strncmp( P, "1.#IND00", 8) == 0) + if( strncmp( mP, "-1.#IND00", 9) == 0 || strncmp( mP, "1.#IND00", 8) == 0) { - P += 9; + mP += 9; CheckForSeparator(); return 0.0; } else - if( strncmp( P, "1.#QNAN0", 8) == 0) + if( strncmp( mP, "1.#QNAN0", 8) == 0) { - P += 8; + mP += 8; CheckForSeparator(); return 0.0; } ai_real result = 0.0; - P = fast_atoreal_move( P, result); + mP = fast_atoreal_move( mP, result); CheckForSeparator(); diff --git a/code/XFileParser.h b/code/XFileParser.h index 050ecb9ca..24eb6104d 100644 --- a/code/XFileParser.h +++ b/code/XFileParser.h @@ -49,10 +49,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -namespace Assimp -{ - namespace XFile - { +namespace Assimp { + namespace XFile { struct Node; struct Mesh; struct Scene; @@ -61,21 +59,20 @@ namespace Assimp struct AnimBone; } -/** The XFileParser reads a XFile either in text or binary form and builds a temporary - * data structure out of it. - */ -class XFileParser -{ +/** + * @brief The XFileParser reads a XFile either in text or binary form and builds a temporary + * data structure out of it. + */ +class XFileParser { public: - /** Constructor. Creates a data structure out of the XFile given in the memory block. - * @param pBuffer Null-terminated memory buffer containing the XFile - */ + /// Constructor. Creates a data structure out of the XFile given in the memory block. + /// @param pBuffer Null-terminated memory buffer containing the XFile explicit XFileParser( const std::vector& pBuffer); - /** Destructor. Destroys all imported data along with it */ + /// Destructor. Destroys all imported data along with it ~XFileParser(); - /** Returns the temporary representation of the imported data */ + /// Returns the temporary representation of the imported data. XFile::Scene* GetImportedData() const { return mScene; } protected: @@ -101,10 +98,10 @@ protected: //! places pointer to next begin of a token, and ignores comments void FindNextNoneWhiteSpace(); - //! returns next parseable token. Returns empty string if no token there + //! returns next valid token. Returns empty string if no token there std::string GetNextToken(); - //! reads header of dataobject including the opening brace. + //! reads header of data object including the opening brace. //! returns false if error happened, and writes name of object //! if there is one void readHeadOfDataObject( std::string* poName = NULL); @@ -118,8 +115,8 @@ protected: //! checks for a separator char, either a ',' or a ';' void CheckForSeparator(); - /// tests and possibly consumes a separator char, but does nothing if there was no separator - void TestForSeparator(); + /// tests and possibly consumes a separator char, but does nothing if there was no separator + void TestForSeparator(); //! reads a x file style string void GetNextTokenAsString( std::string& poString); @@ -138,27 +135,23 @@ protected: /** Throws an exception with a line number and the given text. */ AI_WONT_RETURN void ThrowException( const std::string& pText) AI_WONT_RETURN_SUFFIX; - /** Filters the imported hierarchy for some degenerated cases that some exporters produce. - * @param pData The sub-hierarchy to filter - */ + /** + * @brief Filters the imported hierarchy for some degenerated cases that some exporters produce. + * @param pData The sub-hierarchy to filter + */ void FilterHierarchy( XFile::Node* pNode); protected: unsigned int mMajorVersion, mMinorVersion; ///< version numbers bool mIsBinaryFormat; ///< true if the file is in binary, false if it's in text form unsigned int mBinaryFloatSize; ///< float size in bytes, either 4 or 8 - // counter for number arrays in binary format - unsigned int mBinaryNumCount; - - const char* P; - const char* End; - - /// Line number when reading in text format - unsigned int mLineNumber; - - /// Imported data - XFile::Scene* mScene; + unsigned int mBinaryNumCount; /// < counter for number arrays in binary format + const char* mP; + const char* mEnd; + unsigned int mLineNumber; ///< Line number when reading in text format + XFile::Scene* mScene; ///< Imported data }; -} +} //! ns Assimp + #endif // AI_XFILEPARSER_H_INC From 862a145ea50088585319512f49aaaca2397034a2 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 4 Feb 2018 22:18:54 +0100 Subject: [PATCH 026/183] X: add unittest to ensure no exception will be thrown. --- test/unit/utXImporterExporter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/unit/utXImporterExporter.cpp b/test/unit/utXImporterExporter.cpp index 770e2be47..d4c742e6f 100644 --- a/test/unit/utXImporterExporter.cpp +++ b/test/unit/utXImporterExporter.cpp @@ -62,3 +62,8 @@ public: TEST_F( utXImporterExporter, importXFromFileTest ) { EXPECT_TRUE( importerTest() ); } + +TEST_F( utXImporterExporter, heap_overflow_in_tokenizer ) { + Assimp::Importer importer; + EXPECT_NO_THROW( importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/X/OV_GetNextToken", 0 ) ); +} From a8f940dd8365ac8076889ee7751949dc879862bc Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 5 Feb 2018 00:05:08 +0100 Subject: [PATCH 027/183] add missing test file. --- test/models/X/OV_GetNextToken | Bin 0 -> 775 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/models/X/OV_GetNextToken diff --git a/test/models/X/OV_GetNextToken b/test/models/X/OV_GetNextToken new file mode 100644 index 0000000000000000000000000000000000000000..8b2a0d0ca49350f0a8dcdda8967ccd910da116bd GIT binary patch literal 775 zcmV+i1Ni)SZ)PAcGcYq^dTDSlFf%d$3jhG{3hx3#OWjsYXcIvceu*Y+vQ7G9tkp|F z5mHYEa`R_NQ)(lo5gUK-woRt#&}3J3w>6+(4uW{{AYMgKMDgNH(L;On;@?53OVlh9XK{uYRbs~>0ywsAoD^s< zE_`|(rj{?&X~l4gF`q7KPqyRO;s43aB zMopuR?!2WeU+iOx)I%qI)`KAP(3n=q8bco$Nm%b+)N=RUw)U3}{&<-Yfn3`#odwHo z%vx14YczpL2^f{9@%CxeC#h)V;%dFyl@)vV=A6{`z-pD^YcH+zRm5Hus^wr%{l&t}8 zoF)=wX%U+m_1~p9+Y^@c!TkfeB!HHlPOnWu`{(fvDGz##u>WbB)ine=8GFXE+&ELG zq`EBY?^OfC8gb*64uOxV=X@&&6A(JTJWsYCdh0q2jq&gZs*!(Hx({YN?5ZGL&}sll zz5ca*jpO=*?bQDc)*l1)K0khYPF-THcF8knP2T{kZ(_r9j6#Oh2v5ciQQI)1kRnSS z3G0k6q=+I%c&$AdXF1L&rpPfqqn?a$l8h3HjQpUGaWe1>4J$J8yM&BWg)hwAij4do zo~I_}VU|ssQFnrdK@LPuyuNk5SKqzn)JrZKp65XR{}~2NTs$}qWzuw)ZIZVvyXqT? z!|?Av+NPfbQP?MTJf^fU>}y+8v`)4uE?Onqk`%3xZ5R=)kYyiLNbuU1%8V~`Nl#DX+hsB;m*C0VkUe4QhoZ^XPrEHf13EU?ITs+9fQ@y F-#UtLa=-up literal 0 HcmV?d00001 From ff556027ef4424ba2f1ed64908be54bdcf98038e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 5 Feb 2018 00:33:41 +0100 Subject: [PATCH 028/183] X: fix some review findings. --- code/XFileParser.cpp | 61 ++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/code/XFileParser.cpp b/code/XFileParser.cpp index 34d8c89f9..92d8a579b 100644 --- a/code/XFileParser.cpp +++ b/code/XFileParser.cpp @@ -466,12 +466,11 @@ void XFileParser::ParseDataObjectMesh( Mesh* pMesh) // read position faces unsigned int numPosFaces = ReadInt(); pMesh->mPosFaces.resize( numPosFaces); - for( unsigned int a = 0; a < numPosFaces; a++) - { + for( unsigned int a = 0; a < numPosFaces; ++a) { // read indices unsigned int numIndices = ReadInt(); Face& face = pMesh->mPosFaces[a]; - for (unsigned int b = 0; b < numIndices; b++) { + for (unsigned int b = 0; b < numIndices; ++b) { face.mIndices.push_back( ReadInt() ); } TestForSeparator(); @@ -479,11 +478,10 @@ void XFileParser::ParseDataObjectMesh( Mesh* pMesh) // here, other data objects may follow bool running = true; - while ( running ) - { + while ( running ) { std::string objectName = GetNextToken(); - if( objectName.size() == 0) + if( objectName.empty() ) ThrowException( "Unexpected end of file while parsing mesh structure"); else if( objectName == "}") @@ -518,8 +516,10 @@ void XFileParser::ParseDataObjectMesh( Mesh* pMesh) } // ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectSkinWeights( Mesh *pMesh) -{ +void XFileParser::ParseDataObjectSkinWeights( Mesh *pMesh) { + if ( nullptr == pMesh ) { + return; + } readHeadOfDataObject(); std::string transformNodeName; @@ -1053,39 +1053,40 @@ void XFileParser::readHeadOfDataObject( std::string* poName) } // ------------------------------------------------------------------------------------------------ -std::string XFileParser::GetNextToken() -{ +std::string XFileParser::GetNextToken() { std::string s; // process binary-formatted file - if( mIsBinaryFormat) - { + if( mIsBinaryFormat) { // in binary mode it will only return NAME and STRING token // and (correctly) skip over other tokens. - - if( mEnd - mP < 2) return s; + if ( mEnd - mP < 2 ) { + return s; + } unsigned int tok = ReadBinWord(); unsigned int len; // standalone tokens - switch( tok) - { + switch( tok ) { case 1: { - // name token - if ( mEnd - mP < 4 ) return s; - len = ReadBinDWord(); - const int bounds( mEnd - mP ); - const int iLen( len ); - if ( iLen < 0 ) { - return s; - } - if ( bounds < iLen ) { - return s; - } - s = std::string( mP, len ); - mP += len; + // name token + if ( mEnd - mP < 4 ) { + return s; } - return s; + len = ReadBinDWord(); + const int bounds( mEnd - mP ); + const int iLen( len ); + if ( iLen < 0 ) { + return s; + } + if ( bounds < iLen ) { + return s; + } + s = std::string( mP, len ); + mP += len; + } + return s; + case 2: // string token if( mEnd - mP < 4) return s; From eb23946fe74955eca8978e168e5a6fb00eba6e36 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 5 Feb 2018 13:44:19 +0100 Subject: [PATCH 029/183] Update XFileParser.cpp Fix alignment for float and double. --- code/XFileParser.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/XFileParser.cpp b/code/XFileParser.cpp index 92d8a579b..76ea71e21 100644 --- a/code/XFileParser.cpp +++ b/code/XFileParser.cpp @@ -1349,20 +1349,20 @@ ai_real XFileParser::ReadFloat() } --mBinaryNumCount; - if( mBinaryFloatSize == 8) - { + if( mBinaryFloatSize == 8) { if( mEnd - mP >= 8) { - ai_real result = (ai_real) (*(double*) mP); + double res; + ::memcpy( &res, mP, 8 ); mP += 8; return result; } else { mP = mEnd; return 0; } - } else - { + } else { if( mEnd - mP >= 4) { - ai_real result = *(ai_real*) mP; + ai_real result; + ::memcpy( &result, mP, 4 ); mP += 4; return result; } else { From dceb7257ddf888908581380fd8bf1a7ec1a98d66 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 5 Feb 2018 13:53:06 +0100 Subject: [PATCH 030/183] Update XFileParser.cpp Fix the build. --- code/XFileParser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/XFileParser.cpp b/code/XFileParser.cpp index 76ea71e21..cf3529653 100644 --- a/code/XFileParser.cpp +++ b/code/XFileParser.cpp @@ -1354,6 +1354,7 @@ ai_real XFileParser::ReadFloat() double res; ::memcpy( &res, mP, 8 ); mP += 8; + const ai_real result( static_cast( res ) ); return result; } else { mP = mEnd; From 2a7ebc570c12d0793337e79ab55d9eb812d8cf0f Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Mon, 5 Feb 2018 17:13:53 +0100 Subject: [PATCH 031/183] Update Readme.md --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index ca6c9ba4d..a6f2e3d3d 100644 --- a/Readme.md +++ b/Readme.md @@ -120,7 +120,7 @@ Take a look into the `INSTALL` file. Our build system is CMake, if you used CMak * [Pascal](port/AssimpPascal/Readme.md) * [Javascript (Alpha)](https://github.com/makc/assimp2json) * [Unity 3d Plugin](https://www.assetstore.unity3d.com/en/#!/content/91777) -* [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (currently supported obj, ply, stl, collada, md2) +* [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (current [status](https://github.com/kotlin-graphics/assimp/wiki/Status)) ### Other tools ### [open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities. From d284d107e73cd3ae80733e3351c213025365d74c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 6 Feb 2018 18:43:51 +0200 Subject: [PATCH 032/183] XGLLoader: Fix a memory leak --- code/XGLLoader.cpp | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/code/XGLLoader.cpp b/code/XGLLoader.cpp index 2d7a40362..a9fd6a5ab 100644 --- a/code/XGLLoader.cpp +++ b/code/XGLLoader.cpp @@ -72,17 +72,6 @@ using namespace irr::io; #endif -// scopeguard for a malloc'ed buffer -struct free_it -{ - free_it(void* free) : free(free) {} - ~free_it() { - ::free(this->free); - } - - void* free; -}; - namespace Assimp { // this has to be in here because LogFunctions is in ::Assimp template<> const char* LogFunctions::Prefix() { @@ -155,8 +144,7 @@ void XGLImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { #ifndef ASSIMP_BUILD_NO_COMPRESSED_XGL - Bytef* dest = NULL; - free_it free_it_really(dest); + std::vector uncompressed; #endif m_scene = pScene; @@ -192,6 +180,7 @@ void XGLImporter::InternReadFile( const std::string& pFile, size_t total = 0l; + // TODO: be smarter about this, decompress directly into heap buffer // and decompress the data .... do 1k chunks in the hope that we won't kill the stack #define MYBLOCK 1024 Bytef block[MYBLOCK]; @@ -206,8 +195,8 @@ void XGLImporter::InternReadFile( const std::string& pFile, } const size_t have = MYBLOCK - zstream.avail_out; total += have; - dest = reinterpret_cast( realloc(dest,total) ); - memcpy(dest + total - have,block,have); + uncompressed.resize(total); + memcpy(uncompressed.data() + total - have,block,have); } while (ret != Z_STREAM_END); @@ -215,7 +204,7 @@ void XGLImporter::InternReadFile( const std::string& pFile, inflateEnd(&zstream); // replace the input stream with a memory stream - stream.reset(new MemoryIOStream(reinterpret_cast(dest),total)); + stream.reset(new MemoryIOStream(reinterpret_cast(uncompressed.data()),total)); #endif } From c42dd9104cb7a4faea288a634cd2f0c170366308 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 6 Feb 2018 18:52:23 +0200 Subject: [PATCH 033/183] BlenderLoader: Fix memory leak --- code/BlenderLoader.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/code/BlenderLoader.cpp b/code/BlenderLoader.cpp index 482f9ae5c..d4d6473c5 100644 --- a/code/BlenderLoader.cpp +++ b/code/BlenderLoader.cpp @@ -154,14 +154,6 @@ void BlenderImporter::SetupProperties(const Importer* /*pImp*/) // nothing to be done for the moment } -struct free_it { - free_it(void* free) : free(free) {} - ~free_it() { - ::free(this->free); - } - - void* free; -}; // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. @@ -169,8 +161,7 @@ void BlenderImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND - Bytef* dest = NULL; - free_it free_it_really(dest); + std::vector uncompressed; #endif @@ -218,6 +209,7 @@ void BlenderImporter::InternReadFile( const std::string& pFile, size_t total = 0l; + // TODO: be smarter about this, decompress directly into heap buffer // and decompress the data .... do 1k chunks in the hope that we won't kill the stack #define MYBLOCK 1024 Bytef block[MYBLOCK]; @@ -232,8 +224,8 @@ void BlenderImporter::InternReadFile( const std::string& pFile, } const size_t have = MYBLOCK - zstream.avail_out; total += have; - dest = reinterpret_cast( realloc(dest,total) ); - memcpy(dest + total - have,block,have); + uncompressed.resize(total); + memcpy(uncompressed.data() + total - have,block,have); } while (ret != Z_STREAM_END); @@ -241,7 +233,7 @@ void BlenderImporter::InternReadFile( const std::string& pFile, inflateEnd(&zstream); // replace the input stream with a memory stream - stream.reset(new MemoryIOStream(reinterpret_cast(dest),total)); + stream.reset(new MemoryIOStream(reinterpret_cast(uncompressed.data()),total)); // .. and retry stream->Read(magic,7,1); From 880be5403f466541335cce1b6fcef26b18cc9f15 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 6 Feb 2018 19:03:47 +0200 Subject: [PATCH 034/183] OpenGEX: Replace raw pointer with vector to fix a memory leak --- code/OpenGEXImporter.cpp | 12 ++++-------- code/OpenGEXImporter.h | 3 +-- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 37c765f4c..a2844ccdf 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -221,9 +221,7 @@ static void propId2StdString( Property *prop, std::string &name, std::string &ke //------------------------------------------------------------------------------------------------ OpenGEXImporter::VertexContainer::VertexContainer() -: m_numVerts( 0 ) -, m_vertices( nullptr ) -, m_numColors( 0 ) +: m_numColors( 0 ) , m_colors( nullptr ) , m_numNormals( 0 ) , m_normals( nullptr ) @@ -234,7 +232,6 @@ OpenGEXImporter::VertexContainer::VertexContainer() //------------------------------------------------------------------------------------------------ OpenGEXImporter::VertexContainer::~VertexContainer() { - delete[] m_vertices; delete[] m_colors; delete[] m_normals; @@ -857,9 +854,8 @@ void OpenGEXImporter::handleVertexArrayNode( ODDLParser::DDLNode *node, aiScene const size_t numItems( countDataArrayListItems( vaList ) ); if( Position == attribType ) { - m_currentVertices.m_numVerts = numItems; - m_currentVertices.m_vertices = new aiVector3D[ numItems ]; - copyVectorArray( numItems, vaList, m_currentVertices.m_vertices ); + m_currentVertices.m_vertices.resize( numItems ); + copyVectorArray( numItems, vaList, m_currentVertices.m_vertices.data() ); } else if ( Color == attribType ) { m_currentVertices.m_numColors = numItems; m_currentVertices.m_colors = new aiColor4D[ numItems ]; @@ -922,7 +918,7 @@ void OpenGEXImporter::handleIndexArrayNode( ODDLParser::DDLNode *node, aiScene * Value *next( vaList->m_dataList ); for( size_t indices = 0; indices < current.mNumIndices; indices++ ) { const int idx( next->getUnsignedInt32() ); - ai_assert( static_cast( idx ) <= m_currentVertices.m_numVerts ); + ai_assert( static_cast( idx ) <= m_currentVertices.m_vertices.size() ); ai_assert( index < m_currentMesh->mNumVertices ); aiVector3D &pos = ( m_currentVertices.m_vertices[ idx ] ); m_currentMesh->mVertices[ index ].Set( pos.x, pos.y, pos.z ); diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index bbbe3678d..eedb84375 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -144,8 +144,7 @@ protected: private: struct VertexContainer { - size_t m_numVerts; - aiVector3D *m_vertices; + std::vector m_vertices; size_t m_numColors; aiColor4D *m_colors; size_t m_numNormals; From 1aed63afb73a83fbfd2bb246f54f2110471aa724 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 6 Feb 2018 19:13:54 +0200 Subject: [PATCH 035/183] OpenGEX: Replace another raw pointer with vector to fix a memory leak --- code/OpenGEXImporter.cpp | 10 +++------- code/OpenGEXImporter.h | 3 +-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index a2844ccdf..dac8299c5 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -223,8 +223,6 @@ static void propId2StdString( Property *prop, std::string &name, std::string &ke OpenGEXImporter::VertexContainer::VertexContainer() : m_numColors( 0 ) , m_colors( nullptr ) -, m_numNormals( 0 ) -, m_normals( nullptr ) , m_numUVComps() , m_textureCoords() { // empty @@ -233,7 +231,6 @@ OpenGEXImporter::VertexContainer::VertexContainer() //------------------------------------------------------------------------------------------------ OpenGEXImporter::VertexContainer::~VertexContainer() { delete[] m_colors; - delete[] m_normals; for(auto &texcoords : m_textureCoords) { delete [] texcoords; @@ -861,9 +858,8 @@ void OpenGEXImporter::handleVertexArrayNode( ODDLParser::DDLNode *node, aiScene m_currentVertices.m_colors = new aiColor4D[ numItems ]; copyColor4DArray( numItems, vaList, m_currentVertices.m_colors ); } else if( Normal == attribType ) { - m_currentVertices.m_numNormals = numItems; - m_currentVertices.m_normals = new aiVector3D[ numItems ]; - copyVectorArray( numItems, vaList, m_currentVertices.m_normals ); + m_currentVertices.m_normals.resize( numItems ); + copyVectorArray( numItems, vaList, m_currentVertices.m_normals.data() ); } else if( TexCoord == attribType ) { m_currentVertices.m_numUVComps[ 0 ] = numItems; m_currentVertices.m_textureCoords[ 0 ] = new aiVector3D[ numItems ]; @@ -900,7 +896,7 @@ void OpenGEXImporter::handleIndexArrayNode( ODDLParser::DDLNode *node, aiScene * hasColors = true; } bool hasNormalCoords( false ); - if ( m_currentVertices.m_numNormals > 0 ) { + if ( !m_currentVertices.m_normals.empty() ) { m_currentMesh->mNormals = new aiVector3D[ m_currentMesh->mNumVertices ]; hasNormalCoords = true; } diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index eedb84375..ad331d8e6 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -147,8 +147,7 @@ private: std::vector m_vertices; size_t m_numColors; aiColor4D *m_colors; - size_t m_numNormals; - aiVector3D *m_normals; + std::vector m_normals; size_t m_numUVComps[ AI_MAX_NUMBER_OF_TEXTURECOORDS ]; aiVector3D *m_textureCoords[ AI_MAX_NUMBER_OF_TEXTURECOORDS ]; From 9344074a04357fb0e8fd2537557f148dbc15ce8f Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 6 Feb 2018 19:22:32 +0200 Subject: [PATCH 036/183] MDLLoader: Replace raw pointer with vector to fix a memory leak --- code/MDLFileData.h | 4 ++-- code/MDLLoader.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/MDLFileData.h b/code/MDLFileData.h index 56f686280..ba732add0 100644 --- a/code/MDLFileData.h +++ b/code/MDLFileData.h @@ -844,11 +844,11 @@ struct IntGroupInfo_MDL7 struct IntGroupData_MDL7 { IntGroupData_MDL7() - : pcFaces(NULL), bNeed2UV(false) + : bNeed2UV(false) {} //! Array of faces that belong to the group - MDL::IntFace_MDL7* pcFaces; + std::vector pcFaces; //! Array of vertex positions std::vector vPositions; diff --git a/code/MDLLoader.cpp b/code/MDLLoader.cpp index 5dd471cf5..3f2bb084b 100644 --- a/code/MDLLoader.cpp +++ b/code/MDLLoader.cpp @@ -1502,7 +1502,7 @@ void MDLImporter::InternReadFile_3DGS_MDL7( ) groupData.bNeed2UV = true; } } - groupData.pcFaces = new MDL::IntFace_MDL7[groupInfo.pcGroup->numtris]; + groupData.pcFaces.resize(groupInfo.pcGroup->numtris); // read all faces into the preallocated arrays ReadFaces_3DGS_MDL7(groupInfo, groupData); From 3b68ffe363401cafd8da701a4a757bc88845e164 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 6 Feb 2018 19:32:34 +0200 Subject: [PATCH 037/183] LWO: Use C++11 auto for easier refactoring --- code/LWOLoader.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/LWOLoader.cpp b/code/LWOLoader.cpp index 38e330b8f..10cfd67bd 100644 --- a/code/LWOLoader.cpp +++ b/code/LWOLoader.cpp @@ -584,7 +584,7 @@ void LWOImporter::GenerateNodeGraph(std::map& apcNodes) //Set parent of all children, inserting pivots //std::cout << "Set parent of all children" << std::endl; std::map mapPivot; - for (std::map::iterator itapcNodes = apcNodes.begin(); itapcNodes != apcNodes.end(); ++itapcNodes) { + for (auto itapcNodes = apcNodes.begin(); itapcNodes != apcNodes.end(); ++itapcNodes) { //Get the parent index LWO::Layer* nodeLayer = (LWO::Layer*)(itapcNodes->second->mParent); @@ -615,14 +615,14 @@ void LWOImporter::GenerateNodeGraph(std::map& apcNodes) //Merge pivot map into node map //std::cout << "Merge pivot map into node map" << std::endl; - for (std::map::iterator itMapPivot = mapPivot.begin(); itMapPivot != mapPivot.end(); ++itMapPivot) { + for (auto itMapPivot = mapPivot.begin(); itMapPivot != mapPivot.end(); ++itMapPivot) { apcNodes[itMapPivot->first] = itMapPivot->second; } //Set children of all parents apcNodes[-1] = root; - for (std::map::iterator itMapParentNodes = apcNodes.begin(); itMapParentNodes != apcNodes.end(); ++itMapParentNodes) { - for (std::map::iterator itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) { + for (auto itMapParentNodes = apcNodes.begin(); itMapParentNodes != apcNodes.end(); ++itMapParentNodes) { + for (auto itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) { if ((itMapParentNodes->first != itMapChildNodes->first) && (itMapParentNodes->second == itMapChildNodes->second->mParent)) { ++(itMapParentNodes->second->mNumChildren); } @@ -630,7 +630,7 @@ void LWOImporter::GenerateNodeGraph(std::map& apcNodes) if (itMapParentNodes->second->mNumChildren) { itMapParentNodes->second->mChildren = new aiNode* [ itMapParentNodes->second->mNumChildren ]; uint16_t p = 0; - for (std::map::iterator itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) { + for (auto itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) { if ((itMapParentNodes->first != itMapChildNodes->first) && (itMapParentNodes->second == itMapChildNodes->second->mParent)) { itMapParentNodes->second->mChildren[p++] = itMapChildNodes->second; } From ef891fb850af5edb86869a1e175ce0e07e13ac02 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 6 Feb 2018 19:36:27 +0200 Subject: [PATCH 038/183] LWO: Move some assignments to make it clearer when the thing should be moved --- code/LWOLoader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/LWOLoader.cpp b/code/LWOLoader.cpp index 10cfd67bd..e908ea178 100644 --- a/code/LWOLoader.cpp +++ b/code/LWOLoader.cpp @@ -432,7 +432,6 @@ void LWOImporter::InternReadFile( const std::string& pFile, unsigned int num = static_cast(apcMeshes.size() - meshStart); if (layer.mName != "" || num > 0) { aiNode* pcNode = new aiNode(); - apcNodes[layer.mIndex] = pcNode; pcNode->mName.Set(layer.mName); pcNode->mParent = (aiNode*)&layer; pcNode->mNumMeshes = num; @@ -442,6 +441,7 @@ void LWOImporter::InternReadFile( const std::string& pFile, for (unsigned int p = 0; p < pcNode->mNumMeshes;++p) pcNode->mMeshes[p] = p + meshStart; } + apcNodes[layer.mIndex] = pcNode; } } @@ -593,7 +593,6 @@ void LWOImporter::GenerateNodeGraph(std::map& apcNodes) //Create pivot node, store it into the pivot map, and set the parent as the pivot aiNode* pivotNode = new aiNode(); pivotNode->mName.Set("Pivot-"+std::string(itapcNodes->second->mName.data)); - mapPivot[-(itapcNodes->first+2)] = pivotNode; itapcNodes->second->mParent = pivotNode; //Look for the parent node to attach the pivot to @@ -611,6 +610,7 @@ void LWOImporter::GenerateNodeGraph(std::map& apcNodes) pivotNode->mTransformation.a4 = nodeLayer->mPivot.x; pivotNode->mTransformation.b4 = nodeLayer->mPivot.y; pivotNode->mTransformation.c4 = nodeLayer->mPivot.z; + mapPivot[-(itapcNodes->first+2)] = pivotNode; } //Merge pivot map into node map From aa434b956648af35917c9b18933b066eb16171dd Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 6 Feb 2018 20:05:02 +0200 Subject: [PATCH 039/183] OpenGEX: Add comment about pointer ownership --- code/OpenGEXImporter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index ad331d8e6..5b87d5ce5 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -192,7 +192,7 @@ private: MetricInfo m_metrics[ MetricInfo::Max ]; aiNode *m_currentNode; VertexContainer m_currentVertices; - aiMesh *m_currentMesh; + aiMesh *m_currentMesh; // not owned, target is owned by m_meshCache aiMaterial *m_currentMaterial; aiLight *m_currentLight; aiCamera *m_currentCamera; From 5ce9ece0ccc110bf858bd21a19b10986236bf63e Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 6 Feb 2018 20:08:49 +0200 Subject: [PATCH 040/183] OpenGEX: Replace std::copy with explicit loop --- code/OpenGEXImporter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index dac8299c5..a0bef75a9 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -1137,7 +1137,9 @@ void OpenGEXImporter::copyMeshes( aiScene *pScene ) { pScene->mNumMeshes = static_cast(m_meshCache.size()); pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ]; - std::copy( m_meshCache.begin(), m_meshCache.end(), pScene->mMeshes ); + for (unsigned int i = 0; i < pScene->mNumMeshes; i++) { + pScene->mMeshes[i] = m_meshCache[i]; + } } //------------------------------------------------------------------------------------------------ From 17b26c91e254a17091f96ab861569f10b408a9c8 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 6 Feb 2018 20:20:16 +0200 Subject: [PATCH 041/183] OpenGEX: Use std::unique_ptr to fix some memory leaks --- code/OpenGEXImporter.cpp | 5 +++-- code/OpenGEXImporter.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index a0bef75a9..43c92c4f5 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -691,7 +691,8 @@ void OpenGEXImporter::handleTransformNode( ODDLParser::DDLNode *node, aiScene * void OpenGEXImporter::handleMeshNode( ODDLParser::DDLNode *node, aiScene *pScene ) { m_currentMesh = new aiMesh; const size_t meshidx( m_meshCache.size() ); - m_meshCache.push_back( m_currentMesh ); + // ownership is transfered but a reference remains in m_currentMesh + m_meshCache.emplace_back( m_currentMesh ); Property *prop = node->getProperties(); if( nullptr != prop ) { @@ -1138,7 +1139,7 @@ void OpenGEXImporter::copyMeshes( aiScene *pScene ) { pScene->mNumMeshes = static_cast(m_meshCache.size()); pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ]; for (unsigned int i = 0; i < pScene->mNumMeshes; i++) { - pScene->mMeshes[i] = m_meshCache[i]; + pScene->mMeshes[i] = m_meshCache[i].release(); } } diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index 5b87d5ce5..8e86a4aa8 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -183,7 +183,7 @@ private: typedef std::map > NodeChildMap; NodeChildMap m_nodeChildMap; - std::vector m_meshCache; + std::vector > m_meshCache; typedef std::map ReferenceMap; std::map m_mesh2refMap; std::map m_material2refMap; From 495ae70cc5423b1cda7c5999c4b44cf9fb9de0cd Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 6 Feb 2018 19:21:56 +0100 Subject: [PATCH 042/183] XFileParser: release x-file-based scene when throwing an exception. --- code/3DSLoader.cpp | 16 +- code/PlyLoader.cpp | 352 +++++++++++++++----------------- code/PlyLoader.h | 1 - code/PlyParser.h | 43 ++-- code/XFileParser.cpp | 42 ++-- test/unit/utPLYImportExport.cpp | 25 +++ 6 files changed, 243 insertions(+), 236 deletions(-) diff --git a/code/3DSLoader.cpp b/code/3DSLoader.cpp index 7a5e2b6e5..9d61e125c 100644 --- a/code/3DSLoader.cpp +++ b/code/3DSLoader.cpp @@ -113,22 +113,24 @@ Discreet3DSImporter::Discreet3DSImporter() , mScene() , mMasterScale() , bHasBG() -, bIsPrj() -{} +, bIsPrj() { + // empty +} // ------------------------------------------------------------------------------------------------ // Destructor, private as well -Discreet3DSImporter::~Discreet3DSImporter() -{} +Discreet3DSImporter::~Discreet3DSImporter() { + // empty +} // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const -{ +bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { std::string extension = GetExtension(pFile); if(extension == "3ds" || extension == "prj" ) { return true; } + if (!extension.length() || checkSig) { uint16_t token[3]; token[0] = 0x4d4d; @@ -210,7 +212,7 @@ void Discreet3DSImporter::InternReadFile( const std::string& pFile, ConvertScene(pScene); // Generate the node graph for the scene. This is a little bit - // tricky since we'll need to split some meshes into submeshes + // tricky since we'll need to split some meshes into sub-meshes GenerateNodeGraph(pScene); // Now apply the master scaling factor to the scene diff --git a/code/PlyLoader.cpp b/code/PlyLoader.cpp index b6ae368df..5f4e556c1 100644 --- a/code/PlyLoader.cpp +++ b/code/PlyLoader.cpp @@ -58,16 +58,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; static const aiImporterDesc desc = { - "Stanford Polygon Library (PLY) Importer", - "", - "", - "", - aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "ply" + "Stanford Polygon Library (PLY) Importer", + "", + "", + "", + aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "ply" }; @@ -92,229 +92,209 @@ namespace // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer PLYImporter::PLYImporter() - : mBuffer(nullptr) - , pcDOM(nullptr) - , mGeneratedMesh(nullptr){ - // empty +: mBuffer(nullptr) +, pcDOM(nullptr) +, mGeneratedMesh(nullptr) { + // empty } // ------------------------------------------------------------------------------------------------ // Destructor, private as well PLYImporter::~PLYImporter() { - // empty + // empty } // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool PLYImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const -{ - const std::string extension = GetExtension(pFile); +bool PLYImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { + const std::string extension = GetExtension(pFile); - if (extension == "ply") - return true; - else if (!extension.length() || checkSig) - { - if (!pIOHandler)return true; - const char* tokens[] = { "ply" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); - } - return false; + if ( extension == "ply" ) { + return true; + } else if (!extension.length() || checkSig) { + if ( !pIOHandler ) { + return true; + } + static const char* tokens[] = { "ply" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); + } + + return false; } // ------------------------------------------------------------------------------------------------ -const aiImporterDesc* PLYImporter::GetInfo() const -{ - return &desc; +const aiImporterDesc* PLYImporter::GetInfo() const { + return &desc; } // ------------------------------------------------------------------------------------------------ static bool isBigEndian(const char* szMe) { - ai_assert(NULL != szMe); + ai_assert(NULL != szMe); - // binary_little_endian - // binary_big_endian - bool isBigEndian(false); + // binary_little_endian + // binary_big_endian + bool isBigEndian(false); #if (defined AI_BUILD_BIG_ENDIAN) - if ( 'l' == *szMe || 'L' == *szMe ) { - isBigEndian = true; - } + if ( 'l' == *szMe || 'L' == *szMe ) { + isBigEndian = true; + } #else - if ('b' == *szMe || 'B' == *szMe) { - isBigEndian = true; - } + if ('b' == *szMe || 'B' == *szMe) { + isBigEndian = true; + } #endif // ! AI_BUILD_BIG_ENDIAN - return isBigEndian; + return isBigEndian; } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. -void PLYImporter::InternReadFile(const std::string& pFile, - aiScene* pScene, IOSystem* pIOHandler) -{ - static const std::string mode = "rb"; - std::unique_ptr fileStream(pIOHandler->Open(pFile, mode)); - if (!fileStream.get()) { - throw DeadlyImportError("Failed to open file " + pFile + "."); - } +void PLYImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { + static const std::string mode = "rb"; + std::unique_ptr fileStream(pIOHandler->Open(pFile, mode)); + if (!fileStream.get()) { + throw DeadlyImportError("Failed to open file " + pFile + "."); + } - // Get the file-size - size_t fileSize = fileStream->FileSize(); - if ( 0 == fileSize ) { - throw DeadlyImportError("File " + pFile + " is empty."); - } + // Get the file-size + const size_t fileSize( fileStream->FileSize() ); + if ( 0 == fileSize ) { + throw DeadlyImportError("File " + pFile + " is empty."); + } - IOStreamBuffer streamedBuffer(1024 * 1024); - streamedBuffer.open(fileStream.get()); + IOStreamBuffer streamedBuffer(1024 * 1024); + streamedBuffer.open(fileStream.get()); - // the beginning of the file must be PLY - magic, magic - std::vector headerCheck; - streamedBuffer.getNextLine(headerCheck); + // the beginning of the file must be PLY - magic, magic + std::vector headerCheck; + streamedBuffer.getNextLine(headerCheck); - if ((headerCheck.size() < 3) || - (headerCheck[0] != 'P' && headerCheck[0] != 'p') || - (headerCheck[1] != 'L' && headerCheck[1] != 'l') || - (headerCheck[2] != 'Y' && headerCheck[2] != 'y') ) - { - streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Magic number \'ply\' is no there"); - } + if ((headerCheck.size() < 3) || + (headerCheck[0] != 'P' && headerCheck[0] != 'p') || + (headerCheck[1] != 'L' && headerCheck[1] != 'l') || + (headerCheck[2] != 'Y' && headerCheck[2] != 'y') ) { + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Magic number \'ply\' is no there"); + } - std::vector mBuffer2; - streamedBuffer.getNextLine(mBuffer2); - mBuffer = (unsigned char*)&mBuffer2[0]; + std::vector mBuffer2; + streamedBuffer.getNextLine(mBuffer2); + mBuffer = (unsigned char*)&mBuffer2[0]; - char* szMe = (char*)&this->mBuffer[0]; - SkipSpacesAndLineEnd(szMe, (const char**)&szMe); + char* szMe = (char*)&this->mBuffer[0]; + SkipSpacesAndLineEnd(szMe, (const char**)&szMe); - // determine the format of the file data and construct the aimesh - PLY::DOM sPlyDom; - this->pcDOM = &sPlyDom; + // determine the format of the file data and construct the aimesh + PLY::DOM sPlyDom; + this->pcDOM = &sPlyDom; - if (TokenMatch(szMe, "format", 6)) { - if (TokenMatch(szMe, "ascii", 5)) { - SkipLine(szMe, (const char**)&szMe); - if (!PLY::DOM::ParseInstance(streamedBuffer, &sPlyDom, this)) - { - if (mGeneratedMesh != NULL) - { - delete(mGeneratedMesh); - mGeneratedMesh = nullptr; + if (TokenMatch(szMe, "format", 6)) { + if (TokenMatch(szMe, "ascii", 5)) { + SkipLine(szMe, (const char**)&szMe); + if (!PLY::DOM::ParseInstance(streamedBuffer, &sPlyDom, this)) { + if (mGeneratedMesh != NULL) { + delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } + + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#1)"); + } + } else if (!::strncmp(szMe, "binary_", 7)) { + szMe += 7; + const bool bIsBE(isBigEndian(szMe)); + + // skip the line, parse the rest of the header and build the DOM + if (!PLY::DOM::ParseInstanceBinary(streamedBuffer, &sPlyDom, this, bIsBE)) { + if (mGeneratedMesh != NULL) { + delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } + + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#2)"); + } + } else { + if (mGeneratedMesh != NULL) { + delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } + + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Unknown file format"); + } + } else { + AI_DEBUG_INVALIDATE_PTR(this->mBuffer); + if (mGeneratedMesh != NULL) { + delete(mGeneratedMesh); + mGeneratedMesh = nullptr; } streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#1)"); - } + throw DeadlyImportError("Invalid .ply file: Missing format specification"); } - else if (!::strncmp(szMe, "binary_", 7)) - { - szMe += 7; - const bool bIsBE(isBigEndian(szMe)); - // skip the line, parse the rest of the header and build the DOM - if (!PLY::DOM::ParseInstanceBinary(streamedBuffer, &sPlyDom, this, bIsBE)) - { - if (mGeneratedMesh != NULL) - { - delete(mGeneratedMesh); - mGeneratedMesh = nullptr; + //free the file buffer + streamedBuffer.close(); + + if (mGeneratedMesh == NULL) { + throw DeadlyImportError("Invalid .ply file: Unable to extract mesh data "); + } + + // if no face list is existing we assume that the vertex + // list is containing a list of points + bool pointsOnly = mGeneratedMesh->mFaces == NULL ? true : false; + if (pointsOnly) { + if (mGeneratedMesh->mNumVertices < 3) { + if (mGeneratedMesh != NULL) { + delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } + + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Not enough " + "vertices to build a proper face list. "); } - streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#2)"); - } - } - else - { - if (mGeneratedMesh != NULL) - { - delete(mGeneratedMesh); - mGeneratedMesh = nullptr; - } + const unsigned int iNum = (unsigned int)mGeneratedMesh->mNumVertices / 3; + mGeneratedMesh->mNumFaces = iNum; + mGeneratedMesh->mFaces = new aiFace[mGeneratedMesh->mNumFaces]; - streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Unknown file format"); - } - } - else - { - AI_DEBUG_INVALIDATE_PTR(this->mBuffer); - if (mGeneratedMesh != NULL) - { - delete(mGeneratedMesh); - mGeneratedMesh = nullptr; + for (unsigned int i = 0; i < iNum; ++i) { + mGeneratedMesh->mFaces[i].mNumIndices = 3; + mGeneratedMesh->mFaces[i].mIndices = new unsigned int[3]; + mGeneratedMesh->mFaces[i].mIndices[0] = (i * 3); + mGeneratedMesh->mFaces[i].mIndices[1] = (i * 3) + 1; + mGeneratedMesh->mFaces[i].mIndices[2] = (i * 3) + 2; + } } - streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Missing format specification"); - } + // now load a list of all materials + std::vector avMaterials; + std::string defaultTexture; + LoadMaterial(&avMaterials, defaultTexture, pointsOnly); - //free the file buffer - streamedBuffer.close(); - - if (mGeneratedMesh == NULL) - { - throw DeadlyImportError("Invalid .ply file: Unable to extract mesh data "); - } - - // if no face list is existing we assume that the vertex - // list is containing a list of points - bool pointsOnly = mGeneratedMesh->mFaces == NULL ? true : false; - if (pointsOnly) - { - if (mGeneratedMesh->mNumVertices < 3) - { - if (mGeneratedMesh != NULL) - { - delete(mGeneratedMesh); - mGeneratedMesh = nullptr; - } - - streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Not enough " - "vertices to build a proper face list. "); + // now generate the output scene object. Fill the material list + pScene->mNumMaterials = (unsigned int)avMaterials.size(); + pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; + for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) { + pScene->mMaterials[i] = avMaterials[i]; } - const unsigned int iNum = (unsigned int)mGeneratedMesh->mNumVertices / 3; - mGeneratedMesh->mNumFaces = iNum; - mGeneratedMesh->mFaces = new aiFace[mGeneratedMesh->mNumFaces]; + // fill the mesh list + pScene->mNumMeshes = 1; + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + pScene->mMeshes[0] = mGeneratedMesh; + mGeneratedMesh = nullptr; - for (unsigned int i = 0; i < iNum; ++i) - { - mGeneratedMesh->mFaces[i].mNumIndices = 3; - mGeneratedMesh->mFaces[i].mIndices = new unsigned int[3]; - mGeneratedMesh->mFaces[i].mIndices[0] = (i * 3); - mGeneratedMesh->mFaces[i].mIndices[1] = (i * 3) + 1; - mGeneratedMesh->mFaces[i].mIndices[2] = (i * 3) + 2; + // generate a simple node structure + pScene->mRootNode = new aiNode(); + pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; + pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; + + for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes; ++i) { + pScene->mRootNode->mMeshes[i] = i; } - } - - // now load a list of all materials - std::vector avMaterials; - std::string defaultTexture; - LoadMaterial(&avMaterials, defaultTexture, pointsOnly); - - // now generate the output scene object. Fill the material list - pScene->mNumMaterials = (unsigned int)avMaterials.size(); - pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; - for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) { - pScene->mMaterials[i] = avMaterials[i]; - } - - // fill the mesh list - pScene->mNumMeshes = 1; - pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; - pScene->mMeshes[0] = mGeneratedMesh; - mGeneratedMesh = nullptr; - - // generate a simple node structure - pScene->mRootNode = new aiNode(); - pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; - pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; - - for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes; ++i) { - pScene->mRootNode->mMeshes[i] = i; - } } void PLYImporter::LoadVertex(const PLY::Element* pcElement, const PLY::ElementInstance* instElement, unsigned int pos) { @@ -521,9 +501,7 @@ void PLYImporter::LoadVertex(const PLY::Element* pcElement, const PLY::ElementIn // ------------------------------------------------------------------------------------------------ // Convert a color component to [0...1] -ai_real PLYImporter::NormalizeColorValue(PLY::PropertyInstance::ValueUnion val, - PLY::EDataType eType) -{ +ai_real PLYImporter::NormalizeColorValue(PLY::PropertyInstance::ValueUnion val, PLY::EDataType eType) { switch (eType) { case EDT_Float: diff --git a/code/PlyLoader.h b/code/PlyLoader.h index beada5f23..9199e17d7 100644 --- a/code/PlyLoader.h +++ b/code/PlyLoader.h @@ -57,7 +57,6 @@ struct aiMesh; namespace Assimp { - using namespace PLY; // --------------------------------------------------------------------------- diff --git a/code/PlyParser.h b/code/PlyParser.h index cba48a4b6..261ac7b82 100644 --- a/code/PlyParser.h +++ b/code/PlyParser.h @@ -39,12 +39,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ - /** @file Defines the helper data structures for importing PLY files */ +#pragma once #ifndef AI_PLYFILEHELPER_H_INC #define AI_PLYFILEHELPER_H_INC - #include #include #include @@ -58,8 +57,7 @@ class PLYImporter; // http://local.wasp.uwa.edu.au/~pbourke/dataformats/ply/ // http://w3.impa.br/~lvelho/outgoing/sossai/old/ViHAP_D4.4.2_PLY_format_v1.1.pdf // http://www.okino.com/conv/exp_ply.htm -namespace PLY -{ +namespace PLY { // --------------------------------------------------------------------------------- /* @@ -78,8 +76,7 @@ int8 int16 uint8 ... forms are also used */ -enum EDataType -{ +enum EDataType { EDT_Char = 0x0u, EDT_UChar, EDT_Short, @@ -98,8 +95,7 @@ enum EDataType * * Semantics define the usage of a property, e.g. x coordinate */ -enum ESemantic -{ +enum ESemantic { //! vertex position x coordinate EST_XCoord = 0x0u, //! vertex position x coordinate @@ -182,15 +178,14 @@ enum ESemantic * * Semantics define the usage of an element, e.g. vertex or material */ -enum EElementSemantic -{ +enum EElementSemantic { //! The element is a vertex EEST_Vertex = 0x0u, //! The element is a face description (index table) EEST_Face, - //! The element is a tristrip description (index table) + //! The element is a triangle-strip description (index table) EEST_TriStrip, //! The element is an edge description (ignored) @@ -211,17 +206,16 @@ enum EElementSemantic * * This can e.g. be a part of the vertex declaration */ -class Property -{ +class Property { public: - //! Default constructor Property() - : eType (EDT_Int), - Semantic(), - bIsList(false), - eFirstType(EDT_UChar) - {} + : eType (EDT_Int) + , Semantic() + , bIsList(false) + , eFirstType(EDT_UChar) { + // empty + } //! Data type of the property EDataType eType; @@ -260,15 +254,14 @@ public: * This can e.g. be the vertex declaration. Elements contain a * well-defined number of properties. */ -class Element -{ +class Element { public: - //! Default constructor Element() - : eSemantic (EEST_INVALID) - , NumOccur(0) - {} + : eSemantic (EEST_INVALID) + , NumOccur(0) { + // empty + } //! List of properties assigned to the element //! std::vector to support operator[] diff --git a/code/XFileParser.cpp b/code/XFileParser.cpp index cf3529653..4017ed596 100644 --- a/code/XFileParser.cpp +++ b/code/XFileParser.cpp @@ -1047,8 +1047,10 @@ void XFileParser::readHeadOfDataObject( std::string* poName) if( poName) *poName = nameOrBrace; - if( GetNextToken() != "{") - ThrowException( "Opening brace expected."); + if ( GetNextToken() != "{" ) { + delete mScene; + ThrowException( "Opening brace expected." ); + } } } @@ -1224,21 +1226,29 @@ void XFileParser::GetNextTokenAsString( std::string& poString) } FindNextNoneWhiteSpace(); - if( mP >= mEnd) - ThrowException( "Unexpected end of file while parsing string"); + if ( mP >= mEnd ) { + delete mScene; + ThrowException( "Unexpected end of file while parsing string" ); + } - if( *mP != '"') - ThrowException( "Expected quotation mark."); + if ( *mP != '"' ) { + delete mScene; + ThrowException( "Expected quotation mark." ); + } ++mP; while( mP < mEnd && *mP != '"') poString.append( mP++, 1); - if( mP >= mEnd-1) - ThrowException( "Unexpected end of file while parsing string"); + if ( mP >= mEnd - 1 ) { + delete mScene; + ThrowException( "Unexpected end of file while parsing string" ); + } - if( mP[1] != ';' || mP[0] != '"') - ThrowException( "Expected quotation mark and semicolon at the end of a string."); + if ( mP[ 1 ] != ';' || mP[ 0 ] != '"' ) { + delete mScene; + ThrowException( "Expected quotation mark and semicolon at the end of a string." ); + } mP+=2; } @@ -1449,15 +1459,15 @@ aiColor3D XFileParser::ReadRGB() // ------------------------------------------------------------------------------------------------ // Throws an exception with a line number and the given text. -AI_WONT_RETURN void XFileParser::ThrowException( const std::string& pText) -{ - if( mIsBinaryFormat) - throw DeadlyImportError( pText); - else +AI_WONT_RETURN void XFileParser::ThrowException( const std::string& pText) { + delete mScene; + if ( mIsBinaryFormat ) { + throw DeadlyImportError( pText ); + } else { throw DeadlyImportError( format() << "Line " << mLineNumber << ": " << pText ); + } } - // ------------------------------------------------------------------------------------------------ // Filters the imported hierarchy for some degenerated cases that some exporters produce. void XFileParser::FilterHierarchy( XFile::Node* pNode) diff --git a/test/unit/utPLYImportExport.cpp b/test/unit/utPLYImportExport.cpp index fdd20008d..633beda5f 100644 --- a/test/unit/utPLYImportExport.cpp +++ b/test/unit/utPLYImportExport.cpp @@ -103,3 +103,28 @@ TEST_F( utPLYImportExport, vertexColorTest ) { const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/PLY/float-color.ply", 0 ); EXPECT_NE( nullptr, scene ); } + +static const char *test_file = + "ply\n" + "format ascii 1.0\n" + "element vertex 4\n" + "property float x\n" + "property float y\n" + "property float z\n" + "property uchar red\n" + "property uchar green\n" + "property uchar blue\n" + "property float nx\n" + "property float ny\n" + "property float nz\n" + "end_header\n" + "0.0 0.0 0.0 255 255 255 0.0 1.0 0.0\n" + "0.0 0.0 1.0 255 0 255 0.0 0.0 1.0\n" + "0.0 1.0 0.0 255 255 0 1.0 0.0 0.0\n" + "0.0 1.0 1.0 0 255 255 1.0 1.0 0.0\n"; + +TEST_F( utPLYImportExport, parseErrorTest ) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFileFromMemory( test_file, strlen( test_file ), 0 ); + EXPECT_NE( nullptr, scene ); +} From 4736160b793ed86840cb07c156a469516c1c92b4 Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Tue, 6 Feb 2018 22:36:37 +0100 Subject: [PATCH 043/183] Update BlenderDNA.h --- code/BlenderDNA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/BlenderDNA.h b/code/BlenderDNA.h index 932784e9f..43c9d2d82 100644 --- a/code/BlenderDNA.h +++ b/code/BlenderDNA.h @@ -204,7 +204,7 @@ enum ErrorPolicy { // ------------------------------------------------------------------------------- /** Represents a data structure in a BLEND file. A Structure defines n fields - * and their locatios and encodings the input stream. Usually, every + * and their locations and encodings the input stream. Usually, every * Structure instance pertains to one equally-named data structure in the * BlenderScene.h header. This class defines various utilities to map a * binary `blob` read from the file to such a structure instance with From 57c1fe5954aa44107e86a7c17b0df2ff9a2cd846 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 6 Feb 2018 23:59:46 +0100 Subject: [PATCH 044/183] x-parser: fix the crash. --- code/XFileImporter.cpp | 341 ++++++++++++++++++----------------------- code/XFileParser.cpp | 1 - 2 files changed, 153 insertions(+), 189 deletions(-) diff --git a/code/XFileImporter.cpp b/code/XFileImporter.cpp index 93ae6173b..fd28fbb79 100644 --- a/code/XFileImporter.cpp +++ b/code/XFileImporter.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2018, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -44,7 +42,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @brief Implementation of the XFile importer class */ - #ifndef ASSIMP_BUILD_NO_X_IMPORTER #include "XFileImporter.h" @@ -79,17 +76,19 @@ static const aiImporterDesc desc = { // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer XFileImporter::XFileImporter() -{} +: mBuffer() { + // empty +} // ------------------------------------------------------------------------------------------------ // Destructor, private as well -XFileImporter::~XFileImporter() -{} +XFileImporter::~XFileImporter() { + // empty +} // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const -{ +bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { std::string extension = GetExtension(pFile); if(extension == "x") { return true; @@ -104,23 +103,24 @@ bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, boo // ------------------------------------------------------------------------------------------------ // Get file extension list -const aiImporterDesc* XFileImporter::GetInfo () const -{ +const aiImporterDesc* XFileImporter::GetInfo () const { return &desc; } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. -void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) -{ +void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { // read file into memory std::unique_ptr file( pIOHandler->Open( pFile)); - if( file.get() == NULL) - throw DeadlyImportError( "Failed to open file " + pFile + "."); + if ( file.get() == NULL ) { + throw DeadlyImportError( "Failed to open file " + pFile + "." ); + } + static const size_t MinSize = 16; size_t fileSize = file->FileSize(); - if( fileSize < 16) - throw DeadlyImportError( "XFile is too small."); + if ( fileSize < MinSize ) { + throw DeadlyImportError( "XFile is too small." ); + } // in the hope that binary files will never start with a BOM ... mBuffer.resize( fileSize + 1); @@ -134,8 +134,9 @@ void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, I CreateDataRepresentationFromImport( pScene, parser.GetImportedData()); // if nothing came from it, report it as error - if( !pScene->mRootNode) - throw DeadlyImportError( "XFile is ill-formatted - no content imported."); + if ( !pScene->mRootNode ) { + throw DeadlyImportError( "XFile is ill-formatted - no content imported." ); + } } // ------------------------------------------------------------------------------------------------ @@ -146,17 +147,15 @@ void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, XFile:: ConvertMaterials( pScene, pData->mGlobalMaterials); // copy nodes, extracting meshes and materials on the way - pScene->mRootNode = CreateNodes( pScene, NULL, pData->mRootNode); + pScene->mRootNode = CreateNodes( pScene, nullptr, pData->mRootNode); // extract animations CreateAnimations( pScene, pData); // read the global meshes that were stored outside of any node - if( pData->mGlobalMeshes.size() > 0) - { + if( !pData->mGlobalMeshes.empty() ) { // create a root node to hold them if there isn't any, yet - if( pScene->mRootNode == NULL) - { + if( pScene->mRootNode == nullptr ) { pScene->mRootNode = new aiNode; pScene->mRootNode->mName.Set( "$dummy_node"); } @@ -180,8 +179,7 @@ void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, XFile:: flipper.Execute(pScene); // finally: create a dummy material if not material was imported - if( pScene->mNumMaterials == 0) - { + if( pScene->mNumMaterials == 0) { pScene->mNumMaterials = 1; // create the Material aiMaterial* mat = new aiMaterial; @@ -205,10 +203,10 @@ void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, XFile:: // ------------------------------------------------------------------------------------------------ // Recursively creates scene nodes from the imported hierarchy. -aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode) -{ - if( !pNode) - return NULL; +aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode) { + if ( !pNode ) { + return nullptr; + } // create node aiNode* node = new aiNode; @@ -222,13 +220,13 @@ aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFil CreateMeshes( pScene, node, pNode->mMeshes); // handle childs - if( pNode->mChildren.size() > 0) - { + if( !pNode->mChildren.empty() ) { node->mNumChildren = (unsigned int)pNode->mChildren.size(); node->mChildren = new aiNode* [node->mNumChildren]; - for( unsigned int a = 0; a < pNode->mChildren.size(); a++) - node->mChildren[a] = CreateNodes( pScene, node, pNode->mChildren[a]); + for ( unsigned int a = 0; a < pNode->mChildren.size(); ++a ) { + node->mChildren[ a ] = CreateNodes( pScene, node, pNode->mChildren[ a ] ); + } } return node; @@ -236,16 +234,14 @@ aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFil // ------------------------------------------------------------------------------------------------ // Creates the meshes for the given node. -void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector& pMeshes) -{ +void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector& pMeshes) { if (pMeshes.empty()) { return; } // create a mesh for each mesh-material combination in the source node std::vector meshes; - for( unsigned int a = 0; a < pMeshes.size(); a++) - { + for( unsigned int a = 0; a < pMeshes.size(); ++a ) { XFile::Mesh* sourceMesh = pMeshes[a]; if ( nullptr == sourceMesh ) { continue; @@ -255,35 +251,30 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec ConvertMaterials( pScene, sourceMesh->mMaterials); unsigned int numMaterials = std::max( (unsigned int)sourceMesh->mMaterials.size(), 1u); - for( unsigned int b = 0; b < numMaterials; b++) - { + for( unsigned int b = 0; b < numMaterials; ++b ) { // collect the faces belonging to this material std::vector faces; unsigned int numVertices = 0; - if( sourceMesh->mFaceMaterials.size() > 0) - { + if( !sourceMesh->mFaceMaterials.empty() ) { // if there is a per-face material defined, select the faces with the corresponding material - for( unsigned int c = 0; c < sourceMesh->mFaceMaterials.size(); c++) - { - if( sourceMesh->mFaceMaterials[c] == b) - { + for( unsigned int c = 0; c < sourceMesh->mFaceMaterials.size(); ++c ) { + if( sourceMesh->mFaceMaterials[c] == b) { faces.push_back( c); numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size(); } } - } else - { + } else { // if there is no per-face material, place everything into one mesh - for( unsigned int c = 0; c < sourceMesh->mPosFaces.size(); c++) - { + for( unsigned int c = 0; c < sourceMesh->mPosFaces.size(); ++c ) { faces.push_back( c); numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size(); } } // no faces/vertices using this material? strange... - if( numVertices == 0) + if ( numVertices == 0 ) { continue; + } // create a submesh using this material aiMesh* mesh = new aiMesh; @@ -291,11 +282,9 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec // find the material in the scene's material list. Either own material // or referenced material, it should already have a valid index - if( sourceMesh->mFaceMaterials.size() > 0) - { + if( !sourceMesh->mFaceMaterials.empty() ) { mesh->mMaterialIndex = static_cast(sourceMesh->mMaterials[b].sceneIndex); - } else - { + } else { mesh->mMaterialIndex = 0; } @@ -310,28 +299,28 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec mesh->mName.Set(sourceMesh->mName); // normals? - if( sourceMesh->mNormals.size() > 0) - mesh->mNormals = new aiVector3D[numVertices]; + if ( sourceMesh->mNormals.size() > 0 ) { + mesh->mNormals = new aiVector3D[ numVertices ]; + } // texture coords - for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; c++) - { - if( sourceMesh->mTexCoords[c].size() > 0) - mesh->mTextureCoords[c] = new aiVector3D[numVertices]; + for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c ) { + if ( !sourceMesh->mTexCoords[ c ].empty() ) { + mesh->mTextureCoords[ c ] = new aiVector3D[ numVertices ]; + } } // vertex colors - for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; c++) - { - if( sourceMesh->mColors[c].size() > 0) - mesh->mColors[c] = new aiColor4D[numVertices]; + for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c ) { + if ( !sourceMesh->mColors[ c ].empty() ) { + mesh->mColors[ c ] = new aiColor4D[ numVertices ]; + } } // now collect the vertex data of all data streams present in the imported mesh - unsigned int newIndex = 0; + unsigned int newIndex( 0 ); std::vector orgPoints; // from which original point each new vertex stems orgPoints.resize( numVertices, 0); - for( unsigned int c = 0; c < faces.size(); c++) - { + for( unsigned int c = 0; c < faces.size(); ++c ) { unsigned int f = faces[c]; // index of the source face const XFile::Face& pf = sourceMesh->mPosFaces[f]; // position source face @@ -341,30 +330,30 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec df.mIndices = new unsigned int[ df.mNumIndices]; // collect vertex data for indices of this face - for( unsigned int d = 0; d < df.mNumIndices; d++) - { + for( unsigned int d = 0; d < df.mNumIndices; ++d ) { df.mIndices[d] = newIndex; orgPoints[newIndex] = pf.mIndices[d]; // Position mesh->mVertices[newIndex] = sourceMesh->mPositions[pf.mIndices[d]]; // Normal, if present - if( mesh->HasNormals()) - mesh->mNormals[newIndex] = sourceMesh->mNormals[sourceMesh->mNormFaces[f].mIndices[d]]; + if ( mesh->HasNormals() ) { + mesh->mNormals[ newIndex ] = sourceMesh->mNormals[ sourceMesh->mNormFaces[ f ].mIndices[ d ] ]; + } // texture coord sets - for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_TEXTURECOORDS; e++) - { - if( mesh->HasTextureCoords( e)) - { + for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++e ) { + if( mesh->HasTextureCoords( e)) { aiVector2D tex = sourceMesh->mTexCoords[e][pf.mIndices[d]]; mesh->mTextureCoords[e][newIndex] = aiVector3D( tex.x, 1.0f - tex.y, 0.0f); } } // vertex color sets - for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_COLOR_SETS; e++) - if( mesh->HasVertexColors( e)) - mesh->mColors[e][newIndex] = sourceMesh->mColors[e][pf.mIndices[d]]; + for ( unsigned int e = 0; e < AI_MAX_NUMBER_OF_COLOR_SETS; ++e ) { + if ( mesh->HasVertexColors( e ) ) { + mesh->mColors[ e ][ newIndex ] = sourceMesh->mColors[ e ][ pf.mIndices[ d ] ]; + } + } newIndex++; } @@ -376,28 +365,29 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec // convert all bones of the source mesh which influence vertices in this newly created mesh const std::vector& bones = sourceMesh->mBones; std::vector newBones; - for( unsigned int c = 0; c < bones.size(); c++) - { + for( unsigned int c = 0; c < bones.size(); ++c ) { const XFile::Bone& obone = bones[c]; // set up a vertex-linear array of the weights for quick searching if a bone influences a vertex std::vector oldWeights( sourceMesh->mPositions.size(), 0.0); - for( unsigned int d = 0; d < obone.mWeights.size(); d++) - oldWeights[obone.mWeights[d].mVertex] = obone.mWeights[d].mWeight; + for ( unsigned int d = 0; d < obone.mWeights.size(); ++d ) { + oldWeights[ obone.mWeights[ d ].mVertex ] = obone.mWeights[ d ].mWeight; + } // collect all vertex weights that influence a vertex in the new mesh std::vector newWeights; newWeights.reserve( numVertices); - for( unsigned int d = 0; d < orgPoints.size(); d++) - { + for( unsigned int d = 0; d < orgPoints.size(); ++d ) { // does the new vertex stem from an old vertex which was influenced by this bone? ai_real w = oldWeights[orgPoints[d]]; - if( w > 0.0) - newWeights.push_back( aiVertexWeight( d, w)); + if ( w > 0.0 ) { + newWeights.push_back( aiVertexWeight( d, w ) ); + } } // if the bone has no weights in the newly created mesh, ignore it - if( newWeights.size() == 0) + if ( newWeights.empty() ) { continue; + } // create aiBone* nbone = new aiBone; @@ -407,14 +397,14 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec nbone->mOffsetMatrix = obone.mOffsetMatrix; nbone->mNumWeights = (unsigned int)newWeights.size(); nbone->mWeights = new aiVertexWeight[nbone->mNumWeights]; - for( unsigned int d = 0; d < newWeights.size(); d++) - nbone->mWeights[d] = newWeights[d]; + for ( unsigned int d = 0; d < newWeights.size(); ++d ) { + nbone->mWeights[ d ] = newWeights[ d ]; + } } // store the bones in the mesh mesh->mNumBones = (unsigned int)newBones.size(); - if( newBones.size() > 0) - { + if( !newBones.empty()) { mesh->mBones = new aiBone*[mesh->mNumBones]; std::copy( newBones.begin(), newBones.end(), mesh->mBones); } @@ -424,8 +414,7 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec // reallocate scene mesh array to be large enough aiMesh** prevArray = pScene->mMeshes; pScene->mMeshes = new aiMesh*[pScene->mNumMeshes + meshes.size()]; - if( prevArray) - { + if( prevArray) { memcpy( pScene->mMeshes, prevArray, pScene->mNumMeshes * sizeof( aiMesh*)); delete [] prevArray; } @@ -435,8 +424,7 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; // store all meshes in the mesh library of the scene and store their indices in the node - for( unsigned int a = 0; a < meshes.size(); a++) - { + for( unsigned int a = 0; a < meshes.size(); a++) { pScene->mMeshes[pScene->mNumMeshes] = meshes[a]; pNode->mMeshes[a] = pScene->mNumMeshes; pScene->mNumMeshes++; @@ -445,16 +433,15 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec // ------------------------------------------------------------------------------------------------ // Converts the animations from the given imported data and creates them in the scene. -void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData) -{ +void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData) { std::vector newAnims; - for( unsigned int a = 0; a < pData->mAnims.size(); a++) - { + for( unsigned int a = 0; a < pData->mAnims.size(); ++a ) { const XFile::Animation* anim = pData->mAnims[a]; // some exporters mock me with empty animation tags. - if( anim->mAnims.size() == 0) + if ( anim->mAnims.empty() ) { continue; + } // create a new animation to hold the data aiAnimation* nanim = new aiAnimation; @@ -466,15 +453,14 @@ void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData nanim->mNumChannels = (unsigned int)anim->mAnims.size(); nanim->mChannels = new aiNodeAnim*[nanim->mNumChannels]; - for( unsigned int b = 0; b < anim->mAnims.size(); b++) - { + for( unsigned int b = 0; b < anim->mAnims.size(); ++b ) { const XFile::AnimBone* bone = anim->mAnims[b]; aiNodeAnim* nbone = new aiNodeAnim; nbone->mNodeName.Set( bone->mBoneName); nanim->mChannels[b] = nbone; // keyframes are given as combined transformation matrix keys - if( bone->mTrafoKeys.size() > 0) + if( !bone->mTrafoKeys.empty() ) { nbone->mNumPositionKeys = (unsigned int)bone->mTrafoKeys.size(); nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys]; @@ -483,8 +469,7 @@ void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData nbone->mNumScalingKeys = (unsigned int)bone->mTrafoKeys.size(); nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys]; - for( unsigned int c = 0; c < bone->mTrafoKeys.size(); c++) - { + for( unsigned int c = 0; c < bone->mTrafoKeys.size(); ++c) { // deconstruct each matrix into separate position, rotation and scaling double time = bone->mTrafoKeys[c].mTime; aiMatrix4x4 trafo = bone->mTrafoKeys[c].mMatrix; @@ -516,13 +501,11 @@ void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData // longest lasting key sequence determines duration nanim->mDuration = std::max( nanim->mDuration, bone->mTrafoKeys.back().mTime); - } else - { + } else { // separate key sequences for position, rotation, scaling nbone->mNumPositionKeys = (unsigned int)bone->mPosKeys.size(); nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys]; - for( unsigned int c = 0; c < nbone->mNumPositionKeys; c++) - { + for( unsigned int c = 0; c < nbone->mNumPositionKeys; ++c ) { aiVector3D pos = bone->mPosKeys[c].mValue; nbone->mPositionKeys[c].mTime = bone->mPosKeys[c].mTime; @@ -532,8 +515,7 @@ void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData // rotation nbone->mNumRotationKeys = (unsigned int)bone->mRotKeys.size(); nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys]; - for( unsigned int c = 0; c < nbone->mNumRotationKeys; c++) - { + for( unsigned int c = 0; c < nbone->mNumRotationKeys; ++c ) { aiMatrix3x3 rotmat = bone->mRotKeys[c].mValue.GetMatrix(); nbone->mRotationKeys[c].mTime = bone->mRotKeys[c].mTime; @@ -573,56 +555,51 @@ void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector& pMaterials) { // count the non-referrer materials in the array - unsigned int numNewMaterials = 0; - for( unsigned int a = 0; a < pMaterials.size(); a++) - if( !pMaterials[a].mIsReference) - numNewMaterials++; + unsigned int numNewMaterials( 0 ); + for ( unsigned int a = 0; a < pMaterials.size(); ++a ) { + if ( !pMaterials[ a ].mIsReference ) { + ++numNewMaterials; + } + } // resize the scene's material list to offer enough space for the new materials - if( numNewMaterials > 0 ) - { - aiMaterial** prevMats = pScene->mMaterials; - pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials + numNewMaterials]; - if( prevMats) - { - memcpy( pScene->mMaterials, prevMats, pScene->mNumMaterials * sizeof( aiMaterial*)); - delete [] prevMats; - } - } + if( numNewMaterials > 0 ) { + aiMaterial** prevMats = pScene->mMaterials; + pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials + numNewMaterials]; + if( nullptr != prevMats) { + ::memcpy( pScene->mMaterials, prevMats, pScene->mNumMaterials * sizeof( aiMaterial*)); + delete [] prevMats; + } + } // convert all the materials given in the array - for( unsigned int a = 0; a < pMaterials.size(); a++) - { + for( unsigned int a = 0; a < pMaterials.size(); ++a ) { XFile::Material& oldMat = pMaterials[a]; - if( oldMat.mIsReference) - { - // find the material it refers to by name, and store its index - for( size_t a = 0; a < pScene->mNumMaterials; ++a ) - { - aiString name; - pScene->mMaterials[a]->Get( AI_MATKEY_NAME, name); - if( strcmp( name.C_Str(), oldMat.mName.data()) == 0 ) - { - oldMat.sceneIndex = a; - break; + if( oldMat.mIsReference) { + // find the material it refers to by name, and store its index + for( size_t a = 0; a < pScene->mNumMaterials; ++a ) { + aiString name; + pScene->mMaterials[a]->Get( AI_MATKEY_NAME, name); + if( strcmp( name.C_Str(), oldMat.mName.data()) == 0 ) { + oldMat.sceneIndex = a; + break; + } + } + + if( oldMat.sceneIndex == SIZE_MAX ) { + DefaultLogger::get()->warn( format() << "Could not resolve global material reference \"" << oldMat.mName << "\"" ); + oldMat.sceneIndex = 0; + } + + continue; } - } - - if( oldMat.sceneIndex == SIZE_MAX ) - { - DefaultLogger::get()->warn( format() << "Could not resolve global material reference \"" << oldMat.mName << "\"" ); - oldMat.sceneIndex = 0; - } - - continue; - } aiMaterial* mat = new aiMaterial; aiString name; name.Set( oldMat.mName); mat->AddProperty( &name, AI_MATKEY_NAME); - // Shading model: hardcoded to PHONG, there is no such information in an XFile + // Shading model: hard-coded to PHONG, there is no such information in an XFile // FIX (aramis): If the specular exponent is 0, use gouraud shading. This is a bugfix // for some models in the SDK (e.g. good old tiny.x) int shadeMode = (int)oldMat.mSpecularExponent == 0.0f @@ -630,8 +607,8 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vectorAddProperty( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); // material colours - // Unclear: there's no ambient colour, but emissive. What to put for ambient? - // Probably nothing at all, let the user select a suitable default. + // Unclear: there's no ambient colour, but emissive. What to put for ambient? + // Probably nothing at all, let the user select a suitable default. mat->AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); mat->AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); mat->AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); @@ -639,36 +616,33 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vectorAddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS(0)); - else - mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(0)); + if ( otex.mIsNormalMap ) { + mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS( 0 ) ); + } else { + mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE( 0 ) ); + } } - } - else - { + } else { // Otherwise ... try to search for typical strings in the // texture's file name like 'bump' or 'diffuse' unsigned int iHM = 0,iNM = 0,iDM = 0,iSM = 0,iAM = 0,iEM = 0; - for( unsigned int b = 0; b < oldMat.mTextures.size(); b++) - { + for( unsigned int b = 0; b < oldMat.mTextures.size(); ++b ) { const XFile::TexEntry& otex = oldMat.mTextures[b]; std::string sz = otex.mName; - if (!sz.length())continue; - + if ( !sz.length() ) { + continue; + } // find the file name - //const size_t iLen = sz.length(); std::string::size_type s = sz.find_last_of("\\/"); - if (std::string::npos == s) + if ( std::string::npos == s ) { s = 0; + } // cut off the file extension std::string::size_type sExt = sz.find_last_of('.'); @@ -677,36 +651,27 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vectorAddProperty( &tex, AI_MATKEY_TEXTURE_HEIGHT(iHM++)); - } else - if (otex.mIsNormalMap || std::string::npos != sz.find( "normal", s) || std::string::npos != sz.find("nm", s)) - { + } else if (otex.mIsNormalMap || std::string::npos != sz.find( "normal", s) || std::string::npos != sz.find("nm", s)) { mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS(iNM++)); - } else - if (std::string::npos != sz.find( "spec", s) || std::string::npos != sz.find( "glanz", s)) - { + } else if (std::string::npos != sz.find( "spec", s) || std::string::npos != sz.find( "glanz", s)) { mat->AddProperty( &tex, AI_MATKEY_TEXTURE_SPECULAR(iSM++)); - } else - if (std::string::npos != sz.find( "ambi", s) || std::string::npos != sz.find( "env", s)) - { + } else if (std::string::npos != sz.find( "ambi", s) || std::string::npos != sz.find( "env", s)) { mat->AddProperty( &tex, AI_MATKEY_TEXTURE_AMBIENT(iAM++)); - } else - if (std::string::npos != sz.find( "emissive", s) || std::string::npos != sz.find( "self", s)) - { + } else if (std::string::npos != sz.find( "emissive", s) || std::string::npos != sz.find( "self", s)) { mat->AddProperty( &tex, AI_MATKEY_TEXTURE_EMISSIVE(iEM++)); - } else - { + } else { // Assume it is a diffuse texture mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(iDM++)); } diff --git a/code/XFileParser.cpp b/code/XFileParser.cpp index 4017ed596..bda59ba59 100644 --- a/code/XFileParser.cpp +++ b/code/XFileParser.cpp @@ -1460,7 +1460,6 @@ aiColor3D XFileParser::ReadRGB() // ------------------------------------------------------------------------------------------------ // Throws an exception with a line number and the given text. AI_WONT_RETURN void XFileParser::ThrowException( const std::string& pText) { - delete mScene; if ( mIsBinaryFormat ) { throw DeadlyImportError( pText ); } else { From 990dc983ed2634bc764e6c4c1e6d63499eff6d58 Mon Sep 17 00:00:00 2001 From: Max Vollmer Date: Wed, 7 Feb 2018 10:48:39 +0100 Subject: [PATCH 045/183] Issue #1776 Fixed potential crash bug in ObjectCompare, because it didn't follow strict weak ordering. As counter-intuitive as it seems, a comparator must return false for equal values. The C++ standard defines and expects this behavior: true if lhs < rhs, false otherwise. --- code/BlenderIntermediate.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/code/BlenderIntermediate.h b/code/BlenderIntermediate.h index 74184a1f1..f3d34d1b2 100644 --- a/code/BlenderIntermediate.h +++ b/code/BlenderIntermediate.h @@ -122,9 +122,11 @@ namespace Blender { # pragma warning(disable:4351) #endif + // As counter-intuitive as it may seem, a comparator must return false for equal values. + // The C++ standard defines and expects this behavior: true if lhs < rhs, false otherwise. struct ObjectCompare { bool operator() (const Object* left, const Object* right) const { - return ::strncmp(left->id.name, right->id.name, strlen( left->id.name ) ) == 0; + return ::strncmp(left->id.name, right->id.name, strlen( left->id.name ) ) < 0; } }; @@ -143,9 +145,11 @@ namespace Blender { , db(db) {} + // As counter-intuitive as it may seem, a comparator must return false for equal values. + // The C++ standard defines and expects this behavior: true if lhs < rhs, false otherwise. struct ObjectCompare { bool operator() (const Object* left, const Object* right) const { - return ::strncmp( left->id.name, right->id.name, strlen( left->id.name ) ) == 0; + return ::strncmp( left->id.name, right->id.name, strlen( left->id.name ) ) < 0; } }; From b3d48d0e9a5e815c2a4acc7662736bb902a362cd Mon Sep 17 00:00:00 2001 From: Max Vollmer Date: Wed, 7 Feb 2018 11:02:08 +0100 Subject: [PATCH 046/183] Issue #1776: Updated and fixed test in utBlenderIntermediate.cpp for Blender::ObjectCompare --- test/unit/utBlenderIntermediate.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/test/unit/utBlenderIntermediate.cpp b/test/unit/utBlenderIntermediate.cpp index 9146d6d1e..55490e613 100644 --- a/test/unit/utBlenderIntermediate.cpp +++ b/test/unit/utBlenderIntermediate.cpp @@ -57,17 +57,26 @@ class BlenderIntermediateTest : public ::testing::Test { #define NAME_1 "name1" #define NAME_2 "name2" +// Updated this test after fixing #1776: +// A comparator in C++ is used for ordering and must implement strict weak ordering, +// which means it must return false for equal values. +// The C++ standard defines and expects this behavior: true if lhs < rhs, false otherwise. TEST_F( BlenderIntermediateTest,ConversionData_ObjectCompareTest ) { Object obj1, obj2; strncpy( obj1.id.name, NAME_1, sizeof(NAME_1) ); strncpy( obj2.id.name, NAME_2, sizeof(NAME_2) ); - Blender::ObjectCompare cmp_false; - bool res( cmp_false( &obj1, &obj2 ) ); + + Blender::ObjectCompare cmp_true_because_first_is_smaller_than_second; + bool res( cmp_true_because_first_is_smaller_than_second( &obj1, &obj2 ) ); + EXPECT_TRUE( res ); + + Blender::ObjectCompare cmp_false_because_equal; + res = cmp_false_because_equal( &obj1, &obj1 ); EXPECT_FALSE( res ); - Blender::ObjectCompare cmp_true; - res = cmp_true( &obj1, &obj1 ); - EXPECT_TRUE( res ); + Blender::ObjectCompare cmp_false_because_first_is_greater_than_second; + res = cmp_false_because_first_is_greater_than_second( &obj2, &obj1 ); + EXPECT_FALSE( res ); } From c7ea536351ffe7114992a1903af0de0ead46e8b5 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 7 Feb 2018 20:21:05 +0100 Subject: [PATCH 047/183] closes https://github.com/assimp/assimp/issues/1386: fix undefined behaviour in compare function. --- code/BlenderDNA.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/code/BlenderDNA.cpp b/code/BlenderDNA.cpp index 285cb05b8..1d88f8fa6 100644 --- a/code/BlenderDNA.cpp +++ b/code/BlenderDNA.cpp @@ -58,12 +58,11 @@ using namespace Assimp::Formatter; static bool match4(StreamReaderAny& stream, const char* string) { ai_assert( nullptr != string ); - char tmp[] = { - (const char)(stream).GetI1(), - (const char)(stream).GetI1(), - (const char)(stream).GetI1(), - (const char)(stream).GetI1() - }; + char tmp[4]; + tmp[ 0 ] = ( stream ).GetI1(); + tmp[ 1 ] = ( stream ).GetI1(); + tmp[ 2 ] = ( stream ).GetI1(); + tmp[ 3 ] = ( stream ).GetI1(); return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]); } From 674a47dd3eaffa1a89db75d258d2a2cd190cbcec Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 8 Feb 2018 20:24:18 +0100 Subject: [PATCH 048/183] closes https://github.com/assimp/assimp/issues/567: prevend dependency cycle --- code/ASELoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ASELoader.cpp b/code/ASELoader.cpp index 122b226f4..32b0eae2d 100644 --- a/code/ASELoader.cpp +++ b/code/ASELoader.cpp @@ -583,7 +583,7 @@ void ASEImporter::AddNodes (const std::vector& nodes, node->mTransformation = mParentAdjust*snode->mTransform; // Add sub nodes - prevent stack overflow due to recursive parenting - if (node->mName != node->mParent->mName) { + if (node->mName != node->mParent->mName && node->mName != node->mParent->mParent->mName ) { AddNodes(nodes,node,node->mName.data,snode->mTransform); } From ebdad4f883adfc16133badf5087043894a773f1d Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 9 Feb 2018 15:40:28 +0200 Subject: [PATCH 049/183] Ogre: Avoid creating function-scoped static constants They're causing false positive race condition messages from Helgrind --- code/OgreXmlSerializer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/OgreXmlSerializer.cpp b/code/OgreXmlSerializer.cpp index c777cf363..78acaa7fd 100644 --- a/code/OgreXmlSerializer.cpp +++ b/code/OgreXmlSerializer.cpp @@ -835,7 +835,7 @@ void OgreXmlSerializer::ReadAnimationTracks(Animation *dest) void OgreXmlSerializer::ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *dest) { - static const aiVector3D zeroVec(0.f, 0.f, 0.f); + const aiVector3D zeroVec(0.f, 0.f, 0.f); NextNode(); while(m_currentNodeName == nnKeyFrame) From 59ea3b6c8577c4e46e8ca0ca68afcf9efa92cf0a Mon Sep 17 00:00:00 2001 From: Marco Di Benedetto Date: Fri, 9 Feb 2018 16:02:27 +0100 Subject: [PATCH 050/183] fixed android zlib compile error. --- CMakeLists.txt | 8 ++++---- contrib/zlib/zlib.h | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d3616ab7..15374a3fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -230,10 +230,10 @@ ELSEIF( CMAKE_COMPILER_IS_MINGW ) ADD_DEFINITIONS( -U__STRICT_ANSI__ ) ENDIF() -if(IOS) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3") -endif(IOS) +if (IOS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3") +endif() if (ASSIMP_COVERALLS) MESSAGE(STATUS "Coveralls enabled") diff --git a/contrib/zlib/zlib.h b/contrib/zlib/zlib.h index 2d6bb2976..dcb7b5063 100644 --- a/contrib/zlib/zlib.h +++ b/contrib/zlib/zlib.h @@ -77,8 +77,9 @@ extern "C" { the consistency of the compressed data, so the library should never crash even in the case of corrupted input. */ -#ifdef __ANDROID__ -using zcrc_t = unsigned_long; + +#ifdef __ANDROID__ +typedef unsigned long zcrc_t; #endif typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); From c874fd8ae2626334f7920715d4e6977529297dff Mon Sep 17 00:00:00 2001 From: Marco Di Benedetto Date: Fri, 9 Feb 2018 16:18:49 +0100 Subject: [PATCH 051/183] changed std::to_string to to_string. --- code/3DSLoader.cpp | 2 +- code/glTFAsset.inl | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/code/3DSLoader.cpp b/code/3DSLoader.cpp index 9d61e125c..92a64e3d8 100644 --- a/code/3DSLoader.cpp +++ b/code/3DSLoader.cpp @@ -349,7 +349,7 @@ void Discreet3DSImporter::ParseObjectChunk() case Discreet3DS::CHUNK_MAT_MATERIAL: // Add a new material to the list - mScene->mMaterials.push_back(D3DS::Material(std::string("UNNAMED_" + std::to_string(mScene->mMaterials.size())))); + mScene->mMaterials.push_back(D3DS::Material(std::string("UNNAMED_" + to_string(mScene->mMaterials.size())))); ParseMaterialChunk(); break; diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl index b31846abd..bd43b19f2 100644 --- a/code/glTFAsset.inl +++ b/code/glTFAsset.inl @@ -948,24 +948,24 @@ Ref buf = pAsset_Root.buffers.Get(pCompression_Open3DGC.Buffer); size_t size_coordindex = ifs.GetNCoordIndex() * 3;// See float attributes note. if(primitives[0].indices->count != size_coordindex) - throw DeadlyImportError("GLTF: Open3DGC. Compressed indices count (" + std::to_string(size_coordindex) + - ") not equal to uncompressed (" + std::to_string(primitives[0].indices->count) + ")."); + throw DeadlyImportError("GLTF: Open3DGC. Compressed indices count (" + to_string(size_coordindex) + + ") not equal to uncompressed (" + to_string(primitives[0].indices->count) + ")."); size_coordindex *= sizeof(IndicesType); // Coordinates size_t size_coord = ifs.GetNCoord();// See float attributes note. if(primitives[0].attributes.position[0]->count != size_coord) - throw DeadlyImportError("GLTF: Open3DGC. Compressed positions count (" + std::to_string(size_coord) + - ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.position[0]->count) + ")."); + throw DeadlyImportError("GLTF: Open3DGC. Compressed positions count (" + to_string(size_coord) + + ") not equal to uncompressed (" + to_string(primitives[0].attributes.position[0]->count) + ")."); size_coord *= 3 * sizeof(float); // Normals size_t size_normal = ifs.GetNNormal();// See float attributes note. if(primitives[0].attributes.normal[0]->count != size_normal) - throw DeadlyImportError("GLTF: Open3DGC. Compressed normals count (" + std::to_string(size_normal) + - ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.normal[0]->count) + ")."); + throw DeadlyImportError("GLTF: Open3DGC. Compressed normals count (" + to_string(size_normal) + + ") not equal to uncompressed (" + to_string(primitives[0].attributes.normal[0]->count) + ")."); size_normal *= 3 * sizeof(float); // Additional attributes. @@ -989,8 +989,8 @@ Ref buf = pAsset_Root.buffers.Get(pCompression_Open3DGC.Buffer); if(idx_texcoord < primitives[0].attributes.texcoord.size()) { if(primitives[0].attributes.texcoord[idx]->count != tval) - throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (" + std::to_string(tval) + - ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.texcoord[idx]->count) + ")."); + throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (" + to_string(tval) + + ") not equal to uncompressed (" + to_string(primitives[0].attributes.texcoord[idx]->count) + ")."); idx_texcoord++; } From f00d450d254d5ba56fdad4bf004127847ee6b551 Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Sun, 11 Feb 2018 17:43:49 +0100 Subject: [PATCH 052/183] Update BlenderDNA.inl --- code/BlenderDNA.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/BlenderDNA.inl b/code/BlenderDNA.inl index de85815cc..c6a3779b1 100644 --- a/code/BlenderDNA.inl +++ b/code/BlenderDNA.inl @@ -501,7 +501,7 @@ const FileBlockHead* Structure :: LocateFileBlockForAddress(const Pointer & ptrv { // the file blocks appear in list sorted by // with ascending base addresses so we can run a - // binary search to locate the pointee quickly. + // binary search to locate the pointer quickly. // NOTE: Blender seems to distinguish between side-by-side // data (stored in the same data block) and far pointers, From 76b981aa2a7542e71220f737f2a8c8a22ab0d740 Mon Sep 17 00:00:00 2001 From: mbuchner Date: Mon, 12 Feb 2018 10:52:49 +0100 Subject: [PATCH 053/183] Make MemoryIOStream::Seek accept pos=length as valid Fixes assimp/assimp#1781. --- include/assimp/MemoryIOWrapper.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/assimp/MemoryIOWrapper.h b/include/assimp/MemoryIOWrapper.h index 0e9b23447..bfcfff9c2 100644 --- a/include/assimp/MemoryIOWrapper.h +++ b/include/assimp/MemoryIOWrapper.h @@ -99,19 +99,19 @@ public: // Seek specific position aiReturn Seek(size_t pOffset, aiOrigin pOrigin) { if (aiOrigin_SET == pOrigin) { - if (pOffset >= length) { + if (pOffset > length) { return AI_FAILURE; } pos = pOffset; } else if (aiOrigin_END == pOrigin) { - if (pOffset >= length) { + if (pOffset > length) { return AI_FAILURE; } pos = length-pOffset; } else { - if (pOffset+pos >= length) { + if (pOffset+pos > length) { return AI_FAILURE; } pos += pOffset; From 12999050262d8a6213cf6b1f136a11fa80f57066 Mon Sep 17 00:00:00 2001 From: mbuchner Date: Mon, 12 Feb 2018 11:20:03 +0100 Subject: [PATCH 054/183] Make FileSystemFilter forward all virtual functions to wrapped IOSystem instance This makes it possible to override those functions. Previously the default implementation was always used. Fixes #1773 --- code/FileSystemFilter.h | 49 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/code/FileSystemFilter.h b/code/FileSystemFilter.h index 0fabb41dd..10b18bd78 100644 --- a/code/FileSystemFilter.h +++ b/code/FileSystemFilter.h @@ -168,6 +168,55 @@ public: return wrapped->ComparePaths (one,second); } + // ------------------------------------------------------------------- + /** Pushes a new directory onto the directory stack. */ + bool PushDirectory(const std::string &path) + { + return wrapped->PushDirectory(path); + } + + // ------------------------------------------------------------------- + /** Returns the top directory from the stack. */ + const std::string &CurrentDirectory() const + { + return wrapped->CurrentDirectory(); + } + + // ------------------------------------------------------------------- + /** Returns the number of directories stored on the stack. */ + size_t StackSize() const + { + return wrapped->StackSize(); + } + + // ------------------------------------------------------------------- + /** Pops the top directory from the stack. */ + bool PopDirectory() + { + return wrapped->PopDirectory(); + } + + // ------------------------------------------------------------------- + /** Creates an new directory at the given path. */ + bool CreateDirectory(const std::string &path) + { + return wrapped->CreateDirectory(path); + } + + // ------------------------------------------------------------------- + /** Will change the current directory to the given path. */ + bool ChangeDirectory(const std::string &path) + { + return wrapped->ChangeDirectory(path); + } + + // ------------------------------------------------------------------- + /** Delete file. */ + bool DeleteFile(const std::string &file) + { + return wrapped->DeleteFile(file); + } + private: // ------------------------------------------------------------------- From 78173177e895e08548c1f3ecc68352d048355634 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 12 Feb 2018 21:45:47 +0100 Subject: [PATCH 055/183] closes https://github.com/assimp/assimp/issues/1564: remove copy constructor. --- code/Importer.cpp | 14 +--- include/assimp/Importer.hpp | 2 +- include/assimp/SpatialSort.h | 18 +++-- include/assimp/fast_atof.h | 147 ++++++++++++++++------------------- include/assimp/mesh.h | 47 +++++++---- 5 files changed, 115 insertions(+), 113 deletions(-) diff --git a/code/Importer.cpp b/code/Importer.cpp index 4ebc72420..feddadf58 100644 --- a/code/Importer.cpp +++ b/code/Importer.cpp @@ -190,7 +190,7 @@ Importer::~Importer() delete pimpl->mIOHandler; delete pimpl->mProgressHandler; - // Kill imported scene. Destructors should do that recursivly + // Kill imported scene. Destructor's should do that recursively delete pimpl->mScene; // Delete shared post-processing data @@ -200,18 +200,6 @@ Importer::~Importer() delete pimpl; } -// ------------------------------------------------------------------------------------------------ -// Copy constructor - copies the config of another Importer, not the scene -Importer::Importer(const Importer &other) - : pimpl(NULL) { - new(this) Importer(); - - pimpl->mIntProperties = other.pimpl->mIntProperties; - pimpl->mFloatProperties = other.pimpl->mFloatProperties; - pimpl->mStringProperties = other.pimpl->mStringProperties; - pimpl->mMatrixProperties = other.pimpl->mMatrixProperties; -} - // ------------------------------------------------------------------------------------------------ // Register a custom post-processing step aiReturn Importer::RegisterPPStep(BaseProcess* pImp) diff --git a/include/assimp/Importer.hpp b/include/assimp/Importer.hpp index 9ae8fccc6..7445c9797 100644 --- a/include/assimp/Importer.hpp +++ b/include/assimp/Importer.hpp @@ -137,7 +137,7 @@ public: * If this Importer owns a scene it won't be copied. * Call ReadFile() to start the import process. */ - Importer(const Importer& other); + Importer(const Importer& other)=delete; // ------------------------------------------------------------------- /** Assignment operator has been deleted diff --git a/include/assimp/SpatialSort.h b/include/assimp/SpatialSort.h index 323c2970f..4c45e95ad 100644 --- a/include/assimp/SpatialSort.h +++ b/include/assimp/SpatialSort.h @@ -47,8 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -namespace Assimp -{ +namespace Assimp { // ------------------------------------------------------------------------------------------------ /** A little helper class to quickly find all vertices in the epsilon environment of a given @@ -148,17 +147,20 @@ protected: aiVector3D mPlaneNormal; /** An entry in a spatially sorted position array. Consists of a vertex index, - * its position and its precalculated distance from the reference plane */ - struct Entry - { + * its position and its pre-calculated distance from the reference plane */ + struct Entry { unsigned int mIndex; ///< The vertex referred by this entry aiVector3D mPosition; ///< Position ai_real mDistance; ///< Distance of this vertex to the sorting plane - Entry() { /** intentionally not initialized.*/ } + Entry() + : mIndex( 999999999 ), mPosition(), mDistance( 99999. ) { + // empty + } Entry( unsigned int pIndex, const aiVector3D& pPosition, ai_real pDistance) - : mIndex( pIndex), mPosition( pPosition), mDistance( pDistance) - { } + : mIndex( pIndex), mPosition( pPosition), mDistance( pDistance) { + // empty + } bool operator < (const Entry& e) const { return mDistance < e.mDistance; } }; diff --git a/include/assimp/fast_atof.h b/include/assimp/fast_atof.h index 058a7ff87..06fd938c0 100644 --- a/include/assimp/fast_atof.h +++ b/include/assimp/fast_atof.h @@ -59,69 +59,65 @@ const double fast_atof_table[16] = { // we write [16] here instead of [] to wo // ------------------------------------------------------------------------------------ // Convert a string in decimal format to a number // ------------------------------------------------------------------------------------ -inline unsigned int strtoul10( const char* in, const char** out=0) -{ +inline +unsigned int strtoul10( const char* in, const char** out=0) { unsigned int value = 0; - bool running = true; - while ( running ) - { + for ( ;; ) { if ( *in < '0' || *in > '9' ) break; value = ( value * 10 ) + ( *in - '0' ); ++in; } - if (out)*out = in; + if ( out ) { + *out = in; + } return value; } // ------------------------------------------------------------------------------------ // Convert a string in octal format to a number // ------------------------------------------------------------------------------------ -inline unsigned int strtoul8( const char* in, const char** out=0) -{ - unsigned int value = 0; - - bool running = true; - while ( running ) - { - if ( *in < '0' || *in > '7' ) +inline +unsigned int strtoul8( const char* in, const char** out=0) { + unsigned int value( 0 ); + for ( ;; ) { + if ( *in < '0' || *in > '7' ) { break; + } value = ( value << 3 ) + ( *in - '0' ); ++in; } - if (out)*out = in; + if ( out ) { + *out = in; + } return value; } // ------------------------------------------------------------------------------------ // Convert a string in hex format to a number // ------------------------------------------------------------------------------------ -inline unsigned int strtoul16( const char* in, const char** out=0) -{ - unsigned int value = 0; - - bool running = true; - while ( running ) - { - if ( *in >= '0' && *in <= '9' ) - { +inline +unsigned int strtoul16( const char* in, const char** out=0) { + unsigned int value( 0 ); + for ( ;; ) { + if ( *in >= '0' && *in <= '9' ) { value = ( value << 4u ) + ( *in - '0' ); - } - else if (*in >= 'A' && *in <= 'F') - { + } else if (*in >= 'A' && *in <= 'F') { value = ( value << 4u ) + ( *in - 'A' ) + 10; - } - else if (*in >= 'a' && *in <= 'f') - { + } else if (*in >= 'a' && *in <= 'f') { value = ( value << 4u ) + ( *in - 'a' ) + 10; } - else break; + else { + break; + } ++in; } - if (out)*out = in; + if ( out ) { + *out = in; + } return value; } @@ -129,17 +125,16 @@ inline unsigned int strtoul16( const char* in, const char** out=0) // Convert just one hex digit // Return value is UINT_MAX if the input character is not a hex digit. // ------------------------------------------------------------------------------------ -inline unsigned int HexDigitToDecimal(char in) -{ - unsigned int out = UINT_MAX; - if (in >= '0' && in <= '9') +inline +unsigned int HexDigitToDecimal(char in) { + unsigned int out( UINT_MAX ); + if ( in >= '0' && in <= '9' ) { out = in - '0'; - - else if (in >= 'a' && in <= 'f') + } else if ( in >= 'a' && in <= 'f' ) { out = 10u + in - 'a'; - - else if (in >= 'A' && in <= 'F') + } else if ( in >= 'A' && in <= 'F' ) { out = 10u + in - 'A'; + } // return value is UINT_MAX if the input is not a hex digit return out; @@ -148,8 +143,8 @@ inline unsigned int HexDigitToDecimal(char in) // ------------------------------------------------------------------------------------ // Convert a hex-encoded octet (2 characters, i.e. df or 1a). // ------------------------------------------------------------------------------------ -inline uint8_t HexOctetToDecimal(const char* in) -{ +inline +uint8_t HexOctetToDecimal(const char* in) { return ((uint8_t)HexDigitToDecimal(in[0])<<4)+(uint8_t)HexDigitToDecimal(in[1]); } @@ -157,11 +152,12 @@ inline uint8_t HexOctetToDecimal(const char* in) // ------------------------------------------------------------------------------------ // signed variant of strtoul10 // ------------------------------------------------------------------------------------ -inline int strtol10( const char* in, const char** out=0) -{ +inline +int strtol10( const char* in, const char** out=0) { bool inv = (*in=='-'); - if (inv || *in=='+') + if ( inv || *in == '+' ) { ++in; + } int value = strtoul10(in,out); if (inv) { @@ -176,10 +172,9 @@ inline int strtol10( const char* in, const char** out=0) // 0NNN - oct // NNN - dec // ------------------------------------------------------------------------------------ -inline unsigned int strtoul_cppstyle( const char* in, const char** out=0) -{ - if ('0' == in[0]) - { +inline +unsigned int strtoul_cppstyle( const char* in, const char** out=0) { + if ('0' == in[0]) { return 'x' == in[1] ? strtoul16(in+2,out) : strtoul8(in+1,out); } return strtoul10(in, out); @@ -189,19 +184,20 @@ inline unsigned int strtoul_cppstyle( const char* in, const char** out=0) // Special version of the function, providing higher accuracy and safety // It is mainly used by fast_atof to prevent ugly and unwanted integer overflows. // ------------------------------------------------------------------------------------ -inline uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_inout=0) -{ +inline +uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_inout=0) { unsigned int cur = 0; uint64_t value = 0; - if ( *in < '0' || *in > '9' ) - throw std::invalid_argument(std::string("The string \"") + in + "\" cannot be converted into a value."); + if ( *in < '0' || *in > '9' ) { + throw std::invalid_argument( std::string( "The string \"" ) + in + "\" cannot be converted into a value." ); + } bool running = true; - while ( running ) - { - if ( *in < '0' || *in > '9' ) + for ( ;; ) { + if ( *in < '0' || *in > '9' ) { break; + } const uint64_t new_value = ( value * 10 ) + ( *in - '0' ); @@ -240,8 +236,8 @@ inline uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* // ------------------------------------------------------------------------------------ // signed variant of strtoul10_64 // ------------------------------------------------------------------------------------ -inline int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* max_inout = 0) -{ +inline +int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* max_inout = 0) { bool inv = (*in == '-'); if (inv || *in == '+') ++in; @@ -263,8 +259,8 @@ inline int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* m // If you find any bugs, please send them to me, niko (at) irrlicht3d.org. // ------------------------------------------------------------------------------------ template -inline const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) -{ +inline +const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) { Real f = 0; bool inv = (*c == '-'); @@ -272,37 +268,32 @@ inline const char* fast_atoreal_move(const char* c, Real& out, bool check_comma ++c; } - if ((c[0] == 'N' || c[0] == 'n') && ASSIMP_strincmp(c, "nan", 3) == 0) - { + if ((c[0] == 'N' || c[0] == 'n') && ASSIMP_strincmp(c, "nan", 3) == 0) { out = std::numeric_limits::quiet_NaN(); c += 3; return c; } - if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inf", 3) == 0) - { + if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inf", 3) == 0) { out = std::numeric_limits::infinity(); if (inv) { out = -out; } c += 3; - if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inity", 5) == 0) - { + if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inity", 5) == 0) { c += 5; } return c; } if (!(c[0] >= '0' && c[0] <= '9') && - !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) - { + !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) { throw std::invalid_argument("Cannot parse string " "as real number: does not start with digit " "or decimal point followed by digit."); } - if (*c != '.' && (! check_comma || c[0] != ',')) - { + if (*c != '.' && (! check_comma || c[0] != ',')) { f = static_cast( strtoul10_64 ( c, &c) ); } @@ -358,30 +349,30 @@ inline const char* fast_atoreal_move(const char* c, Real& out, bool check_comma // ------------------------------------------------------------------------------------ // The same but more human. -inline ai_real fast_atof(const char* c) -{ +inline +ai_real fast_atof(const char* c) { ai_real ret(0.0); fast_atoreal_move(c, ret); return ret; } -inline ai_real fast_atof( const char* c, const char** cout) -{ +inline +ai_real fast_atof( const char* c, const char** cout) { ai_real ret(0.0); *cout = fast_atoreal_move(c, ret); return ret; } -inline ai_real fast_atof( const char** inout) -{ +inline +ai_real fast_atof( const char** inout) { ai_real ret(0.0); *inout = fast_atoreal_move(*inout, ret); return ret; } -} // end of namespace Assimp +} //! namespace Assimp -#endif +#endif // __FAST_A_TO_F_H_INCLUDED__ diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index 5896284f6..b2f64195f 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -200,8 +200,7 @@ struct aiFace // --------------------------------------------------------------------------- /** @brief A single influence of a bone on a vertex. */ -struct aiVertexWeight -{ +struct aiVertexWeight { //! Index of the vertex which is influenced by the bone. unsigned int mVertexId; @@ -214,15 +213,26 @@ struct aiVertexWeight //! Default constructor aiVertexWeight() : mVertexId(0) - , mWeight(0.0f) - { } + , mWeight(0.0f) { + // empty + } //! Initialisation from a given index and vertex weight factor //! \param pID ID //! \param pWeight Vertex weight factor - aiVertexWeight( unsigned int pID, float pWeight) - : mVertexId( pID), mWeight( pWeight) - { /* nothing to do here */ } + aiVertexWeight( unsigned int pID, float pWeight ) + : mVertexId( pID ) + , mWeight( pWeight ) { + // empty + } + + bool operator == ( const aiVertexWeight &rhs ) const { + return ( mVertexId == rhs.mVertexId && mWeight == rhs.mWeight ); + } + + bool operator != ( const aiVertexWeight &rhs ) const { + return ( *this == rhs ); + } #endif // __cplusplus }; @@ -235,8 +245,7 @@ struct aiVertexWeight * which it can be addressed by animations. In addition it has a number of * influences on vertices. */ -struct aiBone -{ +struct aiBone { //! The name of the bone. C_STRUCT aiString mName; @@ -254,10 +263,10 @@ struct aiBone //! Default constructor aiBone() - : mName() - , mNumWeights( 0 ) - , mWeights( NULL ) - { + : mName() + , mNumWeights( 0 ) + , mWeights( nullptr ) { + // empty } //! Copy constructor @@ -298,7 +307,19 @@ struct aiBone return *this; } + bool operator == ( const aiBone &rhs ) const { + if ( mName != rhs.mName || mNumWeights != rhs.mNumWeights ) { + return false; + } + for ( size_t i = 0; i < mNumWeights; ++i ) { + if ( mWeights[ i ] != rhs.mWeights[ i ] ) { + return false; + } + } + + return true; + } //! Destructor - deletes the array of vertex weights ~aiBone() { From 8a4fd2f9477b6d624f9f94f2f1367c84e81e5a5d Mon Sep 17 00:00:00 2001 From: Sergio Acereda Date: Mon, 12 Feb 2018 21:51:40 +0100 Subject: [PATCH 056/183] globalScale process not registered --- code/PostStepRegistry.cpp | 9 +++++++-- code/ScaleProcess.cpp | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/code/PostStepRegistry.cpp b/code/PostStepRegistry.cpp index 49f5d7375..2a5e211c1 100644 --- a/code/PostStepRegistry.cpp +++ b/code/PostStepRegistry.cpp @@ -125,6 +125,9 @@ corresponding preprocessor flag to selectively disable steps. #ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS # include "DeboneProcess.h" #endif +#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) +# include "ScaleProcess.h" +#endif namespace Assimp { @@ -136,7 +139,7 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out) // of sequence it is executed. Steps that are added here are not // validated - as RegisterPPStep() does - all dependencies must be given. // ---------------------------------------------------------------------------- - out.reserve(30); + out.reserve(31); #if (!defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS) out.push_back( new MakeLeftHandedProcess()); #endif @@ -197,7 +200,9 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out) #if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS) out.push_back( new GenFaceNormalsProcess()); #endif - +#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) + out.push_back( new ScaleProcess()); +#endif // ......................................................................... // DON'T change the order of these five .. // XXX this is actually a design weakness that dates back to the time diff --git a/code/ScaleProcess.cpp b/code/ScaleProcess.cpp index ef7357b68..0f60fbdb4 100644 --- a/code/ScaleProcess.cpp +++ b/code/ScaleProcess.cpp @@ -39,6 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ +#ifndef ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS + #include "ScaleProcess.h" #include @@ -104,3 +106,5 @@ void ScaleProcess::applyScaling( aiNode *currentNode ) { } } // Namespace Assimp + +#endif // !! ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS From 331e67c32d7b1e685c704d552ed08f25bb03e7ba Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 12 Feb 2018 22:07:12 +0100 Subject: [PATCH 057/183] 3mf: introduce first prototype for basematerial support. --- code/3MFXmlTags.h | 7 ++- code/D3MFImporter.cpp | 137 ++++++++++++++++++++++++++++++------------ 2 files changed, 106 insertions(+), 38 deletions(-) diff --git a/code/3MFXmlTags.h b/code/3MFXmlTags.h index 30aed0e95..20971a38f 100644 --- a/code/3MFXmlTags.h +++ b/code/3MFXmlTags.h @@ -69,6 +69,12 @@ namespace XmlTag { static const std::string objectid = "objectid"; static const std::string transform = "transform"; + // Material definitions + static const std::string basematerials = "basematerials"; + static const std::string basematerials_base = "base"; + static const std::string basematerials_name = "name"; + static const std::string basematerials_displaycolor = "displaycolor"; + static const std::string CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; static const std::string ROOT_RELATIONSHIPS_ARCHIVE = "_rels/.rels"; static const std::string SCHEMA_CONTENTTYPES = "http://schemas.openxmlformats.org/package/2006/content-types"; @@ -83,7 +89,6 @@ namespace XmlTag { static const std::string PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture"; static const std::string PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; static const std::string PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; - } } // Namespace D3MF diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 0777a55fa..823fae74c 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -68,7 +68,9 @@ namespace D3MF { class XmlSerializer { public: XmlSerializer(XmlReader* xmlReader) - : xmlReader(xmlReader) { + : xmlReader(xmlReader) + , mMeshes() + , mMaterials() { // empty } @@ -77,6 +79,10 @@ public: } void ImportXml(aiScene* scene) { + if ( nullptr != scene ) { + return; + } + scene->mRootNode = new aiNode(); std::vector children; @@ -84,7 +90,9 @@ public: if(xmlReader->getNodeName() == D3MF::XmlTag::object) { children.push_back(ReadObject(scene)); } else if(xmlReader->getNodeName() == D3MF::XmlTag::build) { - + // + } else if ( xmlReader->getNodeName() == D3MF::XmlTag::basematerials ) { + ReadBaseMaterials(); } } @@ -92,10 +100,10 @@ public: scene->mRootNode->mName.Set( "3MF" ); } - scene->mNumMeshes = static_cast(meshes.size()); + scene->mNumMeshes = static_cast( mMeshes.size()); scene->mMeshes = new aiMesh*[scene->mNumMeshes](); - std::copy(meshes.begin(), meshes.end(), scene->mMeshes); + std::copy( mMeshes.begin(), mMeshes.end(), scene->mMeshes); scene->mRootNode->mNumChildren = static_cast(children.size()); scene->mRootNode->mChildren = new aiNode*[scene->mRootNode->mNumChildren](); @@ -104,8 +112,7 @@ public: } private: - aiNode* ReadObject(aiScene* scene) - { + aiNode* ReadObject(aiScene* scene) { std::unique_ptr node(new aiNode()); std::vector meshIds; @@ -124,19 +131,16 @@ private: node->mParent = scene->mRootNode; node->mName.Set(name); - size_t meshIdx = meshes.size(); + size_t meshIdx = mMeshes.size(); - while(ReadToEndElement(D3MF::XmlTag::object)) - { - if(xmlReader->getNodeName() == D3MF::XmlTag::mesh) - { + while(ReadToEndElement(D3MF::XmlTag::object)) { + if(xmlReader->getNodeName() == D3MF::XmlTag::mesh) { auto mesh = ReadMesh(); mesh->mName.Set(name); - meshes.push_back(mesh); + mMeshes.push_back(mesh); meshIds.push_back(static_cast(meshIdx)); - meshIdx++; - + ++meshIdx; } } @@ -147,19 +151,14 @@ private: std::copy(meshIds.begin(), meshIds.end(), node->mMeshes); return node.release(); - } aiMesh* ReadMesh() { aiMesh* mesh = new aiMesh(); - while(ReadToEndElement(D3MF::XmlTag::mesh)) - { - if(xmlReader->getNodeName() == D3MF::XmlTag::vertices) - { + while(ReadToEndElement(D3MF::XmlTag::mesh)) { + if(xmlReader->getNodeName() == D3MF::XmlTag::vertices) { ImportVertices(mesh); - } - else if(xmlReader->getNodeName() == D3MF::XmlTag::triangles) - { + } else if(xmlReader->getNodeName() == D3MF::XmlTag::triangles) { ImportTriangles(mesh); } } @@ -167,8 +166,7 @@ private: return mesh; } - void ImportVertices(aiMesh* mesh) - { + void ImportVertices(aiMesh* mesh) { std::vector vertices; while(ReadToEndElement(D3MF::XmlTag::vertices)) @@ -182,11 +180,9 @@ private: mesh->mVertices = new aiVector3D[mesh->mNumVertices]; std::copy(vertices.begin(), vertices.end(), mesh->mVertices); - } - aiVector3D ReadVertex() - { + aiVector3D ReadVertex() { aiVector3D vertex; vertex.x = ai_strtof(xmlReader->getAttributeValue(D3MF::XmlTag::x.c_str()), nullptr); @@ -196,15 +192,11 @@ private: return vertex; } - void ImportTriangles(aiMesh* mesh) - { + void ImportTriangles(aiMesh* mesh) { std::vector faces; - - while(ReadToEndElement(D3MF::XmlTag::triangles)) - { - if(xmlReader->getNodeName() == D3MF::XmlTag::triangle) - { + while(ReadToEndElement(D3MF::XmlTag::triangles)) { + if(xmlReader->getNodeName() == D3MF::XmlTag::triangle) { faces.push_back(ReadTriangle()); } } @@ -216,8 +208,7 @@ private: std::copy(faces.begin(), faces.end(), mesh->mFaces); } - aiFace ReadTriangle() - { + aiFace ReadTriangle() { aiFace face; face.mNumIndices = 3; @@ -229,6 +220,77 @@ private: return face; } + void ReadBaseMaterials() { + while ( ReadToEndElement( D3MF::XmlTag::basematerials ) ) { + mMaterials.push_back( readMaterialDef() ); + } + } + + bool parseColor( const char *color, aiColor4D &diffuse ) { + if ( nullptr == color ) { + return false; + } + + const size_t len( strlen( color ) ); + if ( 9 != len ) { + return false; + } + + const char *buf( color ); + if ( '#' != *buf ) { + return false; + } + + char comp[ 2 ] = { 0,0 }; + comp[ 0 ] = *buf; + ++buf; + comp[ 1 ] = *buf; + ++buf; + diffuse.r = static_cast( std::atoi( comp ) ); + + comp[ 0 ] = *buf; + ++buf; + comp[ 1 ] = *buf; + ++buf; + diffuse.g = static_cast( std::atoi( comp ) ); + + comp[ 0 ] = *buf; + ++buf; + comp[ 1 ] = *buf; + ++buf; + diffuse.b = static_cast( std::atoi( comp ) ); + + comp[ 0 ] = *buf; + ++buf; + comp[ 1 ] = *buf; + ++buf; + diffuse.a = static_cast( std::atoi( comp ) ); + + return true; + } + + aiMaterial *readMaterialDef() { + while ( ReadToEndElement( D3MF::XmlTag::basematerials_base ) ) { + const char *name( nullptr ); + const char *color( nullptr ); + if ( xmlReader->getNodeName() == D3MF::XmlTag::basematerials_name ) { + name = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_name.c_str() ); + + aiString matName; + matName.Set( name ); + aiMaterial *mat = new aiMaterial; + mat->AddProperty( &matName, AI_MATKEY_NAME ); + + color = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_displaycolor.c_str() ); + aiColor4D diffuse; + if ( parseColor( color, diffuse ) ) { + mat->AddProperty( &diffuse, 1, AI_MATKEY_COLOR_DIFFUSE ); + } + } + } + + } + private: bool ReadToStartElement(const std::string& startTag) { @@ -267,7 +329,8 @@ private: private: - std::vector meshes; + std::vector mMeshes; + std::vector mMaterials; XmlReader* xmlReader; }; From 9a7611a93ea9708bd840cf6febfb11fba3d12158 Mon Sep 17 00:00:00 2001 From: Sergio Acereda Date: Mon, 12 Feb 2018 22:33:51 +0100 Subject: [PATCH 058/183] AI_MATKEY_GLTF_... -> assimp/pbrmaterial.h --- code/glTF2Asset.h | 29 +------------- include/assimp/pbrmaterial.h | 76 ++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 27 deletions(-) create mode 100644 include/assimp/pbrmaterial.h diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 1ac1d538b..de3ae5cf8 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -137,7 +137,7 @@ namespace glTF2 // Vec/matrix types, as raw float arrays typedef float (vec3)[3]; typedef float (vec4)[4]; - typedef float (mat4)[16]; + typedef float (mat4)[16]; namespace Util { @@ -166,33 +166,8 @@ namespace glTF2 //! Magic number for GLB files #define AI_GLB_MAGIC_NUMBER "glTF" + #include - #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 - #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 - #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 - #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 - #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 - #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 - #define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 - #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 - #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 - - #define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" - #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" - #define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid" - #define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag" - #define _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE "$tex.mappingfiltermin" - #define _AI_MATKEY_GLTF_SCALE_BASE "$tex.scale" - #define _AI_MATKEY_GLTF_STRENGTH_BASE "$tex.strength" - - #define AI_MATKEY_GLTF_TEXTURE_TEXCOORD _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N - #define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N - #define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N - #define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N - #define AI_MATKEY_GLTF_MAPPINGFILTER_MIN(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE, type, N - #define AI_MATKEY_GLTF_TEXTURE_SCALE(type, N) _AI_MATKEY_GLTF_SCALE_BASE, type, N - #define AI_MATKEY_GLTF_TEXTURE_STRENGTH(type, N) _AI_MATKEY_GLTF_STRENGTH_BASE, type, N - #ifdef ASSIMP_API #include "./../include/assimp/Compiler/pushpack1.h" #endif diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h new file mode 100644 index 000000000..3d8f48ca1 --- /dev/null +++ b/include/assimp/pbrmaterial.h @@ -0,0 +1,76 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file pbrmaterial.h + * @brief Defines the material system of the library + */ +#ifndef AI_PBRMATERIAL_H_INC +#define AI_PBRMATERIAL_H_INC + +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 +#define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 +#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 +#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 + +#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" +#define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" +#define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid" +#define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag" +#define _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE "$tex.mappingfiltermin" +#define _AI_MATKEY_GLTF_SCALE_BASE "$tex.scale" +#define _AI_MATKEY_GLTF_STRENGTH_BASE "$tex.strength" + +#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N +#define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N +#define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N +#define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N +#define AI_MATKEY_GLTF_MAPPINGFILTER_MIN(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE, type, N +#define AI_MATKEY_GLTF_TEXTURE_SCALE(type, N) _AI_MATKEY_GLTF_SCALE_BASE, type, N +#define AI_MATKEY_GLTF_TEXTURE_STRENGTH(type, N) _AI_MATKEY_GLTF_STRENGTH_BASE, type, N + +#endif //!!AI_PBRMATERIAL_H_INC From 11a3ee109e0e1ad6b42918ba5ecbe83040ddb6a9 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 13 Feb 2018 11:36:05 +0100 Subject: [PATCH 059/183] Update fast_atof.h Remove unused variable. --- include/assimp/fast_atof.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/assimp/fast_atof.h b/include/assimp/fast_atof.h index 06fd938c0..d577b417f 100644 --- a/include/assimp/fast_atof.h +++ b/include/assimp/fast_atof.h @@ -193,7 +193,6 @@ uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_ino throw std::invalid_argument( std::string( "The string \"" ) + in + "\" cannot be converted into a value." ); } - bool running = true; for ( ;; ) { if ( *in < '0' || *in > '9' ) { break; @@ -239,8 +238,9 @@ uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_ino inline int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* max_inout = 0) { bool inv = (*in == '-'); - if (inv || *in == '+') + if (inv || *in == '+') { ++in; + } int64_t value = strtoul10_64(in, out, max_inout); if (inv) { @@ -297,8 +297,7 @@ const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) f = static_cast( strtoul10_64 ( c, &c) ); } - if ((*c == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9') - { + if ((*c == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9') { ++c; // NOTE: The original implementation is highly inaccurate here. The precision of a single From ccbcaa2ee8482c196e6d7cb1a86b6f75d34c8ea0 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 13 Feb 2018 20:36:22 +0100 Subject: [PATCH 060/183] fix some static-analysis findings. --- code/FindInstancesProcess.h | 14 +++++----- code/OpenGEXImporter.cpp | 27 ++++++++++-------- code/SceneCombiner.cpp | 47 ++++++++++++++++---------------- code/SpatialSort.cpp | 2 +- code/VertexTriangleAdjacency.cpp | 15 ++++------ code/VertexTriangleAdjacency.h | 29 ++++++-------------- include/assimp/IOStreamBuffer.h | 4 +-- include/assimp/SGSpatialSort.h | 2 +- 8 files changed, 65 insertions(+), 75 deletions(-) diff --git a/code/FindInstancesProcess.h b/code/FindInstancesProcess.h index 2ef6c1ca7..fb2ac6eb6 100644 --- a/code/FindInstancesProcess.h +++ b/code/FindInstancesProcess.h @@ -60,9 +60,9 @@ namespace Assimp { * @param in Input mesh * @return Hash. */ -inline uint64_t GetMeshHash(aiMesh* in) -{ - ai_assert(NULL != in); +inline +uint64_t GetMeshHash(aiMesh* in) { + ai_assert(nullptr != in); // ... get an unique value representing the vertex format of the mesh const unsigned int fhash = GetMeshVFormatUnique(in); @@ -78,14 +78,14 @@ inline uint64_t GetMeshHash(aiMesh* in) /** @brief Perform a component-wise comparison of two arrays * * @param first First array - * @param second Second aray + * @param second Second array * @param size Size of both arrays * @param e Epsilon * @return true if the arrays are identical */ -inline bool CompareArrays(const aiVector3D* first, const aiVector3D* second, - unsigned int size, float e) -{ +inline +bool CompareArrays(const aiVector3D* first, const aiVector3D* second, + unsigned int size, float e) { for (const aiVector3D* end = first+size; first != end; ++first,++second) { if ( (*first - *second).SquareLength() >= e) return false; diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 43c92c4f5..652ba0f98 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -731,17 +731,22 @@ enum MeshAttribute { TexCoord }; +static const std::string PosToken = "position"; +static const std::string ColToken = "color"; +static const std::string NormalToken = "normal"; +static const std::string TexCoordToken = "texcoord"; + //------------------------------------------------------------------------------------------------ static MeshAttribute getAttributeByName( const char *attribName ) { ai_assert( nullptr != attribName ); - if ( 0 == strncmp( "position", attribName, strlen( "position" ) ) ) { + if ( 0 == strncmp( PosToken.c_str(), attribName, PosToken.size() ) ) { return Position; - } else if ( 0 == strncmp( "color", attribName, strlen( "color" ) ) ) { + } else if ( 0 == strncmp( ColToken.c_str(), attribName, ColToken.size() ) ) { return Color; - } else if( 0 == strncmp( "normal", attribName, strlen( "normal" ) ) ) { + } else if( 0 == strncmp( NormalToken.c_str(), attribName, NormalToken.size() ) ) { return Normal; - } else if( 0 == strncmp( "texcoord", attribName, strlen( "texcoord" ) ) ) { + } else if( 0 == strncmp( TexCoordToken.c_str(), attribName, TexCoordToken.size() ) ) { return TexCoord; } @@ -1098,14 +1103,12 @@ void OpenGEXImporter::handleParamNode( ODDLParser::DDLNode *node, aiScene * /*pS return; } const float floatVal( val->getFloat() ); - if ( prop->m_value != nullptr ) { - if ( 0 == ASSIMP_strincmp( "fov", prop->m_value->getString(), 3 ) ) { - m_currentCamera->mHorizontalFOV = floatVal; - } else if ( 0 == ASSIMP_strincmp( "near", prop->m_value->getString(), 3 ) ) { - m_currentCamera->mClipPlaneNear = floatVal; - } else if ( 0 == ASSIMP_strincmp( "far", prop->m_value->getString(), 3 ) ) { - m_currentCamera->mClipPlaneFar = floatVal; - } + if ( 0 == ASSIMP_strincmp( "fov", prop->m_value->getString(), 3 ) ) { + m_currentCamera->mHorizontalFOV = floatVal; + } else if ( 0 == ASSIMP_strincmp( "near", prop->m_value->getString(), 4 ) ) { + m_currentCamera->mClipPlaneNear = floatVal; + } else if ( 0 == ASSIMP_strincmp( "far", prop->m_value->getString(), 3 ) ) { + m_currentCamera->mClipPlaneFar = floatVal; } } } diff --git a/code/SceneCombiner.cpp b/code/SceneCombiner.cpp index 30722618f..8647bd1ba 100644 --- a/code/SceneCombiner.cpp +++ b/code/SceneCombiner.cpp @@ -1256,29 +1256,30 @@ void SceneCombiner::Copy(aiMetadata** _dest, const aiMetadata* src) { aiMetadataEntry& out = dest->mValues[i]; out.mType = in.mType; switch (dest->mValues[i].mType) { - case AI_BOOL: - out.mData = new bool(*static_cast(in.mData)); - break; - case AI_INT32: - out.mData = new int32_t(*static_cast(in.mData)); - break; - case AI_UINT64: - out.mData = new uint64_t(*static_cast(in.mData)); - break; - case AI_FLOAT: - out.mData = new float(*static_cast(in.mData)); - break; - case AI_DOUBLE: - out.mData = new double(*static_cast(in.mData)); - break; - case AI_AISTRING: - out.mData = new aiString(*static_cast(in.mData)); - break; - case AI_AIVECTOR3D: - out.mData = new aiVector3D(*static_cast(in.mData)); - break; - default: - ai_assert(false); + case AI_BOOL: + out.mData = new bool(*static_cast(in.mData)); + break; + case AI_INT32: + out.mData = new int32_t(*static_cast(in.mData)); + break; + case AI_UINT64: + out.mData = new uint64_t(*static_cast(in.mData)); + break; + case AI_FLOAT: + out.mData = new float(*static_cast(in.mData)); + break; + case AI_DOUBLE: + out.mData = new double(*static_cast(in.mData)); + break; + case AI_AISTRING: + out.mData = new aiString(*static_cast(in.mData)); + break; + case AI_AIVECTOR3D: + out.mData = new aiVector3D(*static_cast(in.mData)); + break; + default: + ai_assert(false); + break; } } } diff --git a/code/SpatialSort.cpp b/code/SpatialSort.cpp index 010cad7c1..e6ffeb260 100644 --- a/code/SpatialSort.cpp +++ b/code/SpatialSort.cpp @@ -294,7 +294,7 @@ void SpatialSort::FindIdenticalPositions( const aiVector3D& pPosition, index++; // Now start iterating from there until the first position lays outside of the distance range. - // Add all positions inside the distance range within the tolerance to the result aray + // Add all positions inside the distance range within the tolerance to the result array std::vector::const_iterator it = mPositions.begin() + index; while( ToBinary(it->mDistance) < maxDistBinary) { diff --git a/code/VertexTriangleAdjacency.cpp b/code/VertexTriangleAdjacency.cpp index 022d5d902..5886ca372 100644 --- a/code/VertexTriangleAdjacency.cpp +++ b/code/VertexTriangleAdjacency.cpp @@ -48,7 +48,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "VertexTriangleAdjacency.h" #include - using namespace Assimp; // ------------------------------------------------------------------------------------------------ @@ -60,8 +59,8 @@ VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces, // compute the number of referenced vertices if it wasn't specified by the caller const aiFace* const pcFaceEnd = pcFaces + iNumFaces; if (!iNumVertices) { - for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) { + ai_assert( nullptr != pcFace ); ai_assert(3 == pcFace->mNumIndices); iNumVertices = std::max(iNumVertices,pcFace->mIndices[0]); iNumVertices = std::max(iNumVertices,pcFace->mIndices[1]); @@ -69,19 +68,18 @@ VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces, } } - this->iNumVertices = iNumVertices; + mNumVertices = iNumVertices; unsigned int* pi; // allocate storage if (bComputeNumTriangles) { pi = mLiveTriangles = new unsigned int[iNumVertices+1]; - memset(mLiveTriangles,0,sizeof(unsigned int)*(iNumVertices+1)); + ::memset(mLiveTriangles,0,sizeof(unsigned int)*(iNumVertices+1)); mOffsetTable = new unsigned int[iNumVertices+2]+1; - } - else { + } else { pi = mOffsetTable = new unsigned int[iNumVertices+2]+1; - memset(mOffsetTable,0,sizeof(unsigned int)*(iNumVertices+1)); + ::memset(mOffsetTable,0,sizeof(unsigned int)*(iNumVertices+1)); mLiveTriangles = NULL; // important, otherwise the d'tor would crash } @@ -90,8 +88,7 @@ VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces, *piEnd++ = 0u; // first pass: compute the number of faces referencing each vertex - for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) - { + for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) { pi[pcFace->mIndices[0]]++; pi[pcFace->mIndices[1]]++; pi[pcFace->mIndices[2]]++; diff --git a/code/VertexTriangleAdjacency.h b/code/VertexTriangleAdjacency.h index 566783835..23624a5be 100644 --- a/code/VertexTriangleAdjacency.h +++ b/code/VertexTriangleAdjacency.h @@ -60,10 +60,8 @@ namespace Assimp { * @note Although it is called #VertexTriangleAdjacency, the current version does also * support arbitrary polygons. */ // -------------------------------------------------------------------------------------------- -class ASSIMP_API VertexTriangleAdjacency -{ +class ASSIMP_API VertexTriangleAdjacency { public: - // ---------------------------------------------------------------------------- /** @brief Construction from an existing index buffer * @param pcFaces Index buffer @@ -77,39 +75,30 @@ public: unsigned int iNumVertices = 0, bool bComputeNumTriangles = true); - // ---------------------------------------------------------------------------- /** @brief Destructor */ ~VertexTriangleAdjacency(); - -public: - // ---------------------------------------------------------------------------- /** @brief Get all triangles adjacent to a vertex * @param iVertIndex Index of the vertex * @return A pointer to the adjacency list. */ - unsigned int* GetAdjacentTriangles(unsigned int iVertIndex) const - { - ai_assert(iVertIndex < iNumVertices); + unsigned int* GetAdjacentTriangles(unsigned int iVertIndex) const { + ai_assert(iVertIndex < mNumVertices); return &mAdjacencyTable[ mOffsetTable[iVertIndex]]; } - // ---------------------------------------------------------------------------- /** @brief Get the number of triangles that are referenced by * a vertex. This function returns a reference that can be modified * @param iVertIndex Index of the vertex * @return Number of referenced triangles */ - unsigned int& GetNumTrianglesPtr(unsigned int iVertIndex) - { - ai_assert(iVertIndex < iNumVertices && NULL != mLiveTriangles); + unsigned int& GetNumTrianglesPtr(unsigned int iVertIndex) { + ai_assert( iVertIndex < mNumVertices ); + ai_assert( nullptr != mLiveTriangles ); return mLiveTriangles[iVertIndex]; } - -public: - //! Offset table unsigned int* mOffsetTable; @@ -120,9 +109,9 @@ public: unsigned int* mLiveTriangles; //! Debug: Number of referenced vertices - unsigned int iNumVertices; - + unsigned int mNumVertices; }; -} + +} //! ns Assimp #endif // !! AI_VTADJACENCY_H_INC diff --git a/include/assimp/IOStreamBuffer.h b/include/assimp/IOStreamBuffer.h index a503b3874..fdc339062 100644 --- a/include/assimp/IOStreamBuffer.h +++ b/include/assimp/IOStreamBuffer.h @@ -248,9 +248,9 @@ bool IOStreamBuffer::getNextDataLine( std::vector &buffer, T continuationT } } - bool continuationFound( false ), endOfDataLine( false ); + bool continuationFound( false ); size_t i = 0; - while ( !endOfDataLine ) { + for( ;; ) { if ( continuationToken == m_cache[ m_cachePos ] ) { continuationFound = true; ++m_cachePos; diff --git a/include/assimp/SGSpatialSort.h b/include/assimp/SGSpatialSort.h index d8b4b418b..203e32512 100644 --- a/include/assimp/SGSpatialSort.h +++ b/include/assimp/SGSpatialSort.h @@ -123,7 +123,7 @@ protected: Entry() { /** intentionally not initialized.*/ } Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance,uint32_t pSG) - : + : mIndex( pIndex), mPosition( pPosition), mSmoothGroups (pSG), From c4ff978a2f4227434f530dd83979e1f099cfe1fd Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 16 Feb 2018 12:06:14 +0100 Subject: [PATCH 061/183] Some StreamWriter improvements / additions. Added the following functions: * Flush() to flush the internal StreamWriter buffer to the stream * Tell() gives the current stream position * Seek() seeks to a given offset in the stream * PutString(aiString&) to write an aiString * PutString(std::string&) to write a std::string --- include/assimp/StreamWriter.h | 60 ++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/include/assimp/StreamWriter.h b/include/assimp/StreamWriter.h index 7ee0944a7..e955a7255 100644 --- a/include/assimp/StreamWriter.h +++ b/include/assimp/StreamWriter.h @@ -58,7 +58,7 @@ namespace Assimp { // -------------------------------------------------------------------------------------------- /** Wrapper class around IOStream to allow for consistent writing of binary data in both * little and big endian format. Don't attempt to instance the template directly. Use - * StreamWriterLE to read from a little-endian stream and StreamWriterBE to read from a + * StreamWriterLE to write to a little-endian stream and StreamWriterBE to write to a * BE stream. Alternatively, there is StreamWriterAny if the endianness of the output * stream is to be determined at runtime. */ @@ -108,6 +108,38 @@ public: stream->Flush(); } +public: + + // --------------------------------------------------------------------- + /** Flush the contents of the internal buffer, and the output IOStream */ + void Flush() + { + stream->Write(&buffer[0], 1, buffer.size()); + stream->Flush(); + buffer.clear(); + cursor = 0; + } + + // --------------------------------------------------------------------- + /** Seek to the given offset / origin in the output IOStream. + * + * Flushes the internal buffer and the output IOStream prior to seeking. */ + aiReturn Seek(size_t pOffset, aiOrigin pOrigin=aiOrigin_SET) + { + Flush(); + return stream->Seek(pOffset, pOrigin); + } + + // --------------------------------------------------------------------- + /** Tell the current position in the output IOStream. + * + * First flushes the internal buffer and the output IOStream. */ + size_t Tell() + { + Flush(); + return stream->Tell(); + } + public: // --------------------------------------------------------------------- @@ -171,6 +203,32 @@ public: Put(n); } + // --------------------------------------------------------------------- + /** Write an aiString to the stream */ + void PutString(const aiString& s) + { + // as Put(T f) below + if (cursor + s.length >= buffer.size()) { + buffer.resize(cursor + s.length); + } + void* dest = &buffer[cursor]; + ::memcpy(dest, s.C_Str(), s.length); + cursor += s.length; + } + + // --------------------------------------------------------------------- + /** Write a std::string to the stream */ + void PutString(const std::string& s) + { + // as Put(T f) below + if (cursor + s.size() >= buffer.size()) { + buffer.resize(cursor + s.size()); + } + void* dest = &buffer[cursor]; + ::memcpy(dest, s.c_str(), s.size()); + cursor += s.size(); + } + public: // --------------------------------------------------------------------- From b9e60e674e5f2e4f746f2a91187f3544a822d0a4 Mon Sep 17 00:00:00 2001 From: Sergio Acereda Date: Fri, 16 Feb 2018 12:23:21 +0100 Subject: [PATCH 062/183] define missing type,N params --- include/assimp/pbrmaterial.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index 3d8f48ca1..cd9b5e2bf 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -65,7 +65,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define _AI_MATKEY_GLTF_SCALE_BASE "$tex.scale" #define _AI_MATKEY_GLTF_STRENGTH_BASE "$tex.strength" -#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N +#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N From 58436068e7e08e496a1d415461de706a33f7832f Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Fri, 16 Feb 2018 22:16:10 +0100 Subject: [PATCH 063/183] Update BlenderScene.cpp --- code/BlenderScene.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/BlenderScene.cpp b/code/BlenderScene.cpp index 0e1dec7f3..e4373909e 100644 --- a/code/BlenderScene.cpp +++ b/code/BlenderScene.cpp @@ -116,7 +116,7 @@ template <> void Structure :: Convert ( ReadField(temp,"projy",db); dest.projy = static_cast(temp); ReadField(temp,"projz",db); - dest.projx = static_cast(temp); + dest.projz = static_cast(temp); ReadField(dest.mapping,"mapping",db); ReadFieldArray(dest.ofs,"ofs",db); ReadFieldArray(dest.size,"size",db); From 8950504cf079d60a18c56796218c722d52120db2 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 18 Feb 2018 21:30:23 +0100 Subject: [PATCH 064/183] Fix import of materials. --- code/3MFXmlTags.h | 1 + code/D3MFImporter.cpp | 30 ++++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/code/3MFXmlTags.h b/code/3MFXmlTags.h index 20971a38f..00938a45e 100644 --- a/code/3MFXmlTags.h +++ b/code/3MFXmlTags.h @@ -62,6 +62,7 @@ namespace XmlTag { static const std::string v2 = "v2"; static const std::string v3 = "v3"; static const std::string id = "id"; + static const std::string pid = "pid"; static const std::string name = "name"; static const std::string type = "type"; static const std::string build = "build"; diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 823fae74c..29e27371e 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -79,7 +79,7 @@ public: } void ImportXml(aiScene* scene) { - if ( nullptr != scene ) { + if ( nullptr == scene ) { return; } @@ -87,11 +87,12 @@ public: std::vector children; while(ReadToEndElement(D3MF::XmlTag::model)) { - if(xmlReader->getNodeName() == D3MF::XmlTag::object) { + const std::string nodeName( xmlReader->getNodeName() ); + if( nodeName == D3MF::XmlTag::object) { children.push_back(ReadObject(scene)); - } else if(xmlReader->getNodeName() == D3MF::XmlTag::build) { + } else if( nodeName == D3MF::XmlTag::build) { // - } else if ( xmlReader->getNodeName() == D3MF::XmlTag::basematerials ) { + } else if ( nodeName == D3MF::XmlTag::basematerials ) { ReadBaseMaterials(); } } @@ -105,6 +106,11 @@ public: std::copy( mMeshes.begin(), mMeshes.end(), scene->mMeshes); + scene->mNumMaterials = mMaterials.size(); + if ( 0 != scene->mNumMaterials ) { + scene->mMaterials = new aiMaterial*[ scene->mNumMaterials ]; + std::copy( mMaterials.begin(), mMaterials.end(), scene->mMaterials ); + } scene->mRootNode->mNumChildren = static_cast(children.size()); scene->mRootNode->mChildren = new aiNode*[scene->mRootNode->mNumChildren](); @@ -196,8 +202,13 @@ private: std::vector faces; while(ReadToEndElement(D3MF::XmlTag::triangles)) { + const std::string nodeName( xmlReader->getNodeName() ); if(xmlReader->getNodeName() == D3MF::XmlTag::triangle) { faces.push_back(ReadTriangle()); + } else if ( nodeName == D3MF::XmlTag::pid ) { + const std::string matId( xmlReader->getAttributeValue( nodeName.c_str() ) ); + int matIdx( std::atoi( matId.c_str() ) ); + mesh->mMaterialIndex = matIdx; } } @@ -270,15 +281,17 @@ private: } aiMaterial *readMaterialDef() { - while ( ReadToEndElement( D3MF::XmlTag::basematerials_base ) ) { + aiMaterial *mat( nullptr ); + //while ( ReadToEndElement( D3MF::XmlTag::basematerials_base ) ) { const char *name( nullptr ); const char *color( nullptr ); - if ( xmlReader->getNodeName() == D3MF::XmlTag::basematerials_name ) { + const std::string nodeName( xmlReader->getNodeName() ); + if ( nodeName == D3MF::XmlTag::basematerials_base ) { name = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_name.c_str() ); aiString matName; matName.Set( name ); - aiMaterial *mat = new aiMaterial; + mat = new aiMaterial; mat->AddProperty( &matName, AI_MATKEY_NAME ); color = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_displaycolor.c_str() ); @@ -287,8 +300,9 @@ private: mat->AddProperty( &diffuse, 1, AI_MATKEY_COLOR_DIFFUSE ); } } - } + //} + return mat; } private: From 24d452e27c40a1de917fb033c38688e090ebd83c Mon Sep 17 00:00:00 2001 From: Tommy Date: Mon, 19 Feb 2018 11:34:26 +0100 Subject: [PATCH 065/183] Fix aiBone->mOffsetMatrix documentation, which was incorrect. Also elaborated a bit on what mOffsetMatrix is, and what it is for. --- include/assimp/mesh.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index b2f64195f..731053614 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -243,20 +243,31 @@ struct aiVertexWeight { * * A bone has a name by which it can be found in the frame hierarchy and by * which it can be addressed by animations. In addition it has a number of - * influences on vertices. + * influences on vertices, and a matrix relating the mesh position to the + * position of the bone at the time of binding. */ struct aiBone { //! The name of the bone. C_STRUCT aiString mName; - //! The number of vertices affected by this bone + //! The number of vertices affected by this bone. //! The maximum value for this member is #AI_MAX_BONE_WEIGHTS. unsigned int mNumWeights; - //! The vertices affected by this bone + //! The influence weights of this bone, by vertex index. C_STRUCT aiVertexWeight* mWeights; - //! Matrix that transforms from mesh space to bone space in bind pose + /** Matrix that transforms from bone space to mesh space in bind pose. + * + * This matrix describes the position of the mesh + * in the local space of this bone when the skeleton was bound. + * Thus it can be used directly to determine a desired vertex position, + * given the world-space transform of the bone when animated, + * and the position of the vertex in mesh space. + * + * It is sometimes called an inverse-bind matrix, + * or inverse bind pose matrix. + */ C_STRUCT aiMatrix4x4 mOffsetMatrix; #ifdef __cplusplus From 242b5d58742d963dad899af947d5e6844b753e44 Mon Sep 17 00:00:00 2001 From: Tommy Date: Mon, 19 Feb 2018 13:46:02 +0100 Subject: [PATCH 066/183] Initial FBX Export Support, sponsored by MyDidimo (mydidimo.com). Supports: * mesh data with normals and uvs * lambert and phong materials * file textures * bones / skeletons * skinning Does not yet support: * animations * cameras * lights * embedded textures --- CREDITS | 17 +- code/CMakeLists.txt | 9 +- code/Exporter.cpp | 7 + code/FBXCommon.h | 86 ++ code/FBXExportNode.cpp | 284 +++++ code/FBXExportNode.h | 197 ++++ code/FBXExportProperty.cpp | 201 ++++ code/FBXExportProperty.h | 123 +++ code/FBXExporter.cpp | 2036 ++++++++++++++++++++++++++++++++++++ code/FBXExporter.h | 146 +++ 10 files changed, 3104 insertions(+), 2 deletions(-) create mode 100644 code/FBXCommon.h create mode 100644 code/FBXExportNode.cpp create mode 100644 code/FBXExportNode.h create mode 100644 code/FBXExportProperty.cpp create mode 100644 code/FBXExportProperty.h create mode 100644 code/FBXExporter.cpp create mode 100644 code/FBXExporter.h diff --git a/CREDITS b/CREDITS index df41c482d..26e21d2f4 100644 --- a/CREDITS +++ b/CREDITS @@ -157,12 +157,27 @@ Contributed ExportProperties interface Contributed X File exporter Contributed Step (stp) exporter +- Thomas Iorns (mesilliac) +Initial FBX Export support + For a more detailed list just check: https://github.com/assimp/assimp/network/members -Patreons: + +======== +Patreons +======== + +Huge thanks to our Patreons! + - migenius - Marcus - Cort - elect - Steffen + +=================== +Commercial Sponsors +=================== + +- MyDidimo (mydidimo.com): Sponsored development of FBX Export support diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 376725e7e..6430187b0 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -522,6 +522,13 @@ ADD_ASSIMP_IMPORTER( FBX FBXDeformer.cpp FBXBinaryTokenizer.cpp FBXDocumentUtil.cpp + FBXExporter.h + FBXExporter.cpp + FBXExportNode.h + FBXExportNode.cpp + FBXExportProperty.h + FBXExportProperty.cpp + FBXCommon.h ) SET( PostProcessing_SRCS @@ -641,7 +648,7 @@ ADD_ASSIMP_IMPORTER( X XFileExporter.cpp ) -ADD_ASSIMP_IMPORTER(X3D +ADD_ASSIMP_IMPORTER( X3D X3DExporter.cpp X3DExporter.hpp X3DImporter.cpp diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 51c639cfd..4951c70ef 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -97,6 +97,8 @@ void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportPropert void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*); +//void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* ); // ------------------------------------------------------------------------------------------------ @@ -169,6 +171,11 @@ Exporter::ExportFormatEntry gExporters[] = Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0 ), #endif +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ), + //Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ), +#endif + #ifndef ASSIMP_BUILD_NO_3MF_EXPORTER Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ) #endif diff --git a/code/FBXCommon.h b/code/FBXCommon.h new file mode 100644 index 000000000..60b040552 --- /dev/null +++ b/code/FBXCommon.h @@ -0,0 +1,86 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXCommon.h +* Some useful constants and enums for dealing with FBX files. +*/ +#ifndef AI_FBXCOMMON_H_INC +#define AI_FBXCOMMON_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + + +namespace FBX +{ + const std::string NULL_RECORD = { // 13 null bytes + '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0' + }; // who knows why + const std::string SEPARATOR = {'\x00', '\x01'}; // for use inside strings + const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import + const int64_t SECOND = 46186158000; // FBX's kTime unit + + // rotation order. We'll probably use EulerXYZ for everything + enum RotOrder { + RotOrder_EulerXYZ = 0, + RotOrder_EulerXZY, + RotOrder_EulerYZX, + RotOrder_EulerYXZ, + RotOrder_EulerZXY, + RotOrder_EulerZYX, + + RotOrder_SphericXYZ, + + RotOrder_MAX // end-of-enum sentinel + }; + + // transformation inheritance method. Most of the time RSrs + enum TransformInheritance { + TransformInheritance_RrSs = 0, + TransformInheritance_RSrs, + TransformInheritance_Rrs, + + TransformInheritance_MAX // end-of-enum sentinel + }; +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXCOMMON_H_INC diff --git a/code/FBXExportNode.cpp b/code/FBXExportNode.cpp new file mode 100644 index 000000000..f218f068d --- /dev/null +++ b/code/FBXExportNode.cpp @@ -0,0 +1,284 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportNode.h" +#include "FBXCommon.h" + +#include // StreamWriterLE +#include + +#include +#include // shared_ptr + +// AddP70 helpers... there's no usable pattern here, +// so all are defined as separate functions. +// Even "animatable" properties are often completely different +// from the standard (nonanimated) property definition, +// so they are specified with an 'A' suffix. + +void FBX::Node::AddP70int( + const std::string& name, int32_t value +) { + FBX::Node n("P"); + n.AddProperties(name, "int", "Integer", "", value); + AddChild(n); +} + +void FBX::Node::AddP70bool( + const std::string& name, bool value +) { + FBX::Node n("P"); + n.AddProperties(name, "bool", "", "", int32_t(value)); + AddChild(n); +} + +void FBX::Node::AddP70double( + const std::string& name, double value +) { + FBX::Node n("P"); + n.AddProperties(name, "double", "Number", "", value); + AddChild(n); +} + +void FBX::Node::AddP70numberA( + const std::string& name, double value +) { + FBX::Node n("P"); + n.AddProperties(name, "Number", "", "A", value); + AddChild(n); +} + +void FBX::Node::AddP70color( + const std::string& name, double r, double g, double b +) { + FBX::Node n("P"); + n.AddProperties(name, "ColorRGB", "Color", "", r, g, b); + AddChild(n); +} + +void FBX::Node::AddP70colorA( + const std::string& name, double r, double g, double b +) { + FBX::Node n("P"); + n.AddProperties(name, "Color", "", "A", r, g, b); + AddChild(n); +} + +void FBX::Node::AddP70vector( + const std::string& name, double x, double y, double z +) { + FBX::Node n("P"); + n.AddProperties(name, "Vector3D", "Vector", "", x, y, z); + AddChild(n); +} + +void FBX::Node::AddP70vectorA( + const std::string& name, double x, double y, double z +) { + FBX::Node n("P"); + n.AddProperties(name, "Vector", "", "A", x, y, z); + AddChild(n); +} + +void FBX::Node::AddP70string( + const std::string& name, const std::string& value +) { + FBX::Node n("P"); + n.AddProperties(name, "KString", "", "", value); + AddChild(n); +} + +void FBX::Node::AddP70enum( + const std::string& name, int32_t value +) { + FBX::Node n("P"); + n.AddProperties(name, "enum", "", "", value); + AddChild(n); +} + +void FBX::Node::AddP70time( + const std::string& name, int64_t value +) { + FBX::Node n("P"); + n.AddProperties(name, "KTime", "Time", "", value); + AddChild(n); +} + + +// public member functions for writing to binary fbx + +void FBX::Node::Dump(std::shared_ptr outfile) +{ + Assimp::StreamWriterLE outstream(outfile); + Dump(outstream); +} + +void FBX::Node::Dump(Assimp::StreamWriterLE &s) +{ + // write header section (with placeholders for some things) + Begin(s); + + // write properties + DumpProperties(s); + + // go back and fill in property related placeholders + EndProperties(s, properties.size()); + + // write children + DumpChildren(s); + + // finish, filling in end offset placeholder + End(s, !children.empty()); +} + +void FBX::Node::Begin(Assimp::StreamWriterLE &s) +{ + // remember start pos so we can come back and write the end pos + this->start_pos = s.Tell(); + + // placeholders for end pos and property section info + s.PutU4(0); // end pos + s.PutU4(0); // number of properties + s.PutU4(0); // total property section length + + // node name + s.PutU1(name.size()); // length of node name + s.PutString(name); // node name as raw bytes + + // property data comes after here + this->property_start = s.Tell(); +} + +void FBX::Node::DumpProperties(Assimp::StreamWriterLE& s) +{ + for (auto &p : properties) { + p.Dump(s); + } +} + +void FBX::Node::DumpChildren(Assimp::StreamWriterLE& s) +{ + for (FBX::Node& child : children) { + child.Dump(s); + } +} + +void FBX::Node::EndProperties(Assimp::StreamWriterLE &s) +{ + EndProperties(s, properties.size()); +} + +void FBX::Node::EndProperties( + Assimp::StreamWriterLE &s, + size_t num_properties +) { + if (num_properties == 0) { return; } + size_t pos = s.Tell(); + ai_assert(pos > property_start); + size_t property_section_size = pos - property_start; + s.Seek(start_pos + 4); + s.PutU4(num_properties); + s.PutU4(property_section_size); + s.Seek(pos); +} + +void FBX::Node::End( + Assimp::StreamWriterLE &s, + bool has_children +) { + // if there were children, add a null record + if (has_children) { s.PutString(FBX::NULL_RECORD); } + + // now go back and write initial pos + this->end_pos = s.Tell(); + s.Seek(start_pos); + s.PutU4(end_pos); + s.Seek(end_pos); +} + + +// static member functions + +// convenience function to create and write a property node, +// holding a single property which is an array of values. +// does not copy the data, so is efficient for large arrays. +// TODO: optional zip compression! +void FBX::Node::WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s +){ + Node node(name); + node.Begin(s); + s.PutU1('d'); + s.PutU4(v.size()); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + s.PutU4(v.size() * 8); // data size + for (auto it = v.begin(); it != v.end(); ++it) { s.PutF8(*it); } + node.EndProperties(s, 1); + node.End(s, false); +} + +// convenience function to create and write a property node, +// holding a single property which is an array of values. +// does not copy the data, so is efficient for large arrays. +// TODO: optional zip compression! +void FBX::Node::WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s +){ + Node node(name); + node.Begin(s); + s.PutU1('i'); + s.PutU4(v.size()); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + s.PutU4(v.size() * 4); // data size + for (auto it = v.begin(); it != v.end(); ++it) { s.PutI4(*it); } + node.EndProperties(s, 1); + node.End(s, false); +} + + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/code/FBXExportNode.h b/code/FBXExportNode.h new file mode 100644 index 000000000..edce8f700 --- /dev/null +++ b/code/FBXExportNode.h @@ -0,0 +1,197 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXExportNode.h +* Declares the FBX::Node helper class for fbx export. +*/ +#ifndef AI_FBXEXPORTNODE_H_INC +#define AI_FBXEXPORTNODE_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportProperty.h" + +#include // StreamWriterLE + +#include +#include + +namespace FBX { + class Node; +} + +class FBX::Node +{ +public: // public data members + // TODO: accessors + std::string name; // node name + std::vector properties; // node properties + std::vector children; // child nodes + +public: // constructors + Node() = default; + Node(const std::string& n) : name(n) {} + Node(const std::string& n, const FBX::Property &p) + : name(n) + { properties.push_back(p); } + Node(const std::string& n, const std::vector &pv) + : name(n), properties(pv) {} + +public: // functions to add properties or children + // add a single property to the node + template + void AddProperty(T value) { + properties.emplace_back(value); + } + + // convenience function to add multiple properties at once + template + void AddProperties(T value, More... more) { + properties.emplace_back(value); + AddProperties(more...); + } + void AddProperties() {} + + // add a child node directly + void AddChild(const Node& node) { children.push_back(node); } + + // convenience function to add a child node with a single property + template + void AddChild( + const std::string& name, + More... more + ) { + FBX::Node c(name); + c.AddProperties(more...); + children.push_back(c); + } + +public: // support specifically for dealing with Properties70 nodes + + // it really is simpler to make these all separate functions. + // the versions with 'A' suffixes are for animatable properties. + // those often follow a completely different format internally in FBX. + void AddP70int(const std::string& name, int32_t value); + void AddP70bool(const std::string& name, bool value); + void AddP70double(const std::string& name, double value); + void AddP70numberA(const std::string& name, double value); + void AddP70color(const std::string& name, double r, double g, double b); + void AddP70colorA(const std::string& name, double r, double g, double b); + void AddP70vector(const std::string& name, double x, double y, double z); + void AddP70vectorA(const std::string& name, double x, double y, double z); + void AddP70string(const std::string& name, const std::string& value); + void AddP70enum(const std::string& name, int32_t value); + void AddP70time(const std::string& name, int64_t value); + + // template for custom P70 nodes. + // anything that doesn't fit in the above can be created manually. + template + void AddP70( + const std::string& name, + const std::string& type, + const std::string& type2, + const std::string& flags, + More... more + ) { + Node n("P"); + n.AddProperties(name, type, type2, flags, more...); + AddChild(n); + } + +public: // member functions for writing data to a file or stream + + // write the full node as binary data to the given file or stream + void Dump(std::shared_ptr outfile); + void Dump(Assimp::StreamWriterLE &s); + + // these other functions are for writing data piece by piece. + // they must be used carefully. + // for usage examples see FBXExporter.cpp. + void Begin(Assimp::StreamWriterLE &s); + void DumpProperties(Assimp::StreamWriterLE& s); + void EndProperties(Assimp::StreamWriterLE &s); + void EndProperties(Assimp::StreamWriterLE &s, size_t num_properties); + void DumpChildren(Assimp::StreamWriterLE& s); + void End(Assimp::StreamWriterLE &s, bool has_children); + +private: // data used for binary dumps + size_t start_pos; // starting position in stream + size_t end_pos; // ending position in stream + size_t property_start; // starting position of property section + +public: // static member functions + + // convenience function to create a node with a single property, + // and write it to the stream. + template + static void WritePropertyNode( + const std::string& name, + const T value, + Assimp::StreamWriterLE& s + ) { + FBX::Property p(value); + FBX::Node node(name, p); + node.Dump(s); + } + + // convenience function to create and write a property node, + // holding a single property which is an array of values. + // does not copy the data, so is efficient for large arrays. + static void WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s + ); + + // convenience function to create and write a property node, + // holding a single property which is an array of values. + // does not copy the data, so is efficient for large arrays. + static void WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s + ); +}; + + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXEXPORTNODE_H_INC diff --git a/code/FBXExportProperty.cpp b/code/FBXExportProperty.cpp new file mode 100644 index 000000000..1beaa4d27 --- /dev/null +++ b/code/FBXExportProperty.cpp @@ -0,0 +1,201 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportProperty.h" + +#include // StreamWriterLE +#include // DeadlyExportError + +#include +#include +#include // stringstream + + +// constructors for single element properties + +FBX::Property::Property(bool v) + : type('C'), data(1) +{ + data = {uint8_t(v)}; +} + +FBX::Property::Property(int16_t v) : type('Y'), data(2) +{ + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + +FBX::Property::Property(int32_t v) : type('I'), data(4) +{ + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + +FBX::Property::Property(float v) : type('F'), data(4) +{ + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + +FBX::Property::Property(double v) : type('D'), data(8) +{ + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + +FBX::Property::Property(int64_t v) : type('L'), data(8) +{ + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + + +// constructors for array-type properties + +FBX::Property::Property(const char* c, bool raw) + : Property(std::string(c), raw) +{} + +// strings can either be saved as "raw" (R) data, or "string" (S) data +FBX::Property::Property(const std::string& s, bool raw) + : type(raw ? 'R' : 'S'), data(s.size()) +{ + for (size_t i = 0; i < s.size(); ++i) { + data[i] = uint8_t(s[i]); + } +} + +FBX::Property::Property(const std::vector& r) + : type('R'), data(r) +{} + +FBX::Property::Property(const std::vector& va) + : type('i'), data(4*va.size()) +{ + int32_t* d = reinterpret_cast(data.data()); + for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } +} + +FBX::Property::Property(const std::vector& va) + : type('d'), data(8*va.size()) +{ + double* d = reinterpret_cast(data.data()); + for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } +} + +FBX::Property::Property(const aiMatrix4x4& vm) + : type('d'), data(8*16) +{ + double* d = reinterpret_cast(data.data()); + for (size_t c = 0; c < 4; ++c) { + for (size_t r = 0; r < 4; ++r) { + d[4*c+r] = vm[r][c]; + } + } +} + +// public member functions + +size_t FBX::Property::size() +{ + switch (type) { + case 'C': case 'Y': case 'I': case 'F': case 'D': case 'L': + return data.size() + 1; + case 'S': case 'R': + return data.size() + 5; + case 'i': case 'd': + return data.size() + 13; + default: + throw DeadlyExportError("Requested size on property of unknown type"); + } +} + +void FBX::Property::Dump(Assimp::StreamWriterLE &s) +{ + s.PutU1(type); + uint8_t* d; + size_t N; + switch (type) { + case 'C': s.PutU1(*(reinterpret_cast(data.data()))); return; + case 'Y': s.PutI2(*(reinterpret_cast(data.data()))); return; + case 'I': s.PutI4(*(reinterpret_cast(data.data()))); return; + case 'F': s.PutF4(*(reinterpret_cast(data.data()))); return; + case 'D': s.PutF8(*(reinterpret_cast(data.data()))); return; + case 'L': s.PutI8(*(reinterpret_cast(data.data()))); return; + case 'S': + case 'R': + s.PutU4(data.size()); + for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); } + return; + case 'i': + N = data.size() / 4; + s.PutU4(N); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(data.size()); // data size + d = data.data(); + for (size_t i = 0; i < N; ++i) { + s.PutI4((reinterpret_cast(d))[i]); + } + return; + case 'd': + N = data.size() / 8; + s.PutU4(N); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(data.size()); // data size + d = data.data(); + for (size_t i = 0; i < N; ++i) { + s.PutF8((reinterpret_cast(d))[i]); + } + return; + default: + std::stringstream err; + err << "Tried to dump property with invalid type '"; + err << type << "'!"; + throw DeadlyExportError(err.str()); + } +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/code/FBXExportProperty.h b/code/FBXExportProperty.h new file mode 100644 index 000000000..8920346a3 --- /dev/null +++ b/code/FBXExportProperty.h @@ -0,0 +1,123 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXExportProperty.h +* Declares the FBX::Property helper class for fbx export. +*/ +#ifndef AI_FBXEXPORTPROPERTY_H_INC +#define AI_FBXEXPORTPROPERTY_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + + +#include // aiMatrix4x4 +#include // StreamWriterLE + +#include +#include +#include // is_void + +namespace FBX { + class Property; +} + +/** FBX::Property + * + * Holds a value of any of FBX's recognized types, + * each represented by a particular one-character code. + * C : 1-byte uint8, usually 0x00 or 0x01 to represent boolean false and true + * Y : 2-byte int16 + * I : 4-byte int32 + * F : 4-byte float + * D : 8-byte double + * L : 8-byte int64 + * i : array of int32 + * f : array of float + * d : array of double + * l : array of int64 + * b : array of 1-byte booleans (0x00 or 0x01) + * S : string (array of 1-byte char) + * R : raw data (array of bytes) + */ +class FBX::Property +{ +public: + // constructors for basic types. + // all explicit to avoid accidental typecasting + explicit Property(bool v); + // TODO: determine if there is actually a byte type, + // or if this always means . 'C' seems to imply , + // so possibly the above was intended to represent both. + explicit Property(int16_t v); + explicit Property(int32_t v); + explicit Property(float v); + explicit Property(double v); + explicit Property(int64_t v); + // strings can either be stored as 'R' (raw) or 'S' (string) type + explicit Property(const char* c, bool raw=false); + explicit Property(const std::string& s, bool raw=false); + explicit Property(const std::vector& r); + explicit Property(const std::vector& va); + explicit Property(const std::vector& va); + explicit Property(const aiMatrix4x4& vm); + + // this will catch any type not defined above, + // so that we don't accidentally convert something we don't want. + // for example (const char*) --> (bool)... seriously wtf C++ + template + explicit Property(T v) : type('X') { + static_assert(std::is_void::value, "TRIED TO CREATE FBX PROPERTY WITH UNSUPPORTED TYPE, CHECK YOUR PROPERTY INSTANTIATION"); + } // note: no line wrap so it appears verbatim on the compiler error + + // the size of this property node in a binary file, in bytes + size_t size(); + + // write this property node as binary data to the given stream + void Dump(Assimp::StreamWriterLE &s); + +private: + char type; + std::vector data; +}; + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXEXPORTPROPERTY_H_INC diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp new file mode 100644 index 000000000..d13c97f6b --- /dev/null +++ b/code/FBXExporter.cpp @@ -0,0 +1,2036 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExporter.h" +#include "FBXExportNode.h" +#include "FBXExportProperty.h" +#include "FBXCommon.h" + +#include // aiGetVersion +#include +#include +#include +#include // StreamWriterLE +#include // DeadlyExportError +#include // aiTextureType +#include +#include + +// Header files, standard library. +#include // shared_ptr +#include +#include // stringstream +#include // localtime, tm_* +#include +#include +#include +#include // endl + +// RESOURCES: +// https://code.blender.org/2013/08/fbx-binary-file-format-specification/ +// https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure + +constexpr double DEG = 360.0 / 6.283185307179586476925286766559; + +// some constants that we'll use for writing metadata +namespace FBX { + const std::string EXPORT_VERSION_STR = "7.4.0"; + const uint32_t EXPORT_VERSION_INT = 7400; // 7.4 == 2014/2015 + // FBX files have some hashed values that depend on the creation time field, + // but for now we don't actually know how to generate these. + // what we can do is set them to a known-working version. + // this is the data that Blender uses in their FBX export process. + const std::string GENERIC_CTIME = "1970-01-01 10:00:00:000"; + const std::string GENERIC_FILEID = + "\x28\xb3\x2a\xeb\xb6\x24\xcc\xc2\xbf\xc8\xb0\x2a\xa9\x2b\xfc\xf1"; + const std::string GENERIC_FOOTID = + "\xfa\xbc\xab\x09\xd0\xc8\xd4\x66\xb1\x76\xfb\x83\x1c\xf7\x26\x7e"; + const std::string FOOT_MAGIC = + "\xf8\x5a\x8c\x6a\xde\xf5\xd9\x7e\xec\xe9\x0c\xe3\x75\x8f\x29\x0b"; +} + +using namespace Assimp; +using namespace FBX; + +namespace Assimp { + + // --------------------------------------------------------------------- + // Worker function for exporting a scene to binary FBX. + // Prototyped and registered in Exporter.cpp + void ExportSceneFBX ( + const char* pFile, + IOSystem* pIOSystem, + const aiScene* pScene, + const ExportProperties* pProperties + ){ + // initialize the exporter + FBXExporter exporter(pScene, pProperties); + + // perform binary export + exporter.ExportBinary(pFile, pIOSystem); + } + + // --------------------------------------------------------------------- + // Worker function for exporting a scene to ASCII FBX. + // Prototyped and registered in Exporter.cpp + /*void ExportSceneFBXA ( + const char* pFile, + IOSystem* pIOSystem, + const aiScene* pScene, + const ExportProperties* pProperties + ){ + // initialize the exporter + FBXExporter exporter(pScene, pProperties); + + // perform ascii export + exporter.ExportAscii(pFile, pIOSystem); + }*/ // TODO + +} // end of namespace Assimp + +FBXExporter::FBXExporter ( + const aiScene* pScene, + const ExportProperties* pProperties +) + : mScene(pScene) + , mProperties(pProperties) +{ + // will probably need to determine UIDs, connections, etc here. + // basically anything that needs to be known + // before we start writing sections to the stream. +} + +void FBXExporter::ExportBinary ( + const char* pFile, + IOSystem* pIOSystem +){ + // remember that we're exporting in binary mode + binary = true; + + // open the indicated file for writing (in binary mode) + outfile.reset(pIOSystem->Open(pFile,"wb")); + if (!outfile) { + throw DeadlyExportError( + "could not open output .fbx file: " + std::string(pFile) + ); + } + + // first a binary-specific file header + WriteBinaryHeader(); + + // the rest of the file is in node entries. + // we have to serialize each entry before we write to the output, + // as the first thing we write is the byte offset of the _next_ entry. + // Either that or we can skip back to write the offset when we finish. + WriteAllNodes(); + + // finally we have a binary footer to the file + WriteBinaryFooter(); + + // explicitly release file pointer, + // so we don't have to rely on class destruction. + outfile.reset(); +} + +void FBXExporter::ExportAscii ( + const char* pFile, + IOSystem* pIOSystem +){ + // remember that we're exporting in ascii mode + binary = false; + + // open the indicated file for writing in text mode + outfile.reset(pIOSystem->Open(pFile,"wt")); + if (!outfile) { + throw DeadlyExportError( + "could not open output .fbx file: " + std::string(pFile) + ); + } + + // this isn't really necessary, + // but the Autodesk FBX SDK puts a similar comment at the top of the file. + // Theirs declares that the file copyright is owned by Autodesk... + std::stringstream head; + using std::endl; + head << "; FBX " << EXPORT_VERSION_STR << " project file" << endl; + head << "; Created by the Open Asset Import Library (Assimp)" << endl; + head << "; http://assimp.org" << endl; + head << "; -------------------------------------------------" << endl; + head << endl; + const std::string ascii_header = head.str(); + outfile->Write(ascii_header.c_str(), ascii_header.size(), 1); + + // write all the sections + WriteAllNodes(); + + // explicitly release file pointer, + // so we don't have to rely on class destruction. + outfile.reset(); +} + +void FBXExporter::WriteBinaryHeader() +{ + // first a specific sequence of 23 bytes, always the same + const char binary_header[24] = "Kaydara FBX Binary\x20\x20\x00\x1a\x00"; + outfile->Write(binary_header, 1, 23); + + // then FBX version number, "multiplied" by 1000, as little-endian uint32. + // so 7.3 becomes 7300 == 0x841C0000, 7.4 becomes 7400 == 0xE81C0000, etc + { + StreamWriterLE outstream(outfile); + outstream.PutU4(EXPORT_VERSION_INT); + } // StreamWriter destructor writes the data to the file + + // after this the node data starts immediately + // (probably with the FBXHEaderExtension node) +} + +void FBXExporter::WriteBinaryFooter() +{ + outfile->Write(NULL_RECORD.c_str(), NULL_RECORD.size(), 1); + + outfile->Write(GENERIC_FOOTID.c_str(), GENERIC_FOOTID.size(), 1); + for (size_t i = 0; i < 4; ++i) { + outfile->Write("\x00", 1, 1); + } + + // here some padding is added for alignment to 16 bytes. + // if already aligned, the full 16 bytes is added. + size_t pos = outfile->Tell(); + size_t pad = 16 - (pos % 16); + for (size_t i = 0; i < pad; ++i) { + outfile->Write("\x00", 1, 1); + } + + // now the file version again + { + StreamWriterLE outstream(outfile); + outstream.PutU4(EXPORT_VERSION_INT); + } // StreamWriter destructor writes the data to the file + + // and finally some binary footer added to all files + for (size_t i = 0; i < 120; ++i) { + outfile->Write("\x00", 1, 1); + } + outfile->Write(FOOT_MAGIC.c_str(), FOOT_MAGIC.size(), 1); +} + +void FBXExporter::WriteAllNodes () +{ + // header + // (and fileid, creation time, creator, if binary) + WriteHeaderExtension(); + + // global settings + WriteGlobalSettings(); + + // documents + WriteDocuments(); + + // references + WriteReferences(); + + // definitions + WriteDefinitions(); + + // objects + WriteObjects(); + + // connections + WriteConnections(); + + // WriteTakes? (deprecated since at least 2015 (fbx 7.4)) +} + +//FBXHeaderExtension top-level node +void FBXExporter::WriteHeaderExtension () +{ + FBX::Node n("FBXHeaderExtension"); + StreamWriterLE outstream(outfile); + + // begin node + n.Begin(outstream); + + // write properties + // (none) + + // finish properties + n.EndProperties(outstream, 0); + + // write child nodes + FBX::Node::WritePropertyNode( + "FBXHeaderVersion", int32_t(1003), outstream + ); + FBX::Node::WritePropertyNode( + "FBXVersion", int32_t(EXPORT_VERSION_INT), outstream + ); + FBX::Node::WritePropertyNode( + "EncryptionType", int32_t(0), outstream + ); + + FBX::Node CreationTimeStamp("CreationTimeStamp"); + time_t rawtime; + time(&rawtime); + struct tm * now = localtime(&rawtime); + CreationTimeStamp.AddChild("Version", int32_t(1000)); + CreationTimeStamp.AddChild("Year", int32_t(now->tm_year + 1900)); + CreationTimeStamp.AddChild("Month", int32_t(now->tm_mon + 1)); + CreationTimeStamp.AddChild("Day", int32_t(now->tm_mday)); + CreationTimeStamp.AddChild("Hour", int32_t(now->tm_hour)); + CreationTimeStamp.AddChild("Minute", int32_t(now->tm_min)); + CreationTimeStamp.AddChild("Second", int32_t(now->tm_sec)); + CreationTimeStamp.AddChild("Millisecond", int32_t(0)); + CreationTimeStamp.Dump(outstream); + + std::stringstream creator; + creator << "Open Asset Import Library (Assimp) " << aiGetVersionMajor() + << "." << aiGetVersionMinor() << "." << aiGetVersionRevision(); + FBX::Node::WritePropertyNode("Creator", creator.str(), outstream); + + FBX::Node sceneinfo("SceneInfo"); + //sceneinfo.AddProperty("GlobalInfo" + FBX::SEPARATOR + "SceneInfo"); + // not sure if any of this is actually needed, + // so just write an empty node for now. + sceneinfo.Dump(outstream); + + // finish node + n.End(outstream, true); + + // that's it for FBXHeaderExtension... + + // but binary files also need top-level FileID, CreationTime, Creator: + std::vector raw(GENERIC_FILEID.size()); + for (size_t i = 0; i < GENERIC_FILEID.size(); ++i) { + raw[i] = uint8_t(GENERIC_FILEID[i]); + } + FBX::Node::WritePropertyNode("FileId", raw, outstream); + FBX::Node::WritePropertyNode("CreationTime", GENERIC_CTIME, outstream); + FBX::Node::WritePropertyNode("Creator", creator.str(), outstream); +} + +void FBXExporter::WriteGlobalSettings () +{ + FBX::Node gs("GlobalSettings"); + gs.AddChild("Version", int32_t(1000)); + + FBX::Node p("Properties70"); + p.AddP70int("UpAxis", 1); + p.AddP70int("UpAxisSign", 1); + p.AddP70int("FrontAxis", 2); + p.AddP70int("FrontAxisSign", 1); + p.AddP70int("CoordAxis", 0); + p.AddP70int("CoordAxisSign", 1); + p.AddP70int("OriginalUpAxis", 1); + p.AddP70int("OriginalUpAxisSign", 1); + p.AddP70double("UnitScaleFactor", 1.0); + p.AddP70double("OriginalUnitScaleFactor", 1.0); + p.AddP70color("AmbientColor", 0.0, 0.0, 0.0); + p.AddP70string("DefaultCamera", "Producer Perspective"); + p.AddP70enum("TimeMode", 11); + p.AddP70enum("TimeProtocol", 2); + p.AddP70enum("SnapOnFrameMode", 0); + p.AddP70time("TimeSpanStart", 0); // TODO: animation support + p.AddP70time("TimeSpanStop", FBX::SECOND); // TODO: animation support + p.AddP70double("CustomFrameRate", -1.0); + p.AddP70("TimeMarker", "Compound", "", ""); // not sure what this is + p.AddP70int("CurrentTimeMarker", -1); + gs.AddChild(p); + + gs.Dump(outfile); +} + +void FBXExporter::WriteDocuments () +{ + // not sure what the use of multiple documents would be, + // or whether any end-application supports it + FBX::Node docs("Documents"); + docs.AddChild("Count", int32_t(1)); + FBX::Node doc("Document"); + + // generate uid + int64_t uid = generate_uid(); + doc.AddProperties(uid, "", "Scene"); + FBX::Node p("Properties70"); + p.AddP70("SourceObject", "object", "", ""); // what is this even for? + p.AddP70string("ActiveAnimStackName", ""); // should do this properly? + doc.AddChild(p); + + // UID for root node in scene heirarchy. + // always set to 0 in the case of a single document. + // not sure what happens if more than one document exists, + // but that won't matter to us as we're exporting a single scene. + doc.AddChild("RootNode", int64_t(0)); + + docs.AddChild(doc); + docs.Dump(outfile); +} + +void FBXExporter::WriteReferences () +{ + // always empty for now. + // not really sure what this is for. + FBX::Node n("References"); + n.Dump(outfile); +} + + +// --------------------------------------------------------------- +// some internal helper functions used for writing the definitions +// (before any actual data is written) +// --------------------------------------------------------------- + +size_t count_nodes(const aiNode* n) { + size_t count = 1; + for (size_t i = 0; i < n->mNumChildren; ++i) { + count += count_nodes(n->mChildren[i]); + } + return count; +} + +bool has_phong_mat(const aiScene* scene) +{ + // just search for any material with a shininess exponent + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + aiMaterial* mat = scene->mMaterials[i]; + float shininess = 0; + mat->Get(AI_MATKEY_SHININESS, shininess); + if (shininess > 0) { + return true; + } + } + return false; +} + +size_t count_images(const aiScene* scene) { + std::unordered_set images; + aiString texpath; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + aiMaterial* mat = scene->mMaterials[i]; + for ( + size_t tt = aiTextureType_DIFFUSE; + tt < aiTextureType_UNKNOWN; + ++tt + ){ + const aiTextureType textype = static_cast(tt); + const size_t texcount = mat->GetTextureCount(textype); + for (size_t j = 0; j < texcount; ++j) { + mat->GetTexture(textype, j, &texpath); + images.insert(std::string(texpath.C_Str())); + } + } + } + //for (auto &s : images) { + // std::cout << "found image: " << s << std::endl; + //} + return images.size(); +} + +size_t count_textures(const aiScene* scene) { + size_t count = 0; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + aiMaterial* mat = scene->mMaterials[i]; + for ( + size_t tt = aiTextureType_DIFFUSE; + tt < aiTextureType_UNKNOWN; + ++tt + ){ + // TODO: handle layered textures + if (mat->GetTextureCount(static_cast(tt)) > 0) { + count += 1; + } + } + } + return count; +} + +size_t count_deformers(const aiScene* scene) { + size_t count = 0; + for (size_t i = 0; i < scene->mNumMeshes; ++i) { + const size_t n = scene->mMeshes[i]->mNumBones; + if (n) { + // 1 main deformer, 1 subdeformer per bone + count += n + 1; + } + } + return count; +} + +void FBXExporter::WriteDefinitions () +{ + // basically this is just bookkeeping: + // determining how many of each type of object there are + // and specifying the base properties to use when otherwise unspecified. + + // we need to count the objects + int32_t count; + int32_t total_count = 0; + + // and store them + std::vector object_nodes; + FBX::Node n, pt, p; + + // GlobalSettings + // this seems to always be here in Maya exports + n = FBX::Node("ObjectType", Property("GlobalSettings")); + count = 1; + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + + // AnimationStack / FbxAnimStack + // this seems to always be here in Maya exports + count = 0; + if (count) { + n = FBX::Node("ObjectType", Property("AnimationStack")); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", Property("FbxAnimStack")); + p = FBX::Node("Properties70"); + p.AddP70string("Description", ""); + p.AddP70time("LocalStart", 0); + p.AddP70time("LocalStop", 0); + p.AddP70time("ReferenceStart", 0); + p.AddP70time("ReferenceStop", 0); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // AnimationLayer / FbxAnimLayer + // this seems to always be here in Maya exports + count = 0; + if (count) { + n = FBX::Node("ObjectType", Property("AnimationLayer")); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", Property("FBXAnimLayer")); + p = FBX::Node("Properties70"); + p.AddP70("Weight", "Number", "", "A", double(100)); + p.AddP70bool("Mute", 0); + p.AddP70bool("Solo", 0); + p.AddP70bool("Lock", 0); + p.AddP70color("Color", 0.8, 0.8, 0.8); + p.AddP70("BlendMode", "enum", "", "", int32_t(0)); + p.AddP70("RotationAccumulationMode", "enum", "", "", int32_t(0)); + p.AddP70("ScaleAccumulationMode", "enum", "", "", int32_t(0)); + p.AddP70("BlendModeBypass", "ULongLong", "", "", int64_t(0)); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // NodeAttribute + // this is completely absurd. + // there can only be one "NodeAttribute" template, + // but FbxSkeleton, FbxCamera, FbxLight all are "NodeAttributes". + // so if only one exists we should set the template for that, + // otherwise... we just pick one :/. + // the others have to set all their properties every instance, + // because there's no template. + count = 1; // TODO: select properly + if (count) { + // FbxSkeleton + n = FBX::Node("ObjectType", Property("NodeAttribute")); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", Property("FbxSkeleton")); + p = FBX::Node("Properties70"); + p.AddP70color("Color", 0.8, 0.8, 0.8); + p.AddP70double("Size", 33.333333333333); + p.AddP70("LimbLength", "double", "Number", "H", double(1)); + // note: not sure what the "H" flag is for - hidden? + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Model / FbxNode + // <~~ node heirarchy + count = count_nodes(mScene->mRootNode) - 1; // (not counting root node) + if (count) { + n = FBX::Node("ObjectType", Property("Model")); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", Property("FbxNode")); + p = FBX::Node("Properties70"); + p.AddP70enum("QuaternionInterpolate", 0); + p.AddP70vector("RotationOffset", 0.0, 0.0, 0.0); + p.AddP70vector("RotationPivot", 0.0, 0.0, 0.0); + p.AddP70vector("ScalingOffset", 0.0, 0.0, 0.0); + p.AddP70vector("ScalingPivot", 0.0, 0.0, 0.0); + p.AddP70bool("TranslationActive", 0); + p.AddP70vector("TranslationMin", 0.0, 0.0, 0.0); + p.AddP70vector("TranslationMax", 0.0, 0.0, 0.0); + p.AddP70bool("TranslationMinX", 0); + p.AddP70bool("TranslationMinY", 0); + p.AddP70bool("TranslationMinZ", 0); + p.AddP70bool("TranslationMaxX", 0); + p.AddP70bool("TranslationMaxY", 0); + p.AddP70bool("TranslationMaxZ", 0); + p.AddP70enum("RotationOrder", 0); + p.AddP70bool("RotationSpaceForLimitOnly", 0); + p.AddP70double("RotationStiffnessX", 0.0); + p.AddP70double("RotationStiffnessY", 0.0); + p.AddP70double("RotationStiffnessZ", 0.0); + p.AddP70double("AxisLen", 10.0); + p.AddP70vector("PreRotation", 0.0, 0.0, 0.0); + p.AddP70vector("PostRotation", 0.0, 0.0, 0.0); + p.AddP70bool("RotationActive", 0); + p.AddP70vector("RotationMin", 0.0, 0.0, 0.0); + p.AddP70vector("RotationMax", 0.0, 0.0, 0.0); + p.AddP70bool("RotationMinX", 0); + p.AddP70bool("RotationMinY", 0); + p.AddP70bool("RotationMinZ", 0); + p.AddP70bool("RotationMaxX", 0); + p.AddP70bool("RotationMaxY", 0); + p.AddP70bool("RotationMaxZ", 0); + p.AddP70enum("InheritType", 0); + p.AddP70bool("ScalingActive", 0); + p.AddP70vector("ScalingMin", 0.0, 0.0, 0.0); + p.AddP70vector("ScalingMax", 1.0, 1.0, 1.0); + p.AddP70bool("ScalingMinX", 0); + p.AddP70bool("ScalingMinY", 0); + p.AddP70bool("ScalingMinZ", 0); + p.AddP70bool("ScalingMaxX", 0); + p.AddP70bool("ScalingMaxY", 0); + p.AddP70bool("ScalingMaxZ", 0); + p.AddP70vector("GeometricTranslation", 0.0, 0.0, 0.0); + p.AddP70vector("GeometricRotation", 0.0, 0.0, 0.0); + p.AddP70vector("GeometricScaling", 1.0, 1.0, 1.0); + p.AddP70double("MinDampRangeX", 0.0); + p.AddP70double("MinDampRangeY", 0.0); + p.AddP70double("MinDampRangeZ", 0.0); + p.AddP70double("MaxDampRangeX", 0.0); + p.AddP70double("MaxDampRangeY", 0.0); + p.AddP70double("MaxDampRangeZ", 0.0); + p.AddP70double("MinDampStrengthX", 0.0); + p.AddP70double("MinDampStrengthY", 0.0); + p.AddP70double("MinDampStrengthZ", 0.0); + p.AddP70double("MaxDampStrengthX", 0.0); + p.AddP70double("MaxDampStrengthY", 0.0); + p.AddP70double("MaxDampStrengthZ", 0.0); + p.AddP70double("PreferedAngleX", 0.0); + p.AddP70double("PreferedAngleY", 0.0); + p.AddP70double("PreferedAngleZ", 0.0); + p.AddP70("LookAtProperty", "object", "", ""); + p.AddP70("UpVectorProperty", "object", "", ""); + p.AddP70bool("Show", 1); + p.AddP70bool("NegativePercentShapeSupport", 1); + p.AddP70int("DefaultAttributeIndex", -1); + p.AddP70bool("Freeze", 0); + p.AddP70bool("LODBox", 0); + p.AddP70( + "Lcl Translation", "Lcl Translation", "", "A", + double(0), double(0), double(0) + ); + p.AddP70( + "Lcl Rotation", "Lcl Rotation", "", "A", + double(0), double(0), double(0) + ); + p.AddP70( + "Lcl Scaling", "Lcl Scaling", "", "A", + double(1), double(1), double(1) + ); + p.AddP70("Visibility", "Visibility", "", "A", double(1)); + p.AddP70( + "Visibility Inheritance", "Visibility Inheritance", "", "", + int32_t(1) + ); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Geometry / FbxMesh + // <~~ aiMesh + count = mScene->mNumMeshes; + if (count) { + n = FBX::Node("ObjectType", Property("Geometry")); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", Property("FbxMesh")); + p = FBX::Node("Properties70"); + p.AddP70color("Color", 0, 0, 0); + p.AddP70vector("BBoxMin", 0, 0, 0); + p.AddP70vector("BBoxMax", 0, 0, 0); + p.AddP70bool("Primary Visibility", 1); + p.AddP70bool("Casts Shadows", 1); + p.AddP70bool("Receive Shadows", 1); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Material / FbxSurfacePhong, FbxSurfaceLambert, FbxSurfaceMaterial + // <~~ aiMaterial + // basically if there's any phong material this is defined as phong, + // and otherwise lambert. + // More complex materials cause a bare-bones FbxSurfaceMaterial definition + // and are treated specially, as they're not really supported by FBX. + // TODO: support Maya's Stingray PBS material + count = mScene->mNumMaterials; + if (count) { + bool has_phong = has_phong_mat(mScene); + n = FBX::Node("ObjectType", Property("Material")); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate"); + if (has_phong) { + pt.AddProperty("FbxSurfacePhong"); + } else { + pt.AddProperty("FbxSurfaceLambert"); + } + p = FBX::Node("Properties70"); + if (has_phong) { + p.AddP70string("ShadingModel", "Phong"); + } else { + p.AddP70string("ShadingModel", "Lambert"); + } + p.AddP70bool("MultiLayer", 0); + p.AddP70colorA("EmissiveColor", 0.0, 0.0, 0.0); + p.AddP70numberA("EmissiveFactor", 1.0); + p.AddP70colorA("AmbientColor", 0.2, 0.2, 0.2); + p.AddP70numberA("AmbientFactor", 1.0); + p.AddP70colorA("DiffuseColor", 0.8, 0.8, 0.8); + p.AddP70numberA("DiffuseFactor", 1.0); + p.AddP70vector("Bump", 0.0, 0.0, 0.0); + p.AddP70vector("NormalMap", 0.0, 0.0, 0.0); + p.AddP70double("BumpFactor", 1.0); + p.AddP70colorA("TransparentColor", 0.0, 0.0, 0.0); + p.AddP70numberA("TransparencyFactor", 0.0); + p.AddP70color("DisplacementColor", 0.0, 0.0, 0.0); + p.AddP70double("DisplacementFactor", 1.0); + p.AddP70color("VectorDisplacementColor", 0.0, 0.0, 0.0); + p.AddP70double("VectorDisplacementFactor", 1.0); + if (has_phong) { + p.AddP70colorA("SpecularColor", 0.2, 0.2, 0.2); + p.AddP70numberA("SpecularFactor", 1.0); + p.AddP70numberA("ShininessExponent", 20.0); + p.AddP70colorA("ReflectionColor", 0.0, 0.0, 0.0); + p.AddP70numberA("ReflectionFactor", 1.0); + } + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Video / FbxVideo + // one for each image file. + count = count_images(mScene); + if (count) { + n = FBX::Node("ObjectType", Property("Video")); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", Property("FbxVideo")); + p = FBX::Node("Properties70"); + p.AddP70bool("ImageSequence", 0); + p.AddP70int("ImageSequenceOffset", 0); + p.AddP70double("FrameRate", 0.0); + p.AddP70int("LastFrame", 0); + p.AddP70int("Width", 0); + p.AddP70int("Height", 0); + p.AddP70("Path", "KString", "XRefUrl", "", ""); + p.AddP70int("StartFrame", 0); + p.AddP70int("StopFrame", 0); + p.AddP70double("PlaySpeed", 0.0); + p.AddP70time("Offset", 0); + p.AddP70enum("InterlaceMode", 0); + p.AddP70bool("FreeRunning", 0); + p.AddP70bool("Loop", 0); + p.AddP70enum("AccessMode", 0); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Texture / FbxFileTexture + // <~~ aiTexture + count = count_textures(mScene); + if (count) { + n = FBX::Node("ObjectType", Property("Texture")); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", Property("FbxFileTexture")); + p = FBX::Node("Properties70"); + p.AddP70enum("TextureTypeUse", 0); + p.AddP70numberA("Texture alpha", 1.0); + p.AddP70enum("CurrentMappingType", 0); + p.AddP70enum("WrapModeU", 0); + p.AddP70enum("WrapModeV", 0); + p.AddP70bool("UVSwap", 0); + p.AddP70bool("PremultiplyAlpha", 1); + p.AddP70vectorA("Translation", 0.0, 0.0, 0.0); + p.AddP70vectorA("Rotation", 0.0, 0.0, 0.0); + p.AddP70vectorA("Scaling", 1.0, 1.0, 1.0); + p.AddP70vector("TextureRotationPivot", 0.0, 0.0, 0.0); + p.AddP70vector("TextureScalingPivot", 0.0, 0.0, 0.0); + p.AddP70enum("CurrentTextureBlendMode", 1); + p.AddP70string("UVSet", "default"); + p.AddP70bool("UseMaterial", 0); + p.AddP70bool("UseMipMap", 0); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // AnimationCurveNode / FbxAnimCurveNode + count = 0; + if (count) { + n = FBX::Node("ObjectType", Property("AnimationCurveNode")); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", Property("FbxAnimCurveNode")); + p = FBX::Node("Properties70"); + p.AddP70("d", "Compound", "", ""); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Pose + count = 0; + for (size_t i = 0; i < mScene->mNumMeshes; ++i) { + aiMesh* mesh = mScene->mMeshes[i]; + if (mesh->HasBones()) { ++count; } + } + if (count) { + n = FBX::Node("ObjectType", Property("Pose")); + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + } + + // Deformer + count = count_deformers(mScene); + if (count) { + n = FBX::Node("ObjectType", Property("Deformer")); + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + } + + // (template) + count = 0; + if (count) { + n = FBX::Node("ObjectType", Property("")); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", Property("")); + p = FBX::Node("Properties70"); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // now write it all + FBX::Node defs("Definitions"); + defs.AddChild("Version", int32_t(100)); + defs.AddChild("Count", int32_t(total_count)); + for (auto &n : object_nodes) { defs.AddChild(n); } + defs.Dump(outfile); +} + + +// ------------------------------------------------------------------- +// some internal helper functions used for writing the objects section +// (which holds the actual data) +// ------------------------------------------------------------------- + +aiNode* get_node_for_mesh(unsigned int meshIndex, aiNode* node) +{ + for (size_t i = 0; i < node->mNumMeshes; ++i) { + if (node->mMeshes[i] == meshIndex) { + return node; + } + } + for (size_t i = 0; i < node->mNumChildren; ++i) { + aiNode* ret = get_node_for_mesh(meshIndex, node->mChildren[i]); + if (ret) { return ret; } + } + return nullptr; +} + +aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene) +{ + std::vector node_chain; + while (node != scene->mRootNode) { + node_chain.push_back(node); + node = node->mParent; + } + aiMatrix4x4 transform; + for (auto n = node_chain.rbegin(); n != node_chain.rend(); ++n) { + transform *= (*n)->mTransformation; + } + return transform; +} + + +void FBXExporter::WriteObjects () +{ + // numbers should match those given in definitions! make sure to check + StreamWriterLE outstream(outfile); + FBX::Node object_node("Objects"); + object_node.Begin(outstream); + object_node.EndProperties(outstream); + + // geometry (aiMesh) + mesh_uids.clear(); + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + // it's all about this mesh + aiMesh* m = mScene->mMeshes[mi]; + + // start the node record + FBX::Node n("Geometry"); + int64_t uid = generate_uid(); + mesh_uids.push_back(uid); + n.AddProperty(uid); + n.AddProperty(FBX::SEPARATOR + "Geometry"); + n.AddProperty("Mesh"); + n.Begin(outstream); + n.DumpProperties(outstream); + n.EndProperties(outstream); + + // output vertex data - each vertex should be unique (probably) + std::vector flattened_vertices; + // index of original vertex in vertex data vector + std::vector vertex_indices; + // map of vertex value to its index in the data vector + std::map index_by_vertex_value; + size_t index = 0; + for (size_t vi = 0; vi < m->mNumVertices; ++vi) { + aiVector3D vtx = m->mVertices[vi]; + auto elem = index_by_vertex_value.find(vtx); + if (elem == index_by_vertex_value.end()) { + vertex_indices.push_back(index); + index_by_vertex_value[vtx] = index; + flattened_vertices.push_back(vtx[0]); + flattened_vertices.push_back(vtx[1]); + flattened_vertices.push_back(vtx[2]); + ++index; + } else { + vertex_indices.push_back(elem->second); + } + } + FBX::Node::WritePropertyNode( + "Vertices", flattened_vertices, outstream + ); + + // output polygon data as a flattened array of vertex indices. + // the last vertex index of each polygon is negated and - 1 + std::vector polygon_data; + for (size_t fi = 0; fi < m->mNumFaces; ++fi) { + const aiFace &f = m->mFaces[fi]; + for (size_t pvi = 0; pvi < f.mNumIndices - 1; ++pvi) { + polygon_data.push_back(vertex_indices[f.mIndices[pvi]]); + } + polygon_data.push_back( + -1 - vertex_indices[f.mIndices[f.mNumIndices-1]] + ); + } + FBX::Node::WritePropertyNode( + "PolygonVertexIndex", polygon_data, outstream + ); + + // here could be edges but they're insane. + // it's optional anyway, so let's ignore it. + + FBX::Node::WritePropertyNode( + "GeometryVersion", int32_t(124), outstream + ); + + // normals, if any + if (m->HasNormals()) { + FBX::Node normals("LayerElementNormal", Property(int32_t(0))); + normals.Begin(outstream); + normals.DumpProperties(outstream); + normals.EndProperties(outstream); + FBX::Node::WritePropertyNode("Version", int32_t(101), outstream); + FBX::Node::WritePropertyNode("Name", "", outstream); + FBX::Node::WritePropertyNode( + "MappingInformationType", "ByPolygonVertex", outstream + ); + // TODO: vertex-normals or indexed normals when appropriate + FBX::Node::WritePropertyNode( + "ReferenceInformationType", "Direct", outstream + ); + std::vector normal_data; + normal_data.reserve(3 * polygon_data.size()); + for (size_t fi = 0; fi < m->mNumFaces; ++fi) { + const aiFace &f = m->mFaces[fi]; + for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) { + const aiVector3D &n = m->mNormals[f.mIndices[pvi]]; + normal_data.push_back(n.x); + normal_data.push_back(n.y); + normal_data.push_back(n.z); + } + } + FBX::Node::WritePropertyNode("Normals", normal_data, outstream); + // note: version 102 has a NormalsW also... not sure what it is, + // so we can stick with version 101 for now. + normals.End(outstream, true); + } + + // uvs, if any + for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) { + if (m->mNumUVComponents[uvi] > 2) { + // FBX only supports 2-channel UV maps... + // or at least i'm not sure how to indicate a different number + std::stringstream err; + err << "Only 2-channel UV maps supported by FBX,"; + err << " but mesh " << mi; + if (m->mName.length) { + err << " (" << m->mName.C_Str() << ")"; + } + err << " UV map " << uvi; + err << " has " << m->mNumUVComponents[uvi]; + err << " components! Data will be preserved,"; + err << " but may be incorrectly interpreted on load."; + DefaultLogger::get()->warn(err.str()); + } + FBX::Node uv("LayerElementUV", Property(int32_t(uvi))); + uv.Begin(outstream); + uv.DumpProperties(outstream); + uv.EndProperties(outstream); + FBX::Node::WritePropertyNode("Version", int32_t(101), outstream); + // it doesn't seem like assimp keeps the uv map name, + // so just leave it blank. + FBX::Node::WritePropertyNode("Name", "", outstream); + FBX::Node::WritePropertyNode( + "MappingInformationType", "ByPolygonVertex", outstream + ); + FBX::Node::WritePropertyNode( + "ReferenceInformationType", "IndexToDirect", outstream + ); + + std::vector uv_data; + std::vector uv_indices; + std::map index_by_uv; + size_t index = 0; + for (size_t fi = 0; fi < m->mNumFaces; ++fi) { + const aiFace &f = m->mFaces[fi]; + for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) { + const aiVector3D &uv = + m->mTextureCoords[uvi][f.mIndices[pvi]]; + auto elem = index_by_uv.find(uv); + if (elem == index_by_uv.end()) { + index_by_uv[uv] = index; + uv_indices.push_back(index); + for (size_t x = 0; x < m->mNumUVComponents[uvi]; ++x) { + uv_data.push_back(uv[x]); + } + ++index; + } else { + uv_indices.push_back(elem->second); + } + } + } + FBX::Node::WritePropertyNode("UV", uv_data, outstream); + FBX::Node::WritePropertyNode("UVIndex", uv_indices, outstream); + uv.End(outstream, true); + } + + // i'm not really sure why this material section exists, + // as the material is linked via "Connections". + // it seems to always have the same "0" value. + FBX::Node mat("LayerElementMaterial", Property(int32_t(0))); + mat.AddChild("Version", int32_t(101)); + mat.AddChild("Name", ""); + mat.AddChild("MappingInformationType", "AllSame"); + mat.AddChild("ReferenceInformationType", "IndexToDirect"); + std::vector mat_indices = {0}; + mat.AddChild("Materials", mat_indices); + mat.Dump(outstream); + + // finally we have the layer specifications, + // which select the normals / UV set / etc to use. + // TODO: handle multiple uv sets correctly? + FBX::Node layer("Layer", Property(int32_t(0))); + layer.AddChild("Version", int32_t(100)); + FBX::Node le("LayerElement"); + le.AddChild("Type", "LayerElementNormal"); + le.AddChild("TypedIndex", int32_t(0)); + layer.AddChild(le); + le = FBX::Node("LayerElement"); + le.AddChild("Type", "LayerElementMaterial"); + le.AddChild("TypedIndex", int32_t(0)); + layer.AddChild(le); + le = FBX::Node("LayerElement"); + le.AddChild("Type", "LayerElementUV"); + le.AddChild("TypedIndex", int32_t(0)); + layer.AddChild(le); + layer.Dump(outstream); + + // finish the node record + n.End(outstream, true); + } + + // aiMaterial + material_uids.clear(); + for (size_t i = 0; i < mScene->mNumMaterials; ++i) { + // it's all about this material + aiMaterial* m = mScene->mMaterials[i]; + + // these are used to recieve material data + float f; aiColor3D c; + + // start the node record + FBX::Node n("Material"); + + int64_t uid = generate_uid(); + material_uids.push_back(uid); + n.AddProperty(uid); + + aiString name; + m->Get(AI_MATKEY_NAME, name); + n.AddProperty(name.C_Str() + FBX::SEPARATOR + "Material"); + + n.AddProperty(""); + + n.AddChild("Version", int32_t(102)); + f = 0; + m->Get(AI_MATKEY_SHININESS, f); + bool phong = (f > 0); + if (phong) { + n.AddChild("ShadingModel", "phong"); + } else { + n.AddChild("ShadingModel", "lambert"); + } + n.AddChild("MultiLayer", int32_t(0)); + + FBX::Node p("Properties70"); + + // materials exported using the FBX SDK have two sets of fields. + // there are the properties specified in the PropertyTemplate, + // which are those supported by the modernFBX SDK, + // and an extra set of properties with simpler names. + // The extra properties are a legacy material system from pre-2009. + // + // In the modern system, each property has "color" and "factor". + // Generally the interpretation of these seems to be + // that the colour is multiplied by the factor before use, + // but this is not always clear-cut. + // + // Usually assimp only stores the colour, + // so we can just leave the factors at the default "1.0". + + // first we can export the "standard" properties + if (m->Get(AI_MATKEY_COLOR_AMBIENT, c) == aiReturn_SUCCESS) { + p.AddP70colorA("AmbientColor", c.r, c.g, c.b); + //p.AddP70numberA("AmbientFactor", 1.0); + } + if (m->Get(AI_MATKEY_COLOR_DIFFUSE, c) == aiReturn_SUCCESS) { + p.AddP70colorA("DiffuseColor", c.r, c.g, c.b); + //p.AddP70numberA("DiffuseFactor", 1.0); + } + if (m->Get(AI_MATKEY_COLOR_TRANSPARENT, c) == aiReturn_SUCCESS) { + // "TransparentColor" / "TransparencyFactor"... + // thanks FBX, for your insightful interpretation of consistency + p.AddP70colorA("TransparentColor", c.r, c.g, c.b); + // TransparencyFactor defaults to 0.0, so set it to 1.0. + // note: Maya always sets this to 1.0, + // so we can't use it sensibly as "Opacity". + // In stead we rely on the legacy "Opacity" value, below. + // Blender also relies on "Opacity" not "TransparencyFactor", + // probably for a similar reason. + p.AddP70numberA("TransparencyFactor", 1.0); + } + if (m->Get(AI_MATKEY_COLOR_REFLECTIVE, c) == aiReturn_SUCCESS) { + p.AddP70colorA("ReflectionColor", c.r, c.g, c.b); + } + if (m->Get(AI_MATKEY_REFLECTIVITY, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ReflectionFactor", f); + } + if (phong) { + if (m->Get(AI_MATKEY_COLOR_SPECULAR, c) == aiReturn_SUCCESS) { + p.AddP70colorA("SpecularColor", c.r, c.g, c.b); + } + if (m->Get(AI_MATKEY_SHININESS_STRENGTH, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ShininessFactor", f); + } + if (m->Get(AI_MATKEY_SHININESS, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ShininessExponent", f); + } + if (m->Get(AI_MATKEY_REFLECTIVITY, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ReflectionFactor", f); + } + } + + // Now the legacy system. + // For safety let's include it. + // thrse values don't exist in the property template, + // and usualy are completely ignored when loading. + // One notable exception is the "Opacity" property, + // which Blender uses as (1.0 - alpha). + c.r = 0; c.g = 0; c.b = 0; + m->Get(AI_MATKEY_COLOR_EMISSIVE, c); + p.AddP70vector("Emissive", c.r, c.g, c.b); + c.r = 0.2; c.g = 0.2; c.b = 0.2; + m->Get(AI_MATKEY_COLOR_AMBIENT, c); + p.AddP70vector("Ambient", c.r, c.g, c.b); + c.r = 0.8; c.g = 0.8; c.b = 0.8; + m->Get(AI_MATKEY_COLOR_DIFFUSE, c); + p.AddP70vector("Diffuse", c.r, c.g, c.b); + // The FBX SDK determines "Opacity" from transparency colour (RGB) + // and factor (F) as: O = (1.0 - F * ((R + G + B) / 3)). + // However we actually have an opacity value, + // so we should take it from AI_MATKEY_OPACITY if possible. + // It might make more sense to use TransparencyFactor, + // but Blender actually loads "Opacity" correctly, so let's use it. + f = 1.0; + if (m->Get(AI_MATKEY_COLOR_TRANSPARENT, c) == aiReturn_SUCCESS) { + f = 1.0 - ((c.r + c.g + c.b) / 3); + } + m->Get(AI_MATKEY_OPACITY, f); + p.AddP70double("Opacity", f); + if (phong) { + // specular color is multiplied by shininess_strength + c.r = 0.2; c.g = 0.2; c.b = 0.2; + m->Get(AI_MATKEY_COLOR_SPECULAR, c); + f = 1.0; + m->Get(AI_MATKEY_SHININESS_STRENGTH, f); + p.AddP70vector("Specular", f*c.r, f*c.g, f*c.b); + f = 20.0; + m->Get(AI_MATKEY_SHININESS, f); + p.AddP70double("Shininess", f); + // Legacy "Reflectivity" is F*F*((R+G+B)/3), + // where F is the proportion of light reflected (AKA reflectivity), + // and RGB is the reflective colour of the material. + // No idea why, but we might as well set it the same way. + f = 0.0; + m->Get(AI_MATKEY_REFLECTIVITY, f); + c.r = 1.0, c.g = 1.0, c.b = 1.0; + m->Get(AI_MATKEY_COLOR_REFLECTIVE, c); + p.AddP70double("Reflectivity", f*f*((c.r+c.g+c.b)/3.0)); + } + + n.AddChild(p); + + n.Dump(outstream); + } + + // we need to look up all the images we're using, + // so we can generate uids, and eliminate duplicates. + std::map uid_by_image; + for (size_t i = 0; i < mScene->mNumMaterials; ++i) { + aiString texpath; + aiMaterial* mat = mScene->mMaterials[i]; + for ( + size_t tt = aiTextureType_DIFFUSE; + tt < aiTextureType_UNKNOWN; + ++tt + ){ + const aiTextureType textype = static_cast(tt); + const size_t texcount = mat->GetTextureCount(textype); + for (size_t j = 0; j < texcount; ++j) { + mat->GetTexture(textype, j, &texpath); + const std::string texstring = texpath.C_Str(); + auto elem = uid_by_image.find(texstring); + if (elem == uid_by_image.end()) { + uid_by_image[texstring] = generate_uid(); + } + } + } + } + + // FbxVideo - stores images used by textures. + for (const auto &it : uid_by_image) { + if (it.first.compare(0, 1, "*") == 0) { + // TODO: embedded textures + continue; + } + FBX::Node n("Video"); + const int64_t& uid = it.second; + const std::string name = ""; // TODO: ... name??? + n.AddProperties(uid, name + FBX::SEPARATOR + "Video", "Clip"); + n.AddChild("Type", "Clip"); + FBX::Node p("Properties70"); + // TODO: get full path... relative path... etc... ugh... + // for now just use the same path for everything, + // and hopefully one of them will work out. + const std::string& path = it.first; + p.AddP70("Path", "KString", "XRefUrl", "", path); + n.AddChild(p); + n.AddChild("UseMipMap", int32_t(0)); + n.AddChild("Filename", path); + n.AddChild("RelativeFilename", path); + n.Dump(outstream); + } + + // Textures + // referenced by material_index/texture_type pairs. + std::map,int64_t> texture_uids; + const std::map prop_name_by_tt = { + {aiTextureType_DIFFUSE, "DiffuseColor"}, + {aiTextureType_SPECULAR, "SpecularColor"}, + {aiTextureType_AMBIENT, "AmbientColor"}, + {aiTextureType_EMISSIVE, "EmissiveColor"}, + {aiTextureType_HEIGHT, "Bump"}, + {aiTextureType_NORMALS, "NormalMap"}, + {aiTextureType_SHININESS, "ShininessExponent"}, + {aiTextureType_OPACITY, "TransparentColor"}, + {aiTextureType_DISPLACEMENT, "DisplacementColor"}, + //{aiTextureType_LIGHTMAP, "???"}, + {aiTextureType_REFLECTION, "ReflectionColor"} + //{aiTextureType_UNKNOWN, ""} + }; + for (size_t i = 0; i < mScene->mNumMaterials; ++i) { + // textures are attached to materials + aiMaterial* mat = mScene->mMaterials[i]; + int64_t material_uid = material_uids[i]; + + for ( + size_t j = aiTextureType_DIFFUSE; + j < aiTextureType_UNKNOWN; + ++j + ) { + const aiTextureType tt = static_cast(j); + size_t n = mat->GetTextureCount(tt); + + if (n < 1) { // no texture of this type + continue; + } + + if (n > 1) { + // TODO: multilayer textures + std::stringstream err; + err << "Multilayer textures not supported (for now),"; + err << " skipping texture type " << j; + err << " of material " << i; + DefaultLogger::get()->warn(err.str()); + } + + // get image path for this (single-image) texture + aiString tpath; + if (mat->GetTexture(tt, 0, &tpath) != aiReturn_SUCCESS) { + std::stringstream err; + err << "Failed to get texture 0 for texture of type " << tt; + err << " on material " << i; + err << ", however GetTextureCount returned 1."; + throw DeadlyExportError(err.str()); + } + const std::string texture_path(tpath.C_Str()); + + // get connected image uid + auto elem = uid_by_image.find(texture_path); + if (elem == uid_by_image.end()) { + // this should never happen + std::stringstream err; + err << "Failed to find video element for texture with path"; + err << " \"" << texture_path << "\""; + err << ", type " << j << ", material " << i; + throw DeadlyExportError(err.str()); + } + const int64_t image_uid = elem->second; + + // get the name of the material property to connect to + auto elem2 = prop_name_by_tt.find(tt); + if (elem2 == prop_name_by_tt.end()) { + // don't know how to handle this type of texture, + // so skip it. + std::stringstream err; + err << "Not sure how to handle texture of type " << j; + err << " on material " << i; + err << ", skipping..."; + DefaultLogger::get()->warn(err.str()); + continue; + } + const std::string& prop_name = elem2->second; + + // generate a uid for this texture + const int64_t texture_uid = generate_uid(); + + // link the texture to the material + FBX::Node c("C"); + c.AddProperties("OP", texture_uid, material_uid, prop_name); + connections.push_back(c); + + // link the image data to the texture + c = FBX::Node("C"); + c.AddProperties("OO", image_uid, texture_uid); + connections.push_back(c); + + // now write the actual texture node + FBX::Node tnode("Texture"); + // TODO: some way to determine texture name? + const std::string texture_name = "" + FBX::SEPARATOR + "Texture"; + tnode.AddProperties(texture_uid, texture_name, ""); + // there really doesn't seem to be a better type than this: + tnode.AddChild("Type", "TextureVideoClip"); + tnode.AddChild("Version", int32_t(202)); + tnode.AddChild("TextureName", texture_name); + FBX::Node p("Properties70"); + p.AddP70enum("CurrentTextureBlendMode", 0); // TODO: verify + //p.AddP70string("UVSet", ""); // TODO: how should this work? + p.AddP70bool("UseMaterial", 1); + tnode.AddChild(p); + // can't easily detrmine which texture path will be correct, + // so just store what we have in every field. + // these being incorrect is a common problem with FBX anyway. + tnode.AddChild("FileName", texture_path); + tnode.AddChild("RelativeFilename", texture_path); + tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0)); + tnode.AddChild("ModelUVScaling", double(1.0), double(1.0)); + tnode.AddChild("Texture_Alpha_Soutce", "None"); + tnode.AddChild( + "Cropping", int32_t(0), int32_t(0), int32_t(0), int32_t(0) + ); + tnode.Dump(outstream); + } + } + + // bones. + // + // output structure: + // subset of node heirarchy that are "skeleton", + // i.e. do not have meshes but only bones. + // but.. i'm not sure how anyone could guarantee that... + // + // input... + // well, for each mesh it has "bones", + // and the bone names correspond to nodes. + // of course we also need the parent nodes, + // as they give some of the transform........ + // + // well. we can assume a sane input, i suppose. + // + // so input is the bone node heirarchy, + // with an extra thing for the transformation of the MESH in BONE space. + // + // output is a set of bone nodes, + // a "bindpose" which indicates the default local transform of all bones, + // and a set of "deformers". + // each deformer is parented to a mesh geometry, + // and has one or more "subdeformer"s as children. + // each subdeformer has one bone node as a child, + // and represents the influence of that bone on the grandparent mesh. + // the subdeformer has a list of indices, and weights, + // with indices specifying vertex indices, + // and weights specifying the correspoding influence of this bone. + // it also has Transform and TransformLink elements, + // specifying the transform of the MESH in BONE space, + // and the transformation of the BONE in WORLD space, + // likely in the bindpose. + // + // the input bone structure is different but similar, + // storing the number of weights for this bone, + // and an array of (vertex index, weight) pairs. + // + // one sticky point is that the number of vertices may not match, + // because assimp splits vertices by normal, uv, etc. + + // first we should mark all the skeleton nodes, + // so that they can be treated as LimbNode in stead of Mesh or Null. + // at the same time we can build up a map of bone nodes. + std::unordered_set limbnodes; + std::map node_by_bone; + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + const aiMesh* m = mScene->mMeshes[mi]; + for (size_t bi =0; bi < m->mNumBones; ++bi) { + const aiBone* b = m->mBones[bi]; + const std::string name(b->mName.C_Str()); + if (node_by_bone.count(name) > 0) { + // already processed, skip + continue; + } + aiNode* n = mScene->mRootNode->FindNode(b->mName); + if (!n) { + // this should never happen + std::stringstream err; + err << "Failed to find node for bone: \"" << name << "\""; + throw DeadlyExportError(err.str()); + } + node_by_bone[name] = n; + limbnodes.insert(n); + if (n == mScene->mRootNode) { continue; } + // mark all parent nodes as skeleton as well, + // up until we find the root node, + // or else the node containing the mesh, + // or else the parent of a node containig the mesh. + for ( + const aiNode* parent = n->mParent; + parent != mScene->mRootNode; + parent = parent->mParent + ) { + bool end = false; + for (size_t i = 0; i < parent->mNumMeshes; ++i) { + if (parent->mMeshes[i] == mi) { + end = true; + break; + } + } + for (size_t j = 0; j < parent->mNumChildren; ++j) { + aiNode* child = parent->mChildren[j]; + for (size_t i = 0; i < child->mNumMeshes; ++i) { + if (child->mMeshes[i] == mi) { + end = true; + break; + } + } + if (end) { break; } + } + if (end) { break; } + limbnodes.insert(parent); + } + } + } + + // we'll need the uids for the bone nodes, so generate them now + std::map bone_uids; + for (auto &bone : limbnodes) { + std::string bone_name(bone->mName.C_Str()); + aiNode* bone_node = mScene->mRootNode->FindNode(bone->mName); + if (!bone_node) { + throw DeadlyExportError("Couldn't find node for bone" + bone_name); + } + auto elem = node_uids.find(bone_node); + if (elem == node_uids.end()) { + int64_t uid = generate_uid(); + node_uids[bone_node] = uid; + bone_uids[bone_name] = uid; + } else { + bone_uids[bone_name] = elem->second; + } + } + + // now, for each aiMesh, we need to export a deformer, + // and for each aiBone a subdeformer, + // which should have all the skinning info. + // these will need to be connected properly to the mesh, + // and we can do that all now. + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + const aiMesh* m = mScene->mMeshes[mi]; + if (!m->HasBones()) { + continue; + } + // make a deformer for this mesh + int64_t deformer_uid = generate_uid(); + FBX::Node dnode("Deformer"); + dnode.AddProperties(deformer_uid, FBX::SEPARATOR + "Deformer", "Skin"); + dnode.AddChild("Version", int32_t(101)); + // "acuracy"... this is not a typo.... + dnode.AddChild("Link_DeformAcuracy", double(50)); + dnode.AddChild("SkinningType", "Linear"); // TODO: other modes? + dnode.Dump(outstream); + + // connect it + FBX::Node c("C"); + c.AddProperties("OO", deformer_uid, mesh_uids[mi]); + connections.push_back(c); // TODO: emplace_back + + // we will be indexing by vertex... + // but there might be a different number of "vertices" + // between assimp and our output FBX. + // this code is cut-and-pasted from the geometry section above... + // ideally this should not be so. + // --- + // index of original vertex in vertex data vector + std::vector vertex_indices; + // map of vertex value to its index in the data vector + std::map index_by_vertex_value; + size_t index = 0; + for (size_t vi = 0; vi < m->mNumVertices; ++vi) { + aiVector3D vtx = m->mVertices[vi]; + auto elem = index_by_vertex_value.find(vtx); + if (elem == index_by_vertex_value.end()) { + vertex_indices.push_back(index); + index_by_vertex_value[vtx] = index; + ++index; + } else { + vertex_indices.push_back(elem->second); + } + } + + // first get this mesh's position in world space, + // as we'll need it for each subdeformer. + // + // ...of course taking the position of the MESH doesn't make sense, + // as it can be instanced to many nodes. + // All we can do is assume no instancing, + // and take the first node we find that contains the mesh. + // + // We could in stead take the transform from the bone's node, + // but there's no guarantee that the bone is in the bindpose, + // so this would be even less reliable. + aiNode* mesh_node = get_node_for_mesh(mi, mScene->mRootNode); + aiMatrix4x4 mesh_node_xform = get_world_transform(mesh_node, mScene); + + // now make a subdeformer for each bone + for (size_t bi =0; bi < m->mNumBones; ++bi) { + const aiBone* b = m->mBones[bi]; + const std::string name(b->mName.C_Str()); + const int64_t subdeformer_uid = generate_uid(); + FBX::Node sdnode("Deformer"); + sdnode.AddProperties( + subdeformer_uid, FBX::SEPARATOR + "SubDeformer", "Cluster" + ); + sdnode.AddChild("Version", int32_t(100)); + sdnode.AddChild("UserData", "", ""); + + // get indices and weights + std::vector subdef_indices; + std::vector subdef_weights; + int32_t last_index = -1; + for (size_t wi = 0; wi < b->mNumWeights; ++wi) { + int32_t vi = vertex_indices[b->mWeights[wi].mVertexId]; + if (vi == last_index) { + // only for vertices we exported to fbx + // TODO, FIXME: this assumes identically-located vertices + // will always deform in the same way. + // as assimp doesn't store a separate list of "positions", + // there's not much that can be done about this + // other than assuming that identical position means + // identical vertex. + continue; + } + subdef_indices.push_back(vi); + subdef_weights.push_back(b->mWeights[wi].mWeight); + last_index = vi; + } + // yes, "indexes" + sdnode.AddChild("Indexes", subdef_indices); + sdnode.AddChild("Weights", subdef_weights); + // transform is the transform of the mesh, but in bone space... + // which is exactly what assimp's mOffsetMatrix is, + // no matter what the assimp docs may say. + aiMatrix4x4 tr = b->mOffsetMatrix; + sdnode.AddChild("Transform", tr); + // transformlink should be the position of the bone in world space, + // in the bind pose. + // For now let's use the inverse of mOffsetMatrix, + // and the (assumedly static) mesh position in world space. + // TODO: find a better way of doing this? there aren't many options + tr = b->mOffsetMatrix; + tr.Inverse(); + tr *= mesh_node_xform; + sdnode.AddChild("TransformLink", tr); + + // done + sdnode.Dump(outstream); + + // lastly, connect to the parent deformer + c = FBX::Node("C"); + c.AddProperties("OO", subdeformer_uid, deformer_uid); + connections.push_back(c); // TODO: emplace_back + + // we also need to connect the limb node to the subdeformer. + c = FBX::Node("C"); + c.AddProperties("OO", bone_uids[name], subdeformer_uid); + connections.push_back(c); // TODO: emplace_back + } + + + } + + // BindPose + // + // This is a legacy system, which should be unnecessary. + // + // Somehow including it slows file loading by the official FBX SDK, + // and as it can reconstruct it from the deformers anyway, + // this is not currently included. + // + // The code is kept here in case it's useful in the future, + // but it's pretty much a hack anyway, + // as assimp doesn't store bindpose information for full skeletons. + // + /*for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + aiMesh* mesh = mScene->mMeshes[mi]; + if (! mesh->HasBones()) { continue; } + int64_t bindpose_uid = generate_uid(); + FBX::Node bpnode("Pose"); + bpnode.AddProperty(bindpose_uid); + // note: this uid is never linked or connected to anything. + bpnode.AddProperty(FBX::SEPARATOR + "Pose"); // blank name + bpnode.AddProperty("BindPose"); + + bpnode.AddChild("Type", "BindPose"); + bpnode.AddChild("Version", int32_t(100)); + + aiNode* mesh_node = get_node_for_mesh(mi, mScene->mRootNode); + + // next get the whole skeleton for this mesh. + // we need it all to define the bindpose section. + // the FBX SDK will complain if it's missing, + // and also if parents of used bones don't have a subdeformer. + // order shouldn't matter. + std::set skeleton; + for (size_t bi = 0; bi < mesh->mNumBones; ++bi) { + // bone node should have already been indexed + const aiBone* b = mesh->mBones[bi]; + const std::string bone_name(b->mName.C_Str()); + aiNode* parent = node_by_bone[bone_name]; + // insert all nodes down to the root or mesh node + while ( + parent + && parent != mScene->mRootNode + && parent != mesh_node + ) { + skeleton.insert(parent); + parent = parent->mParent; + } + } + + // number of pose nodes. includes one for the mesh itself. + bpnode.AddChild("NbPoseNodes", int32_t(1 + skeleton.size())); + + // the first pose node is always the mesh itself + FBX::Node pose("PoseNode"); + pose.AddChild("Node", mesh_uids[mi]); + aiMatrix4x4 mesh_node_xform = get_world_transform(mesh_node, mScene); + pose.AddChild("Matrix", mesh_node_xform); + bpnode.AddChild(pose); + + for (aiNode* bonenode : skeleton) { + // does this node have a uid yet? + int64_t node_uid; + auto node_uid_iter = node_uids.find(bonenode); + if (node_uid_iter != node_uids.end()) { + node_uid = node_uid_iter->second; + } else { + node_uid = generate_uid(); + node_uids[bonenode] = node_uid; + } + + // make a pose thingy + pose = FBX::Node("PoseNode"); + pose.AddChild("Node", node_uid); + aiMatrix4x4 node_xform = get_world_transform(bonenode, mScene); + pose.AddChild("Matrix", node_xform); + bpnode.AddChild(pose); + } + + // now write it + bpnode.Dump(outstream); + }*/ + + // TODO: cameras, lights + + // write nodes (i.e. model heirarchy) + // start at root node + WriteModelNodes( + outstream, mScene->mRootNode, 0, bone_uids + ); + + object_node.End(outstream, true); +} + +// convenience map of magic node name strings to FBX properties, +// including the expected type of transform. +const std::map> transform_types = { + {"Translation", {"Lcl Translation", 't'}}, + {"RotationOffset", {"RotationOffset", 't'}}, + {"RotationPivot", {"RotationPivot", 't'}}, + {"PreRotation", {"PreRotation", 'r'}}, + {"Rotation", {"Lcl Rotation", 'r'}}, + {"PostRotation", {"PostRotation", 'r'}}, + {"RotationPivotInverse", {"RotationPivotInverse", 'i'}}, + {"ScalingOffset", {"ScalingOffset", 't'}}, + {"ScalingPivot", {"ScalingPivot", 't'}}, + {"Scaling", {"Lcl Scaling", 's'}}, + {"ScalingPivotInverse", {"ScalingPivotInverse", 'i'}}, + {"GeometricScaling", {"GeometricScaling", 's'}}, + {"GeometricRotation", {"GeometricRotation", 'r'}}, + {"GeometricTranslation", {"GeometricTranslation", 't'}} +}; + +// write a single model node to the stream +void WriteModelNode( + StreamWriterLE& outstream, + const aiNode* node, + int64_t node_uid, + const std::string& type, + const std::vector>& transform_chain, + TransformInheritance inherit_type=TransformInheritance_RSrs +){ + const aiVector3D zero = {0, 0, 0}; + const aiVector3D one = {1, 1, 1}; + FBX::Node m("Model"); + std::string name = node->mName.C_Str() + FBX::SEPARATOR + "Model"; + m.AddProperties(node_uid, name, type); + m.AddChild("Version", int32_t(232)); + FBX::Node p("Properties70"); + p.AddP70bool("RotationActive", 1); + p.AddP70int("DefaultAttributeIndex", 0); + p.AddP70enum("InheritType", inherit_type); + if (transform_chain.empty()) { + // decompose 4x4 transform matrix into TRS + aiVector3D t, r, s; + node->mTransformation.Decompose(s, r, t); + if (t != zero) { + p.AddP70( + "Lcl Translation", "Lcl Translation", "", "A", + double(t.x), double(t.y), double(t.z) + ); + } + if (r != zero) { + p.AddP70( + "Lcl Rotation", "Lcl Rotation", "", "A", + double(DEG*r.x), double(DEG*r.y), double(DEG*r.z) + ); + } + if (s != one) { + p.AddP70( + "Lcl Scaling", "Lcl Scaling", "", "A", + double(s.x), double(s.y), double(s.z) + ); + } + } else { + // apply the transformation chain. + // these transformation elements are created when importing FBX, + // which has a complex transformation heirarchy for each node. + // as such we can bake the heirarchy back into the node on export. + for (auto &item : transform_chain) { + auto elem = transform_types.find(item.first); + if (elem == transform_types.end()) { + // then this is a bug + std::stringstream err; + err << "unrecognized FBX transformation type: "; + err << item.first; + throw DeadlyExportError(err.str()); + } + const std::string &name = elem->second.first; + const aiVector3D &v = item.second; + if (name.compare(0, 4, "Lcl ") == 0) { + // special handling for animatable properties + p.AddP70( + name, name, "", "A", + double(v.x), double(v.y), double(v.z) + ); + } else { + p.AddP70vector(name, v.x, v.y, v.z); + } + } + } + m.AddChild(p); + + // not sure what these are for, + // but they seem to be omnipresent + m.AddChild("Shading", Property(true)); + m.AddChild("Culling", Property("CullingOff")); + + m.Dump(outstream); +} + +// wrapper for WriteModelNodes to create and pass a blank transform chain +void FBXExporter::WriteModelNodes( + StreamWriterLE& s, + const aiNode* node, + int64_t parent_uid, + const std::map& bone_uids +) { + std::vector> chain; + WriteModelNodes(s, node, parent_uid, bone_uids, chain); +} + +void FBXExporter::WriteModelNodes( + StreamWriterLE& outstream, + const aiNode* node, + int64_t parent_uid, + const std::map& bone_uids, + std::vector>& transform_chain +) { + // first collapse any expanded transformation chains created by FBX import. + std::string node_name(node->mName.C_Str()); + if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) { + if (node->mNumChildren != 1) { + // this should never happen + std::stringstream err; + err << "FBX transformation node should have exactly 1 child,"; + err << " but " << node->mNumChildren << " found"; + err << " on node \"" << node_name << "\"!"; + throw DeadlyExportError(err.str()); + } + aiNode* next_node = node->mChildren[0]; + auto pos = node_name.find(MAGIC_NODE_TAG) + MAGIC_NODE_TAG.size() + 1; + std::string type_name = node_name.substr(pos); + auto elem = transform_types.find(type_name); + if (elem == transform_types.end()) { + // then this is a bug and should be fixed + std::stringstream err; + err << "unrecognized FBX transformation node"; + err << " of type " << type_name << " in node " << node_name; + throw DeadlyExportError(err.str()); + } + aiVector3D t, r, s; + node->mTransformation.Decompose(s, r, t); + switch (elem->second.second) { + case 'i': // inverse + // we don't need to worry about the inverse matrices + break; + case 't': // translation + transform_chain.emplace_back(elem->first, t); + break; + case 'r': // rotation + r *= DEG; + transform_chain.emplace_back(elem->first, r); + break; + case 's': // scale + transform_chain.emplace_back(elem->first, s); + break; + default: + // this should never happen + std::stringstream err; + err << "unrecognized FBX transformation type code: "; + err << elem->second.second; + throw DeadlyExportError(err.str()); + } + // now just continue to the next node + WriteModelNodes( + outstream, next_node, parent_uid, bone_uids, transform_chain + ); + return; + } + + int64_t node_uid = 0; + // generate uid and connect to parent, if not the root node, + if (node != mScene->mRootNode) { + auto elem = node_uids.find(node); + if (elem != node_uids.end()) { + node_uid = elem->second; + } else { + node_uid = generate_uid(); + node_uids[node] = node_uid; + } + FBX::Node c("C"); + c.AddProperties("OO", node_uid, parent_uid); + connections.push_back(c); + } + + // what type of node is this? + if (node == mScene->mRootNode) { + // handled later + } else if (node->mNumMeshes == 1) { + // connect to child mesh, which should have been written previously + FBX::Node c("C"); + c.AddProperties("OO", mesh_uids[node->mMeshes[0]], node_uid); + connections.push_back(c); + // also connect to the material for the child mesh + c = FBX::Node("C"); + c.AddProperties( + "OO", + material_uids[mScene->mMeshes[node->mMeshes[0]]->mMaterialIndex], + node_uid + ); + connections.push_back(c); + // write model node + WriteModelNode(outstream, node, node_uid, "Mesh", transform_chain); + } else if (bone_uids.count(node_name)) { + WriteModelNode(outstream, node, node_uid, "LimbNode", transform_chain); + // we also need to write a nodeattribute to mark it as a skeleton + int64_t node_attribute_uid = generate_uid(); + FBX::Node na("NodeAttribute"); + na.AddProperties( + node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode" + ); + na.AddChild("TypeFlags", Property("Skeleton")); + na.Dump(outstream); + // and connect them + FBX::Node c("C"); + c.AddProperties("OO", node_attribute_uid, node_uid); + connections.push_back(c); + } else { + // generate a null node so we can add children to it + WriteModelNode(outstream, node, node_uid, "Null", transform_chain); + } + + // if more than one child mesh, make nodes for each mesh + if (node->mNumMeshes > 1 || node == mScene->mRootNode) { + for (size_t i = 0; i < node->mNumMeshes; ++i) { + // make a new model node + int64_t new_node_uid = generate_uid(); + // connect to parent node + FBX::Node c("C"); + c.AddProperties("OO", new_node_uid, node_uid); + connections.push_back(c); + // connect to child mesh, which should have been written previously + c = FBX::Node("C"); + c.AddProperties("OO", mesh_uids[node->mMeshes[i]], new_node_uid); + connections.push_back(c); + // also connect to the material for the child mesh + c = FBX::Node("C"); + c.AddProperties( + "OO", + material_uids[ + mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex + ], + new_node_uid + ); + connections.push_back(c); + // write model node + FBX::Node m("Model"); + // take name from mesh name, if it exists + std::string name = mScene->mMeshes[node->mMeshes[i]]->mName.C_Str(); + name += FBX::SEPARATOR + "Model"; + m.AddProperties(new_node_uid, name, "Mesh"); + m.AddChild("Version", int32_t(232)); + FBX::Node p("Properties70"); + p.AddP70enum("InheritType", 1); + m.AddChild(p); + m.Dump(outstream); + } + } + + // now recurse into children + for (size_t i = 0; i < node->mNumChildren; ++i) { + WriteModelNodes( + outstream, node->mChildren[i], node_uid, bone_uids + ); + } +} + +void FBXExporter::WriteConnections () +{ + // we should have completed the connection graph already, + // so basically just dump it here + FBX::Node conn("Connections"); + StreamWriterLE outstream(outfile); + conn.Begin(outstream); + for (auto &n : connections) { + n.Dump(outstream); + } + conn.End(outstream, !connections.empty()); + connections.clear(); +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/code/FBXExporter.h b/code/FBXExporter.h new file mode 100644 index 000000000..39c04ffee --- /dev/null +++ b/code/FBXExporter.h @@ -0,0 +1,146 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXExporter.h +* Declares the exporter class to write a scene to an fbx file +*/ +#ifndef AI_FBXEXPORTER_H_INC +#define AI_FBXEXPORTER_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportNode.h" // FBX::Node + +#include +//#include +#include // StreamWriterLE +#include // DeadlyExportError + +#include +#include +#include // shared_ptr +#include // stringstream + +struct aiScene; +struct aiNode; +//struct aiMaterial; + +namespace Assimp +{ + class IOSystem; + class IOStream; + class ExportProperties; + + // --------------------------------------------------------------------- + /** Helper class to export a given scene to an FBX file. */ + // --------------------------------------------------------------------- + class FBXExporter + { + public: + /// Constructor for a specific scene to export + FBXExporter(const aiScene* pScene, const ExportProperties* pProperties); + + // call one of these methods to export + void ExportBinary(const char* pFile, IOSystem* pIOSystem); + void ExportAscii(const char* pFile, IOSystem* pIOSystem); + + private: + bool binary; // whether current export is in binary or ascii format + const aiScene* mScene; // the scene to export + const ExportProperties* mProperties; // currently unused + std::shared_ptr outfile; // file to write to + + std::vector connections; // conection storage + + std::vector mesh_uids; + std::vector material_uids; + std::map node_uids; + + // this crude unique-ID system is actually fine + int64_t last_uid = 999999; + int64_t generate_uid() { return ++last_uid; } + + // binary files have a specific header and footer, + // in addition to the actual data + void WriteBinaryHeader(); + void WriteBinaryFooter(); + + // WriteAllNodes does the actual export. + // It just calls all the Write
methods below in order. + void WriteAllNodes(); + + // Methods to write individual sections. + // The order here matches the order inside an FBX file. + // Each method corresponds to a top-level FBX section, + // except WriteHeader which also includes some binary-only sections + // and WriteFooter which is binary data only. + void WriteHeaderExtension(); + // WriteFileId(); // binary-only, included in WriteHeader + // WriteCreationTime(); // binary-only, included in WriteHeader + // WriteCreator(); // binary-only, included in WriteHeader + void WriteGlobalSettings(); + void WriteDocuments(); + void WriteReferences(); + void WriteDefinitions(); + void WriteObjects(); + void WriteConnections(); + // WriteTakes(); // deprecated since at least 2015 (fbx 7.4) + + // helpers + void WriteModelNodes( + Assimp::StreamWriterLE& s, + const aiNode* node, + int64_t parent_uid, + const std::map& bone_uids + ); + void WriteModelNodes( // usually don't call this directly + StreamWriterLE& s, + const aiNode* node, + int64_t parent_uid, + const std::map& bone_uids, + std::vector>& transform_chain + ); + }; +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXEXPORTER_H_INC From 95f66f84efb4abc52b110008154a8909853fbf34 Mon Sep 17 00:00:00 2001 From: Tommy Date: Mon, 19 Feb 2018 14:33:33 +0100 Subject: [PATCH 067/183] Remove constexpr for MSVC. --- code/FBXExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index d13c97f6b..9cdb34f40 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -70,7 +70,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // https://code.blender.org/2013/08/fbx-binary-file-format-specification/ // https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure -constexpr double DEG = 360.0 / 6.283185307179586476925286766559; +const double DEG = 57.29577951308232087679815481; // degrees per radian // some constants that we'll use for writing metadata namespace FBX { From 1de9b3232aa20bd532f284f8a9729a44fa31d147 Mon Sep 17 00:00:00 2001 From: Tommy Date: Mon, 19 Feb 2018 15:04:46 +0100 Subject: [PATCH 068/183] Silence a clang warning. --- code/FBXExporter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 9cdb34f40..86804bccc 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -147,6 +147,11 @@ void FBXExporter::ExportBinary ( // remember that we're exporting in binary mode binary = true; + // we're not currently using these preferences, + // but clang will cry about it if we never touch it. + // TODO: some of these might be relevant to export + (void)mProperties; + // open the indicated file for writing (in binary mode) outfile.reset(pIOSystem->Open(pFile,"wb")); if (!outfile) { From f0ccff70a4448f5888955eb5a6215f3cd2efe3ac Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Feb 2018 09:03:18 +0100 Subject: [PATCH 069/183] Update FileSystemFilter.h Fix review findings --- code/FileSystemFilter.h | 117 +++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 63 deletions(-) diff --git a/code/FileSystemFilter.h b/code/FileSystemFilter.h index 0fabb41dd..668d79625 100644 --- a/code/FileSystemFilter.h +++ b/code/FileSystemFilter.h @@ -42,13 +42,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * Implements a filter system to filter calls to Exists() and Open() * in order to improve the success rate of file opening ... */ +#pragma once #ifndef AI_FILESYSTEMFILTER_H_INC #define AI_FILESYSTEMFILTER_H_INC -#include "../include/assimp/IOSystem.hpp" -#include "../include/assimp/DefaultLogger.hpp" -#include "../include/assimp/fast_atof.h" -#include "../include/assimp/ParsingUtils.h" +#include +#include +#include +#include namespace Assimp { @@ -64,90 +65,84 @@ class FileSystemFilter : public IOSystem public: /** Constructor. */ FileSystemFilter(const std::string& file, IOSystem* old) - : wrapped (old) - , src_file (file) - , sep(wrapped->getOsSeparator()) - { - ai_assert(NULL != wrapped); + : mWrapped (old) + , mSrc_file(file) + , sep(wrapped->getOsSeparator()) { + ai_assert(nullptr != mWrapped); // Determine base directory - base = src_file; + mBase = mSrc_file; std::string::size_type ss2; - if (std::string::npos != (ss2 = base.find_last_of("\\/"))) { - base.erase(ss2,base.length()-ss2); - } - else { - base = ""; - // return; + if (std::string::npos != (ss2 = mBase.find_last_of("\\/"))) { + mBase.erase(ss2,mBase.length()-ss2); + } else { + mBase = ""; } // make sure the directory is terminated properly char s; - if (base.length() == 0) { - base = "."; - base += getOsSeparator(); - } - else if ((s = *(base.end()-1)) != '\\' && s != '/') { - base += getOsSeparator(); + if ( mBase.empty() ) { + mBase = "."; + mBase += getOsSeparator(); + } else if ((s = *(base.end()-1)) != '\\' && s != '/') { + mBase += getOsSeparator(); } - DefaultLogger::get()->info("Import root directory is \'" + base + "\'"); + DefaultLogger::get()->info("Import root directory is \'" + mBase + "\'"); } /** Destructor. */ - ~FileSystemFilter() - { - // haha + ~FileSystemFilter() { + // empty } // ------------------------------------------------------------------- /** Tests for the existence of a file at the given path. */ - bool Exists( const char* pFile) const - { + bool Exists( const char* pFile) const { + ai_assert( nullptr != mWrapped ); std::string tmp = pFile; // Currently this IOSystem is also used to open THE ONE FILE. - if (tmp != src_file) { + if (tmp != mSrc_file) { BuildPath(tmp); Cleanup(tmp); } - return wrapped->Exists(tmp); + return mWrapped->Exists(tmp); } // ------------------------------------------------------------------- /** Returns the directory separator. */ - char getOsSeparator() const - { + char getOsSeparator() const { return sep; } // ------------------------------------------------------------------- /** Open a new file with a given path. */ - IOStream* Open( const char* pFile, const char* pMode = "rb") - { - ai_assert(pFile); - ai_assert(pMode); + IOStream* Open( const char* pFile, const char* pMode = "rb") { + ai_assert( nullptr != mWrapped ); + ai_assert( nullptr != pFile ); + ai_assert( nullptr != pMode ); // First try the unchanged path - IOStream* s = wrapped->Open(pFile,pMode); + IOStream* s = mWrapped->Open(pFile,pMode); - if (!s) { + if (nullptr == s) { std::string tmp = pFile; // Try to convert between absolute and relative paths BuildPath(tmp); - s = wrapped->Open(tmp,pMode); + s = mWrapped->Open(tmp,pMode); - if (!s) { + if (nullptr == s) { // Finally, look for typical issues with paths // and try to correct them. This is our last // resort. tmp = pFile; Cleanup(tmp); BuildPath(tmp); - s = wrapped->Open(tmp,pMode); + s = mWrapped->Open(tmp,pMode); } } @@ -156,27 +151,26 @@ public: // ------------------------------------------------------------------- /** Closes the given file and releases all resources associated with it. */ - void Close( IOStream* pFile) - { - return wrapped->Close(pFile); + void Close( IOStream* pFile) { + ai_assert( nullptr != mWrapped ); + return mWrapped->Close(pFile); } // ------------------------------------------------------------------- /** Compare two paths */ - bool ComparePaths (const char* one, const char* second) const - { - return wrapped->ComparePaths (one,second); + bool ComparePaths (const char* one, const char* second) const { + ai_assert( nullptr != mWrapped ); + return mWrapped->ComparePaths (one,second); } private: - // ------------------------------------------------------------------- /** Build a valid path from a given relative or absolute path. */ - void BuildPath (std::string& in) const - { + void BuildPath (std::string& in) const { + ai_assert( nullptr != mWrapped ); // if we can already access the file, great. - if (in.length() < 3 || wrapped->Exists(in)) { + if (in.length() < 3 || mWrapped->Exists(in)) { return; } @@ -184,8 +178,8 @@ private: if (in[1] != ':') { // append base path and try - const std::string tmp = base + in; - if (wrapped->Exists(tmp)) { + const std::string tmp = mBase + in; + if (mWrapped->Exists(tmp)) { in = tmp; return; } @@ -207,7 +201,7 @@ private: std::string::size_type last_dirsep = std::string::npos; while(true) { - tmp = base; + tmp = mBase; tmp += sep; std::string::size_type dirsep = in.rfind('/', last_dirsep); @@ -223,7 +217,7 @@ private: last_dirsep = dirsep-1; tmp += in.substr(dirsep+1, in.length()-pos); - if (wrapped->Exists(tmp)) { + if (mWrapped->Exists(tmp)) { in = tmp; return; } @@ -236,15 +230,14 @@ private: // ------------------------------------------------------------------- /** Cleanup the given path */ - void Cleanup (std::string& in) const - { - char last = 0; + void Cleanup (std::string& in) const { if(in.empty()) { return; } // Remove a very common issue when we're parsing file names: spaces at the // beginning of the path. + char last = 0; std::string::iterator it = in.begin(); while (IsSpaceOrNewLine( *it ))++it; if (it != in.begin()) { @@ -274,9 +267,7 @@ private: it = in.erase(it); --it; } - } - else if (*it == '%' && in.end() - it > 2) { - + } else if (*it == '%' && in.end() - it > 2) { // Hex sequence in URIs if( IsHex((&*it)[0]) && IsHex((&*it)[1]) ) { *it = HexOctetToDecimal(&*it); @@ -290,8 +281,8 @@ private: } private: - IOSystem* wrapped; - std::string src_file, base; + IOSystem *mWrapped; + std::string mSrc_file, mBase; char sep; }; From a92dbabc2538747620316ec1548e2ed64eb58d0a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Feb 2018 16:36:55 +0100 Subject: [PATCH 070/183] Update FileSystemFilter.h Fix missing save. --- code/FileSystemFilter.h | 45 +++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/code/FileSystemFilter.h b/code/FileSystemFilter.h index 464bb4c91..23d555eb8 100644 --- a/code/FileSystemFilter.h +++ b/code/FileSystemFilter.h @@ -67,7 +67,7 @@ public: FileSystemFilter(const std::string& file, IOSystem* old) : mWrapped (old) , mSrc_file(file) - , sep(wrapped->getOsSeparator()) { + , sep(mWrapped->getOsSeparator()) { ai_assert(nullptr != mWrapped); // Determine base directory @@ -101,6 +101,7 @@ public: /** Tests for the existence of a file at the given path. */ bool Exists( const char* pFile) const { ai_assert( nullptr != mWrapped ); + std::string tmp = pFile; // Currently this IOSystem is also used to open THE ONE FILE. @@ -165,51 +166,51 @@ public: // ------------------------------------------------------------------- /** Pushes a new directory onto the directory stack. */ - bool PushDirectory(const std::string &path) - { - return wrapped->PushDirectory(path); + bool PushDirectory(const std::string &path ) { + ai_assert( nullptr != mWrapped ); + return mWrapped->PushDirectory(path); } // ------------------------------------------------------------------- /** Returns the top directory from the stack. */ - const std::string &CurrentDirectory() const - { - return wrapped->CurrentDirectory(); + const std::string &CurrentDirectory() const { + ai_assert( nullptr != mWrapped ); + return mWrapped->CurrentDirectory(); } // ------------------------------------------------------------------- /** Returns the number of directories stored on the stack. */ - size_t StackSize() const - { - return wrapped->StackSize(); + size_t StackSize() const { + ai_assert( nullptr != mWrapped ); + return mWrapped->StackSize(); } // ------------------------------------------------------------------- /** Pops the top directory from the stack. */ - bool PopDirectory() - { - return wrapped->PopDirectory(); + bool PopDirectory() { + ai_assert( nullptr != mWrapped ); + return mWrapped->PopDirectory(); } // ------------------------------------------------------------------- /** Creates an new directory at the given path. */ - bool CreateDirectory(const std::string &path) - { - return wrapped->CreateDirectory(path); + bool CreateDirectory(const std::string &path) { + ai_assert( nullptr != mWrapped ); + return mWrapped->CreateDirectory(path); } // ------------------------------------------------------------------- /** Will change the current directory to the given path. */ - bool ChangeDirectory(const std::string &path) - { - return wrapped->ChangeDirectory(path); + bool ChangeDirectory(const std::string &path) { + ai_assert( nullptr != mWrapped ); + return mWrapped->ChangeDirectory(path); } // ------------------------------------------------------------------- /** Delete file. */ - bool DeleteFile(const std::string &file) - { - return wrapped->DeleteFile(file); + bool DeleteFile(const std::string &file) { + ai_assert( nullptr != mWrapped ); + return mWrapped->DeleteFile(file); } private: From 7cd1a66c8bc22a2c94460900f47c48d46be14bd1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Feb 2018 22:08:40 +0100 Subject: [PATCH 071/183] Use correct lookup. --- code/D3MFImporter.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 29e27371e..5c036bf76 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -205,10 +205,11 @@ private: const std::string nodeName( xmlReader->getNodeName() ); if(xmlReader->getNodeName() == D3MF::XmlTag::triangle) { faces.push_back(ReadTriangle()); - } else if ( nodeName == D3MF::XmlTag::pid ) { - const std::string matId( xmlReader->getAttributeValue( nodeName.c_str() ) ); - int matIdx( std::atoi( matId.c_str() ) ); - mesh->mMaterialIndex = matIdx; + const char *pidToken( xmlReader->getAttributeValue( D3MF::XmlTag::pid.c_str() ) ); + if ( nullptr != pidToken ) { + int matIdx( std::atoi( pidToken ) ); + mesh->mMaterialIndex = matIdx; + } } } @@ -234,6 +235,7 @@ private: void ReadBaseMaterials() { while ( ReadToEndElement( D3MF::XmlTag::basematerials ) ) { mMaterials.push_back( readMaterialDef() ); + xmlReader->read(); } } @@ -324,8 +326,7 @@ private: return false; } - bool ReadToEndElement(const std::string& closeTag) - { + bool ReadToEndElement(const std::string& closeTag) { while(xmlReader->read()) { if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT) { From fb213e4bac445484d80bbde8888520ba83cfcf1f Mon Sep 17 00:00:00 2001 From: Tommy Date: Wed, 21 Feb 2018 12:23:28 +0100 Subject: [PATCH 072/183] FBX Importer: apply inverse of geometric transform to child nodes. --- code/FBXConverter.cpp | 66 +++++++++++++++++++++++++++++++++++++++---- code/FBXConverter.h | 7 +++-- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 6929e5c33..7134bc2e0 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -142,6 +142,7 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa nodes.reserve( conns.size() ); std::vector nodes_chain; + std::vector post_nodes_chain; try { for( const Connection* con : conns ) { @@ -161,6 +162,7 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa if ( model ) { nodes_chain.clear(); + post_nodes_chain.clear(); aiMatrix4x4 new_abs_transform = parent_transform; @@ -168,7 +170,7 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa // 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 ); + GenerateTransformationNodeChain( *model, nodes_chain, post_nodes_chain ); ai_assert( nodes_chain.size() ); @@ -213,8 +215,25 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa // attach geometry ConvertModel( *model, *nodes_chain.back(), new_abs_transform ); + // now link the geometric transform inverse nodes, + // before we attach any child nodes + for( aiNode* postnode : post_nodes_chain ) { + ai_assert( postnode ); + + if ( last_parent != &parent ) { + last_parent->mNumChildren = 1; + last_parent->mChildren = new aiNode*[ 1 ]; + last_parent->mChildren[ 0 ] = postnode; + } + + postnode->mParent = last_parent; + last_parent = postnode; + + new_abs_transform *= postnode->mTransformation; + } + // attach sub-nodes - ConvertNodes( model->ID(), *nodes_chain.back(), new_abs_transform ); + ConvertNodes( model->ID(), *last_parent, new_abs_transform ); if ( doc.Settings().readLights ) { ConvertLights( *model ); @@ -396,6 +415,12 @@ const char* Converter::NameTransformationComp( TransformationComp comp ) return "GeometricRotation"; case TransformationComp_GeometricTranslation: return "GeometricTranslation"; + case TransformationComp_GeometricScalingInverse: + return "GeometricScalingInverse"; + case TransformationComp_GeometricRotationInverse: + return "GeometricRotationInverse"; + case TransformationComp_GeometricTranslationInverse: + return "GeometricTranslationInverse"; case TransformationComp_MAXIMUM: // this is to silence compiler warnings default: break; @@ -437,6 +462,12 @@ const char* Converter::NameTransformationCompProperty( TransformationComp comp ) return "GeometricRotation"; case TransformationComp_GeometricTranslation: return "GeometricTranslation"; + case TransformationComp_GeometricScalingInverse: + return "GeometricScalingInverse"; + case TransformationComp_GeometricRotationInverse: + return "GeometricRotationInverse"; + case TransformationComp_GeometricTranslationInverse: + return "GeometricTranslationInverse"; case TransformationComp_MAXIMUM: // this is to silence compiler warnings break; } @@ -570,7 +601,7 @@ std::string Converter::NameTransformationChainNode( const std::string& name, Tra return name + std::string( MAGIC_NODE_TAG ) + "_" + NameTransformationComp( comp ); } -void Converter::GenerateTransformationNodeChain( const Model& model, std::vector& output_nodes ) +void Converter::GenerateTransformationNodeChain( const Model& model, std::vector& output_nodes, std::vector& post_output_nodes ) { const PropertyTable& props = model.Props(); const Model::RotOrder rot = model.RotationOrder(); @@ -646,16 +677,33 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector const aiVector3D& GeometricScaling = PropertyGet( props, "GeometricScaling", ok ); if ( ok && std::fabs( GeometricScaling.SquareLength() - 1.0f ) > zero_epsilon ) { aiMatrix4x4::Scaling( GeometricScaling, chain[ TransformationComp_GeometricScaling ] ); + aiVector3D GeometricScalingInverse = GeometricScaling; + bool canscale = true; + for (size_t i = 0; i < 3; ++i) { + if ( std::fabs( GeometricScalingInverse[i] ) > zero_epsilon ) { + GeometricScalingInverse[i] = 1.0f / GeometricScaling[i]; + } else { + FBXImporter::LogError( "cannot invert geometric scaling matrix with a 0.0 scale component" ); + canscale = false; + break; + } + } + if (canscale) { + aiMatrix4x4::Scaling( GeometricScalingInverse, chain[ TransformationComp_GeometricScalingInverse ] ); + } } const aiVector3D& GeometricRotation = PropertyGet( props, "GeometricRotation", ok ); if ( ok && GeometricRotation.SquareLength() > zero_epsilon ) { GetRotationMatrix( rot, GeometricRotation, chain[ TransformationComp_GeometricRotation ] ); + GetRotationMatrix( rot, GeometricRotation, chain[ TransformationComp_GeometricRotationInverse ] ); + chain[ TransformationComp_GeometricRotationInverse ].Inverse(); } const aiVector3D& GeometricTranslation = PropertyGet( props, "GeometricTranslation", ok ); if ( ok && GeometricTranslation.SquareLength() > zero_epsilon ) { aiMatrix4x4::Translation( GeometricTranslation, chain[ TransformationComp_GeometricTranslation ] ); + aiMatrix4x4::Translation( -GeometricTranslation, chain[ TransformationComp_GeometricTranslationInverse ] ); } // is_complex needs to be consistent with NeedsComplexTransformationChain() @@ -690,10 +738,18 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector } aiNode* nd = new aiNode(); - output_nodes.push_back( nd ); - nd->mName.Set( NameTransformationChainNode( name, comp ) ); nd->mTransformation = chain[ i ]; + + // geometric inverses go in a post-node chain + if ( comp == TransformationComp_GeometricScalingInverse || + comp == TransformationComp_GeometricRotationInverse || + comp == TransformationComp_GeometricTranslationInverse + ) { + post_output_nodes.push_back( nd ); + } else { + output_nodes.push_back( nd ); + } } ai_assert( output_nodes.size() ); diff --git a/code/FBXConverter.h b/code/FBXConverter.h index c882e9326..06a6f07a7 100644 --- a/code/FBXConverter.h +++ b/code/FBXConverter.h @@ -82,7 +82,10 @@ public: * The different parts that make up the final local transformation of a fbx-node */ enum TransformationComp { - TransformationComp_Translation = 0, + TransformationComp_GeometricScalingInverse = 0, + TransformationComp_GeometricRotationInverse, + TransformationComp_GeometricTranslationInverse, + TransformationComp_Translation, TransformationComp_RotationOffset, TransformationComp_RotationPivot, TransformationComp_PreRotation, @@ -153,7 +156,7 @@ private: /** * note: memory for output_nodes will be managed by the caller */ - void GenerateTransformationNodeChain(const Model& model, std::vector& output_nodes); + void GenerateTransformationNodeChain(const Model& model, std::vector& output_nodes, std::vector& post_output_nodes); // ------------------------------------------------------------------------------------------------ void SetupNodeMetadata(const Model& model, aiNode& nd); From a8fc22fd3f0dabc79820d074dd81f1c67d983dee Mon Sep 17 00:00:00 2001 From: Tommy Date: Wed, 21 Feb 2018 12:57:45 +0100 Subject: [PATCH 073/183] assimp_cmd: Add --verbose flag to 'info' command, to print node transforms. --- tools/assimp_cmd/Info.cpp | 49 +++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/tools/assimp_cmd/Info.cpp b/tools/assimp_cmd/Info.cpp index e19746db6..a9f66eb1f 100644 --- a/tools/assimp_cmd/Info.cpp +++ b/tools/assimp_cmd/Info.cpp @@ -47,9 +47,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Main.h" const char* AICMD_MSG_INFO_HELP_E = -"assimp info [-r]\n" +"assimp info [-r] [-v]\n" "\tPrint basic structure of a 3D model\n" -"\t-r,--raw: No postprocessing, do a raw import\n"; +"\t-r,--raw: No postprocessing, do a raw import\n" +"\t-v,--verbose: Print verbose info such as node transform data\n"; // ----------------------------------------------------------------------------------- @@ -184,7 +185,7 @@ std::string FindPTypes(const aiScene* scene) // ----------------------------------------------------------------------------------- void PrintHierarchy(const aiNode* root, unsigned int maxnest, unsigned int maxline, - unsigned int cline, unsigned int cnest=0) + unsigned int cline, bool verbose, unsigned int cnest=0) { if (cline++ >= maxline || cnest >= maxnest) { return; @@ -194,8 +195,29 @@ void PrintHierarchy(const aiNode* root, unsigned int maxnest, unsigned int maxli printf("-- "); } printf("\'%s\', meshes: %u\n",root->mName.data,root->mNumMeshes); + + if (verbose) { + // print the actual transform + //printf(","); + aiVector3D s, r, t; + root->mTransformation.Decompose(s, r, t); + if (s.x != 1.0 || s.y != 1.0 || s.z != 1.0) { + for(unsigned int i = 0; i < cnest; ++i) { printf(" "); } + printf(" S:[%f %f %f]\n", s.x, s.y, s.z); + } + if (r.x || r.y || r.z) { + for(unsigned int i = 0; i < cnest; ++i) { printf(" "); } + printf(" R:[%f %f %f]\n", r.x, r.y, r.z); + } + if (t.x || t.y || t.z) { + for(unsigned int i = 0; i < cnest; ++i) { printf(" "); } + printf(" T:[%f %f %f]\n", t.x, t.y, t.z); + } + } + //printf("\n"); + for (unsigned int i = 0; i < root->mNumChildren; ++i ) { - PrintHierarchy(root->mChildren[i],maxnest,maxline,cline,cnest+1); + PrintHierarchy(root->mChildren[i],maxnest,maxline,cline,verbose,cnest+1); if(i == root->mNumChildren-1) { for(unsigned int i = 0; i < cnest; ++i) { printf(" "); @@ -230,10 +252,23 @@ int Assimp_Info (const char* const* params, unsigned int num) const std::string in = std::string(params[0]); + // get -r and -v arguments + bool raw = false; + bool verbose = false; + for(unsigned int i = 1; i < num; ++i) { + if (!strcmp(params[i],"--raw")||!strcmp(params[i],"-r")) { + raw = true; + } + if (!strcmp(params[i],"--verbose")||!strcmp(params[i],"-v")) { + verbose = true; + } + } + // do maximum post-processing unless -r was specified ImportData import; - import.ppFlags = num>1&&(!strcmp(params[1],"--raw")||!strcmp(params[1],"-r")) ? 0 - : aiProcessPreset_TargetRealtime_MaxQuality; + if (!raw) { + import.ppFlags = aiProcessPreset_TargetRealtime_MaxQuality; + } // import the main model const aiScene* scene = ImportModel(import,in); @@ -346,7 +381,7 @@ int Assimp_Info (const char* const* params, unsigned int num) printf("\nNode hierarchy:\n"); unsigned int cline=0; - PrintHierarchy(scene->mRootNode,20,1000,cline); + PrintHierarchy(scene->mRootNode,20,1000,cline,verbose); printf("\n"); return 0; From 22dbb6c2d2bb8cf0a9e6495073a46434ba28dd34 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 21 Feb 2018 13:10:51 +0100 Subject: [PATCH 074/183] Update FileSystemFilter.h - Fix typo. -test in public metod against invalid parameters --- code/FileSystemFilter.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/FileSystemFilter.h b/code/FileSystemFilter.h index 23d555eb8..8d43c1c27 100644 --- a/code/FileSystemFilter.h +++ b/code/FileSystemFilter.h @@ -85,7 +85,7 @@ public: if ( mBase.empty() ) { mBase = "."; mBase += getOsSeparator(); - } else if ((s = *(base.end()-1)) != '\\' && s != '/') { + } else if ((s = *(mBase.end()-1)) != '\\' && s != '/') { mBase += getOsSeparator(); } @@ -123,6 +123,10 @@ public: /** Open a new file with a given path. */ IOStream* Open( const char* pFile, const char* pMode = "rb") { ai_assert( nullptr != mWrapped ); + if ( nullptr == pFile || nullptr == pMode ) { + return nullptr; + } + ai_assert( nullptr != pFile ); ai_assert( nullptr != pMode ); From c1f353f6c7673d5056d4ea07399e1de607b3838d Mon Sep 17 00:00:00 2001 From: Tommy Date: Wed, 21 Feb 2018 14:15:03 +0100 Subject: [PATCH 075/183] FBX Export: Geometric transformations always create transformation chain. In combination with fb213e4b, this fixes #1112. --- code/FBXConverter.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 7134bc2e0..893f9f405 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -582,8 +582,7 @@ bool Converter::NeedsComplexTransformationChain( const Model& model ) for ( size_t i = 0; i < TransformationComp_MAXIMUM; ++i ) { const TransformationComp comp = static_cast< TransformationComp >( i ); - if ( comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation || - comp == TransformationComp_GeometricScaling || comp == TransformationComp_GeometricRotation || comp == TransformationComp_GeometricTranslation ) { + if ( comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation ) { continue; } @@ -676,6 +675,7 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector const aiVector3D& GeometricScaling = PropertyGet( props, "GeometricScaling", ok ); if ( ok && std::fabs( GeometricScaling.SquareLength() - 1.0f ) > zero_epsilon ) { + is_complex = true; aiMatrix4x4::Scaling( GeometricScaling, chain[ TransformationComp_GeometricScaling ] ); aiVector3D GeometricScalingInverse = GeometricScaling; bool canscale = true; @@ -695,6 +695,7 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector const aiVector3D& GeometricRotation = PropertyGet( props, "GeometricRotation", ok ); if ( ok && GeometricRotation.SquareLength() > zero_epsilon ) { + is_complex = true; GetRotationMatrix( rot, GeometricRotation, chain[ TransformationComp_GeometricRotation ] ); GetRotationMatrix( rot, GeometricRotation, chain[ TransformationComp_GeometricRotationInverse ] ); chain[ TransformationComp_GeometricRotationInverse ].Inverse(); @@ -702,6 +703,7 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector const aiVector3D& GeometricTranslation = PropertyGet( props, "GeometricTranslation", ok ); if ( ok && GeometricTranslation.SquareLength() > zero_epsilon ) { + is_complex = true; aiMatrix4x4::Translation( GeometricTranslation, chain[ TransformationComp_GeometricTranslation ] ); aiMatrix4x4::Translation( -GeometricTranslation, chain[ TransformationComp_GeometricTranslationInverse ] ); } @@ -2265,8 +2267,7 @@ void Converter::GenerateNodeAnimations( std::vector& node_anims, has_any = true; - if ( comp != TransformationComp_Rotation && comp != TransformationComp_Scaling && comp != TransformationComp_Translation && - comp != TransformationComp_GeometricScaling && comp != TransformationComp_GeometricRotation && comp != TransformationComp_GeometricTranslation ) + if ( comp != TransformationComp_Rotation && comp != TransformationComp_Scaling && comp != TransformationComp_Translation ) { has_complex = true; } From f847d4817d3dc2542c33dfb07ad0402d9985e3d5 Mon Sep 17 00:00:00 2001 From: Tommy Date: Wed, 21 Feb 2018 15:29:57 +0100 Subject: [PATCH 076/183] FBX Export: fix logic for determining if scale transformation is identity. Previously it was comparing scale.SquareLength() to 1.0. --- code/FBXConverter.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 893f9f405..746777d65 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -579,6 +579,7 @@ bool Converter::NeedsComplexTransformationChain( const Model& model ) bool ok; const float zero_epsilon = 1e-6f; + const aiVector3D all_ones(1.0f, 1.0f, 1.0f); for ( size_t i = 0; i < TransformationComp_MAXIMUM; ++i ) { const TransformationComp comp = static_cast< TransformationComp >( i ); @@ -586,9 +587,17 @@ bool Converter::NeedsComplexTransformationChain( const Model& model ) continue; } + bool scale_compare = ( comp == TransformationComp_GeometricScaling || comp == TransformationComp_Scaling ); + const aiVector3D& v = PropertyGet( props, NameTransformationCompProperty( comp ), ok ); - if ( ok && v.SquareLength() > zero_epsilon ) { - return true; + if ( ok && scale_compare ) { + if ( (v - all_ones).SquareLength() > zero_epsilon ) { + return true; + } + } else if ( ok ) { + if ( v.SquareLength() > zero_epsilon ) { + return true; + } } } @@ -612,6 +621,7 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector // generate transformation matrices for all the different transformation components const float zero_epsilon = 1e-6f; + const aiVector3D all_ones(1.0f, 1.0f, 1.0f); bool is_complex = false; const aiVector3D& PreRotation = PropertyGet( props, "PreRotation", ok ); @@ -664,7 +674,7 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector } const aiVector3D& Scaling = PropertyGet( props, "Lcl Scaling", ok ); - if ( ok && std::fabs( Scaling.SquareLength() - 1.0f ) > zero_epsilon ) { + if ( ok && (Scaling - all_ones).SquareLength() > zero_epsilon ) { aiMatrix4x4::Scaling( Scaling, chain[ TransformationComp_Scaling ] ); } @@ -674,7 +684,7 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector } const aiVector3D& GeometricScaling = PropertyGet( props, "GeometricScaling", ok ); - if ( ok && std::fabs( GeometricScaling.SquareLength() - 1.0f ) > zero_epsilon ) { + if ( ok && (GeometricScaling - all_ones).SquareLength() > zero_epsilon ) { is_complex = true; aiMatrix4x4::Scaling( GeometricScaling, chain[ TransformationComp_GeometricScaling ] ); aiVector3D GeometricScalingInverse = GeometricScaling; From 1d901f075c8420a18ac291ab4b721d051b250911 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 23 Feb 2018 17:49:29 +0100 Subject: [PATCH 077/183] 3mf-importer: fix parsing of base-material color. --- code/3MFXmlTags.h | 3 ++ code/D3MFImporter.cpp | 88 ++++++++++++++++------------------- include/assimp/ParsingUtils.h | 27 +++++------ include/assimp/fast_atof.h | 4 +- 4 files changed, 60 insertions(+), 62 deletions(-) diff --git a/code/3MFXmlTags.h b/code/3MFXmlTags.h index 00938a45e..c4da2970d 100644 --- a/code/3MFXmlTags.h +++ b/code/3MFXmlTags.h @@ -45,6 +45,7 @@ namespace Assimp { namespace D3MF { namespace XmlTag { + // Model-data specific tags static const std::string model = "model"; static const std::string model_unit = "unit"; static const std::string metadata = "metadata"; @@ -63,6 +64,7 @@ namespace XmlTag { static const std::string v3 = "v3"; static const std::string id = "id"; static const std::string pid = "pid"; + static const std::string p1 = "p1"; static const std::string name = "name"; static const std::string type = "type"; static const std::string build = "build"; @@ -76,6 +78,7 @@ namespace XmlTag { static const std::string basematerials_name = "name"; static const std::string basematerials_displaycolor = "displaycolor"; + // Meta info tags static const std::string CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; static const std::string ROOT_RELATIONSHIPS_ARCHIVE = "_rels/.rels"; static const std::string SCHEMA_CONTENTTYPES = "http://schemas.openxmlformats.org/package/2006/content-types"; diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 5c036bf76..489926204 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -61,6 +61,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include "3MFXmlTags.h" +#include + +#include namespace Assimp { namespace D3MF { @@ -175,10 +178,8 @@ private: void ImportVertices(aiMesh* mesh) { std::vector vertices; - while(ReadToEndElement(D3MF::XmlTag::vertices)) - { - if(xmlReader->getNodeName() == D3MF::XmlTag::vertex) - { + while(ReadToEndElement(D3MF::XmlTag::vertices)) { + if(xmlReader->getNodeName() == D3MF::XmlTag::vertex) { vertices.push_back(ReadVertex()); } } @@ -205,7 +206,7 @@ private: const std::string nodeName( xmlReader->getNodeName() ); if(xmlReader->getNodeName() == D3MF::XmlTag::triangle) { faces.push_back(ReadTriangle()); - const char *pidToken( xmlReader->getAttributeValue( D3MF::XmlTag::pid.c_str() ) ); + const char *pidToken( xmlReader->getAttributeValue( D3MF::XmlTag::p1.c_str() ) ); if ( nullptr != pidToken ) { int matIdx( std::atoi( pidToken ) ); mesh->mMaterialIndex = matIdx; @@ -253,92 +254,85 @@ private: if ( '#' != *buf ) { return false; } - - char comp[ 2 ] = { 0,0 }; - comp[ 0 ] = *buf; ++buf; - comp[ 1 ] = *buf; - ++buf; - diffuse.r = static_cast( std::atoi( comp ) ); + char comp[ 3 ] = { 0,0,'\0' }; comp[ 0 ] = *buf; ++buf; comp[ 1 ] = *buf; ++buf; - diffuse.g = static_cast( std::atoi( comp ) ); + diffuse.r = static_cast( strtol( comp, NULL, 16 ) ); + comp[ 0 ] = *buf; ++buf; comp[ 1 ] = *buf; ++buf; - diffuse.b = static_cast( std::atoi( comp ) ); + diffuse.g = static_cast< ai_real >( strtol( comp, NULL, 16 ) ); comp[ 0 ] = *buf; ++buf; comp[ 1 ] = *buf; ++buf; - diffuse.a = static_cast( std::atoi( comp ) ); + diffuse.b = static_cast< ai_real >( strtol( comp, NULL, 16 ) ); + + comp[ 0 ] = *buf; + ++buf; + comp[ 1 ] = *buf; + ++buf; + diffuse.a = static_cast< ai_real >( strtol( comp, NULL, 16 ) ); return true; } aiMaterial *readMaterialDef() { aiMaterial *mat( nullptr ); - //while ( ReadToEndElement( D3MF::XmlTag::basematerials_base ) ) { - const char *name( nullptr ); - const char *color( nullptr ); - const std::string nodeName( xmlReader->getNodeName() ); - if ( nodeName == D3MF::XmlTag::basematerials_base ) { - name = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_name.c_str() ); + const char *name( nullptr ); + const char *color( nullptr ); + const std::string nodeName( xmlReader->getNodeName() ); + if ( nodeName == D3MF::XmlTag::basematerials_base ) { + name = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_name.c_str() ); - aiString matName; - matName.Set( name ); - mat = new aiMaterial; - mat->AddProperty( &matName, AI_MATKEY_NAME ); + aiString matName; + matName.Set( name ); + mat = new aiMaterial; + mat->AddProperty( &matName, AI_MATKEY_NAME ); - color = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_displaycolor.c_str() ); - aiColor4D diffuse; - if ( parseColor( color, diffuse ) ) { - mat->AddProperty( &diffuse, 1, AI_MATKEY_COLOR_DIFFUSE ); - } + color = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_displaycolor.c_str() ); + aiColor4D diffuse; + if ( parseColor( color, diffuse ) ) { + mat->AddProperty( &diffuse, 1, AI_MATKEY_COLOR_DIFFUSE ); } - //} + } return mat; } private: - bool ReadToStartElement(const std::string& startTag) - { - while(xmlReader->read()) - { - if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT && xmlReader->getNodeName() == startTag) - { + bool ReadToStartElement(const std::string& startTag) { + while(xmlReader->read()) { + const std::string &nodeName( xmlReader->getNodeName() ); + if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT && nodeName == startTag) { return true; - } - else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && - xmlReader->getNodeName() == startTag) - { + } else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == startTag) { return false; } } - //DefaultLogger::get()->error("unexpected EOF, expected closing <" + closeTag + "> tag"); + return false; } bool ReadToEndElement(const std::string& closeTag) { - while(xmlReader->read()) - { + while(xmlReader->read()) { + const std::string &nodeName( xmlReader->getNodeName() ); if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT) { return true; - } - else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END - && xmlReader->getNodeName() == closeTag) - { + } else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == closeTag) { return false; } } DefaultLogger::get()->error("unexpected EOF, expected closing <" + closeTag + "> tag"); + return false; } diff --git a/include/assimp/ParsingUtils.h b/include/assimp/ParsingUtils.h index 555b2a309..40e183a5c 100644 --- a/include/assimp/ParsingUtils.h +++ b/include/assimp/ParsingUtils.h @@ -66,49 +66,50 @@ static const unsigned int BufferSize = 4096; // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE char_t ToLower( char_t in) -{ +AI_FORCE_INLINE +char_t ToLower( char_t in ) { return (in >= (char_t)'A' && in <= (char_t)'Z') ? (char_t)(in+0x20) : in; } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE char_t ToUpper( char_t in) { +AI_FORCE_INLINE +char_t ToUpper( char_t in) { return (in >= (char_t)'a' && in <= (char_t)'z') ? (char_t)(in-0x20) : in; } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool IsUpper( char_t in) -{ +AI_FORCE_INLINE +bool IsUpper( char_t in) { return (in >= (char_t)'A' && in <= (char_t)'Z'); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool IsLower( char_t in) -{ +AI_FORCE_INLINE +bool IsLower( char_t in) { return (in >= (char_t)'a' && in <= (char_t)'z'); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool IsSpace( char_t in) -{ +AI_FORCE_INLINE +bool IsSpace( char_t in) { return (in == (char_t)' ' || in == (char_t)'\t'); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool IsLineEnd( char_t in) -{ +AI_FORCE_INLINE +bool IsLineEnd( char_t in) { return (in==(char_t)'\r'||in==(char_t)'\n'||in==(char_t)'\0'||in==(char_t)'\f'); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool IsSpaceOrNewLine( char_t in) -{ +AI_FORCE_INLINE +bool IsSpaceOrNewLine( char_t in) { return IsSpace(in) || IsLineEnd(in); } diff --git a/include/assimp/fast_atof.h b/include/assimp/fast_atof.h index 058a7ff87..fa70abfb2 100644 --- a/include/assimp/fast_atof.h +++ b/include/assimp/fast_atof.h @@ -129,8 +129,8 @@ inline unsigned int strtoul16( const char* in, const char** out=0) // Convert just one hex digit // Return value is UINT_MAX if the input character is not a hex digit. // ------------------------------------------------------------------------------------ -inline unsigned int HexDigitToDecimal(char in) -{ +inline +unsigned int HexDigitToDecimal(char in) { unsigned int out = UINT_MAX; if (in >= '0' && in <= '9') out = in - '0'; From 9c663e9630c5c38e8b8fc64b433b7d93b2594bf5 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 23 Feb 2018 18:14:46 +0100 Subject: [PATCH 078/183] fast_a_to_f: add some brackets. --- include/assimp/fast_atof.h | 92 ++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 48 deletions(-) diff --git a/include/assimp/fast_atof.h b/include/assimp/fast_atof.h index fa70abfb2..5c25db8e1 100644 --- a/include/assimp/fast_atof.h +++ b/include/assimp/fast_atof.h @@ -14,8 +14,8 @@ // ------------------------------------------------------------------------------------ -#ifndef __FAST_A_TO_F_H_INCLUDED__ -#define __FAST_A_TO_F_H_INCLUDED__ +#ifndef FAST_A_TO_F_H_INCLUDED +#define FAST_A_TO_F_H_INCLUDED #include #include @@ -148,8 +148,8 @@ unsigned int HexDigitToDecimal(char in) { // ------------------------------------------------------------------------------------ // Convert a hex-encoded octet (2 characters, i.e. df or 1a). // ------------------------------------------------------------------------------------ -inline uint8_t HexOctetToDecimal(const char* in) -{ +inline +uint8_t HexOctetToDecimal(const char* in) { return ((uint8_t)HexDigitToDecimal(in[0])<<4)+(uint8_t)HexDigitToDecimal(in[1]); } @@ -157,8 +157,8 @@ inline uint8_t HexOctetToDecimal(const char* in) // ------------------------------------------------------------------------------------ // signed variant of strtoul10 // ------------------------------------------------------------------------------------ -inline int strtol10( const char* in, const char** out=0) -{ +inline +int strtol10( const char* in, const char** out=0) { bool inv = (*in=='-'); if (inv || *in=='+') ++in; @@ -176,10 +176,9 @@ inline int strtol10( const char* in, const char** out=0) // 0NNN - oct // NNN - dec // ------------------------------------------------------------------------------------ -inline unsigned int strtoul_cppstyle( const char* in, const char** out=0) -{ - if ('0' == in[0]) - { +inline +unsigned int strtoul_cppstyle( const char* in, const char** out=0) { + if ('0' == in[0]) { return 'x' == in[1] ? strtoul16(in+2,out) : strtoul8(in+1,out); } return strtoul10(in, out); @@ -189,19 +188,20 @@ inline unsigned int strtoul_cppstyle( const char* in, const char** out=0) // Special version of the function, providing higher accuracy and safety // It is mainly used by fast_atof to prevent ugly and unwanted integer overflows. // ------------------------------------------------------------------------------------ -inline uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_inout=0) -{ +inline +uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_inout=0) { unsigned int cur = 0; uint64_t value = 0; - if ( *in < '0' || *in > '9' ) - throw std::invalid_argument(std::string("The string \"") + in + "\" cannot be converted into a value."); + if ( *in < '0' || *in > '9' ) { + throw std::invalid_argument( std::string( "The string \"" ) + in + "\" cannot be converted into a value." ); + } bool running = true; - while ( running ) - { - if ( *in < '0' || *in > '9' ) + while ( running ) { + if ( *in < '0' || *in > '9' ) { break; + } const uint64_t new_value = ( value * 10 ) + ( *in - '0' ); @@ -210,7 +210,6 @@ inline uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* DefaultLogger::get()->warn( std::string( "Converting the string \"" ) + in + "\" into a value resulted in overflow." ); return 0; } - //throw std::overflow_error(); value = new_value; @@ -218,21 +217,23 @@ inline uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* ++cur; if (max_inout && *max_inout == cur) { - if (out) { /* skip to end */ - while (*in >= '0' && *in <= '9') + while ( *in >= '0' && *in <= '9' ) { ++in; + } *out = in; } return value; } } - if (out) + if ( out ) { *out = in; + } - if (max_inout) + if ( max_inout ) { *max_inout = cur; + } return value; } @@ -240,11 +241,12 @@ inline uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* // ------------------------------------------------------------------------------------ // signed variant of strtoul10_64 // ------------------------------------------------------------------------------------ -inline int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* max_inout = 0) -{ +inline +int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* max_inout = 0) { bool inv = (*in == '-'); - if (inv || *in == '+') + if ( inv || *in == '+' ) { ++in; + } int64_t value = strtoul10_64(in, out, max_inout); if (inv) { @@ -253,7 +255,6 @@ inline int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* m return value; } - // Number of relevant decimals for floating-point parsing. #define AI_FAST_ATOF_RELAVANT_DECIMALS 15 @@ -263,8 +264,8 @@ inline int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* m // If you find any bugs, please send them to me, niko (at) irrlicht3d.org. // ------------------------------------------------------------------------------------ template -inline const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) -{ +inline +const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) { Real f = 0; bool inv = (*c == '-'); @@ -272,42 +273,36 @@ inline const char* fast_atoreal_move(const char* c, Real& out, bool check_comma ++c; } - if ((c[0] == 'N' || c[0] == 'n') && ASSIMP_strincmp(c, "nan", 3) == 0) - { + if ((c[0] == 'N' || c[0] == 'n') && ASSIMP_strincmp(c, "nan", 3) == 0) { out = std::numeric_limits::quiet_NaN(); c += 3; return c; } - if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inf", 3) == 0) - { + if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inf", 3) == 0) { out = std::numeric_limits::infinity(); if (inv) { out = -out; } c += 3; - if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inity", 5) == 0) - { + if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inity", 5) == 0) { c += 5; } return c; - } + if (!(c[0] >= '0' && c[0] <= '9') && - !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) - { + !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')){ throw std::invalid_argument("Cannot parse string " "as real number: does not start with digit " "or decimal point followed by digit."); } - if (*c != '.' && (! check_comma || c[0] != ',')) - { + if (*c != '.' && (! check_comma || c[0] != ',')) { f = static_cast( strtoul10_64 ( c, &c) ); } - if ((*c == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9') - { + if ((*c == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9') { ++c; // NOTE: The original implementation is highly inaccurate here. The precision of a single @@ -358,24 +353,25 @@ inline const char* fast_atoreal_move(const char* c, Real& out, bool check_comma // ------------------------------------------------------------------------------------ // The same but more human. -inline ai_real fast_atof(const char* c) -{ +inline +ai_real fast_atof(const char* c) { ai_real ret(0.0); fast_atoreal_move(c, ret); + return ret; } -inline ai_real fast_atof( const char* c, const char** cout) -{ +inline +ai_real fast_atof( const char* c, const char** cout) { ai_real ret(0.0); *cout = fast_atoreal_move(c, ret); return ret; } -inline ai_real fast_atof( const char** inout) -{ +inline +ai_real fast_atof( const char** inout) { ai_real ret(0.0); *inout = fast_atoreal_move(*inout, ret); @@ -384,4 +380,4 @@ inline ai_real fast_atof( const char** inout) } // end of namespace Assimp -#endif +#endif // FAST_A_TO_F_H_INCLUDED From d00c4a54e6ae1a00131bc00682875017d5867c04 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 23 Feb 2018 18:59:38 +0100 Subject: [PATCH 079/183] Update fast_atof.h fix typo --- include/assimp/fast_atof.h | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/include/assimp/fast_atof.h b/include/assimp/fast_atof.h index 9c40a1646..fced5307a 100644 --- a/include/assimp/fast_atof.h +++ b/include/assimp/fast_atof.h @@ -26,15 +26,13 @@ #include "StringComparison.h" #include - #ifdef _MSC_VER # include #else # include #endif -namespace Assimp -{ +namespace Assimp { const double fast_atof_table[16] = { // we write [16] here instead of [] to work around a swig bug 0.0, @@ -64,8 +62,9 @@ unsigned int strtoul10( const char* in, const char** out=0) { unsigned int value = 0; for ( ;; ) { - if ( *in < '0' || *in > '9' ) + if ( *in < '0' || *in > '9' ) { break; + } value = ( value * 10 ) + ( *in - '0' ); ++in; @@ -109,8 +108,7 @@ unsigned int strtoul16( const char* in, const char** out=0) { value = ( value << 4u ) + ( *in - 'A' ) + 10; } else if (*in >= 'a' && *in <= 'f') { value = ( value << 4u ) + ( *in - 'a' ) + 10; - } - else { + } else { break; } ++in; @@ -258,7 +256,7 @@ int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* max_inou //! about 6 times faster than atof in win32. // If you find any bugs, please send them to me, niko (at) irrlicht3d.org. // ------------------------------------------------------------------------------------ -template +template inline const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) { Real f = 0; @@ -284,10 +282,10 @@ const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) c += 5; } return c; - + } if (!(c[0] >= '0' && c[0] <= '9') && - !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')){ + !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) { throw std::invalid_argument("Cannot parse string " "as real number: does not start with digit " "or decimal point followed by digit."); @@ -322,7 +320,6 @@ const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) // A major 'E' must be allowed. Necessary for proper reading of some DXF files. // Thanks to Zhao Lei to point out that this if() must be outside the if (*c == '.' ..) if (*c == 'e' || *c == 'E') { - ++c; const bool einv = (*c=='-'); if (einv || *c=='+') { From 375dd4c1793f9d4d0caa49d8a8b75994b6af0aa9 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 23 Feb 2018 20:34:34 +0100 Subject: [PATCH 080/183] Update D3MFImporter.cpp Fix order of init list. --- code/D3MFImporter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 489926204..fe5e260a4 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -71,9 +71,9 @@ namespace D3MF { class XmlSerializer { public: XmlSerializer(XmlReader* xmlReader) - : xmlReader(xmlReader) - , mMeshes() - , mMaterials() { + : mMeshes() + , mMaterials() + , xmlReader(xmlReader){ // empty } From b91976eead66c14633aa9d4dc803d27d55a54378 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sat, 24 Feb 2018 08:42:03 +0100 Subject: [PATCH 081/183] FBX Export: handle newly-added geometric transform inverse nodes. This also tidies up the imported node structure a little, by not adding the inverse nodes if there are no child nodes. --- code/FBXConverter.cpp | 32 +++++++++++++++++++------------- code/FBXExporter.cpp | 5 ++++- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 746777d65..8aac70a0f 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -215,24 +215,30 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa // attach geometry ConvertModel( *model, *nodes_chain.back(), new_abs_transform ); - // now link the geometric transform inverse nodes, + // check if there will be any child nodes + const std::vector& child_conns + = doc.GetConnectionsByDestinationSequenced( model->ID(), "Model" ); + + // if so, link the geometric transform inverse nodes // before we attach any child nodes - for( aiNode* postnode : post_nodes_chain ) { - ai_assert( postnode ); + if (child_conns.size()) { + for( aiNode* postnode : post_nodes_chain ) { + ai_assert( postnode ); - if ( last_parent != &parent ) { - last_parent->mNumChildren = 1; - last_parent->mChildren = new aiNode*[ 1 ]; - last_parent->mChildren[ 0 ] = postnode; + if ( last_parent != &parent ) { + last_parent->mNumChildren = 1; + last_parent->mChildren = new aiNode*[ 1 ]; + last_parent->mChildren[ 0 ] = postnode; + } + + postnode->mParent = last_parent; + last_parent = postnode; + + new_abs_transform *= postnode->mTransformation; } - - postnode->mParent = last_parent; - last_parent = postnode; - - new_abs_transform *= postnode->mTransformation; } - // attach sub-nodes + // attach sub-nodes (if any) ConvertNodes( model->ID(), *last_parent, new_abs_transform ); if ( doc.Settings().readLights ) { diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 86804bccc..582916d04 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -1775,7 +1775,10 @@ const std::map> transform_types = { {"ScalingPivotInverse", {"ScalingPivotInverse", 'i'}}, {"GeometricScaling", {"GeometricScaling", 's'}}, {"GeometricRotation", {"GeometricRotation", 'r'}}, - {"GeometricTranslation", {"GeometricTranslation", 't'}} + {"GeometricTranslation", {"GeometricTranslation", 't'}}, + {"GeometricTranslationInverse", {"GeometricTranslationInverse", 'i'}}, + {"GeometricRotationInverse", {"GeometricRotationInverse", 'i'}}, + {"GeometricScalingInverse", {"GeometricScalingInverse", 'i'}} }; // write a single model node to the stream From c18a07e47f514866c7b8cc57676611865de14415 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sat, 24 Feb 2018 08:43:36 +0100 Subject: [PATCH 082/183] FBX Import: Properly clean up post_nodes_chain in case of exception. --- code/FBXConverter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 8aac70a0f..a948a7abf 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -265,6 +265,7 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa Util::delete_fun deleter; std::for_each( nodes.begin(), nodes.end(), deleter ); std::for_each( nodes_chain.begin(), nodes_chain.end(), deleter ); + std::for_each( post_nodes_chain.begin(), post_nodes_chain.end(), deleter ); } } From a9d70b2dbc620c52d6cbda53066c330e0c83125a Mon Sep 17 00:00:00 2001 From: Tommy Date: Sat, 24 Feb 2018 09:37:36 +0100 Subject: [PATCH 083/183] FBX Import: properly delete inverse geotrans nodes when not using them. --- code/FBXConverter.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index a948a7abf..5d732c7b2 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -236,6 +236,14 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa new_abs_transform *= postnode->mTransformation; } + } else { + // free the nodes we allocated as we don't need them + Util::delete_fun deleter; + std::for_each( + post_nodes_chain.begin(), + post_nodes_chain.end(), + deleter + ); } // attach sub-nodes (if any) From 4b7cd97fea9dc37f4b741840090c42c6a6a027ae Mon Sep 17 00:00:00 2001 From: Marco Di Benedetto Date: Sat, 24 Feb 2018 17:44:40 +0100 Subject: [PATCH 084/183] added support for embedded textures defined with buffer views. --- code/glTF2Asset.inl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 520785081..cf43d67f1 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -669,7 +669,7 @@ inline Image::Image() } -inline void Image::Read(Value& obj, Asset& /*r*/) +inline void Image::Read(Value& obj, Asset& r) { if (!mDataLength) { if (Value* uri = FindString(obj, "uri")) { @@ -686,6 +686,18 @@ inline void Image::Read(Value& obj, Asset& /*r*/) this->uri = uristr; } } + else if (Value* bufferViewVal = FindUInt(obj, "bufferView")) { + this->bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); + Ref buffer = this->bufferView->buffer; + + this->mDataLength = this->bufferView->byteLength; + this->mData = new uint8_t [this->mDataLength]; + memcpy(this->mData, buffer->GetPointer(), this->mDataLength); + + if (Value* mtype = FindString(obj, "mimeType")) { + this->mimeType = mtype->GetString(); + } + } } } From 138b990d0a9e7833bec242517f448df8ee69e861 Mon Sep 17 00:00:00 2001 From: Marco Di Benedetto Date: Sat, 24 Feb 2018 17:57:42 +0100 Subject: [PATCH 085/183] added missing install of pbrmaterial.h --- code/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 6430187b0..6e2db91db 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -72,6 +72,7 @@ SET( PUBLIC_HEADERS ${HEADER_PATH}/matrix4x4.h ${HEADER_PATH}/matrix4x4.inl ${HEADER_PATH}/mesh.h + ${HEADER_PATH}/pbrmaterial.h ${HEADER_PATH}/postprocess.h ${HEADER_PATH}/quaternion.h ${HEADER_PATH}/quaternion.inl From 72e9f3ecb9bd12056d6992d87d3511546486df72 Mon Sep 17 00:00:00 2001 From: Marco Di Benedetto Date: Sat, 24 Feb 2018 18:47:43 +0100 Subject: [PATCH 086/183] fixed embedded texture reading. --- code/glTF2Asset.inl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index cf43d67f1..8f2b14e1a 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -691,8 +691,9 @@ inline void Image::Read(Value& obj, Asset& r) Ref buffer = this->bufferView->buffer; this->mDataLength = this->bufferView->byteLength; + // maybe this memcpy could be avoided if aiTexture does not delete[] pcData at destruction. this->mData = new uint8_t [this->mDataLength]; - memcpy(this->mData, buffer->GetPointer(), this->mDataLength); + memcpy(this->mData, buffer->GetPointer() + this->bufferView->byteOffset, this->mDataLength); if (Value* mtype = FindString(obj, "mimeType")) { this->mimeType = mtype->GetString(); From e7736022c8aae264011b7d097ae8338e29cebe47 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 25 Feb 2018 09:34:14 +0100 Subject: [PATCH 087/183] assimp_cmd info: list meshes and print basic mesh stats. --- tools/assimp_cmd/Info.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tools/assimp_cmd/Info.cpp b/tools/assimp_cmd/Info.cpp index a9f66eb1f..c4d5fe189 100644 --- a/tools/assimp_cmd/Info.cpp +++ b/tools/assimp_cmd/Info.cpp @@ -329,6 +329,29 @@ int Assimp_Info (const char* const* params, unsigned int num) special_points[2][0],special_points[2][1],special_points[2][2] ) ; + + // meshes + if (scene->mNumMeshes) { + printf("\nMeshes: (name) [vertices / bones / faces | primitive_types]\n"); + } + for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { + const aiMesh* mesh = scene->mMeshes[i]; + printf(" %d (%s)", i, mesh->mName.C_Str()); + printf( + ": [%d / %d / %d |", + mesh->mNumVertices, + mesh->mNumBones, + mesh->mNumFaces + ); + const unsigned int ptypes = mesh->mPrimitiveTypes; + if (ptypes & aiPrimitiveType_POINT) { printf(" point"); } + if (ptypes & aiPrimitiveType_LINE) { printf(" line"); } + if (ptypes & aiPrimitiveType_TRIANGLE) { printf(" triangle"); } + if (ptypes & aiPrimitiveType_POLYGON) { printf(" polygon"); } + printf("]\n"); + } + + // materials unsigned int total=0; for(unsigned int i = 0;i < scene->mNumMaterials; ++i) { aiString name; @@ -340,6 +363,7 @@ int Assimp_Info (const char* const* params, unsigned int num) printf("\n"); } + // textures total=0; for(unsigned int i = 0;i < scene->mNumMaterials; ++i) { aiString name; @@ -369,6 +393,7 @@ int Assimp_Info (const char* const* params, unsigned int num) printf("\n"); } + // animations total=0; for(unsigned int i = 0;i < scene->mNumAnimations; ++i) { if (scene->mAnimations[i]->mName.length) { @@ -379,6 +404,7 @@ int Assimp_Info (const char* const* params, unsigned int num) printf("\n"); } + // node hierarchy printf("\nNode hierarchy:\n"); unsigned int cline=0; PrintHierarchy(scene->mRootNode,20,1000,cline,verbose); From 249f1844aee94af7d73e4c1c06ad1dd3da806536 Mon Sep 17 00:00:00 2001 From: Tommy Date: Thu, 22 Feb 2018 11:06:29 +0100 Subject: [PATCH 088/183] FBX Export: reconstruct full skeleton for any FBX deformers. --- code/FBXExporter.cpp | 201 ++++++++++++++++++++++++++----------------- code/FBXExporter.h | 5 +- 2 files changed, 126 insertions(+), 80 deletions(-) diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 582916d04..6273f1977 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -1463,46 +1463,67 @@ void FBXExporter::WriteObjects () // one sticky point is that the number of vertices may not match, // because assimp splits vertices by normal, uv, etc. - // first we should mark all the skeleton nodes, - // so that they can be treated as LimbNode in stead of Mesh or Null. - // at the same time we can build up a map of bone nodes. + // first we should mark the skeleton for each mesh. + // the skeleton must include not only the aiBones, + // but also all their parent nodes. + // anything that affects the position of any bone node must be included. + std::vector> skeleton_by_mesh(mScene->mNumMeshes); + // at the same time we can build a list of all the skeleton nodes, + // which will be used later to mark them as type "limbNode". std::unordered_set limbnodes; + // and a map of nodes by bone name, as finding them is annoying. std::map node_by_bone; for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { const aiMesh* m = mScene->mMeshes[mi]; + std::set skeleton; for (size_t bi =0; bi < m->mNumBones; ++bi) { const aiBone* b = m->mBones[bi]; const std::string name(b->mName.C_Str()); - if (node_by_bone.count(name) > 0) { - // already processed, skip - continue; + auto elem = node_by_bone.find(name); + aiNode* n; + if (elem != node_by_bone.end()) { + n = elem->second; + } else { + n = mScene->mRootNode->FindNode(b->mName); + if (!n) { + // this should never happen + std::stringstream err; + err << "Failed to find node for bone: \"" << name << "\""; + throw DeadlyExportError(err.str()); + } + node_by_bone[name] = n; + limbnodes.insert(n); } - aiNode* n = mScene->mRootNode->FindNode(b->mName); - if (!n) { - // this should never happen - std::stringstream err; - err << "Failed to find node for bone: \"" << name << "\""; - throw DeadlyExportError(err.str()); - } - node_by_bone[name] = n; - limbnodes.insert(n); - if (n == mScene->mRootNode) { continue; } + skeleton.insert(n); // mark all parent nodes as skeleton as well, // up until we find the root node, // or else the node containing the mesh, // or else the parent of a node containig the mesh. for ( const aiNode* parent = n->mParent; - parent != mScene->mRootNode; + parent && parent != mScene->mRootNode; parent = parent->mParent ) { + // if we've already done this node we can skip it all + if (skeleton.count(parent)) { + break; + } + // ignore fbx transform nodes as these will be collapsed later + // TODO: cache this by aiNode* + const std::string node_name(parent->mName.C_Str()); + if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) { + continue; + } + // otherwise check if this is the root of the skeleton bool end = false; + // is the mesh part of this node? for (size_t i = 0; i < parent->mNumMeshes; ++i) { if (parent->mMeshes[i] == mi) { end = true; break; } } + // is the mesh in one of the children of this node? for (size_t j = 0; j < parent->mNumChildren; ++j) { aiNode* child = parent->mChildren[j]; for (size_t i = 0; i < child->mNumMeshes; ++i) { @@ -1513,27 +1534,23 @@ void FBXExporter::WriteObjects () } if (end) { break; } } - if (end) { break; } limbnodes.insert(parent); + skeleton.insert(parent); + // if it was the skeleton root we can finish here + if (end) { break; } } } + skeleton_by_mesh[mi] = skeleton; } // we'll need the uids for the bone nodes, so generate them now - std::map bone_uids; - for (auto &bone : limbnodes) { - std::string bone_name(bone->mName.C_Str()); - aiNode* bone_node = mScene->mRootNode->FindNode(bone->mName); - if (!bone_node) { - throw DeadlyExportError("Couldn't find node for bone" + bone_name); - } - auto elem = node_uids.find(bone_node); - if (elem == node_uids.end()) { - int64_t uid = generate_uid(); - node_uids[bone_node] = uid; - bone_uids[bone_name] = uid; - } else { - bone_uids[bone_name] = elem->second; + for (size_t i = 0; i < mScene->mNumMeshes; ++i) { + auto &s = skeleton_by_mesh[i]; + for (const aiNode* n : s) { + auto elem = node_uids.find(n); + if (elem == node_uids.end()) { + node_uids[n] = generate_uid(); + } } } @@ -1585,6 +1602,9 @@ void FBXExporter::WriteObjects () } } + // TODO, FIXME: this won't work if anything is not in the bind pose. + // for now if such a situation is detected, we throw an exception. + // first get this mesh's position in world space, // as we'll need it for each subdeformer. // @@ -1597,12 +1617,23 @@ void FBXExporter::WriteObjects () // but there's no guarantee that the bone is in the bindpose, // so this would be even less reliable. aiNode* mesh_node = get_node_for_mesh(mi, mScene->mRootNode); - aiMatrix4x4 mesh_node_xform = get_world_transform(mesh_node, mScene); + aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene); - // now make a subdeformer for each bone - for (size_t bi =0; bi < m->mNumBones; ++bi) { - const aiBone* b = m->mBones[bi]; - const std::string name(b->mName.C_Str()); + // now make a subdeformer for each bone in the skeleton + const std::set &skeleton = skeleton_by_mesh[mi]; + for (const aiNode* bone_node : skeleton) { + // if there's a bone for this node, find it + const aiBone* b = nullptr; + for (size_t bi = 0; bi < m->mNumBones; ++bi) { + // TODO: this probably should index by something else + const std::string name(m->mBones[bi]->mName.C_Str()); + if (node_by_bone[name] == bone_node) { + b = m->mBones[bi]; + break; + } + } + + // start the subdeformer node const int64_t subdeformer_uid = generate_uid(); FBX::Node sdnode("Deformer"); sdnode.AddProperties( @@ -1611,43 +1642,57 @@ void FBXExporter::WriteObjects () sdnode.AddChild("Version", int32_t(100)); sdnode.AddChild("UserData", "", ""); - // get indices and weights - std::vector subdef_indices; - std::vector subdef_weights; - int32_t last_index = -1; - for (size_t wi = 0; wi < b->mNumWeights; ++wi) { - int32_t vi = vertex_indices[b->mWeights[wi].mVertexId]; - if (vi == last_index) { - // only for vertices we exported to fbx - // TODO, FIXME: this assumes identically-located vertices - // will always deform in the same way. - // as assimp doesn't store a separate list of "positions", - // there's not much that can be done about this - // other than assuming that identical position means - // identical vertex. - continue; + // add indices and weights, if any + if (b) { + std::vector subdef_indices; + std::vector subdef_weights; + int32_t last_index = -1; + for (size_t wi = 0; wi < b->mNumWeights; ++wi) { + int32_t vi = vertex_indices[b->mWeights[wi].mVertexId]; + if (vi == last_index) { + // only for vertices we exported to fbx + // TODO, FIXME: this assumes identically-located vertices + // will always deform in the same way. + // as assimp doesn't store a separate list of "positions", + // there's not much that can be done about this + // other than assuming that identical position means + // identical vertex. + continue; + } + subdef_indices.push_back(vi); + subdef_weights.push_back(b->mWeights[wi].mWeight); + last_index = vi; } - subdef_indices.push_back(vi); - subdef_weights.push_back(b->mWeights[wi].mWeight); - last_index = vi; + // yes, "indexes" + sdnode.AddChild("Indexes", subdef_indices); + sdnode.AddChild("Weights", subdef_weights); } - // yes, "indexes" - sdnode.AddChild("Indexes", subdef_indices); - sdnode.AddChild("Weights", subdef_weights); - // transform is the transform of the mesh, but in bone space... - // which is exactly what assimp's mOffsetMatrix is, - // no matter what the assimp docs may say. - aiMatrix4x4 tr = b->mOffsetMatrix; + + // transform is the transform of the mesh, but in bone space. + // To get it we take the inverse of the world-space bone transform, + // and multiply by the world-space transform of the mesh. + aiMatrix4x4 bone_xform = get_world_transform(bone_node, mScene); + aiMatrix4x4 inverse_bone_xform = bone_xform; + inverse_bone_xform.Inverse(); + aiMatrix4x4 tr = inverse_bone_xform * mesh_xform; sdnode.AddChild("Transform", tr); + + // this should match assimp's mOffsetMatrix. + // if it doesn't then we have a problem. + // as assimp doesn't store a mOffsetMatrix for bones with 0 weight + // we have no way of reconstructing that information. + const float epsilon = 1e-5; // some error is to be expected + if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) { + std::stringstream err; + err << "transform matrix for bone \"" << b->mName.C_Str(); + err << "\" does not match mOffsetMatrix!"; + err << " Bones *must* be in the bind pose to export."; + throw DeadlyExportError(err.str()); + } + // transformlink should be the position of the bone in world space, - // in the bind pose. - // For now let's use the inverse of mOffsetMatrix, - // and the (assumedly static) mesh position in world space. - // TODO: find a better way of doing this? there aren't many options - tr = b->mOffsetMatrix; - tr.Inverse(); - tr *= mesh_node_xform; - sdnode.AddChild("TransformLink", tr); + // which we just calculated. + sdnode.AddChild("TransformLink", bone_xform); // done sdnode.Dump(outstream); @@ -1659,7 +1704,7 @@ void FBXExporter::WriteObjects () // we also need to connect the limb node to the subdeformer. c = FBX::Node("C"); - c.AddProperties("OO", bone_uids[name], subdeformer_uid); + c.AddProperties("OO", node_uids[bone_node], subdeformer_uid); connections.push_back(c); // TODO: emplace_back } @@ -1753,7 +1798,7 @@ void FBXExporter::WriteObjects () // write nodes (i.e. model heirarchy) // start at root node WriteModelNodes( - outstream, mScene->mRootNode, 0, bone_uids + outstream, mScene->mRootNode, 0, limbnodes ); object_node.End(outstream, true); @@ -1864,17 +1909,17 @@ void FBXExporter::WriteModelNodes( StreamWriterLE& s, const aiNode* node, int64_t parent_uid, - const std::map& bone_uids + const std::unordered_set& limbnodes ) { std::vector> chain; - WriteModelNodes(s, node, parent_uid, bone_uids, chain); + WriteModelNodes(s, node, parent_uid, limbnodes, chain); } void FBXExporter::WriteModelNodes( StreamWriterLE& outstream, const aiNode* node, int64_t parent_uid, - const std::map& bone_uids, + const std::unordered_set& limbnodes, std::vector>& transform_chain ) { // first collapse any expanded transformation chains created by FBX import. @@ -1924,7 +1969,7 @@ void FBXExporter::WriteModelNodes( } // now just continue to the next node WriteModelNodes( - outstream, next_node, parent_uid, bone_uids, transform_chain + outstream, next_node, parent_uid, limbnodes, transform_chain ); return; } @@ -1962,7 +2007,7 @@ void FBXExporter::WriteModelNodes( connections.push_back(c); // write model node WriteModelNode(outstream, node, node_uid, "Mesh", transform_chain); - } else if (bone_uids.count(node_name)) { + } else if (limbnodes.count(node)) { WriteModelNode(outstream, node, node_uid, "LimbNode", transform_chain); // we also need to write a nodeattribute to mark it as a skeleton int64_t node_attribute_uid = generate_uid(); @@ -2021,7 +2066,7 @@ void FBXExporter::WriteModelNodes( // now recurse into children for (size_t i = 0; i < node->mNumChildren; ++i) { WriteModelNodes( - outstream, node->mChildren[i], node_uid, bone_uids + outstream, node->mChildren[i], node_uid, limbnodes ); } } diff --git a/code/FBXExporter.h b/code/FBXExporter.h index 39c04ffee..ce2f67e24 100644 --- a/code/FBXExporter.h +++ b/code/FBXExporter.h @@ -56,6 +56,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include // shared_ptr #include // stringstream @@ -129,13 +130,13 @@ namespace Assimp Assimp::StreamWriterLE& s, const aiNode* node, int64_t parent_uid, - const std::map& bone_uids + const std::unordered_set& limbnodes ); void WriteModelNodes( // usually don't call this directly StreamWriterLE& s, const aiNode* node, int64_t parent_uid, - const std::map& bone_uids, + const std::unordered_set& limbnodes, std::vector>& transform_chain ); }; From ce7673979be55db31893d22f6b0b6ed93baf91fc Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 25 Feb 2018 10:10:07 +0100 Subject: [PATCH 089/183] assimp_cmd export: print error message on failure. --- tools/assimp_cmd/Main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/assimp_cmd/Main.cpp b/tools/assimp_cmd/Main.cpp index 1b4e5db45..14d00e1e1 100644 --- a/tools/assimp_cmd/Main.cpp +++ b/tools/assimp_cmd/Main.cpp @@ -334,7 +334,8 @@ bool ExportModel(const aiScene* pOut, PrintHorBar(); } if (res != AI_SUCCESS) { - printf("ERROR: Failed to write file\n"); + printf("Failed to write file\n"); + printf("ERROR: %s\n", globalExporter->GetErrorString()); return false; } From 9d9acf6840551d11423e32cd826d17b7da105d0f Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 25 Feb 2018 11:45:38 +0100 Subject: [PATCH 090/183] FBX Export: allow export even when not in bind pose, iff all bones have an offset matrix defined. --- code/FBXExporter.cpp | 82 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 18 deletions(-) diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 6273f1977..f1c6290e2 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -1604,6 +1604,8 @@ void FBXExporter::WriteObjects () // TODO, FIXME: this won't work if anything is not in the bind pose. // for now if such a situation is detected, we throw an exception. + std::set not_in_bind_pose; + std::set no_offset_matrix; // first get this mesh's position in world space, // as we'll need it for each subdeformer. @@ -1612,10 +1614,6 @@ void FBXExporter::WriteObjects () // as it can be instanced to many nodes. // All we can do is assume no instancing, // and take the first node we find that contains the mesh. - // - // We could in stead take the transform from the bone's node, - // but there's no guarantee that the bone is in the bindpose, - // so this would be even less reliable. aiNode* mesh_node = get_node_for_mesh(mi, mScene->mRootNode); aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene); @@ -1632,6 +1630,9 @@ void FBXExporter::WriteObjects () break; } } + if (!b) { + no_offset_matrix.insert(bone_node); + } // start the subdeformer node const int64_t subdeformer_uid = generate_uid(); @@ -1669,30 +1670,52 @@ void FBXExporter::WriteObjects () } // transform is the transform of the mesh, but in bone space. - // To get it we take the inverse of the world-space bone transform, + // if the skeleton is in the bind pose, + // we can take the inverse of the world-space bone transform // and multiply by the world-space transform of the mesh. aiMatrix4x4 bone_xform = get_world_transform(bone_node, mScene); aiMatrix4x4 inverse_bone_xform = bone_xform; inverse_bone_xform.Inverse(); aiMatrix4x4 tr = inverse_bone_xform * mesh_xform; - sdnode.AddChild("Transform", tr); - // this should match assimp's mOffsetMatrix. - // if it doesn't then we have a problem. - // as assimp doesn't store a mOffsetMatrix for bones with 0 weight - // we have no way of reconstructing that information. + // 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-5; // some error is to be expected + bool bone_xform_okay = true; if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) { - std::stringstream err; - err << "transform matrix for bone \"" << b->mName.C_Str(); - err << "\" does not match mOffsetMatrix!"; - err << " Bones *must* be in the bind pose to export."; - throw DeadlyExportError(err.str()); + not_in_bind_pose.insert(b); + bone_xform_okay = false; } - // transformlink should be the position of the bone in world space, - // which we just calculated. - sdnode.AddChild("TransformLink", bone_xform); + // 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 nonexistant), + // we can just use the matrix we already calculated + if (bone_xform_okay) { + sdnode.AddChild("TransformLink", bone_xform); + // otherwise we can only work it out using the mesh position. + } else { + aiMatrix4x4 trl = b->mOffsetMatrix; + trl.Inverse(); + trl *= mesh_xform; + sdnode.AddChild("TransformLink", trl); + } + // note: this means we ALWAYS rely on the mesh node transform + // being unchanged from the time the skeleton was bound. + // there's not really any way around this at the moment. // done sdnode.Dump(outstream); @@ -1708,6 +1731,29 @@ void FBXExporter::WriteObjects () connections.push_back(c); // TODO: emplace_back } + // if we cannot create a valid FBX file, simply die. + // this will both prevent unnecessary bug reports, + // and tell the user what they can do to fix the situation + // (i.e. export their model in the bind pose). + if (no_offset_matrix.size() && not_in_bind_pose.size()) { + std::stringstream err; + err << "Not enough information to construct bind pose"; + err << " for mesh " << mi << "!"; + err << " Transform matrix for bone \""; + err << (*not_in_bind_pose.begin())->mName.C_Str() << "\""; + if (not_in_bind_pose.size() > 1) { + err << " (and " << not_in_bind_pose.size() - 1 << " more)"; + } + err << " does not match mOffsetMatrix,"; + err << " and node \""; + err << (*no_offset_matrix.begin())->mName.C_Str() << "\""; + if (no_offset_matrix.size() > 1) { + err << " (and " << no_offset_matrix.size() - 1 << " more)"; + } + err << " has no offset matrix to rely on."; + err << " Please ensure bones are in the bind pose to export."; + throw DeadlyExportError(err.str()); + } } From e99dfdb050c67844115e3849b4e39927aa2cdbd8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 25 Feb 2018 21:03:09 +0100 Subject: [PATCH 091/183] fix cppcheck findings. --- code/3DSExporter.cpp | 2 +- code/3DSExporter.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/3DSExporter.cpp b/code/3DSExporter.cpp index 65f4bf8d8..fcd24d8aa 100644 --- a/code/3DSExporter.cpp +++ b/code/3DSExporter.cpp @@ -184,7 +184,7 @@ void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScen } // end of namespace Assimp // ------------------------------------------------------------------------------------------------ -Discreet3DSExporter:: Discreet3DSExporter(std::shared_ptr outfile, const aiScene* scene) +Discreet3DSExporter:: Discreet3DSExporter(std::shared_ptr &outfile, const aiScene* scene) : scene(scene) , writer(outfile) { diff --git a/code/3DSExporter.h b/code/3DSExporter.h index 5e138e92d..5db58a4cb 100644 --- a/code/3DSExporter.h +++ b/code/3DSExporter.h @@ -67,7 +67,7 @@ namespace Assimp // ------------------------------------------------------------------------------------------------ class Discreet3DSExporter { public: - Discreet3DSExporter(std::shared_ptr outfile, const aiScene* pScene); + Discreet3DSExporter(std::shared_ptr &outfile, const aiScene* pScene); ~Discreet3DSExporter(); private: From c0f04bf9652d02d8764d2b8aa837cd1ba338c825 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 27 Feb 2018 18:30:36 +0100 Subject: [PATCH 092/183] IMporter: fix lookup for tokens during inmemory imports. --- code/BaseImporter.cpp | 12 ++++++++++-- code/Importer/IFC/IFCLoader.cpp | 3 ++- test/unit/utIFCImportExport.cpp | 20 ++++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/code/BaseImporter.cpp b/code/BaseImporter.cpp index 0892efd9f..e7736d8a4 100644 --- a/code/BaseImporter.cpp +++ b/code/BaseImporter.cpp @@ -178,9 +178,17 @@ void BaseImporter::GetExtensionList(std::set& extensions) } *cur2 = '\0'; + std::string token; for (unsigned int i = 0; i < numTokens;++i) { - ai_assert(NULL != tokens[i]); - const char* r = strstr(buffer,tokens[i]); + ai_assert( nullptr != tokens[i] ); + size_t len( strlen( tokens[ i ] ) ); + token.clear(); + const char *ptr( tokens[ i ] ); + for ( size_t tokIdx = 0; tokIdx < len; ++tokIdx ) { + token.push_back( tolower( *ptr ) ); + ++ptr; + } + const char* r = strstr( buffer, token.c_str() ); if( !r ) { continue; } diff --git a/code/Importer/IFC/IFCLoader.cpp b/code/Importer/IFC/IFCLoader.cpp index de7a37037..5acc89545 100644 --- a/code/Importer/IFC/IFCLoader.cpp +++ b/code/Importer/IFC/IFCLoader.cpp @@ -141,7 +141,8 @@ bool IFCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool // it is only unambiguous as long as we don't support any further // file formats with STEP as their encoding. const char* tokens[] = {"ISO-10303-21"}; - return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); + const bool found( SearchFileHeaderForToken( pIOHandler, pFile, tokens, 1 ) ); + return found; } return false; } diff --git a/test/unit/utIFCImportExport.cpp b/test/unit/utIFCImportExport.cpp index fa27bb7a8..e13244e34 100644 --- a/test/unit/utIFCImportExport.cpp +++ b/test/unit/utIFCImportExport.cpp @@ -62,3 +62,23 @@ public: TEST_F( utIFCImportExport, importIFCFromFileTest ) { EXPECT_TRUE( importerTest() ); } + +TEST_F( utIFCImportExport, importComplextypeAsColor ) { + std::string asset = + "ISO-10303-21;\n" + "HEADER;\n" + "FILE_DESCRIPTION( ( 'ViewDefinition [CoordinationView, SpaceBoundary2ndLevelAddOnView]', 'Option [Filter: ]' ), '2;1' );\n" + "FILE_NAME( 'S:\\[IFC]\\[COMPLETE-BUILDINGS]\\FZK-MODELS\\FZK-Haus\\ArchiCAD-14\\AC14-FZK-Haus.ifc', '2010-10-07T13:40:52', ( 'Architect' ), ( 'Building Designer Office' ), 'PreProc - EDM 5.0', 'ArchiCAD 14.00 Release 1. Windows Build Number of the Ifc 2x3 interface: 3427', 'The authorising person' );\n" + "FILE_SCHEMA( ( 'IFC2X3' ) );\n" + "ENDSEC;\n" + "\n" + "DATA;\n" + "#1 = IFCORGANIZATION( 'GS', 'Graphisoft', 'Graphisoft', $, $ );\n" + "#2 = IFCPROPERTYSINGLEVALUE( 'Red', $, IFCINTEGER( 255 ), $ );\n" + "#3 = IFCPROPERTYSINGLEVALUE( 'Green', $, IFCINTEGER( 255 ), $ );\n" + "#4 = IFCPROPERTYSINGLEVALUE( 'Blue', $, IFCINTEGER( 255 ), $ );\n" + "#5 = IFCCOMPLEXPROPERTY( 'Color', $, 'Color', ( #19, #20, #21 ) );\n"; + Assimp::Importer importer; + const aiScene *scene = importer.ReadFileFromMemory( asset.c_str(), asset.size(), 0 ); + +} From d53f95801180cce74123f58c08fd839222d2f4ff Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 27 Feb 2018 18:56:07 +0100 Subject: [PATCH 093/183] unittest: fix compiler warning --- test/unit/utIFCImportExport.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/utIFCImportExport.cpp b/test/unit/utIFCImportExport.cpp index e13244e34..01132435b 100644 --- a/test/unit/utIFCImportExport.cpp +++ b/test/unit/utIFCImportExport.cpp @@ -80,5 +80,6 @@ TEST_F( utIFCImportExport, importComplextypeAsColor ) { "#5 = IFCCOMPLEXPROPERTY( 'Color', $, 'Color', ( #19, #20, #21 ) );\n"; Assimp::Importer importer; const aiScene *scene = importer.ReadFileFromMemory( asset.c_str(), asset.size(), 0 ); + EXPECT_EQ( nullptr, scene ); } From 190190c1d75c32e0f2a758c9bbe37bd085a0d59e Mon Sep 17 00:00:00 2001 From: Josh Faust Date: Tue, 27 Feb 2018 14:58:04 -0800 Subject: [PATCH 094/183] Fix material index off-by-one error in some OBJ files (seen in a C4D export) --- code/ObjFileMtlImporter.cpp | 5 ++-- test/models/OBJ/cube_mtllib_after_g.mtl | 5 ++++ test/models/OBJ/cube_mtllib_after_g.obj | 32 +++++++++++++++++++++++++ test/unit/utObjImportExport.cpp | 13 ++++++++++ 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 test/models/OBJ/cube_mtllib_after_g.mtl create mode 100644 test/models/OBJ/cube_mtllib_after_g.obj diff --git a/code/ObjFileMtlImporter.cpp b/code/ObjFileMtlImporter.cpp index 835f529cc..584b3115c 100644 --- a/code/ObjFileMtlImporter.cpp +++ b/code/ObjFileMtlImporter.cpp @@ -303,11 +303,12 @@ void ObjFileMtlImporter::createMaterial() // New Material created m_pModel->m_pCurrentMaterial = new ObjFile::Material(); m_pModel->m_pCurrentMaterial->MaterialName.Set( name ); + m_pModel->m_MaterialLib.push_back( name ); + m_pModel->m_MaterialMap[ name ] = m_pModel->m_pCurrentMaterial; + if (m_pModel->m_pCurrentMesh) { m_pModel->m_pCurrentMesh->m_uiMaterialIndex = static_cast(m_pModel->m_MaterialLib.size() - 1); } - m_pModel->m_MaterialLib.push_back( name ); - m_pModel->m_MaterialMap[ name ] = m_pModel->m_pCurrentMaterial; } else { // Use older material m_pModel->m_pCurrentMaterial = (*it).second; diff --git a/test/models/OBJ/cube_mtllib_after_g.mtl b/test/models/OBJ/cube_mtllib_after_g.mtl new file mode 100644 index 000000000..63dde71af --- /dev/null +++ b/test/models/OBJ/cube_mtllib_after_g.mtl @@ -0,0 +1,5 @@ +newmtl MyMaterial +Ka 1.000 1.000 1.000 +Kd 1.000 1.000 1.000 +Ns 200.000 +Ks 0.050 0.050 0.050 diff --git a/test/models/OBJ/cube_mtllib_after_g.obj b/test/models/OBJ/cube_mtllib_after_g.obj new file mode 100644 index 000000000..dfd5997e8 --- /dev/null +++ b/test/models/OBJ/cube_mtllib_after_g.obj @@ -0,0 +1,32 @@ +g Object +mtllib cube_mtllib_after_g.mat +usemtl MyMaterial + +v 0.0 0.0 0.0 +v 0.0 0.0 1.0 +v 0.0 1.0 0.0 +v 0.0 1.0 1.0 +v 1.0 0.0 0.0 +v 1.0 0.0 1.0 +v 1.0 1.0 0.0 +v 1.0 1.0 1.0 + +vn 0.0 0.0 1.0 +vn 0.0 0.0 -1.0 +vn 0.0 1.0 0.0 +vn 0.0 -1.0 0.0 +vn 1.0 0.0 0.0 +vn -1.0 0.0 0.0 + +f 1//2 7//2 5//2 +f 1//2 3//2 7//2 +f 1//6 4//6 3//6 +f 1//6 2//6 4//6 +f 3//3 8//3 7//3 +f 3//3 4//3 8//3 +f 5//5 7//5 8//5 +f 5//5 8//5 6//5 +f 1//4 5//4 6//4 +f 1//4 6//4 2//4 +f 2//1 6//1 8//1 +f 2//1 8//1 4//1 diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index 5a2e42350..10207a5bf 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -354,3 +354,16 @@ TEST_F(utObjImportExport, 0based_array_Test) { const aiScene *scene = myimporter.ReadFileFromMemory(ObjModel.c_str(), ObjModel.size(), 0); EXPECT_EQ(nullptr, scene); } + +TEST_F( utObjImportExport, mtllib_after_g ) { + ::Assimp::Importer importer; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/cube_mtllib_after_g.obj", aiProcess_ValidateDataStructure ); + ASSERT_NE( nullptr, scene ); + + EXPECT_EQ(scene->mNumMeshes, 1U); + const aiMesh *mesh = scene->mMeshes[0]; + const aiMaterial* mat = scene->mMaterials[mesh->mMaterialIndex]; + aiString name; + ASSERT_EQ(aiReturn_SUCCESS, mat->Get(AI_MATKEY_NAME, name)); + EXPECT_STREQ("MyMaterial", name.C_Str()); +} \ No newline at end of file From a58f8e1c1a4127df2051ad955f87e4bab64909bd Mon Sep 17 00:00:00 2001 From: Tommy Date: Wed, 28 Feb 2018 23:38:49 +0100 Subject: [PATCH 095/183] FBX Export: add missing 0 value to file footer. --- code/FBXExporter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index f1c6290e2..8e32ed46c 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -247,6 +247,11 @@ void FBXExporter::WriteBinaryFooter() outfile->Write("\x00", 1, 1); } + // not sure what this is, but it seems to always be 0 in modern files + for (size_t i = 0; i < 4; ++i) { + outfile->Write("\x00", 1, 1); + } + // now the file version again { StreamWriterLE outstream(outfile); From 701f9ccfe9c6fce31a6b7d798b9fc75afcbd0f38 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sat, 3 Mar 2018 19:53:49 +0100 Subject: [PATCH 096/183] FBX Export: minor tweak to footer. Should now be identical to those output by the FBX SDK. --- code/FBXExporter.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 8e32ed46c..a2e6024ed 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -235,9 +235,6 @@ void FBXExporter::WriteBinaryFooter() outfile->Write(NULL_RECORD.c_str(), NULL_RECORD.size(), 1); outfile->Write(GENERIC_FOOTID.c_str(), GENERIC_FOOTID.size(), 1); - for (size_t i = 0; i < 4; ++i) { - outfile->Write("\x00", 1, 1); - } // here some padding is added for alignment to 16 bytes. // if already aligned, the full 16 bytes is added. From 065c264b34fc3151acfe215af5fece11d5010c67 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Sun, 4 Mar 2018 12:52:38 +0100 Subject: [PATCH 097/183] Fix #1415 : float-color.ply is broken float-color.ply was broken because it doesn't have a newline at the end. I'm not sure if a file without newline should be considered valid ? Added more checks to float-color unit-test in order to fail as excepted. Fixed the shipped unit test. Add postprocess validation to PLY unit tests --- test/models/PLY/float-color.ply | 2 +- test/unit/utPLYImportExport.cpp | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/test/models/PLY/float-color.ply b/test/models/PLY/float-color.ply index 34353ad53..f419be34e 100644 --- a/test/models/PLY/float-color.ply +++ b/test/models/PLY/float-color.ply @@ -15,4 +15,4 @@ end_header 0.0 0.0 0.0 0 0 1 1 100.0 0.0 0.0 0 0 1 1 200.0 200.0 0.0 0 0 1 1 -3 0 1 2 \ No newline at end of file +3 0 1 2 diff --git a/test/unit/utPLYImportExport.cpp b/test/unit/utPLYImportExport.cpp index 633beda5f..900f494f8 100644 --- a/test/unit/utPLYImportExport.cpp +++ b/test/unit/utPLYImportExport.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include "AbstractImportExportBase.h" +#include using namespace ::Assimp; @@ -52,7 +53,7 @@ class utPLYImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", aiProcess_ValidateDataStructure); EXPECT_EQ( 1u, scene->mNumMeshes ); EXPECT_NE( nullptr, scene->mMeshes[0] ); EXPECT_EQ( 8u, scene->mMeshes[0]->mNumVertices ); @@ -65,7 +66,7 @@ public: virtual bool exporterTest() { Importer importer; Exporter exporter; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", 0); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "ply", ASSIMP_TEST_MODELS_DIR "/PLY/cube_test.ply")); @@ -89,19 +90,28 @@ TEST_F(utPLYImportExport, exportTest_Success ) { //Test issue 1623, crash when loading two PLY files in a row TEST_F(utPLYImportExport, importerMultipleTest) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", 0); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); - scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", 0); + scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); } TEST_F( utPLYImportExport, vertexColorTest ) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/PLY/float-color.ply", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/PLY/float-color.ply", aiProcess_ValidateDataStructure); EXPECT_NE( nullptr, scene ); + EXPECT_EQ(1u, scene->mMeshes[0]->mNumFaces); + EXPECT_EQ(aiPrimitiveType_TRIANGLE, scene->mMeshes[0]->mPrimitiveTypes); + EXPECT_EQ(true, scene->mMeshes[0]->HasVertexColors(0)); + + auto first_face = scene->mMeshes[0]->mFaces[0]; + EXPECT_EQ(3, first_face.mNumIndices); + EXPECT_EQ(0, first_face.mIndices[0]); + EXPECT_EQ(1, first_face.mIndices[1]); + EXPECT_EQ(2, first_face.mIndices[2]); } static const char *test_file = @@ -125,6 +135,6 @@ static const char *test_file = TEST_F( utPLYImportExport, parseErrorTest ) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFileFromMemory( test_file, strlen( test_file ), 0 ); + const aiScene *scene = importer.ReadFileFromMemory( test_file, strlen( test_file ), aiProcess_ValidateDataStructure); EXPECT_NE( nullptr, scene ); } From cd5881c9c05941f915c551416c2ef4673fb34e49 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Sun, 4 Mar 2018 13:07:40 +0100 Subject: [PATCH 098/183] Add unit-test for PLY with UV coordinates --- test/models/PLY/cube_uv.ply | 45 +++++++++++++++++++++++++++++++++ test/unit/utPLYImportExport.cpp | 12 +++++++++ 2 files changed, 57 insertions(+) create mode 100644 test/models/PLY/cube_uv.ply diff --git a/test/models/PLY/cube_uv.ply b/test/models/PLY/cube_uv.ply new file mode 100644 index 000000000..45ab0607a --- /dev/null +++ b/test/models/PLY/cube_uv.ply @@ -0,0 +1,45 @@ +ply +format ascii 1.0 +comment Created by Blender 2.77 (sub 0) - www.blender.org, source file: '' +element vertex 24 +property float x +property float y +property float z +property float nx +property float ny +property float nz +property float s +property float t +element face 6 +property list uchar uint vertex_indices +end_header +1.000000 1.000000 -1.000000 0.000000 0.000000 -1.000000 0.000000 0.000000 +1.000000 -1.000000 -1.000000 0.000000 0.000000 -1.000000 1.000000 0.000000 +-1.000000 -1.000000 -1.000000 0.000000 0.000000 -1.000000 1.000000 1.000000 +-1.000000 1.000000 -1.000000 0.000000 0.000000 -1.000000 0.000000 1.000000 +1.000000 0.999999 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 +-1.000000 1.000000 1.000000 0.000000 -0.000000 1.000000 1.000000 0.000000 +-1.000000 -1.000000 1.000000 0.000000 -0.000000 1.000000 1.000000 1.000000 +0.999999 -1.000001 1.000000 0.000000 -0.000000 1.000000 0.000000 1.000000 +1.000000 1.000000 -1.000000 1.000000 -0.000000 0.000000 0.000000 0.000000 +1.000000 0.999999 1.000000 1.000000 -0.000000 0.000000 1.000000 0.000000 +0.999999 -1.000001 1.000000 1.000000 -0.000000 0.000000 1.000000 1.000000 +1.000000 -1.000000 -1.000000 1.000000 -0.000000 0.000000 0.000000 1.000000 +1.000000 -1.000000 -1.000000 -0.000000 -1.000000 -0.000000 0.000000 0.000000 +0.999999 -1.000001 1.000000 -0.000000 -1.000000 -0.000000 1.000000 0.000000 +-1.000000 -1.000000 1.000000 -0.000000 -1.000000 -0.000000 1.000000 1.000000 +-1.000000 -1.000000 -1.000000 -0.000000 -1.000000 -0.000000 0.000000 1.000000 +-1.000000 -1.000000 -1.000000 -1.000000 0.000000 -0.000000 0.000000 0.000000 +-1.000000 -1.000000 1.000000 -1.000000 0.000000 -0.000000 1.000000 0.000000 +-1.000000 1.000000 1.000000 -1.000000 0.000000 -0.000000 1.000000 1.000000 +-1.000000 1.000000 -1.000000 -1.000000 0.000000 -0.000000 0.000000 1.000000 +1.000000 0.999999 1.000000 0.000000 1.000000 0.000000 0.000000 0.000000 +1.000000 1.000000 -1.000000 0.000000 1.000000 0.000000 1.000000 0.000000 +-1.000000 1.000000 -1.000000 0.000000 1.000000 0.000000 1.000000 1.000000 +-1.000000 1.000000 1.000000 0.000000 1.000000 0.000000 0.000000 1.000000 +4 0 1 2 3 +4 4 5 6 7 +4 8 9 10 11 +4 12 13 14 15 +4 16 17 18 19 +4 20 21 22 23 diff --git a/test/unit/utPLYImportExport.cpp b/test/unit/utPLYImportExport.cpp index 900f494f8..0bef096e7 100644 --- a/test/unit/utPLYImportExport.cpp +++ b/test/unit/utPLYImportExport.cpp @@ -99,6 +99,18 @@ TEST_F(utPLYImportExport, importerMultipleTest) { EXPECT_NE(nullptr, scene); } +TEST_F(utPLYImportExport, importPLYwithUV) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube_uv.ply", aiProcess_ValidateDataStructure); + + EXPECT_NE(nullptr, scene); + EXPECT_NE(nullptr, scene->mMeshes[0]); + //This test model is using n-gons, so 6 faces instead of 12 tris + EXPECT_EQ(6u, scene->mMeshes[0]->mNumFaces); + EXPECT_EQ(aiPrimitiveType_POLYGON, scene->mMeshes[0]->mPrimitiveTypes); + EXPECT_EQ(true, scene->mMeshes[0]->HasTextureCoords(0)); +} + TEST_F( utPLYImportExport, vertexColorTest ) { Assimp::Importer importer; const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/PLY/float-color.ply", aiProcess_ValidateDataStructure); From bd80e92f788215708b5a0b7f3600c6d69df5c1ce Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Sun, 4 Mar 2018 12:42:21 +0100 Subject: [PATCH 099/183] Add PLY loader unit test for binary files --- test/models/PLY/cube_binary.ply | Bin 0 -> 447 bytes test/unit/utPLYImportExport.cpp | 10 ++++++++++ 2 files changed, 10 insertions(+) create mode 100644 test/models/PLY/cube_binary.ply diff --git a/test/models/PLY/cube_binary.ply b/test/models/PLY/cube_binary.ply new file mode 100644 index 0000000000000000000000000000000000000000..14d29ebd86d5be53fedd1933abe9b1bf95671c59 GIT binary patch literal 447 zcmZvW!EVAZ5JUrMLGoAZ7gSA8+>qdakb34C8D|5Q949iSBAobic8tmMeshes[0]->HasTextureCoords(0)); } +TEST_F(utPLYImportExport, importBinaryPLY) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube_binary.ply", 0); + + EXPECT_NE(nullptr, scene); + EXPECT_NE(nullptr, scene->mMeshes[0]); + //This test model is double sided, so 12 faces instead of 6 + EXPECT_EQ(12u, scene->mMeshes[0]->mNumFaces); +} + TEST_F( utPLYImportExport, vertexColorTest ) { Assimp::Importer importer; const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/PLY/float-color.ply", aiProcess_ValidateDataStructure); From d2547e84f530fa5a243f95068a17f50178f3ad47 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Sun, 4 Mar 2018 21:41:19 +0100 Subject: [PATCH 100/183] Fix for undefined behavior when loading binary PLY This commit fix undefined behavior reported by UBSAN when loading a binary PLY file. --- code/PlyParser.cpp | 68 ++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/code/PlyParser.cpp b/code/PlyParser.cpp index 85c0f823e..672ac9bde 100644 --- a/code/PlyParser.cpp +++ b/code/PlyParser.cpp @@ -1043,71 +1043,91 @@ bool PLY::PropertyInstance::ParseValueBinary(IOStreamBuffer &streamBuffer, switch (eType) { case EDT_UInt: - out->iUInt = (uint32_t)*((uint32_t*)pCur); - pCur += 4; + { + uint32_t t; + memcpy(&t, pCur, sizeof(uint32_t)); + pCur += sizeof(uint32_t); // Swap endianness - if (p_bBE)ByteSwap::Swap((int32_t*)&out->iUInt); + if (p_bBE)ByteSwap::Swap(&t); + out->iUInt = t; break; + } case EDT_UShort: { - uint16_t i = *((uint16_t*)pCur); + uint16_t t; + memcpy(&t, pCur, sizeof(uint16_t)); + pCur += sizeof(uint16_t); // Swap endianness - if (p_bBE)ByteSwap::Swap(&i); - out->iUInt = (uint32_t)i; - pCur += 2; + if (p_bBE)ByteSwap::Swap(&t); + out->iUInt = t; break; } case EDT_UChar: { - out->iUInt = (uint32_t)(*((uint8_t*)pCur)); - pCur++; + uint8_t t; + memcpy(&t, pCur, sizeof(uint8_t)); + pCur += sizeof(uint8_t); + out->iUInt = t; break; } case EDT_Int: - out->iInt = *((int32_t*)pCur); - pCur += 4; + { + int32_t t; + memcpy(&t, pCur, sizeof(int32_t)); + pCur += sizeof(int32_t); // Swap endianness - if (p_bBE)ByteSwap::Swap(&out->iInt); + if (p_bBE)ByteSwap::Swap(&t); + out->iInt = t; break; + } case EDT_Short: { - int16_t i = *((int16_t*)pCur); + int16_t t; + memcpy(&t, pCur, sizeof(int16_t)); + pCur += sizeof(int16_t); // Swap endianness - if (p_bBE)ByteSwap::Swap(&i); - out->iInt = (int32_t)i; - pCur += 2; + if (p_bBE)ByteSwap::Swap(&t); + out->iInt = t; break; } case EDT_Char: - out->iInt = (int32_t)*((int8_t*)pCur); - pCur++; + { + int8_t t; + memcpy(&t, pCur, sizeof(int8_t)); + pCur += sizeof(int8_t); + out->iInt = t; break; + } case EDT_Float: { - out->fFloat = *((float*)pCur); + float t; + memcpy(&t, pCur, sizeof(float)); + pCur += sizeof(float); // Swap endianness - if (p_bBE)ByteSwap::Swap((int32_t*)&out->fFloat); - pCur += 4; + if (p_bBE)ByteSwap::Swap(&t); + out->fFloat = t; break; } case EDT_Double: { - out->fDouble = *((double*)pCur); + double t; + memcpy(&t, pCur, sizeof(double)); + pCur += sizeof(double); // Swap endianness - if (p_bBE)ByteSwap::Swap((int64_t*)&out->fDouble); - pCur += 8; + if (p_bBE)ByteSwap::Swap(&t); + out->fDouble = t; break; } default: From ecb64c5949b534a416726ed5f8e2fbfdc8962ebd Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Sat, 3 Mar 2018 16:44:15 +0100 Subject: [PATCH 101/183] Add unit test for issue 623 --- test/models/PLY/issue623.ply | 36 +++++++++++++++++++++++++++++++++ test/unit/utPLYImportExport.cpp | 12 +++++++++++ 2 files changed, 48 insertions(+) create mode 100644 test/models/PLY/issue623.ply diff --git a/test/models/PLY/issue623.ply b/test/models/PLY/issue623.ply new file mode 100644 index 000000000..af8811752 --- /dev/null +++ b/test/models/PLY/issue623.ply @@ -0,0 +1,36 @@ +ply +format ascii 1.0 +comment Created by Blender 2.77 (sub 0) - www.blender.org, source file: '' +element vertex 24 +property float x +property float y +property float z +property float nx +property float ny +property float nz +property list uchar uint vertex_indices +end_header +7.941797 1.432409 -0.927566 0.000000 0.000000 -1.000000 +7.941797 -2.321273 -0.927566 0.000000 0.000000 -1.000000 +4.188114 -2.321273 -0.927566 0.000000 0.000000 -1.000000 +4.188115 1.432410 -0.927566 0.000000 0.000000 -1.000000 +7.941798 1.432408 2.826117 0.000000 -0.000000 1.000000 +4.188114 1.432409 2.826117 0.000000 -0.000000 1.000000 +4.188113 -2.321273 2.826117 0.000000 -0.000000 1.000000 +7.941795 -2.321275 2.826117 0.000000 -0.000000 1.000000 +7.941797 1.432409 -0.927566 1.000000 -0.000000 0.000000 +7.941798 1.432408 2.826117 1.000000 -0.000000 0.000000 +7.941795 -2.321275 2.826117 1.000000 -0.000000 0.000000 +7.941797 -2.321273 -0.927566 1.000000 -0.000000 0.000000 +7.941797 -2.321273 -0.927566 -0.000000 -1.000000 -0.000000 +7.941795 -2.321275 2.826117 -0.000000 -1.000000 -0.000000 +4.188113 -2.321273 2.826117 -0.000000 -1.000000 -0.000000 +4.188114 -2.321273 -0.927566 -0.000000 -1.000000 -0.000000 +4.188114 -2.321273 -0.927566 -1.000000 0.000000 -0.000000 +4.188113 -2.321273 2.826117 -1.000000 0.000000 -0.000000 +4.188114 1.432409 2.826117 -1.000000 0.000000 -0.000000 +4.188115 1.432410 -0.927566 -1.000000 0.000000 -0.000000 +7.941798 1.432408 2.826117 0.000000 1.000000 0.000000 +7.941797 1.432409 -0.927566 0.000000 1.000000 0.000000 +4.188115 1.432410 -0.927566 0.000000 1.000000 0.000000 +4.188114 1.432409 2.826117 0.000000 1.000000 0.000000 diff --git a/test/unit/utPLYImportExport.cpp b/test/unit/utPLYImportExport.cpp index e5268a400..4e4b4dce9 100644 --- a/test/unit/utPLYImportExport.cpp +++ b/test/unit/utPLYImportExport.cpp @@ -136,6 +136,18 @@ TEST_F( utPLYImportExport, vertexColorTest ) { EXPECT_EQ(2, first_face.mIndices[2]); } +//Test issue #623, PLY importer should not automatically create faces +TEST_F(utPLYImportExport, pointcloudTest) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/issue623.ply", 0); + EXPECT_NE(nullptr, scene); + + EXPECT_EQ(1u, scene->mNumMeshes); + EXPECT_NE(nullptr, scene->mMeshes[0]); + EXPECT_EQ(24u, scene->mMeshes[0]->mNumVertices); + EXPECT_EQ(0u, scene->mMeshes[0]->mNumFaces); +} + static const char *test_file = "ply\n" "format ascii 1.0\n" From f053695176517da997fdb549f65bcc741112d786 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Sat, 3 Mar 2018 16:56:48 +0100 Subject: [PATCH 102/183] Fix issue #623 PLY importer should not create faces When the PLY file contains no faces, we should not create them. --- code/PlyLoader.cpp | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/code/PlyLoader.cpp b/code/PlyLoader.cpp index 5f4e556c1..88be670cc 100644 --- a/code/PlyLoader.cpp +++ b/code/PlyLoader.cpp @@ -244,30 +244,6 @@ void PLYImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSy // if no face list is existing we assume that the vertex // list is containing a list of points bool pointsOnly = mGeneratedMesh->mFaces == NULL ? true : false; - if (pointsOnly) { - if (mGeneratedMesh->mNumVertices < 3) { - if (mGeneratedMesh != NULL) { - delete(mGeneratedMesh); - mGeneratedMesh = nullptr; - } - - streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Not enough " - "vertices to build a proper face list. "); - } - - const unsigned int iNum = (unsigned int)mGeneratedMesh->mNumVertices / 3; - mGeneratedMesh->mNumFaces = iNum; - mGeneratedMesh->mFaces = new aiFace[mGeneratedMesh->mNumFaces]; - - for (unsigned int i = 0; i < iNum; ++i) { - mGeneratedMesh->mFaces[i].mNumIndices = 3; - mGeneratedMesh->mFaces[i].mIndices = new unsigned int[3]; - mGeneratedMesh->mFaces[i].mIndices[0] = (i * 3); - mGeneratedMesh->mFaces[i].mIndices[1] = (i * 3) + 1; - mGeneratedMesh->mFaces[i].mIndices[2] = (i * 3) + 2; - } - } // now load a list of all materials std::vector avMaterials; From 15fa86f100e1d4e4b71593198c408132fc827a97 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Sun, 4 Mar 2018 00:12:53 +0100 Subject: [PATCH 103/183] Set primitive_type to point when PLY is a point cloud --- code/PlyLoader.cpp | 3 +++ test/unit/utPLYImportExport.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/code/PlyLoader.cpp b/code/PlyLoader.cpp index 88be670cc..1bdd6e694 100644 --- a/code/PlyLoader.cpp +++ b/code/PlyLoader.cpp @@ -244,6 +244,9 @@ void PLYImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSy // if no face list is existing we assume that the vertex // list is containing a list of points bool pointsOnly = mGeneratedMesh->mFaces == NULL ? true : false; + if (pointsOnly) { + mGeneratedMesh->mPrimitiveTypes = aiPrimitiveType::aiPrimitiveType_POINT; + } // now load a list of all materials std::vector avMaterials; diff --git a/test/unit/utPLYImportExport.cpp b/test/unit/utPLYImportExport.cpp index 4e4b4dce9..c19571199 100644 --- a/test/unit/utPLYImportExport.cpp +++ b/test/unit/utPLYImportExport.cpp @@ -97,6 +97,8 @@ TEST_F(utPLYImportExport, importerMultipleTest) { scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); + EXPECT_NE(nullptr, scene->mMeshes[0]); + EXPECT_EQ(6u, scene->mMeshes[0]->mNumFaces); } TEST_F(utPLYImportExport, importPLYwithUV) { @@ -145,6 +147,7 @@ TEST_F(utPLYImportExport, pointcloudTest) { EXPECT_EQ(1u, scene->mNumMeshes); EXPECT_NE(nullptr, scene->mMeshes[0]); EXPECT_EQ(24u, scene->mMeshes[0]->mNumVertices); + EXPECT_EQ(aiPrimitiveType::aiPrimitiveType_POINT, scene->mMeshes[0]->mPrimitiveTypes); EXPECT_EQ(0u, scene->mMeshes[0]->mNumFaces); } From e7869c7db3392217e0e2cf9056798bc8f4f00e47 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Sun, 4 Mar 2018 23:10:30 +0100 Subject: [PATCH 104/183] PLY unit test : Fix aiPostProcess validation errors --- test/unit/utPLYImportExport.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/unit/utPLYImportExport.cpp b/test/unit/utPLYImportExport.cpp index c19571199..e0f96195b 100644 --- a/test/unit/utPLYImportExport.cpp +++ b/test/unit/utPLYImportExport.cpp @@ -115,7 +115,7 @@ TEST_F(utPLYImportExport, importPLYwithUV) { TEST_F(utPLYImportExport, importBinaryPLY) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube_binary.ply", 0); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube_binary.ply", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); EXPECT_NE(nullptr, scene->mMeshes[0]); @@ -141,6 +141,7 @@ TEST_F( utPLYImportExport, vertexColorTest ) { //Test issue #623, PLY importer should not automatically create faces TEST_F(utPLYImportExport, pointcloudTest) { Assimp::Importer importer; + //Could not use aiProcess_ValidateDataStructure since it's missing faces. const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/issue623.ply", 0); EXPECT_NE(nullptr, scene); @@ -172,6 +173,7 @@ static const char *test_file = TEST_F( utPLYImportExport, parseErrorTest ) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFileFromMemory( test_file, strlen( test_file ), aiProcess_ValidateDataStructure); + //Could not use aiProcess_ValidateDataStructure since it's missing faces. + const aiScene *scene = importer.ReadFileFromMemory( test_file, strlen( test_file ), 0); EXPECT_NE( nullptr, scene ); } From d82fff757bcb003cb10af9d11f862b8451bebbde Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 5 Mar 2018 15:01:37 +0100 Subject: [PATCH 105/183] Update utObjImportExport.cpp Add missing end of line. --- test/unit/utObjImportExport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index 10207a5bf..8aec9c443 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -366,4 +366,4 @@ TEST_F( utObjImportExport, mtllib_after_g ) { aiString name; ASSERT_EQ(aiReturn_SUCCESS, mat->Get(AI_MATKEY_NAME, name)); EXPECT_STREQ("MyMaterial", name.C_Str()); -} \ No newline at end of file +} From f4c37fa2455f9928348165213fe2a00892fef753 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Sat, 3 Mar 2018 17:03:30 +0100 Subject: [PATCH 106/183] Collada: add importer property that forces the use of collada names. Closes #1375. --- code/ColladaLoader.cpp | 6 ++++++ code/ColladaLoader.h | 1 + include/assimp/config.h.in | 10 ++++++++++ 3 files changed, 17 insertions(+) diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index a3516f9c4..959a905ae 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -131,6 +131,7 @@ void ColladaLoader::SetupProperties(const Importer* pImp) { noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION,0) != 0; + useColladaName = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES,0) != 0; } // ------------------------------------------------------------------------------------------------ @@ -1913,6 +1914,11 @@ const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, c // The name must be unique for proper node-bone association. std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode) { + // If explicitly requested, just use the collada name. + if (useColladaName) { + return pNode->mName; + } + // Now setup the name of the assimp node. The collada name might not be // unique, so we use the collada ID. if (!pNode->mID.empty()) diff --git a/code/ColladaLoader.h b/code/ColladaLoader.h index c63bf2945..d61845b24 100644 --- a/code/ColladaLoader.h +++ b/code/ColladaLoader.h @@ -248,6 +248,7 @@ protected: bool noSkeletonMesh; bool ignoreUpDirection; + bool useColladaName; /** Used by FindNameForNode() to generate unique node names */ unsigned int mNodeNameCounter; diff --git a/include/assimp/config.h.in b/include/assimp/config.h.in index 071d58cd2..c9555fb38 100644 --- a/include/assimp/config.h.in +++ b/include/assimp/config.h.in @@ -934,6 +934,16 @@ enum aiComponent */ #define AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION "IMPORT_COLLADA_IGNORE_UP_DIRECTION" +// --------------------------------------------------------------------------- +/** @brief Specifies whether the Collada loader should use Collada names as node names. + * + * If this property is set to true, the Collada names will be used as the + * node name. The default is to use the id tag (resp. sid tag, if no id tag is present) + * instead. + * Property type: Bool. Default value: false. + */ +#define AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES "IMPORT_COLLADA_USE_COLLADA_NAMES" + // ---------- All the Export defines ------------ /** @brief Specifies the xfile use double for real values of float From 05cf8bfb2ef1289b81a06907d540e78d74c5ee48 Mon Sep 17 00:00:00 2001 From: JeffH-BMG <37119778+JeffH-BMG@users.noreply.github.com> Date: Tue, 6 Mar 2018 13:48:11 -0500 Subject: [PATCH 107/183] Fix import of binary STL files in double-precision builds When ASSIMP_DOUBLE_PRECISION is used, the STL loader attempts to read 8-byte double vertex and normal values from the STL file. STL files are written using 4-byte floats, however, and the import will read past the end of the buffer, and possibly crash. --- code/STLLoader.cpp | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index fc326b2c7..863f7affd 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -449,26 +449,29 @@ bool STLImporter::LoadBinaryFile() aiVector3D *vp = pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; aiVector3D *vn = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + typedef aiVector3t aiVector3F; + aiVector3F* theVec; + for ( unsigned int i = 0; i < pMesh->mNumFaces; ++i ) { // NOTE: Blender sometimes writes empty normals ... this is not // our fault ... the RemoveInvalidData helper step should fix that - ::memcpy( vn, sz, sizeof( aiVector3D ) ); - sz += sizeof(aiVector3D); - *(vn+1) = *vn; - *(vn+2) = *vn; - vn += 3; - ::memcpy( vp, sz, sizeof( aiVector3D ) ); - ++vp; - sz += sizeof(aiVector3D); + // There's one normal for the face in the STL; use it three times + // for vertex normals + theVec = (aiVector3F*) sz; + *vn++ = *theVec; + *vn++ = *theVec; + *vn++ = *theVec++; - ::memcpy( vp, sz, sizeof( aiVector3D ) ); - ++vp; - sz += sizeof(aiVector3D); + // vertex 1 + *vp++ = *theVec++; - ::memcpy( vp, sz, sizeof( aiVector3D ) ); - ++vp; - sz += sizeof(aiVector3D); + // vertex 2 + *vp++ = *theVec++; + + // vertex 3 + *vp++ = *theVec; + sz += 4 * sizeof aiVector3F; uint16_t color = *((uint16_t*)sz); sz += 2; From 0dab5c508e15d72ac9502a7e9f155ab880fa58e2 Mon Sep 17 00:00:00 2001 From: JeffH-BMG <37119778+JeffH-BMG@users.noreply.github.com> Date: Tue, 6 Mar 2018 13:55:32 -0500 Subject: [PATCH 108/183] STL binary Export should write 4-byte floats for vertex and normal coordinates The STL binary format uses 4-byte floats. When using double-precision builds of Asset Importer, the STL exporter was writing out 8-byte double values instead.. --- code/STLExporter.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/code/STLExporter.cpp b/code/STLExporter.cpp index 4c7a8a639..3d681f8c7 100644 --- a/code/STLExporter.cpp +++ b/code/STLExporter.cpp @@ -172,12 +172,16 @@ void STLExporter :: WriteMeshBinary(const aiMesh* m) } nor.Normalize(); } - ai_real nx = nor.x, ny = nor.y, nz = nor.z; + // STL binary files use 4-byte floats. This may possibly cause loss of precision + // for clients using 8-byte doubles + float nx = (float) nor.x; + float ny = (float) nor.y; + float nz = (float) nor.z; AI_SWAP4(nx); AI_SWAP4(ny); AI_SWAP4(nz); mOutput.write((char *)&nx, 4); mOutput.write((char *)&ny, 4); mOutput.write((char *)&nz, 4); for(unsigned int a = 0; a < f.mNumIndices; ++a) { const aiVector3D& v = m->mVertices[f.mIndices[a]]; - ai_real vx = v.x, vy = v.y, vz = v.z; + float vx = (float) v.x, vy = (float) v.y, vz = (float) v.z; AI_SWAP4(vx); AI_SWAP4(vy); AI_SWAP4(vz); mOutput.write((char *)&vx, 4); mOutput.write((char *)&vy, 4); mOutput.write((char *)&vz, 4); } From 5ea06e1bb69e4b61c3f05160c9c38ed1187a5102 Mon Sep 17 00:00:00 2001 From: JeffH-BMG <37119778+JeffH-BMG@users.noreply.github.com> Date: Tue, 6 Mar 2018 14:50:02 -0500 Subject: [PATCH 109/183] Add support for texture file in PLY exports The PLY format has an unofficial way to specify an associated texture, using the "comment TextureFile" comment line. The PLY loader supports this, but the exporter does not. The change looks for a diffuse texture in the scene's materials, and if it finds one, it adds it to the exported mesh using "comment TextureFile". --- code/PlyExporter.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/code/PlyExporter.cpp b/code/PlyExporter.cpp index 0ddba0d2a..2d528c96c 100644 --- a/code/PlyExporter.cpp +++ b/code/PlyExporter.cpp @@ -148,6 +148,17 @@ PlyExporter::PlyExporter(const char* _filename, const aiScene* pScene, bool bina << aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.' << aiGetVersionRevision() << ")" << endl; + // Look through materials for a diffuse texture, and add it if found + for ( unsigned int i = 0; i < pScene->mNumMaterials; ++i ) + { + const aiMaterial* const mat = pScene->mMaterials[i]; + aiString s; + if ( AI_SUCCESS == mat->Get( AI_MATKEY_TEXTURE_DIFFUSE( 0 ), s ) ) + { + mOutput << "comment TextureFile " << s.data << endl; + } + } + // TODO: probably want to check here rather than just assume something // definitely not good to always write float even if we might have double precision From 6fd64b95c32699649e6e392fb05bbf0af01cd685 Mon Sep 17 00:00:00 2001 From: JeffH-BMG <37119778+JeffH-BMG@users.noreply.github.com> Date: Tue, 6 Mar 2018 15:03:44 -0500 Subject: [PATCH 110/183] Fix compile error Add parens to use of 'sizeof' operator --- code/STLLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 863f7affd..115e5f816 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -471,7 +471,7 @@ bool STLImporter::LoadBinaryFile() // vertex 3 *vp++ = *theVec; - sz += 4 * sizeof aiVector3F; + sz += 4 * sizeof( aiVector3F ); uint16_t color = *((uint16_t*)sz); sz += 2; From 9f02c8a97c530e4d5d3ccf70688fd0f84e9c1923 Mon Sep 17 00:00:00 2001 From: Tommy Date: Thu, 1 Mar 2018 18:03:04 +0100 Subject: [PATCH 111/183] Fix default opacity of materials exported to FBX by Blender. --- code/FBXMaterial.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/code/FBXMaterial.cpp b/code/FBXMaterial.cpp index 10f3bbe6c..8bb3920de 100644 --- a/code/FBXMaterial.cpp +++ b/code/FBXMaterial.cpp @@ -54,6 +54,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FBXProperties.h" #include +#include // std::transform + namespace Assimp { namespace FBX { @@ -82,11 +84,12 @@ Material::Material(uint64_t id, const Element& element, const Document& doc, con std::string templateName; - const char* const sh = shading.c_str(); - if(!strcmp(sh,"phong")) { + // lower-case shading because Blender (for example) writes "Phong" + std::transform(shading.begin(), shading.end(), shading.begin(), ::tolower); + if(shading == "phong") { templateName = "Material.FbxSurfacePhong"; } - else if(!strcmp(sh,"lambert")) { + else if(shading == "lambert") { templateName = "Material.FbxSurfaceLambert"; } else { From 89a4cf94957711117e7c64d483bb1fe42d05f7e9 Mon Sep 17 00:00:00 2001 From: JeffH-BMG <37119778+JeffH-BMG@users.noreply.github.com> Date: Wed, 7 Mar 2018 17:26:01 -0500 Subject: [PATCH 112/183] Respond to comments Use memcpy() to read normals and vertices, to mitigate alignment issues, per comments. --- code/STLLoader.cpp | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 115e5f816..e01162a30 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -451,6 +451,7 @@ bool STLImporter::LoadBinaryFile() typedef aiVector3t aiVector3F; aiVector3F* theVec; + aiVector3F theVec3F; for ( unsigned int i = 0; i < pMesh->mNumFaces; ++i ) { // NOTE: Blender sometimes writes empty normals ... this is not @@ -459,19 +460,32 @@ bool STLImporter::LoadBinaryFile() // There's one normal for the face in the STL; use it three times // for vertex normals theVec = (aiVector3F*) sz; - *vn++ = *theVec; - *vn++ = *theVec; - *vn++ = *theVec++; + ::memcpy( &theVec3F, theVec, sizeof(aiVector3F) ); + vn->x = theVec3F.x; vn->y = theVec3F.y; vn->z = theVec3F.z; + *(vn+1) = *vn; + *(vn+2) = *vn; + ++theVec; + vn += 3; // vertex 1 - *vp++ = *theVec++; + ::memcpy( &theVec3F, theVec, sizeof(aiVector3F) ); + vp->x = theVec3F.x; vp->y = theVec3F.y; vp->z = theVec3F.z; + ++theVec; + ++vp; // vertex 2 - *vp++ = *theVec++; + ::memcpy( &theVec3F, theVec, sizeof(aiVector3F) ); + vp->x = theVec3F.x; vp->y = theVec3F.y; vp->z = theVec3F.z; + ++theVec; + ++vp; // vertex 3 - *vp++ = *theVec; - sz += 4 * sizeof( aiVector3F ); + ::memcpy( &theVec3F, theVec, sizeof(aiVector3F) ); + vp->x = theVec3F.x; vp->y = theVec3F.y; vp->z = theVec3F.z; + ++theVec; + ++vp; + + sz = theVec; uint16_t color = *((uint16_t*)sz); sz += 2; From cfd56a43a578f77a1c63a95ed28084a3ae3c4e6e Mon Sep 17 00:00:00 2001 From: JeffH-BMG <37119778+JeffH-BMG@users.noreply.github.com> Date: Wed, 7 Mar 2018 18:24:38 -0500 Subject: [PATCH 113/183] Fix compile error Fixed bad cast. --- code/STLLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index e01162a30..3a85a5495 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -485,7 +485,7 @@ bool STLImporter::LoadBinaryFile() ++theVec; ++vp; - sz = theVec; + sz = (const unsigned char*) theVec; uint16_t color = *((uint16_t*)sz); sz += 2; From 69742670dd55b256923bed0feb52d45146af97c8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 9 Mar 2018 11:40:45 +0100 Subject: [PATCH 114/183] 3mf: use correct material assignment in case of multi-materials. --- code/3MFXmlTags.h | 1 + code/BaseImporter.cpp | 16 ++++----- code/D3MFExporter.cpp | 7 +++- code/D3MFExporter.h | 1 + code/D3MFImporter.cpp | 78 +++++++++++++++++++++++++++++++---------- code/D3MFOpcPackage.cpp | 59 ++++++++++++++++--------------- code/D3MFOpcPackage.h | 9 ++--- 7 files changed, 110 insertions(+), 61 deletions(-) diff --git a/code/3MFXmlTags.h b/code/3MFXmlTags.h index c4da2970d..feedf4538 100644 --- a/code/3MFXmlTags.h +++ b/code/3MFXmlTags.h @@ -74,6 +74,7 @@ namespace XmlTag { // Material definitions static const std::string basematerials = "basematerials"; + static const std::string basematerials_id = "id"; static const std::string basematerials_base = "base"; static const std::string basematerials_name = "name"; static const std::string basematerials_displaycolor = "displaycolor"; diff --git a/code/BaseImporter.cpp b/code/BaseImporter.cpp index e7736d8a4..67c743285 100644 --- a/code/BaseImporter.cpp +++ b/code/BaseImporter.cpp @@ -115,13 +115,12 @@ void BaseImporter::SetupProperties(const Importer* /*pImp*/) } // ------------------------------------------------------------------------------------------------ -void BaseImporter::GetExtensionList(std::set& extensions) -{ +void BaseImporter::GetExtensionList(std::set& extensions) { const aiImporterDesc* desc = GetInfo(); - ai_assert(desc != NULL); + ai_assert(desc != nullptr); const char* ext = desc->mFileExtensions; - ai_assert(ext != NULL); + ai_assert(ext != nullptr ); const char* last = ext; do { @@ -145,12 +144,13 @@ void BaseImporter::GetExtensionList(std::set& extensions) unsigned int searchBytes /* = 200 */, bool tokensSol /* false */) { - ai_assert( NULL != tokens ); + ai_assert( nullptr != tokens ); ai_assert( 0 != numTokens ); ai_assert( 0 != searchBytes); - if (!pIOHandler) + if ( nullptr == pIOHandler ) { return false; + } std::unique_ptr pStream (pIOHandler->Open(pFile)); if (pStream.get() ) { @@ -179,9 +179,9 @@ void BaseImporter::GetExtensionList(std::set& extensions) *cur2 = '\0'; std::string token; - for (unsigned int i = 0; i < numTokens;++i) { + for (unsigned int i = 0; i < numTokens; ++i ) { ai_assert( nullptr != tokens[i] ); - size_t len( strlen( tokens[ i ] ) ); + const size_t len( strlen( tokens[ i ] ) ); token.clear(); const char *ptr( tokens[ i ] ); for ( size_t tokIdx = 0; tokIdx < len; ++tokIdx ) { diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 91f06fc87..a74280af7 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -126,7 +126,6 @@ bool D3MFExporter::exportArchive( const char *file ) { return ok; } - bool D3MFExporter::exportContentTypes() { mContentOutput.clear(); @@ -177,6 +176,8 @@ bool D3MFExporter::export3DModel() { mModelOutput << "<" << XmlTag::resources << ">"; mModelOutput << std::endl; + writeBaseMaterials(); + writeObjects(); @@ -203,6 +204,10 @@ void D3MFExporter::writeHeader() { mModelOutput << std::endl; } +void D3MFExporter::writeBaseMaterials() { + +} + void D3MFExporter::writeObjects() { if ( nullptr == mScene->mRootNode ) { return; diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index 64967f68b..7e6e44e6e 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -76,6 +76,7 @@ public: protected: void writeHeader(); + void writeBaseMaterials(); void writeObjects(); void writeMesh( aiMesh *mesh ); void writeVertex( const aiVector3D &pos ); diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index fe5e260a4..7c0954524 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -70,9 +70,14 @@ namespace D3MF { class XmlSerializer { public: + using MatArray = std::vector; + using MatId2MatArray = std::map>; + XmlSerializer(XmlReader* xmlReader) : mMeshes() - , mMaterials() + , mMatArray() + , mActiveMatGroup( 99999999 ) + , mMatId2MatArray() , xmlReader(xmlReader){ // empty } @@ -109,10 +114,10 @@ public: std::copy( mMeshes.begin(), mMeshes.end(), scene->mMeshes); - scene->mNumMaterials = mMaterials.size(); + scene->mNumMaterials = mMatArray.size(); if ( 0 != scene->mNumMaterials ) { scene->mMaterials = new aiMaterial*[ scene->mNumMaterials ]; - std::copy( mMaterials.begin(), mMaterials.end(), scene->mMaterials ); + std::copy( mMatArray.begin(), mMatArray.end(), scene->mMaterials ); } scene->mRootNode->mNumChildren = static_cast(children.size()); scene->mRootNode->mChildren = new aiNode*[scene->mRootNode->mNumChildren](); @@ -162,7 +167,7 @@ private: return node.release(); } - aiMesh* ReadMesh() { + aiMesh *ReadMesh() { aiMesh* mesh = new aiMesh(); while(ReadToEndElement(D3MF::XmlTag::mesh)) { if(xmlReader->getNodeName() == D3MF::XmlTag::vertices) { @@ -177,7 +182,6 @@ private: void ImportVertices(aiMesh* mesh) { std::vector vertices; - while(ReadToEndElement(D3MF::XmlTag::vertices)) { if(xmlReader->getNodeName() == D3MF::XmlTag::vertex) { vertices.push_back(ReadVertex()); @@ -234,8 +238,27 @@ private: } void ReadBaseMaterials() { + std::vector MatIdArray; + const char *baseMaterialId( xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_id.c_str() ) ); + if ( nullptr != baseMaterialId ) { + unsigned int id = std::atoi( baseMaterialId ); + const size_t newMatIdx( mMatArray.size() ); + if ( id != mActiveMatGroup ) { + mActiveMatGroup = id; + MatId2MatArray::const_iterator it( mMatId2MatArray.find( id ) ); + if ( mMatId2MatArray.end() == it ) { + MatIdArray.clear(); + mMatId2MatArray[ id ] = MatIdArray; + } else { + MatIdArray = it->second; + } + } + MatIdArray.push_back( newMatIdx ); + mMatId2MatArray[ mActiveMatGroup ] = MatIdArray; + } + while ( ReadToEndElement( D3MF::XmlTag::basematerials ) ) { - mMaterials.push_back( readMaterialDef() ); + mMatArray.push_back( readMaterialDef() ); xmlReader->read(); } } @@ -285,24 +308,37 @@ private: return true; } + void assignDiffuseColor( aiMaterial *mat ) { + const char *color = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_displaycolor.c_str() ); + aiColor4D diffuse; + if ( parseColor( color, diffuse ) ) { + mat->AddProperty( &diffuse, 1, AI_MATKEY_COLOR_DIFFUSE ); + } + + } aiMaterial *readMaterialDef() { aiMaterial *mat( nullptr ); const char *name( nullptr ); - const char *color( nullptr ); const std::string nodeName( xmlReader->getNodeName() ); if ( nodeName == D3MF::XmlTag::basematerials_base ) { name = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_name.c_str() ); - + std::string stdMatName; aiString matName; - matName.Set( name ); + std::string strId( to_string( mActiveMatGroup ) ); + stdMatName += "id"; + stdMatName += strId; + stdMatName += "_"; + if ( nullptr != name ) { + stdMatName += std::string( name ); + } else { + stdMatName += "basemat"; + } + matName.Set( stdMatName ); + mat = new aiMaterial; mat->AddProperty( &matName, AI_MATKEY_NAME ); - color = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_displaycolor.c_str() ); - aiColor4D diffuse; - if ( parseColor( color, diffuse ) ) { - mat->AddProperty( &diffuse, 1, AI_MATKEY_COLOR_DIFFUSE ); - } + assignDiffuseColor( mat ); } return mat; @@ -339,7 +375,9 @@ private: private: std::vector mMeshes; - std::vector mMaterials; + MatArray mMatArray; + unsigned int mActiveMatGroup; + MatId2MatArray mMatId2MatArray; XmlReader* xmlReader; }; @@ -370,14 +408,16 @@ D3MFImporter::~D3MFImporter() { // empty } -bool D3MFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(pFile); +bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const { + const std::string extension( GetExtension( filename ) ); if(extension == Extension ) { return true; } else if ( !extension.length() || checkSig ) { - if (nullptr == pIOHandler ) { - return true; + if ( nullptr == pIOHandler ) { + return false; } + D3MF::D3MFOpcPackage opcPackage( pIOHandler, filename ); + return opcPackage.validate(); } return false; diff --git a/code/D3MFOpcPackage.cpp b/code/D3MFOpcPackage.cpp index a7039d34e..81d610e15 100644 --- a/code/D3MFOpcPackage.cpp +++ b/code/D3MFOpcPackage.cpp @@ -247,13 +247,13 @@ private: // ------------------------------------------------------------------------------------------------ // Constructor. D3MFZipArchive::D3MFZipArchive(IOSystem* pIOHandler, const std::string& rFile) -: m_ZipFileHandle(NULL) +: m_ZipFileHandle( nullptr ) , m_ArchiveMap() { if (! rFile.empty()) { zlib_filefunc_def mapping = IOSystem2Unzip::get(pIOHandler); m_ZipFileHandle = unzOpen2(rFile.c_str(), &mapping); - if(m_ZipFileHandle != NULL) { + if(m_ZipFileHandle != nullptr ) { mapArchive(); } } @@ -267,32 +267,32 @@ D3MFZipArchive::~D3MFZipArchive() { } m_ArchiveMap.clear(); - if(m_ZipFileHandle != NULL) { + if(m_ZipFileHandle != nullptr) { unzClose(m_ZipFileHandle); - m_ZipFileHandle = NULL; + m_ZipFileHandle = nullptr; } } // ------------------------------------------------------------------------------------------------ // Returns true, if the archive is already open. bool D3MFZipArchive::isOpen() const { - return (m_ZipFileHandle != NULL); + return (m_ZipFileHandle != nullptr ); } // ------------------------------------------------------------------------------------------------ // Returns true, if the filename is part of the archive. bool D3MFZipArchive::Exists(const char* pFile) const { - ai_assert(pFile != NULL); + ai_assert(pFile != nullptr ); - bool exist = false; + if ( pFile == nullptr ) { + return false; + } - if (pFile != NULL) { - std::string rFile(pFile); - std::map::const_iterator it = m_ArchiveMap.find(rFile); - - if(it != m_ArchiveMap.end()) { - exist = true; - } + std::string rFile(pFile); + std::map::const_iterator it = m_ArchiveMap.find(rFile); + bool exist( false ); + if(it != m_ArchiveMap.end()) { + exist = true; } return exist; @@ -434,8 +434,8 @@ public: std::vector m_relationShips; }; -// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile) : mRootStream(nullptr) , mZipArchive() { @@ -460,7 +460,7 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile) if ( rootFile.size() > 0 && rootFile[ 0 ] == '/' ) { rootFile = rootFile.substr( 1 ); if ( rootFile[ 0 ] == '/' ) { - // deal with zipbug + // deal with zip-bug rootFile = rootFile.substr( 1 ); } } @@ -470,18 +470,9 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile) mRootStream = mZipArchive->Open(rootFile.c_str()); ai_assert( mRootStream != nullptr ); if ( nullptr == mRootStream ) { - throw DeadlyExportError( "Cannot open rootfile in archive : " + rootFile ); + throw DeadlyExportError( "Cannot open root-file in archive : " + rootFile ); } - // const size_t size = zipArchive->FileSize(); - // m_Data.resize( size ); - - // const size_t readSize = pMapFile->Read( &m_Data[0], sizeof( char ), size ); - // if ( readSize != size ) - // { - // m_Data.clear(); - // return false; - // } mZipArchive->Close( fileStream ); } else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) { @@ -498,6 +489,16 @@ IOStream* D3MFOpcPackage::RootStream() const { return mRootStream; } +static const std::string ModelRef = "3D/3dmodel.model"; + +bool D3MFOpcPackage::validate() { + if ( nullptr == mRootStream || nullptr == mZipArchive ) { + return false; + } + + return mZipArchive->Exists( ModelRef.c_str() ); +} + std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream) { std::unique_ptr xmlStream(new CIrrXML_IOStreamReader(stream)); std::unique_ptr xml(irr::io::createIrrXMLReader(xmlStream.get())); @@ -508,14 +509,14 @@ std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream) { return rel->type == XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE; }); - if(itr == reader.m_relationShips.end()) - throw DeadlyImportError("Cannot find " + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE); + if ( itr == reader.m_relationShips.end() ) { + throw DeadlyImportError( "Cannot find " + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE ); + } return (*itr)->target; } } // Namespace D3MF - } // Namespace Assimp #endif //ASSIMP_BUILD_NO_3MF_IMPORTER diff --git a/code/D3MFOpcPackage.h b/code/D3MFOpcPackage.h index 8cf08d092..b49740eff 100644 --- a/code/D3MFOpcPackage.h +++ b/code/D3MFOpcPackage.h @@ -51,8 +51,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace D3MF { -typedef irr::io::IrrXMLReader XmlReader; -typedef std::shared_ptr XmlReaderPtr; +using XmlReader = irr::io::IrrXMLReader ; +using XmlReaderPtr = std::shared_ptr ; struct OpcPackageRelationship { std::string id; @@ -67,6 +67,7 @@ public: D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile); ~D3MFOpcPackage(); IOStream* RootStream() const; + bool validate(); protected: std::string ReadPackageRootRelationship(IOStream* stream); @@ -76,7 +77,7 @@ private: std::unique_ptr mZipArchive; }; -} -} +} // Namespace D3MF +} // Namespace Assimp #endif // D3MFOPCPACKAGE_H From 6668eeb68eaeeff2b0686d07d7ec6e1cdf88c882 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 9 Mar 2018 19:03:05 +0100 Subject: [PATCH 115/183] Fix possible nullptr dereferencing. --- code/D3MFExporter.cpp | 42 ++++++++++++++++++++++++++++++++++++-- include/assimp/fast_atof.h | 2 -- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index a74280af7..98b15b10b 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -49,8 +49,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include - +#include #include + #include "3MFXmlTags.h" #include "D3MFOpcPackage.h" @@ -204,8 +205,45 @@ void D3MFExporter::writeHeader() { mModelOutput << std::endl; } -void D3MFExporter::writeBaseMaterials() { +static std::string to_hex( int to_convert ) { + std::string result; + std::stringstream ss; + ss << std::hex << to_convert; + ss >> result; + return result; +} +void D3MFExporter::writeBaseMaterials() { + mModelOutput << "\n"; + for ( size_t i = 0; i < mScene->mNumMaterials; ++i ) { + aiMaterial *mat = mScene->mMaterials[ i ]; + std::string strName; + aiString name; + if ( mat->Get( AI_MATKEY_NAME, name ) != aiReturn_SUCCESS ) { + strName = "basemat_" + to_string( i ); + } else { + strName = name.C_Str(); + } + std::string hexDiffuseColor; + aiColor4D color; + if ( mat->Get( AI_MATKEY_COLOR_DIFFUSE, color ) == aiReturn_SUCCESS ) { + hexDiffuseColor = "#"; + std::string tmp; + tmp = to_hex( color.r ); + hexDiffuseColor += tmp; + tmp = to_hex( color.g ); + hexDiffuseColor += tmp; + tmp = to_hex( color.b ); + hexDiffuseColor += tmp; + tmp = to_hex( color.a ); + hexDiffuseColor += tmp; + } else { + hexDiffuseColor = "#FFFFFFFF"; + } + + mModelOutput << "\n"; + } + mModelOutput << "\n"; } void D3MFExporter::writeObjects() { diff --git a/include/assimp/fast_atof.h b/include/assimp/fast_atof.h index fced5307a..e66f1b37d 100644 --- a/include/assimp/fast_atof.h +++ b/include/assimp/fast_atof.h @@ -146,7 +146,6 @@ uint8_t HexOctetToDecimal(const char* in) { return ((uint8_t)HexDigitToDecimal(in[0])<<4)+(uint8_t)HexDigitToDecimal(in[1]); } - // ------------------------------------------------------------------------------------ // signed variant of strtoul10 // ------------------------------------------------------------------------------------ @@ -353,7 +352,6 @@ ai_real fast_atof(const char* c) { return ret; } - inline ai_real fast_atof( const char* c, const char** cout) { ai_real ret(0.0); From 336a09ee0e47f5520eb3032d7b377982176fbe6d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 9 Mar 2018 23:35:12 +0100 Subject: [PATCH 116/183] add material reference to faces. --- code/ConvertToLHProcess.cpp | 26 +++++++++++++++++--------- code/D3MFExporter.cpp | 9 ++++++--- code/D3MFExporter.h | 2 +- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/code/ConvertToLHProcess.cpp b/code/ConvertToLHProcess.cpp index 47e2fb949..ba8371439 100644 --- a/code/ConvertToLHProcess.cpp +++ b/code/ConvertToLHProcess.cpp @@ -91,12 +91,14 @@ void MakeLeftHandedProcess::Execute( aiScene* pScene) ProcessNode( pScene->mRootNode, aiMatrix4x4()); // process the meshes accordingly - for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) - ProcessMesh( pScene->mMeshes[a]); + for ( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) { + ProcessMesh( pScene->mMeshes[ a ] ); + } // process the materials accordingly - for( unsigned int a = 0; a < pScene->mNumMaterials; ++a) - ProcessMaterial( pScene->mMaterials[a]); + for ( unsigned int a = 0; a < pScene->mNumMaterials; ++a ) { + ProcessMaterial( pScene->mMaterials[ a ] ); + } // transform all animation channels as well for( unsigned int a = 0; a < pScene->mNumAnimations; a++) @@ -136,8 +138,11 @@ void MakeLeftHandedProcess::ProcessNode( aiNode* pNode, const aiMatrix4x4& pPare // ------------------------------------------------------------------------------------------------ // Converts a single mesh to left handed coordinates. -void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) -{ +void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) { + if ( nullptr == pMesh ) { + DefaultLogger::get()->error( "Nullptr to mesh found." ); + return; + } // mirror positions, normals and stuff along the Z axis for( size_t a = 0; a < pMesh->mNumVertices; ++a) { @@ -173,8 +178,12 @@ void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) // ------------------------------------------------------------------------------------------------ // Converts a single material to left handed coordinates. -void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) -{ +void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) { + if ( nullptr == _mat ) { + DefaultLogger::get()->error( "Nullptr to aiMaterial found." ); + return; + } + aiMaterial* mat = (aiMaterial*)_mat; for (unsigned int a = 0; a < mat->mNumProperties;++a) { aiMaterialProperty* prop = mat->mProperties[a]; @@ -183,7 +192,6 @@ void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) if (!::strcmp( prop->mKey.data, "$tex.mapaxis")) { ai_assert( prop->mDataLength >= sizeof(aiVector3D)); /* something is wrong with the validation if we end up here */ aiVector3D* pff = (aiVector3D*)prop->mData; - pff->z *= -1.f; } } diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 98b15b10b..7c75178e6 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -285,7 +285,9 @@ void D3MFExporter::writeMesh( aiMesh *mesh ) { } mModelOutput << "" << std::endl; - writeFaces( mesh ); + const unsigned int matIdx( mesh->mMaterialIndex ); + + writeFaces( mesh, matIdx ); mModelOutput << "" << std::endl; } @@ -295,7 +297,7 @@ void D3MFExporter::writeVertex( const aiVector3D &pos ) { mModelOutput << std::endl; } -void D3MFExporter::writeFaces( aiMesh *mesh ) { +void D3MFExporter::writeFaces( aiMesh *mesh, unsigned int matIdx ) { if ( nullptr == mesh ) { return; } @@ -307,7 +309,8 @@ void D3MFExporter::writeFaces( aiMesh *mesh ) { for ( unsigned int i = 0; i < mesh->mNumFaces; ++i ) { aiFace ¤tFace = mesh->mFaces[ i ]; mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[ 0 ] << "\" v2=\"" - << currentFace.mIndices[ 1 ] << "\" v3=\"" << currentFace.mIndices[ 2 ] << "\"/>"; + << currentFace.mIndices[ 1 ] << "\" v3=\"" << currentFace.mIndices[ 2 ] + << "\" pid=\"1\" p1=\""+to_string(matIdx)+"\" />"; mModelOutput << std::endl; } mModelOutput << ""; diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index 7e6e44e6e..b553e0ab5 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -80,7 +80,7 @@ protected: void writeObjects(); void writeMesh( aiMesh *mesh ); void writeVertex( const aiVector3D &pos ); - void writeFaces( aiMesh *mesh ); + void writeFaces( aiMesh *mesh, unsigned int matIdx ); void writeBuild(); void exportContentTyp( const std::string &filename ); void writeModelToArchive( const std::string &folder, const std::string &modelName ); From 017b7d1a2f51a4f62047c98f987704fc9fabebe4 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 11 Mar 2018 20:15:25 +0100 Subject: [PATCH 117/183] 3MF: add missig tags for meta data. --- code/3MFXmlTags.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/3MFXmlTags.h b/code/3MFXmlTags.h index feedf4538..e869c33c1 100644 --- a/code/3MFXmlTags.h +++ b/code/3MFXmlTags.h @@ -45,6 +45,10 @@ namespace Assimp { namespace D3MF { namespace XmlTag { + // Meta-data + static const std::string meta = "metadata"; + static const std::string meta_name = "name"; + // Model-data specific tags static const std::string model = "model"; static const std::string model_unit = "unit"; From c8ae0bbb3d43384fde27c28c93343b0f885dc5e8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 11 Mar 2018 20:15:49 +0100 Subject: [PATCH 118/183] 3MF: fix model folder desc. --- code/D3MFExporter.cpp | 25 +++++++++++-------------- code/D3MFImporter.cpp | 6 ++---- include/assimp/StringUtils.h | 36 +++++++++++++++++++++++++++++++++--- 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 7c75178e6..b89e81ce6 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -153,7 +153,11 @@ bool D3MFExporter::exportRelations() { mRelOutput << ""; for ( size_t i = 0; i < mRelations.size(); ++i ) { - mRelOutput << "target << "\" "; + if ( mRelations[ i ]->target[ 0 ] == '/' ) { + mRelOutput << "target << "\" "; + } else { + mRelOutput << "target << "\" "; + } mRelOutput << "Id=\"" << mRelations[i]->id << "\" "; mRelOutput << "Type=\"" << mRelations[ i ]->type << "\" />"; mRelOutput << std::endl; @@ -205,14 +209,6 @@ void D3MFExporter::writeHeader() { mModelOutput << std::endl; } -static std::string to_hex( int to_convert ) { - std::string result; - std::stringstream ss; - ss << std::hex << to_convert; - ss >> result; - return result; -} - void D3MFExporter::writeBaseMaterials() { mModelOutput << "\n"; for ( size_t i = 0; i < mScene->mNumMaterials; ++i ) { @@ -229,19 +225,20 @@ void D3MFExporter::writeBaseMaterials() { if ( mat->Get( AI_MATKEY_COLOR_DIFFUSE, color ) == aiReturn_SUCCESS ) { hexDiffuseColor = "#"; std::string tmp; - tmp = to_hex( color.r ); + + tmp = DecimalToHexa( color.r ); hexDiffuseColor += tmp; - tmp = to_hex( color.g ); + tmp = DecimalToHexa( color.g ); hexDiffuseColor += tmp; - tmp = to_hex( color.b ); + tmp = DecimalToHexa( color.b ); hexDiffuseColor += tmp; - tmp = to_hex( color.a ); + tmp = DecimalToHexa( color.a ); hexDiffuseColor += tmp; } else { hexDiffuseColor = "#FFFFFFFF"; } - mModelOutput << "\n"; + mModelOutput << "\n"; } mModelOutput << "\n"; } diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 7c0954524..1732a5a8a 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -259,7 +259,6 @@ private: while ( ReadToEndElement( D3MF::XmlTag::basematerials ) ) { mMatArray.push_back( readMaterialDef() ); - xmlReader->read(); } } @@ -398,7 +397,6 @@ static const aiImporterDesc desc = { Extension.c_str() }; - D3MFImporter::D3MFImporter() : BaseImporter() { // empty @@ -431,8 +429,8 @@ const aiImporterDesc *D3MFImporter::GetInfo() const { return &desc; } -void D3MFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { - D3MF::D3MFOpcPackage opcPackage(pIOHandler, pFile); +void D3MFImporter::InternReadFile( const std::string &filename, aiScene *pScene, IOSystem *pIOHandler ) { + D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename); std::unique_ptr xmlStream(new CIrrXML_IOStreamReader(opcPackage.RootStream())); std::unique_ptr xmlReader(irr::io::createIrrXMLReader(xmlStream.get())); diff --git a/include/assimp/StringUtils.h b/include/assimp/StringUtils.h index 3c3afd264..157454126 100644 --- a/include/assimp/StringUtils.h +++ b/include/assimp/StringUtils.h @@ -55,7 +55,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /// @return The number of written characters if the buffer size was big enough. If an encoding error occurs, a negative number is returned. #if defined(_MSC_VER) && _MSC_VER < 1900 - inline int c99_ai_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) { + inline + int c99_ai_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) { int count(-1); if (0 != size) { count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); @@ -67,7 +68,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. return count; } - inline int ai_snprintf(char *outBuf, size_t size, const char *format, ...) { + inline + int ai_snprintf(char *outBuf, size_t size, const char *format, ...) { int count; va_list ap; @@ -82,14 +84,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # define ai_snprintf snprintf #endif +/// @fn to_string +/// @brief The portable version of to_string ( some gcc-versions on embedded devices are not supporting this). +/// @param value The value to write into the std::string. +/// @return The value as a std::string template inline std::string to_string( T value ) { std::ostringstream os; os << value; + return os.str(); } +/// @fn ai_strtof +/// @brief The portable version of strtof. +/// @param begin The first character of the string. +/// @param end The last character +/// @return The float value, 0.0f in cas of an error. inline float ai_strtof( const char *begin, const char *end ) { if ( nullptr == begin ) { @@ -107,5 +119,23 @@ float ai_strtof( const char *begin, const char *end ) { return val; } -#endif // INCLUDED_AI_STRINGUTILS_H +/// @fn DecimalToHexa +/// @brief The portable to convert a decimal value into a hexadecimal string. +/// @param toConvert Value to convert +/// @return The hexadecimal string, is empty in case of an error. +template +inline +std::string DecimalToHexa( T toConvert ) { + std::string result; + std::stringstream ss; + ss << std::hex << toConvert; + ss >> result; + for ( size_t i = 0; i < result.size(); ++i ) { + result[ i ] = toupper( result[ i ] ); + } + + return result; +} + +#endif // INCLUDED_AI_STRINGUTILS_H From c7ac32f891a16cc49fcca9b656feec75c116de76 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 12 Mar 2018 22:28:00 +0100 Subject: [PATCH 119/183] fix naming readout + export. --- code/D3MFImporter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 1732a5a8a..fce2743fe 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -133,11 +133,11 @@ private: const char *attrib( nullptr ); std::string name, type; - attrib = xmlReader->getAttributeValue( D3MF::XmlTag::name.c_str() ); + attrib = xmlReader->getAttributeValue( D3MF::XmlTag::id.c_str() ); if ( nullptr != attrib ) { name = attrib; } - attrib = xmlReader->getAttributeValue( D3MF::XmlTag::name.c_str() ); + attrib = xmlReader->getAttributeValue( D3MF::XmlTag::type.c_str() ); if ( nullptr != attrib ) { type = attrib; } From f11d4902c626e08a949f83eefcb38483c3edc5e9 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 13 Mar 2018 17:50:34 +0100 Subject: [PATCH 120/183] fix CanRead-method for the 3MF-Importer. --- code/D3MFImporter.cpp | 3 +++ code/D3MFOpcPackage.cpp | 9 +++++++++ code/D3MFOpcPackage.h | 3 ++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index fce2743fe..c5b4a8c91 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -414,6 +414,9 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo if ( nullptr == pIOHandler ) { return false; } + if ( !D3MF::D3MFOpcPackage::isZipArchive( pIOHandler, filename ) ) { + return false; + } D3MF::D3MFOpcPackage opcPackage( pIOHandler, filename ); return opcPackage.validate(); } diff --git a/code/D3MFOpcPackage.cpp b/code/D3MFOpcPackage.cpp index 81d610e15..2f7c8a25e 100644 --- a/code/D3MFOpcPackage.cpp +++ b/code/D3MFOpcPackage.cpp @@ -499,6 +499,15 @@ bool D3MFOpcPackage::validate() { return mZipArchive->Exists( ModelRef.c_str() ); } +bool D3MFOpcPackage::isZipArchive( IOSystem* pIOHandler, const std::string& rFile ) { + D3MF::D3MFZipArchive ar( pIOHandler, rFile ); + if ( !ar.isOpen() ) { + return false; + } + + return true; +} + std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream) { std::unique_ptr xmlStream(new CIrrXML_IOStreamReader(stream)); std::unique_ptr xml(irr::io::createIrrXMLReader(xmlStream.get())); diff --git a/code/D3MFOpcPackage.h b/code/D3MFOpcPackage.h index b49740eff..6d7b3d478 100644 --- a/code/D3MFOpcPackage.h +++ b/code/D3MFOpcPackage.h @@ -64,10 +64,11 @@ class D3MFZipArchive; class D3MFOpcPackage { public: - D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile); + D3MFOpcPackage( IOSystem* pIOHandler, const std::string& rFile ); ~D3MFOpcPackage(); IOStream* RootStream() const; bool validate(); + static bool isZipArchive( IOSystem* pIOHandler, const std::string& rFile ); protected: std::string ReadPackageRootRelationship(IOStream* stream); From 6b9add5594185afdc90f58c385bb78cf7699fa70 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 13 Mar 2018 18:19:10 +0100 Subject: [PATCH 121/183] fix compiler warning fr 64 bit --- code/D3MFImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index c5b4a8c91..3fffa5aeb 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -114,7 +114,7 @@ public: std::copy( mMeshes.begin(), mMeshes.end(), scene->mMeshes); - scene->mNumMaterials = mMatArray.size(); + scene->mNumMaterials = static_cast( mMatArray.size() ); if ( 0 != scene->mNumMaterials ) { scene->mMaterials = new aiMaterial*[ scene->mNumMaterials ]; std::copy( mMatArray.begin(), mMatArray.end(), scene->mMaterials ); From f857d72dbbb07177e80f80af639f1c7e685f7ddd Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 13 Mar 2018 18:56:47 +0100 Subject: [PATCH 122/183] add roundtrip test for 3mf. --- test/unit/utD3MFImportExport.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/unit/utD3MFImportExport.cpp b/test/unit/utD3MFImportExport.cpp index 3aefeba84..dd54b3042 100644 --- a/test/unit/utD3MFImportExport.cpp +++ b/test/unit/utD3MFImportExport.cpp @@ -86,4 +86,11 @@ TEST_F( utD3MFImporterExporter, export3MFtoMemTest ) { EXPECT_TRUE( exporterTest() ); } +TEST_F( utD3MFImporterExporter, roundtrip3MFtoMemTest ) { + EXPECT_TRUE( exporterTest() ); + + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "test.3mf", 0 ); +} + #endif // ASSIMP_BUILD_NO_EXPORT From 18e9aa13d6a603983e25675bb7a376caf45c3c02 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 13 Mar 2018 19:15:56 +0100 Subject: [PATCH 123/183] fix vs2017-compiler-warning c4002: tr1 is deprecated. --- test/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a9dfaf83f..049dbcdd2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -45,7 +45,9 @@ INCLUDE_DIRECTORIES( ${Assimp_SOURCE_DIR}/include ${Assimp_SOURCE_DIR}/code ) - +if (MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING") +endif() # Add the temporary output directories to the library path to make sure the # Assimp library can be found, even if it is not installed system-wide yet. LINK_DIRECTORIES( ${Assimp_BINARY_DIR} ${AssetImporter_BINARY_DIR}/lib ) From 20c817bc36e9f7cc16abbba8a0b613de910faf43 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 13 Mar 2018 19:42:06 +0100 Subject: [PATCH 124/183] Update utD3MFImportExport.cpp Fix compiler warning: unused var. --- test/unit/utD3MFImportExport.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/utD3MFImportExport.cpp b/test/unit/utD3MFImportExport.cpp index dd54b3042..e8797d74d 100644 --- a/test/unit/utD3MFImportExport.cpp +++ b/test/unit/utD3MFImportExport.cpp @@ -91,6 +91,7 @@ TEST_F( utD3MFImporterExporter, roundtrip3MFtoMemTest ) { Assimp::Importer importer; const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "test.3mf", 0 ); + EXPECT_NE( nullptr, scene ); } #endif // ASSIMP_BUILD_NO_EXPORT From 4c023c3818252c1102c5dc2bca200d0a9fff9d8b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 13 Mar 2018 21:40:23 +0100 Subject: [PATCH 125/183] fix unittest folder for roundtriptest. --- code/IRRLoader.cpp | 20 ++++++++------------ test/unit/utD3MFImportExport.cpp | 2 +- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/code/IRRLoader.cpp b/code/IRRLoader.cpp index fbec3da00..66d15c5c4 100644 --- a/code/IRRLoader.cpp +++ b/code/IRRLoader.cpp @@ -100,26 +100,22 @@ IRRImporter::~IRRImporter() // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool IRRImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const -{ - /* NOTE: A simple check for the file extension is not enough - * here. Irrmesh and irr are easy, but xml is too generic - * and could be collada, too. So we need to open the file and - * search for typical tokens. - */ +bool IRRImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { const std::string extension = GetExtension(pFile); - - if (extension == "irr")return true; - else if (extension == "xml" || checkSig) - { + if ( extension == "irr" ) { + return true; + } else if (extension == "xml" || checkSig) { /* If CanRead() is called in order to check whether we * support a specific file extension in general pIOHandler * might be NULL and it's our duty to return true here. */ - if (!pIOHandler)return true; + if ( nullptr == pIOHandler ) { + return true; + } const char* tokens[] = {"irr_scene"}; return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); } + return false; } diff --git a/test/unit/utD3MFImportExport.cpp b/test/unit/utD3MFImportExport.cpp index e8797d74d..c5fdd003b 100644 --- a/test/unit/utD3MFImportExport.cpp +++ b/test/unit/utD3MFImportExport.cpp @@ -90,7 +90,7 @@ TEST_F( utD3MFImporterExporter, roundtrip3MFtoMemTest ) { EXPECT_TRUE( exporterTest() ); Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "test.3mf", 0 ); + const aiScene *scene = importer.ReadFile( "test.3mf", 0 ); EXPECT_NE( nullptr, scene ); } From 5cf6509fb51c648a2c155ed82920a7ae2947ea2d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 14 Mar 2018 20:42:42 +0100 Subject: [PATCH 126/183] closes https://github.com/assimp/assimp/issues/1120: use euler angles for pre- and post-rotation. --- code/FBXConverter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 5d732c7b2..9bb023b4e 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -643,14 +643,14 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector if ( ok && PreRotation.SquareLength() > zero_epsilon ) { is_complex = true; - GetRotationMatrix( rot, PreRotation, chain[ TransformationComp_PreRotation ] ); + GetRotationMatrix( Model::RotOrder::RotOrder_EulerXYZ, PreRotation, chain[ TransformationComp_PreRotation ] ); } const aiVector3D& PostRotation = PropertyGet( props, "PostRotation", ok ); if ( ok && PostRotation.SquareLength() > zero_epsilon ) { is_complex = true; - GetRotationMatrix( rot, PostRotation, chain[ TransformationComp_PostRotation ] ); + GetRotationMatrix( Model::RotOrder::RotOrder_EulerXYZ, PostRotation, chain[ TransformationComp_PostRotation ] ); } const aiVector3D& RotationPivot = PropertyGet( props, "RotationPivot", ok ); From 2d980c16f0359c3c2e78bd71c86627aa97c705b0 Mon Sep 17 00:00:00 2001 From: Sergey Gonchar Date: Thu, 15 Mar 2018 21:41:40 -0700 Subject: [PATCH 127/183] Support Maya 2018 Collada Export with blendshapes and bones controllers for a mesh --- code/ColladaParser.cpp | 16 ++++++++++++++++ code/ColladaParser.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index d62c5cafc..d96ac0d39 100644 --- a/code/ColladaParser.cpp +++ b/code/ColladaParser.cpp @@ -222,6 +222,7 @@ void ColladaParser::ReadStructure() } PostProcessRootAnimations(); + PostProcessControllers(); } // ------------------------------------------------------------------------------------------------ @@ -360,6 +361,21 @@ void ColladaParser::ReadAnimationClipLibrary() } } +void ColladaParser::PostProcessControllers() +{ + for (ControllerLibrary::iterator it = mControllerLibrary.begin(); it != mControllerLibrary.end(); ++it) + { + std::string meshId = it->second.mMeshId; + ControllerLibrary::iterator findItr = mControllerLibrary.find(meshId); + while(findItr != mControllerLibrary.end()) { + meshId = findItr->second.mMeshId; + findItr = mControllerLibrary.find(meshId); + } + + it->second.mMeshId = meshId; + } +} + // ------------------------------------------------------------------------------------------------ // Re-build animations from animation clip library, if present, otherwise combine single-channel animations void ColladaParser::PostProcessRootAnimations() diff --git a/code/ColladaParser.h b/code/ColladaParser.h index 566386840..21f741551 100644 --- a/code/ColladaParser.h +++ b/code/ColladaParser.h @@ -87,6 +87,9 @@ namespace Assimp /** Reads the animation clip library */ void ReadAnimationClipLibrary(); + /** Unwrap controllers dependency hierarchy */ + void PostProcessControllers(); + /** Re-build animations from animation clip library, if present, otherwise combine single-channel animations */ void PostProcessRootAnimations(); From ef2343506b3f5d279f8408af1d7129777e02ef08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B6ber?= Date: Fri, 16 Mar 2018 11:17:28 +0100 Subject: [PATCH 128/183] Expand the current description about loading of embedded textures --- doc/dox.h | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/doc/dox.h b/doc/dox.h index d9462590b..067592438 100644 --- a/doc/dox.h +++ b/doc/dox.h @@ -667,27 +667,31 @@ need them at all. Normally textures used by assets are stored in separate files, however, there are file formats embedding their textures directly into the model file. Such textures are loaded into an aiTexture structure. -For embedded textures, the value of `AI_MATKEY_TEXTURE(textureType, index)` will be `*` where -`` is the index of the texture in aiScene::mTextures. -
+ +In previous versions, the path from the query for `AI_MATKEY_TEXTURE(textureType, index)` would be +`*` where `` is the index of the texture in aiScene::mTextures. Now this call will +return a file path for embedded textures in FBX files. To test if it is an embdedded texture use +aiScene::GetEmbeddedTexture. If the returned pointer is not null, it is embedded und can be loaded +from the data structure. If it is null, search for a separate file. Other file types still use the +old behaviour.
+If your rely on the old behaviour, you can use Assimp::Importer::SetPropertyBool with the key +#AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING to force the old behaviour. + There are two cases: -
-1) The texture is NOT compressed. Its color data is directly stored -in the aiTexture structure as an array of aiTexture::mWidth * aiTexture::mHeight aiTexel structures. Each aiTexel represents a pixel (or "texel") of the texture -image. The color data is stored in an unsigned RGBA8888 format, which can be easily used for -both Direct3D and OpenGL (swizzling the order of the color components might be necessary). -RGBA8888 has been chosen because it is well-known, easy to use and natively -supported by nearly all graphics APIs. -
-2) This applies if aiTexture::mHeight == 0 is fulfilled. Then, texture is stored in a -"compressed" format such as DDS or PNG. The term "compressed" does not mean that the -texture data must actually be compressed, however the texture was found in the -model file as if it was stored in a separate file on the harddisk. Appropriate -decoders (such as libjpeg, libpng, D3DX, DevIL) are required to load theses textures. -aiTexture::mWidth specifies the size of the texture data in bytes, aiTexture::pcData is -a pointer to the raw image data and aiTexture::achFormatHint is either zeroed or -contains the most common file extension of the embedded texture's format. This value is only -set if assimp is able to determine the file format. +1. The texture is NOT compressed. Its color data is directly stored in the aiTexture structure as an + array of aiTexture::mWidth * aiTexture::mHeight aiTexel structures. Each aiTexel represents a + pixel (or "texel") of the texture image. The color data is stored in an unsigned RGBA8888 format, + which can be easily used for both Direct3D and OpenGL (swizzling the order of the color + components might be necessary). RGBA8888 has been chosen because it is well-known, easy to use + and natively supported by nearly all graphics APIs. +2. This applies if aiTexture::mHeight == 0 is fulfilled. Then, texture is stored in a "compressed" + format such as DDS or PNG. The term "compressed" does not mean that the texture data must + actually be compressed, however the texture was found in the model file as if it was stored in a + separate file on the harddisk. Appropriate decoders (such as libjpeg, libpng, D3DX, DevIL) are + required to load theses textures. aiTexture::mWidth specifies the size of the texture data in + bytes, aiTexture::pcData is a pointer to the raw image data and aiTexture::achFormatHint is + either zeroed or contains the most common file extension of the embedded texture's format. This + value is only set if assimp is able to determine the file format. */ @@ -871,8 +875,11 @@ All material key constants start with 'AI_MATKEY' (it's an ugly macro for histor TEXTURE(t,n) aiString n/a - Defines the path of the n'th texture on the stack 't', where 'n' is any value >= 0 and 't' is one of the #aiTextureType enumerated values. Either a filepath or `*`, where `` is the index of an embedded texture in aiScene::mTextures. - See the 'Textures' section above. + Defines the path of the n'th texture on the stack 't', where 'n' is any value >= 0 and 't' + is one of the #aiTextureType enumerated values. A file path to an external file or an embedded + texture. Use aiScene::GetEmbeddedTexture to test if it is embedded for FBX files, in other cases + embedded textures start with '*' followed by an index into aiScene::mTextures. + See the @ref mat_tex section above. Also see @ref textures for a more information about texture retrieval. From 9d813a48b00b23518519c179a10993c1dcb6a40c Mon Sep 17 00:00:00 2001 From: Marco Di Benedetto Date: Mon, 19 Mar 2018 18:22:27 +0100 Subject: [PATCH 129/183] fixed emply buffer stream write, removed some cast warnings. --- code/D3MFImporter.cpp | 2 +- code/DefaultIOSystem.cpp | 6 ++--- code/EmbedTexturesProcess.cpp | 12 ++++----- code/FBXConverter.cpp | 6 ++--- code/FBXExportNode.cpp | 16 +++++------ code/FBXExportProperty.cpp | 14 +++++----- code/FBXExporter.cpp | 50 +++++++++++++++++------------------ code/XFileParser.cpp | 4 +-- include/assimp/StreamWriter.h | 4 +-- 9 files changed, 57 insertions(+), 57 deletions(-) diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 3fffa5aeb..764f0d28b 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -253,7 +253,7 @@ private: MatIdArray = it->second; } } - MatIdArray.push_back( newMatIdx ); + MatIdArray.push_back( (unsigned int)newMatIdx ); mMatId2MatArray[ mActiveMatGroup ] = MatIdArray; } diff --git a/code/DefaultIOSystem.cpp b/code/DefaultIOSystem.cpp index 5e50be8bd..6e9c0321e 100644 --- a/code/DefaultIOSystem.cpp +++ b/code/DefaultIOSystem.cpp @@ -76,7 +76,7 @@ bool DefaultIOSystem::Exists( const char* pFile) const #ifdef _WIN32 wchar_t fileName16[PATHLIMIT]; - bool isUnicode = IsTextUnicode(pFile, strlen(pFile), NULL); + bool isUnicode = IsTextUnicode(pFile, int(strlen(pFile)), NULL); if (isUnicode) { MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT); @@ -110,7 +110,7 @@ IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode) FILE* file; #ifdef _WIN32 wchar_t fileName16[PATHLIMIT]; - bool isUnicode = IsTextUnicode(strFile, strlen(strFile), NULL ); + bool isUnicode = IsTextUnicode(strFile, int(strlen(strFile)), NULL ); if (isUnicode) { MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT); std::string mode8(strMode); @@ -158,7 +158,7 @@ inline static void MakeAbsolutePath (const char* in, char* _out) { ai_assert(in && _out); #if defined( _MSC_VER ) || defined( __MINGW32__ ) - bool isUnicode = IsTextUnicode(in, strlen(in), NULL); + bool isUnicode = IsTextUnicode(in, int(strlen(in)), NULL); if (isUnicode) { wchar_t out16[PATHLIMIT]; wchar_t in16[PATHLIMIT]; diff --git a/code/EmbedTexturesProcess.cpp b/code/EmbedTexturesProcess.cpp index addfb9746..b434d79d7 100644 --- a/code/EmbedTexturesProcess.cpp +++ b/code/EmbedTexturesProcess.cpp @@ -99,22 +99,22 @@ void EmbedTexturesProcess::Execute(aiScene* pScene) { } bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { - uint32_t imageSize = 0; - std::string imagePath = path; + std::streampos imageSize = 0; + std::string imagePath = path; // Test path directly std::ifstream file(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == -1u) { + if ((imageSize = file.tellg()) == std::streampos(-1)) { DefaultLogger::get()->warn("EmbedTexturesProcess: Cannot find image: " + imagePath + ". Will try to find it in root folder."); // Test path in root path imagePath = mRootPath + path; file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == -1u) { + if ((imageSize = file.tellg()) == std::streampos(-1)) { // Test path basename in root path imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u); file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == -1u) { + if ((imageSize = file.tellg()) == std::streampos(-1)) { DefaultLogger::get()->error("EmbedTexturesProcess: Unable to embed texture: " + path + "."); return false; } @@ -134,7 +134,7 @@ bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { // Add the new texture auto pTexture = new aiTexture(); pTexture->mHeight = 0; // Means that this is still compressed - pTexture->mWidth = imageSize; + pTexture->mWidth = uint32_t(imageSize); pTexture->pcData = imageContent; auto extension = path.substr(path.find_last_of('.') + 1u); diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 9bb023b4e..16874df69 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -704,7 +704,7 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector aiMatrix4x4::Scaling( GeometricScaling, chain[ TransformationComp_GeometricScaling ] ); aiVector3D GeometricScalingInverse = GeometricScaling; bool canscale = true; - for (size_t i = 0; i < 3; ++i) { + for (unsigned int i = 0; i < 3; ++i) { if ( std::fabs( GeometricScalingInverse[i] ) > zero_epsilon ) { GeometricScalingInverse[i] = 1.0f / GeometricScaling[i]; } else { @@ -1888,11 +1888,11 @@ void Converter::SetShadingPropertiesCommon( aiMaterial* out_mat, const PropertyT // TransparentColor / TransparencyFactor... gee thanks FBX :rolleyes: const aiColor3D& Transparent = GetColorPropertyFactored( props, "TransparentColor", "TransparencyFactor", ok ); - float CalculatedOpacity = 1.0; + float CalculatedOpacity = 1.0f; if ( ok ) { out_mat->AddProperty( &Transparent, 1, AI_MATKEY_COLOR_TRANSPARENT ); // as calculated by FBX SDK 2017: - CalculatedOpacity = 1.0 - ((Transparent.r + Transparent.g + Transparent.b) / 3.0); + CalculatedOpacity = 1.0f - ((Transparent.r + Transparent.g + Transparent.b) / 3.0f); } // use of TransparencyFactor is inconsistent. diff --git a/code/FBXExportNode.cpp b/code/FBXExportNode.cpp index f218f068d..596901989 100644 --- a/code/FBXExportNode.cpp +++ b/code/FBXExportNode.cpp @@ -182,7 +182,7 @@ void FBX::Node::Begin(Assimp::StreamWriterLE &s) s.PutU4(0); // total property section length // node name - s.PutU1(name.size()); // length of node name + s.PutU1(uint8_t(name.size())); // length of node name s.PutString(name); // node name as raw bytes // property data comes after here @@ -217,8 +217,8 @@ void FBX::Node::EndProperties( ai_assert(pos > property_start); size_t property_section_size = pos - property_start; s.Seek(start_pos + 4); - s.PutU4(num_properties); - s.PutU4(property_section_size); + s.PutU4(uint32_t(num_properties)); + s.PutU4(uint32_t(property_section_size)); s.Seek(pos); } @@ -232,7 +232,7 @@ void FBX::Node::End( // now go back and write initial pos this->end_pos = s.Tell(); s.Seek(start_pos); - s.PutU4(end_pos); + s.PutU4(uint32_t(end_pos)); s.Seek(end_pos); } @@ -251,9 +251,9 @@ void FBX::Node::WritePropertyNode( Node node(name); node.Begin(s); s.PutU1('d'); - s.PutU4(v.size()); // number of elements + s.PutU4(uint32_t(v.size())); // number of elements s.PutU4(0); // no encoding (1 would be zip-compressed) - s.PutU4(v.size() * 8); // data size + s.PutU4(uint32_t(v.size()) * 8); // data size for (auto it = v.begin(); it != v.end(); ++it) { s.PutF8(*it); } node.EndProperties(s, 1); node.End(s, false); @@ -271,9 +271,9 @@ void FBX::Node::WritePropertyNode( Node node(name); node.Begin(s); s.PutU1('i'); - s.PutU4(v.size()); // number of elements + s.PutU4(uint32_t(v.size())); // number of elements s.PutU4(0); // no encoding (1 would be zip-compressed) - s.PutU4(v.size() * 4); // data size + s.PutU4(uint32_t(v.size()) * 4); // data size for (auto it = v.begin(); it != v.end(); ++it) { s.PutI4(*it); } node.EndProperties(s, 1); node.End(s, false); diff --git a/code/FBXExportProperty.cpp b/code/FBXExportProperty.cpp index 1beaa4d27..975e9a09a 100644 --- a/code/FBXExportProperty.cpp +++ b/code/FBXExportProperty.cpp @@ -127,8 +127,8 @@ FBX::Property::Property(const aiMatrix4x4& vm) : type('d'), data(8*16) { double* d = reinterpret_cast(data.data()); - for (size_t c = 0; c < 4; ++c) { - for (size_t r = 0; r < 4; ++r) { + for (unsigned int c = 0; c < 4; ++c) { + for (unsigned int r = 0; r < 4; ++r) { d[4*c+r] = vm[r][c]; } } @@ -164,15 +164,15 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s) case 'L': s.PutI8(*(reinterpret_cast(data.data()))); return; case 'S': case 'R': - s.PutU4(data.size()); + 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(N); // number of elements + s.PutU4(uint32_t(N)); // number of elements s.PutU4(0); // no encoding (1 would be zip-compressed) // TODO: compress if large? - s.PutU4(data.size()); // data size + s.PutU4(uint32_t(data.size())); // data size d = data.data(); for (size_t i = 0; i < N; ++i) { s.PutI4((reinterpret_cast(d))[i]); @@ -180,10 +180,10 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s) return; case 'd': N = data.size() / 8; - s.PutU4(N); // number of elements + s.PutU4(uint32_t(N)); // number of elements s.PutU4(0); // no encoding (1 would be zip-compressed) // TODO: compress if large? - s.PutU4(data.size()); // data size + s.PutU4(uint32_t(data.size())); // data size d = data.data(); for (size_t i = 0; i < N; ++i) { s.PutF8((reinterpret_cast(d))[i]); diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index a2e6024ed..9ba5c0333 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -460,7 +460,7 @@ size_t count_images(const aiScene* scene) { ){ const aiTextureType textype = static_cast(tt); const size_t texcount = mat->GetTextureCount(textype); - for (size_t j = 0; j < texcount; ++j) { + for (unsigned int j = 0; j < texcount; ++j) { mat->GetTexture(textype, j, &texpath); images.insert(std::string(texpath.C_Str())); } @@ -593,7 +593,7 @@ void FBXExporter::WriteDefinitions () // Model / FbxNode // <~~ node heirarchy - count = count_nodes(mScene->mRootNode) - 1; // (not counting root node) + count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node) if (count) { n = FBX::Node("ObjectType", Property("Model")); n.AddChild("Count", count); @@ -763,7 +763,7 @@ void FBXExporter::WriteDefinitions () // Video / FbxVideo // one for each image file. - count = count_images(mScene); + count = int32_t(count_images(mScene)); if (count) { n = FBX::Node("ObjectType", Property("Video")); n.AddChild("Count", count); @@ -792,7 +792,7 @@ void FBXExporter::WriteDefinitions () // Texture / FbxFileTexture // <~~ aiTexture - count = count_textures(mScene); + count = int32_t(count_textures(mScene)); if (count) { n = FBX::Node("ObjectType", Property("Texture")); n.AddChild("Count", count); @@ -848,7 +848,7 @@ void FBXExporter::WriteDefinitions () } // Deformer - count = count_deformers(mScene); + count = int32_t(count_deformers(mScene)); if (count) { n = FBX::Node("ObjectType", Property("Deformer")); n.AddChild("Count", count); @@ -943,7 +943,7 @@ void FBXExporter::WriteObjects () std::vector vertex_indices; // map of vertex value to its index in the data vector std::map index_by_vertex_value; - size_t index = 0; + int32_t index = 0; for (size_t vi = 0; vi < m->mNumVertices; ++vi) { aiVector3D vtx = m->mVertices[vi]; auto elem = index_by_vertex_value.find(vtx); @@ -955,7 +955,7 @@ void FBXExporter::WriteObjects () flattened_vertices.push_back(vtx[2]); ++index; } else { - vertex_indices.push_back(elem->second); + vertex_indices.push_back(int32_t(elem->second)); } } FBX::Node::WritePropertyNode( @@ -1052,7 +1052,7 @@ void FBXExporter::WriteObjects () std::vector uv_data; std::vector uv_indices; std::map index_by_uv; - size_t index = 0; + int32_t index = 0; for (size_t fi = 0; fi < m->mNumFaces; ++fi) { const aiFace &f = m->mFaces[fi]; for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) { @@ -1062,7 +1062,7 @@ void FBXExporter::WriteObjects () if (elem == index_by_uv.end()) { index_by_uv[uv] = index; uv_indices.push_back(index); - for (size_t x = 0; x < m->mNumUVComponents[uvi]; ++x) { + for (unsigned int x = 0; x < m->mNumUVComponents[uvi]; ++x) { uv_data.push_back(uv[x]); } ++index; @@ -1208,13 +1208,13 @@ void FBXExporter::WriteObjects () // and usualy are completely ignored when loading. // One notable exception is the "Opacity" property, // which Blender uses as (1.0 - alpha). - c.r = 0; c.g = 0; c.b = 0; + c.r = 0.0f; c.g = 0.0f; c.b = 0.0f; m->Get(AI_MATKEY_COLOR_EMISSIVE, c); p.AddP70vector("Emissive", c.r, c.g, c.b); - c.r = 0.2; c.g = 0.2; c.b = 0.2; + c.r = 0.2f; c.g = 0.2f; c.b = 0.2f; m->Get(AI_MATKEY_COLOR_AMBIENT, c); p.AddP70vector("Ambient", c.r, c.g, c.b); - c.r = 0.8; c.g = 0.8; c.b = 0.8; + c.r = 0.8f; c.g = 0.8f; c.b = 0.8f; m->Get(AI_MATKEY_COLOR_DIFFUSE, c); p.AddP70vector("Diffuse", c.r, c.g, c.b); // The FBX SDK determines "Opacity" from transparency colour (RGB) @@ -1223,29 +1223,29 @@ void FBXExporter::WriteObjects () // so we should take it from AI_MATKEY_OPACITY if possible. // It might make more sense to use TransparencyFactor, // but Blender actually loads "Opacity" correctly, so let's use it. - f = 1.0; + f = 1.0f; if (m->Get(AI_MATKEY_COLOR_TRANSPARENT, c) == aiReturn_SUCCESS) { - f = 1.0 - ((c.r + c.g + c.b) / 3); + f = 1.0f - ((c.r + c.g + c.b) / 3.0f); } m->Get(AI_MATKEY_OPACITY, f); p.AddP70double("Opacity", f); if (phong) { // specular color is multiplied by shininess_strength - c.r = 0.2; c.g = 0.2; c.b = 0.2; + c.r = 0.2f; c.g = 0.2f; c.b = 0.2f; m->Get(AI_MATKEY_COLOR_SPECULAR, c); - f = 1.0; + f = 1.0f; m->Get(AI_MATKEY_SHININESS_STRENGTH, f); p.AddP70vector("Specular", f*c.r, f*c.g, f*c.b); - f = 20.0; + f = 20.0f; m->Get(AI_MATKEY_SHININESS, f); p.AddP70double("Shininess", f); // Legacy "Reflectivity" is F*F*((R+G+B)/3), // where F is the proportion of light reflected (AKA reflectivity), // and RGB is the reflective colour of the material. // No idea why, but we might as well set it the same way. - f = 0.0; + f = 0.0f; m->Get(AI_MATKEY_REFLECTIVITY, f); - c.r = 1.0, c.g = 1.0, c.b = 1.0; + c.r = 1.0f, c.g = 1.0f, c.b = 1.0f; m->Get(AI_MATKEY_COLOR_REFLECTIVE, c); p.AddP70double("Reflectivity", f*f*((c.r+c.g+c.b)/3.0)); } @@ -1269,7 +1269,7 @@ void FBXExporter::WriteObjects () const aiTextureType textype = static_cast(tt); const size_t texcount = mat->GetTextureCount(textype); for (size_t j = 0; j < texcount; ++j) { - mat->GetTexture(textype, j, &texpath); + mat->GetTexture(textype, (unsigned int)j, &texpath); const std::string texstring = texpath.C_Str(); auto elem = uid_by_image.find(texstring); if (elem == uid_by_image.end()) { @@ -1591,7 +1591,7 @@ void FBXExporter::WriteObjects () std::vector vertex_indices; // map of vertex value to its index in the data vector std::map index_by_vertex_value; - size_t index = 0; + int32_t index = 0; for (size_t vi = 0; vi < m->mNumVertices; ++vi) { aiVector3D vtx = m->mVertices[vi]; auto elem = index_by_vertex_value.find(vtx); @@ -1600,7 +1600,7 @@ void FBXExporter::WriteObjects () index_by_vertex_value[vtx] = index; ++index; } else { - vertex_indices.push_back(elem->second); + vertex_indices.push_back(int32_t(elem->second)); } } @@ -1616,7 +1616,7 @@ void FBXExporter::WriteObjects () // as it can be instanced to many nodes. // All we can do is assume no instancing, // and take the first node we find that contains the mesh. - aiNode* mesh_node = get_node_for_mesh(mi, mScene->mRootNode); + aiNode* mesh_node = get_node_for_mesh((unsigned int)mi, mScene->mRootNode); aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene); // now make a subdeformer for each bone in the skeleton @@ -1682,7 +1682,7 @@ void FBXExporter::WriteObjects () // 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-5; // some error is to be expected + const float epsilon = 1e-5f; // some error is to be expected bool bone_xform_okay = true; if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) { not_in_bind_pose.insert(b); @@ -2002,7 +2002,7 @@ void FBXExporter::WriteModelNodes( transform_chain.emplace_back(elem->first, t); break; case 'r': // rotation - r *= DEG; + r *= float(DEG); transform_chain.emplace_back(elem->first, r); break; case 's': // scale diff --git a/code/XFileParser.cpp b/code/XFileParser.cpp index bda59ba59..4dd2c990b 100644 --- a/code/XFileParser.cpp +++ b/code/XFileParser.cpp @@ -1076,8 +1076,8 @@ std::string XFileParser::GetNextToken() { return s; } len = ReadBinDWord(); - const int bounds( mEnd - mP ); - const int iLen( len ); + const int bounds = int( mEnd - mP ); + const int iLen = int( len ); if ( iLen < 0 ) { return s; } diff --git a/include/assimp/StreamWriter.h b/include/assimp/StreamWriter.h index e955a7255..6f492df90 100644 --- a/include/assimp/StreamWriter.h +++ b/include/assimp/StreamWriter.h @@ -104,7 +104,7 @@ public: // --------------------------------------------------------------------- ~StreamWriter() { - stream->Write(&buffer[0], 1, buffer.size()); + stream->Write(buffer.data(), 1, buffer.size()); stream->Flush(); } @@ -114,7 +114,7 @@ public: /** Flush the contents of the internal buffer, and the output IOStream */ void Flush() { - stream->Write(&buffer[0], 1, buffer.size()); + stream->Write(buffer.data(), 1, buffer.size()); stream->Flush(); buffer.clear(); cursor = 0; From ef12eb84bbfefc4b999876c4d8ac9a3ec7b7f419 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Mar 2018 10:09:47 +0100 Subject: [PATCH 130/183] 3MF: add metadata import. --- code/D3MFImporter.cpp | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 3fffa5aeb..9153b1790 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -102,6 +102,8 @@ public: // } else if ( nodeName == D3MF::XmlTag::basematerials ) { ReadBaseMaterials(); + } else if ( nodeName == D3MF::XmlTag::meta ) { + ReadMetadata(); } } @@ -109,6 +111,15 @@ public: scene->mRootNode->mName.Set( "3MF" ); } + if ( !mMetaData.empty() ) { + const size_t numMeta( mMetaData.size() ); + scene->mMetaData = aiMetadata::Alloc( numMeta ); + for ( size_t i = 0; i < numMeta; ++i ) { + aiString val( mMetaData[ i ].value ); + scene->mMetaData->Add( mMetaData[ i ].name, val ); + } + } + scene->mNumMeshes = static_cast( mMeshes.size()); scene->mMeshes = new aiMesh*[scene->mNumMeshes](); @@ -180,6 +191,21 @@ private: return mesh; } + void ReadMetadata() { + const std::string name = xmlReader->getAttributeValue( D3MF::XmlTag::meta_name.c_str() ); + xmlReader->read(); + const std::string value = xmlReader->getNodeData(); + + if ( name.empty() ) { + return; + } + + MetaEntry entry; + entry.name = name; + entry.value = value; + mMetaData.push_back( entry ); + } + void ImportVertices(aiMesh* mesh) { std::vector vertices; while(ReadToEndElement(D3MF::XmlTag::vertices)) { @@ -371,8 +397,12 @@ private: return false; } - private: + struct MetaEntry { + std::string name; + std::string value; + }; + std::vector mMetaData; std::vector mMeshes; MatArray mMatArray; unsigned int mActiveMatGroup; From b66bee8d79dc388aba3cc5e3150a40e1e313ecf0 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Mar 2018 14:09:07 +0100 Subject: [PATCH 131/183] Export: Copy metadata to be able to export it properly. --- code/SceneCombiner.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/SceneCombiner.cpp b/code/SceneCombiner.cpp index 8647bd1ba..f67ef547c 100644 --- a/code/SceneCombiner.cpp +++ b/code/SceneCombiner.cpp @@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include "time.h" #include @@ -1001,7 +1002,12 @@ void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src,bool allocate) *_dest = new aiScene(); } aiScene* dest = *_dest; - ai_assert(dest); + ai_assert(nullptr != dest); + + // copy metadata + if ( nullptr != src->mMetaData ) { + dest->mMetaData = new aiMetadata( *src->mMetaData ); + } // copy animations dest->mNumAnimations = src->mNumAnimations; From 3bcef7ed8fe9a196574d0a7fd82a5209a8ae4486 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Mar 2018 14:09:52 +0100 Subject: [PATCH 132/183] Metadata: add test for copying metadata via copy constructor. --- test/unit/utMetadata.cpp | 71 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/test/unit/utMetadata.cpp b/test/unit/utMetadata.cpp index 7fda143b7..42f632bce 100644 --- a/test/unit/utMetadata.cpp +++ b/test/unit/utMetadata.cpp @@ -181,3 +181,74 @@ TEST_F( utMetadata, get_set_aiVector3D_Test ) { EXPECT_TRUE( success ); } +TEST_F( utMetadata, copy_test ) { + m_data = aiMetadata::Alloc( AI_META_MAX ); + bool bv = true; + m_data->Set( 0, "bool", bv ); + int32_t i32v = -10; + m_data->Set( 1, "int32", i32v ); + uint64_t ui64v = static_cast( 10 ); + m_data->Set( 2, "uint64", ui64v ); + float fv = 1.0f; + m_data->Set( 3, "float", fv ); + double dv = 2.0; + m_data->Set( 4, "double", dv ); + const aiString strVal( std::string( "test" ) ); + m_data->Set( 5, "aiString", strVal ); + aiVector3D vecVal( 1, 2, 3 ); + m_data->Set( 6, "aiVector3D", vecVal ); + + aiMetadata copy( *m_data ); + EXPECT_EQ( 7, copy.mNumProperties ); + + // bool test + { + bool v; + EXPECT_TRUE( copy.Get( "bool", v ) ); + EXPECT_EQ( bv, v ); + } + + // int32_t test + { + int32_t v; + bool ok = copy.Get( "int32", v ); + EXPECT_TRUE( ok ); + EXPECT_EQ( i32v, v ); + } + + // uint64_t test + { + uint64_t v; + bool ok = copy.Get( "uint64", v ); + EXPECT_TRUE( ok ); + EXPECT_EQ( ui64v, v ); + } + + // float test + { + float v; + EXPECT_TRUE( copy.Get( "float", v ) ); + EXPECT_EQ( fv, v ); + } + + // double test + { + double v; + EXPECT_TRUE( copy.Get( "double", v ) ); + EXPECT_EQ( dv, v ); + } + + // bool test + { + aiString v; + EXPECT_TRUE( copy.Get( "aiString", v ) ); + EXPECT_EQ( strVal, v ); + } + + // bool test + { + aiVector3D v; + EXPECT_TRUE( copy.Get( "aiVector3D", v ) ); + EXPECT_EQ( vecVal, v ); + } +} From 317f3e2a59059fe689e9182cd922ba185c1a48b6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Mar 2018 14:10:08 +0100 Subject: [PATCH 133/183] 3MF: Export metadata. --- code/D3MFExporter.cpp | 26 +++++++++++ code/D3MFExporter.h | 1 + code/D3MFImporter.cpp | 9 ++-- code/Exporter.cpp | 3 +- include/assimp/metadata.h | 98 ++++++++++++++++++++++++++++++++------- 5 files changed, 116 insertions(+), 21 deletions(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index b89e81ce6..950793f44 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -117,6 +117,7 @@ bool D3MFExporter::exportArchive( const char *file ) { if ( nullptr == m_zipArchive ) { return false; } + ok |= exportContentTypes(); ok |= export3DModel(); ok |= exportRelations(); @@ -181,6 +182,8 @@ bool D3MFExporter::export3DModel() { mModelOutput << "<" << XmlTag::resources << ">"; mModelOutput << std::endl; + writeMetaData(); + writeBaseMaterials(); writeObjects(); @@ -209,6 +212,29 @@ void D3MFExporter::writeHeader() { mModelOutput << std::endl; } +void D3MFExporter::writeMetaData() { + if ( nullptr == mScene->mMetaData ) { + return; + } + + const unsigned int numMetaEntries( mScene->mMetaData->mNumProperties ); + if ( 0 == numMetaEntries ) { + return; + } + + const aiString *key; + const aiMetadataEntry *entry(nullptr); + for ( size_t i = 0; i < numMetaEntries; ++i ) { + mScene->mMetaData->Get( i, key, entry ); + std::string k( key->C_Str() ); + aiString value; + mScene->mMetaData->Get( k, value ); + mModelOutput << "<" << XmlTag::meta << " " << XmlTag::meta_name << "=\"" << key->C_Str() << "\">"; + mModelOutput << value.C_Str(); + mModelOutput << "" << std::endl; + } +} + void D3MFExporter::writeBaseMaterials() { mModelOutput << "\n"; for ( size_t i = 0; i < mScene->mNumMaterials; ++i ) { diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index b553e0ab5..110862b99 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -76,6 +76,7 @@ public: protected: void writeHeader(); + void writeMetaData(); void writeBaseMaterials(); void writeObjects(); void writeMesh( aiMesh *mesh ); diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 9153b1790..d9f498ebf 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -111,28 +111,31 @@ public: scene->mRootNode->mName.Set( "3MF" ); } + // import the metadata if ( !mMetaData.empty() ) { const size_t numMeta( mMetaData.size() ); scene->mMetaData = aiMetadata::Alloc( numMeta ); for ( size_t i = 0; i < numMeta; ++i ) { aiString val( mMetaData[ i ].value ); - scene->mMetaData->Add( mMetaData[ i ].name, val ); + scene->mMetaData->Set( i, mMetaData[ i ].name, val ); } } + // import the meshes scene->mNumMeshes = static_cast( mMeshes.size()); scene->mMeshes = new aiMesh*[scene->mNumMeshes](); - std::copy( mMeshes.begin(), mMeshes.end(), scene->mMeshes); + // import the materials scene->mNumMaterials = static_cast( mMatArray.size() ); if ( 0 != scene->mNumMaterials ) { scene->mMaterials = new aiMaterial*[ scene->mNumMaterials ]; std::copy( mMatArray.begin(), mMatArray.end(), scene->mMaterials ); } + + // create the scenegraph scene->mRootNode->mNumChildren = static_cast(children.size()); scene->mRootNode->mChildren = new aiNode*[scene->mRootNode->mNumChildren](); - std::copy(children.begin(), children.end(), scene->mRootNode->mChildren); } diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 4951c70ef..49523e658 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -308,7 +308,8 @@ bool IsVerboseFormat(const aiScene* pScene) { } // ------------------------------------------------------------------------------------------------ -aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing, const ExportProperties* pProperties) { +aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath, + unsigned int pPreprocessing, const ExportProperties* pProperties) { ASSIMP_BEGIN_EXCEPTION_REGION(); // when they create scenes from scratch, users will likely create them not in verbose diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index 2dba61abc..70a604de9 100644 --- a/include/assimp/metadata.h +++ b/include/assimp/metadata.h @@ -67,6 +67,7 @@ typedef enum aiMetadataType { AI_DOUBLE = 4, AI_AISTRING = 5, AI_AIVECTOR3D = 6, + AI_META_MAX = 7, #ifndef SWIG FORCE_32BIT = INT_MAX @@ -130,42 +131,103 @@ struct aiMetadata { */ aiMetadata() : mNumProperties(0) - , mKeys(NULL) - , mValues(NULL) { + , mKeys(nullptr) + , mValues(nullptr) { // empty } + aiMetadata( const aiMetadata &rhs ) + : mNumProperties( rhs.mNumProperties ) + , mKeys( nullptr ) + , mValues( nullptr ) { + mKeys = new aiString[ mNumProperties ]; + for ( unsigned int i = 0; i < mNumProperties; ++i ) { + mKeys[ i ] = rhs.mKeys[ i ]; + } + mValues = new aiMetadataEntry[ mNumProperties ]; + for ( unsigned int i = 0; i < mNumProperties; ++i ) { + mValues[ i ].mType = rhs.mValues[ i ].mType; + switch ( rhs.mValues[ i ].mType ) { + case AI_BOOL: + mValues[ i ].mData = new bool( rhs.mValues[i].mData ); + break; + case AI_INT32: { + int32_t v; + ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( int32_t ) ); + mValues[ i ].mData = new int32_t( v ); + } + break; + case AI_UINT64: { + uint64_t v; + ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( uint64_t ) ); + mValues[ i ].mData = new uint64_t( v ); + } + break; + case AI_FLOAT: { + float v; + ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( float ) ); + mValues[ i ].mData = new float( v ); + } + break; + case AI_DOUBLE: { + double v; + ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( double ) ); + mValues[ i ].mData = new double( v ); + } + break; + case AI_AISTRING: { + aiString v; + rhs.Get( mKeys[ i ], v ); + mValues[ i ].mData = new aiString( v ); + } + break; + case AI_AIVECTOR3D: { + aiVector3D v; + rhs.Get( mKeys[ i ], v ); + mValues[ i ].mData = new aiVector3D( v ); + } + break; +#ifndef SWIG + case FORCE_32BIT: +#endif + default: + break; + } + + } + } + /** * @brief The destructor. */ ~aiMetadata() { delete [] mKeys; - mKeys = NULL; + mKeys = nullptr; if (mValues) { // Delete each metadata entry for (unsigned i=0; i(data); + delete static_cast< bool* >( data ); break; case AI_INT32: - delete static_cast(data); + delete static_cast< int32_t* >( data ); break; case AI_UINT64: - delete static_cast(data); + delete static_cast< uint64_t* >( data ); break; case AI_FLOAT: - delete static_cast(data); + delete static_cast< float* >( data ); break; case AI_DOUBLE: - delete static_cast(data); + delete static_cast< double* >( data ); break; case AI_AISTRING: - delete static_cast(data); + delete static_cast< aiString* >( data ); break; case AI_AIVECTOR3D: - delete static_cast(data); + delete static_cast< aiVector3D* >( data ); break; #ifndef SWIG case FORCE_32BIT: @@ -177,7 +239,7 @@ struct aiMetadata { // Delete the metadata array delete [] mValues; - mValues = NULL; + mValues = nullptr; } } @@ -208,8 +270,8 @@ struct aiMetadata { } template - inline void Add(const std::string& key, const T& value) - { + inline + void Add(const std::string& key, const T& value) { aiString* new_keys = new aiString[mNumProperties + 1]; aiMetadataEntry* new_values = new aiMetadataEntry[mNumProperties + 1]; @@ -256,7 +318,7 @@ struct aiMetadata { template inline - bool Get( unsigned index, T& value ) { + bool Get( unsigned index, T& value ) const { // In range assertion if ( index >= mNumProperties ) { return false; @@ -277,7 +339,7 @@ struct aiMetadata { template inline - bool Get( const aiString& key, T& value ) { + bool Get( const aiString& key, T& value ) const { // Search for the given key for ( unsigned int i = 0; i < mNumProperties; ++i ) { if ( mKeys[ i ] == key ) { @@ -288,7 +350,8 @@ struct aiMetadata { } template - inline bool Get( const std::string& key, T& value ) { + inline + bool Get( const std::string& key, T& value ) const { return Get(aiString(key), value); } @@ -297,7 +360,8 @@ struct aiMetadata { /// \param [out] pKey - pointer to the key value. /// \param [out] pEntry - pointer to the entry: type and value. /// \return false - if pIndex is out of range, else - true. - inline bool Get(size_t index, const aiString*& key, const aiMetadataEntry*& entry) { + inline + bool Get(size_t index, const aiString*& key, const aiMetadataEntry*& entry) const { if ( index >= mNumProperties ) { return false; } From e36f78482cc561d04a4cf21b457d5b5b4bbc0c03 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Mar 2018 14:53:57 +0100 Subject: [PATCH 134/183] https://github.com/assimp/assimp/issues/1796: return correct value on detach logger. --- code/DefaultLogger.cpp | 124 +++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 68 deletions(-) diff --git a/code/DefaultLogger.cpp b/code/DefaultLogger.cpp index ad101174c..2871a4131 100644 --- a/code/DefaultLogger.cpp +++ b/code/DefaultLogger.cpp @@ -45,7 +45,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @brief Implementation of DefaultLogger (and Logger) */ - // Default log streams #include "Win32DebugLogStream.h" #include "StdOStreamLogStream.h" @@ -62,8 +61,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_SINGLETHREADED # include # include - -std::mutex loggerMutex; + std::mutex loggerMutex; #endif namespace Assimp { @@ -76,22 +74,19 @@ static const unsigned int SeverityAll = Logger::Info | Logger::Err | Logger::War // ---------------------------------------------------------------------------------- // Represents a log-stream + its error severity -struct LogStreamInfo -{ - unsigned int m_uiErrorSeverity; - LogStream *m_pStream; +struct LogStreamInfo { + unsigned int m_uiErrorSeverity; + LogStream *m_pStream; // Constructor LogStreamInfo( unsigned int uiErrorSev, LogStream *pStream ) : m_uiErrorSeverity( uiErrorSev ), - m_pStream( pStream ) - { + m_pStream( pStream ) { // empty } // Destructor - ~LogStreamInfo() - { + ~LogStreamInfo() { delete m_pStream; } }; @@ -109,7 +104,7 @@ LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams, #ifdef WIN32 return new Win32DebugLogStream(); #else - return NULL; + return nullptr; #endif // Platform-independent default streams @@ -118,7 +113,7 @@ LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams, case aiDefaultLogStream_STDOUT: return new StdOStreamLogStream(std::cout); case aiDefaultLogStream_FILE: - return (name && *name ? new FileLogStream(name,io) : NULL); + return (name && *name ? new FileLogStream(name,io) : nullptr ); default: // We don't know this default log stream, so raise an assertion ai_assert(false); @@ -134,34 +129,38 @@ LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams, Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/, LogSeverity severity /*= NORMAL*/, unsigned int defStreams /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/, - IOSystem* io /*= NULL*/) -{ + IOSystem* io /*= NULL*/) { // enter the mutex here to avoid concurrency problems #ifndef ASSIMP_BUILD_SINGLETHREADED std::lock_guard lock(loggerMutex); #endif - if (m_pLogger && !isNullLogger() ) + if ( m_pLogger && !isNullLogger() ) { delete m_pLogger; + } m_pLogger = new DefaultLogger( severity ); // Attach default log streams // Stream the log to the MSVC debugger? - if (defStreams & aiDefaultLogStream_DEBUGGER) - m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_DEBUGGER)); + if ( defStreams & aiDefaultLogStream_DEBUGGER ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_DEBUGGER ) ); + } // Stream the log to COUT? - if (defStreams & aiDefaultLogStream_STDOUT) - m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_STDOUT)); + if ( defStreams & aiDefaultLogStream_STDOUT ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDOUT ) ); + } // Stream the log to CERR? - if (defStreams & aiDefaultLogStream_STDERR) - m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_STDERR)); + if ( defStreams & aiDefaultLogStream_STDERR ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDERR ) ); + } // Stream the log to a file - if (defStreams & aiDefaultLogStream_FILE && name && *name) - m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_FILE,name,io)); + if ( defStreams & aiDefaultLogStream_FILE && name && *name ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_FILE, name, io ) ); + } return m_pLogger; } @@ -200,7 +199,6 @@ void Logger::warn(const char* message) { // ---------------------------------------------------------------------------------- void Logger::error(const char* message) { - // SECURITY FIX: see above if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { return; @@ -209,23 +207,24 @@ void Logger::error(const char* message) { } // ---------------------------------------------------------------------------------- -void DefaultLogger::set( Logger *logger ) -{ +void DefaultLogger::set( Logger *logger ) { // enter the mutex here to avoid concurrency problems #ifndef ASSIMP_BUILD_SINGLETHREADED std::lock_guard lock(loggerMutex); #endif - if (!logger)logger = &s_pNullLogger; - if (m_pLogger && !isNullLogger() ) + if ( nullptr == logger ) { + logger = &s_pNullLogger; + } + if ( nullptr != m_pLogger && !isNullLogger() ) { delete m_pLogger; + } DefaultLogger::m_pLogger = logger; } // ---------------------------------------------------------------------------------- -bool DefaultLogger::isNullLogger() -{ +bool DefaultLogger::isNullLogger() { return m_pLogger == &s_pNullLogger; } @@ -236,8 +235,7 @@ Logger *DefaultLogger::get() { // ---------------------------------------------------------------------------------- // Kills the only instance -void DefaultLogger::kill() -{ +void DefaultLogger::kill() { // enter the mutex here to avoid concurrency problems #ifndef ASSIMP_BUILD_SINGLETHREADED std::lock_guard lock(loggerMutex); @@ -252,10 +250,10 @@ void DefaultLogger::kill() // ---------------------------------------------------------------------------------- // Debug message -void DefaultLogger::OnDebug( const char* message ) -{ - if ( m_Severity == Logger::NORMAL ) - return; +void DefaultLogger::OnDebug( const char* message ) { + if ( m_Severity == Logger::NORMAL ) { + return; + } static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; char msg[Size]; @@ -266,8 +264,7 @@ void DefaultLogger::OnDebug( const char* message ) // ---------------------------------------------------------------------------------- // Logs an info -void DefaultLogger::OnInfo( const char* message ) -{ +void DefaultLogger::OnInfo( const char* message ){ static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; char msg[Size]; ai_snprintf(msg, Size, "Info, T%u: %s", GetThreadID(), message ); @@ -277,8 +274,7 @@ void DefaultLogger::OnInfo( const char* message ) // ---------------------------------------------------------------------------------- // Logs a warning -void DefaultLogger::OnWarn( const char* message ) -{ +void DefaultLogger::OnWarn( const char* message ) { static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; char msg[Size]; ai_snprintf(msg, Size, "Warn, T%u: %s", GetThreadID(), message ); @@ -288,8 +284,7 @@ void DefaultLogger::OnWarn( const char* message ) // ---------------------------------------------------------------------------------- // Logs an error -void DefaultLogger::OnError( const char* message ) -{ +void DefaultLogger::OnError( const char* message ) { static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; char msg[ Size ]; ai_snprintf(msg, Size, "Error, T%u: %s", GetThreadID(), message ); @@ -299,10 +294,10 @@ void DefaultLogger::OnError( const char* message ) // ---------------------------------------------------------------------------------- // Will attach a new stream -bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) -{ - if (!pStream) +bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) { + if ( nullptr == pStream ) { return false; + } if (0 == severity) { severity = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging; @@ -312,8 +307,7 @@ bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) it != m_StreamArray.end(); ++it ) { - if ( (*it)->m_pStream == pStream ) - { + if ( (*it)->m_pStream == pStream ) { (*it)->m_uiErrorSeverity |= severity; return true; } @@ -326,34 +320,31 @@ bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) // ---------------------------------------------------------------------------------- // Detach a stream -bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) -{ - if (!pStream) +bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) { + if ( nullptr == pStream ) { return false; + } if (0 == severity) { severity = SeverityAll; } - for ( StreamIt it = m_StreamArray.begin(); - it != m_StreamArray.end(); - ++it ) - { - if ( (*it)->m_pStream == pStream ) - { + bool res( false ); + for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) { + if ( (*it)->m_pStream == pStream ) { (*it)->m_uiErrorSeverity &= ~severity; - if ( (*it)->m_uiErrorSeverity == 0 ) - { + if ( (*it)->m_uiErrorSeverity == 0 ) { // don't delete the underlying stream 'cause the caller gains ownership again - (**it).m_pStream = NULL; + (**it).m_pStream = nullptr; delete *it; m_StreamArray.erase( it ); + res = true; break; } return true; } } - return false; + return res; } // ---------------------------------------------------------------------------------- @@ -361,15 +352,13 @@ bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) DefaultLogger::DefaultLogger(LogSeverity severity) : Logger ( severity ) , noRepeatMsg (false) - , lastLen( 0 ) -{ + , lastLen( 0 ) { lastMsg[0] = '\0'; } // ---------------------------------------------------------------------------------- // Destructor -DefaultLogger::~DefaultLogger() -{ +DefaultLogger::~DefaultLogger() { for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) { // also frees the underlying stream, we are its owner. delete *it; @@ -378,9 +367,8 @@ DefaultLogger::~DefaultLogger() // ---------------------------------------------------------------------------------- // Writes message to stream -void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev ) -{ - ai_assert(NULL != message); +void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev ) { + ai_assert(nullptr != message); // Check whether this is a repeated message if (! ::strncmp( message,lastMsg, lastLen-1)) From 5814e6f9f066f5f5d24b3796639675a38ba43dfa Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Mar 2018 18:43:54 +0100 Subject: [PATCH 135/183] Optimize some std::string usages. --- code/3DSConverter.cpp | 2 +- code/AMFImporter_Postprocess.cpp | 9 +++++---- code/ASEParser.cpp | 26 +++++++++++++------------- code/BVHLoader.cpp | 27 ++++++++++++++------------- code/BaseImporter.cpp | 3 ++- code/ColladaExporter.cpp | 14 +++++++++----- code/ColladaLoader.cpp | 12 ++++++++---- code/D3MFExporter.cpp | 6 +++--- code/D3MFImporter.cpp | 3 ++- code/D3MFOpcPackage.cpp | 4 ++-- code/DXFHelper.h | 1 - code/FBXConverter.cpp | 5 ++--- code/Importer.cpp | 2 +- code/SceneCombiner.cpp | 18 ++++++++++-------- include/assimp/DefaultLogger.hpp | 4 ---- 15 files changed, 72 insertions(+), 64 deletions(-) diff --git a/code/3DSConverter.cpp b/code/3DSConverter.cpp index cbc6d31d2..081da1bcf 100644 --- a/code/3DSConverter.cpp +++ b/code/3DSConverter.cpp @@ -72,7 +72,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() unsigned int idx( NotSet ); for (unsigned int i = 0; i < mScene->mMaterials.size();++i) { - std::string s = mScene->mMaterials[i].mName; + std::string &s = mScene->mMaterials[i].mName; for ( std::string::iterator it = s.begin(); it != s.end(); ++it ) { *it = static_cast< char >( ::tolower( *it ) ); } diff --git a/code/AMFImporter_Postprocess.cpp b/code/AMFImporter_Postprocess.cpp index f048e1178..192544fcb 100644 --- a/code/AMFImporter_Postprocess.cpp +++ b/code/AMFImporter_Postprocess.cpp @@ -156,10 +156,11 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string& TextureConverted_Index = 0; for(const SPP_Texture& tex_convd: mTexture_Converted) { - if(tex_convd.ID == TextureConverted_ID) - return TextureConverted_Index; - else - TextureConverted_Index++; + if ( tex_convd.ID == TextureConverted_ID ) { + return TextureConverted_Index; + } else { + ++TextureConverted_Index; + } } // diff --git a/code/ASEParser.cpp b/code/ASEParser.cpp index 59af24a10..b953f8d96 100644 --- a/code/ASEParser.cpp +++ b/code/ASEParser.cpp @@ -267,7 +267,9 @@ void Parser::Parse() // at the file extension (ASE, ASK, ASC) // ************************************************************* - if (fmt)iFileFormat = fmt; + if ( fmt ) { + iFileFormat = fmt; + } continue; } // main scene information @@ -427,28 +429,25 @@ void Parser::ParseLV1SoftSkinBlock() // Reserve enough storage vert.mBoneWeights.reserve(numWeights); - for (unsigned int w = 0; w < numWeights;++w) - { - std::string bone; + std::string bone; + for (unsigned int w = 0; w < numWeights;++w) { + bone.clear(); ParseString(bone,"*MESH_SOFTSKINVERTS.Bone"); // Find the bone in the mesh's list std::pair me; me.first = -1; - for (unsigned int n = 0; n < curMesh->mBones.size();++n) - { - if (curMesh->mBones[n].mName == bone) - { + for (unsigned int n = 0; n < curMesh->mBones.size();++n) { + if (curMesh->mBones[n].mName == bone) { me.first = n; break; } } - if (-1 == me.first) - { + if (-1 == me.first) { // We don't have this bone yet, so add it to the list - me.first = (int)curMesh->mBones.size(); - curMesh->mBones.push_back(ASE::Bone(bone)); + me.first = static_cast( curMesh->mBones.size() ); + curMesh->mBones.push_back( ASE::Bone( bone ) ); } ParseLV4MeshFloat( me.second ); @@ -745,6 +744,7 @@ void Parser::ParseLV3MapBlock(Texture& map) // empty the texture won't be used later. // *********************************************************** bool parsePath = true; + std::string temp; while (true) { if ('*' == *filePtr) @@ -753,7 +753,7 @@ void Parser::ParseLV3MapBlock(Texture& map) // type of map if (TokenMatch(filePtr,"MAP_CLASS" ,9)) { - std::string temp; + temp.clear(); if(!ParseString(temp,"*MAP_CLASS")) SkipToNextToken(); if (temp != "Bitmap" && temp != "Normal Bump") diff --git a/code/BVHLoader.cpp b/code/BVHLoader.cpp index dfeeb60ac..0b2a818ae 100644 --- a/code/BVHLoader.cpp +++ b/code/BVHLoader.cpp @@ -199,6 +199,7 @@ aiNode* BVHLoader::ReadNode() Node& internNode = mNodes.back(); // now read the node's contents + std::string siteToken; while( 1) { std::string token = GetNextToken(); @@ -218,7 +219,8 @@ aiNode* BVHLoader::ReadNode() else if( token == "End") { // The real symbol is "End Site". Second part comes in a separate token - std::string siteToken = GetNextToken(); + siteToken.clear(); + siteToken = GetNextToken(); if( siteToken != "Site") ThrowException( format() << "Expected \"End Site\" keyword, but found \"" << token << " " << siteToken << "\"." ); @@ -262,21 +264,18 @@ aiNode* BVHLoader::ReadEndSite( const std::string& pParentName) aiNode* node = new aiNode( "EndSite_" + pParentName); // now read the node's contents. Only possible entry is "OFFSET" - while( 1) - { - std::string token = GetNextToken(); + std::string token; + while( 1) { + token.clear(); + token = GetNextToken(); // end node's offset - if( token == "OFFSET") - { + if( token == "OFFSET") { ReadNodeOffset( node); - } - else if( token == "}") - { + } else if( token == "}") { // we're done with the end node break; - } else - { + } else { // everything else is a parse error ThrowException( format() << "Unknown keyword \"" << token << "\"." ); } @@ -296,8 +295,10 @@ void BVHLoader::ReadNodeOffset( aiNode* pNode) offset.z = GetNextTokenAsFloat(); // build a transformation matrix from it - pNode->mTransformation = aiMatrix4x4( 1.0f, 0.0f, 0.0f, offset.x, 0.0f, 1.0f, 0.0f, offset.y, - 0.0f, 0.0f, 1.0f, offset.z, 0.0f, 0.0f, 0.0f, 1.0f); + pNode->mTransformation = aiMatrix4x4( 1.0f, 0.0f, 0.0f, offset.x, + 0.0f, 1.0f, 0.0f, offset.y, + 0.0f, 0.0f, 1.0f, offset.z, + 0.0f, 0.0f, 0.0f, 1.0f); } // ------------------------------------------------------------------------------------------------ diff --git a/code/BaseImporter.cpp b/code/BaseImporter.cpp index 67c743285..4782e9e2d 100644 --- a/code/BaseImporter.cpp +++ b/code/BaseImporter.cpp @@ -251,7 +251,8 @@ void BaseImporter::GetExtensionList(std::set& extensions) { /* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile, const void* _magic, unsigned int num, unsigned int offset, unsigned int size) { - ai_assert(size <= 16 && _magic); + ai_assert( size <= 16 ); + ai_assert( _magic ); if (!pIOHandler) { return false; diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index dc6b15ec4..96421a532 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -1269,7 +1269,8 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) mOutput << startstr << "" << endstr; PushTag(); - + + std::string node_idstr; for (size_t a = 0; a < anim->mNumChannels; ++a) { const aiNodeAnim * nodeAnim = anim->mChannels[a]; @@ -1277,7 +1278,9 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) if ( nodeAnim->mNumPositionKeys != nodeAnim->mNumScalingKeys || nodeAnim->mNumPositionKeys != nodeAnim->mNumRotationKeys ) continue; { - const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-input"); + node_idstr.clear(); + node_idstr += nodeAnim->mNodeName.data; + node_idstr += std::string( "_matrix-input" ); std::vector frames; for( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { @@ -1289,12 +1292,14 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) } { - const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-output"); + node_idstr.clear(); + + node_idstr += nodeAnim->mNodeName.data; + node_idstr += std::string("_matrix-output"); std::vector keyframes; keyframes.reserve(nodeAnim->mNumPositionKeys * 16); for( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { - aiVector3D Scaling = nodeAnim->mScalingKeys[i].mValue; aiMatrix4x4 ScalingM; // identity ScalingM[0][0] = Scaling.x; ScalingM[1][1] = Scaling.y; ScalingM[2][2] = Scaling.z; @@ -1361,7 +1366,6 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) PopTag(); mOutput << startstr << "" << endstr; } - } for (size_t a = 0; a < anim->mNumChannels; ++a) { diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index 959a905ae..01ba1c400 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -1121,6 +1121,7 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars continue; // now check all channels if they affect the current node + std::string targetID, subElement; for( std::vector::const_iterator cit = pSrcAnim->mChannels.begin(); cit != pSrcAnim->mChannels.end(); ++cit) { @@ -1147,7 +1148,9 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars } if( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos) continue; - std::string targetID = srcChannel.mTarget.substr( 0, slashPos); + + targetID.clear(); + targetID = srcChannel.mTarget.substr( 0, slashPos); if( targetID != srcNode->mID) continue; @@ -1160,7 +1163,8 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars entry.mTransformId = srcChannel.mTarget.substr( slashPos+1, dotPos - slashPos - 1); - std::string subElement = srcChannel.mTarget.substr( dotPos+1); + subElement.clear(); + subElement = srcChannel.mTarget.substr( dotPos+1); if( subElement == "ANGLE") entry.mSubElement = 3; // last number in an Axis-Angle-Transform is the angle else if( subElement == "X") @@ -1181,7 +1185,8 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars if (bracketPos != std::string::npos) { entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1, bracketPos - slashPos - 1); - std::string subElement = srcChannel.mTarget.substr(bracketPos); + subElement.clear(); + subElement = srcChannel.mTarget.substr(bracketPos); if (subElement == "(0)(0)") entry.mSubElement = 0; @@ -1215,7 +1220,6 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars entry.mSubElement = 14; else if (subElement == "(3)(3)") entry.mSubElement = 15; - } // determine which transform step is affected by this channel diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index b89e81ce6..633f96ad9 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -211,20 +211,20 @@ void D3MFExporter::writeHeader() { void D3MFExporter::writeBaseMaterials() { mModelOutput << "\n"; + std::string strName, hexDiffuseColor , tmp; for ( size_t i = 0; i < mScene->mNumMaterials; ++i ) { aiMaterial *mat = mScene->mMaterials[ i ]; - std::string strName; aiString name; if ( mat->Get( AI_MATKEY_NAME, name ) != aiReturn_SUCCESS ) { strName = "basemat_" + to_string( i ); } else { strName = name.C_Str(); } - std::string hexDiffuseColor; aiColor4D color; if ( mat->Get( AI_MATKEY_COLOR_DIFFUSE, color ) == aiReturn_SUCCESS ) { + hexDiffuseColor.clear(); + tmp.clear(); hexDiffuseColor = "#"; - std::string tmp; tmp = DecimalToHexa( color.r ); hexDiffuseColor += tmp; diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 3fffa5aeb..dc99f007e 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -94,8 +94,9 @@ public: scene->mRootNode = new aiNode(); std::vector children; + std::string nodeName; while(ReadToEndElement(D3MF::XmlTag::model)) { - const std::string nodeName( xmlReader->getNodeName() ); + nodeName = xmlReader->getNodeName(); if( nodeName == D3MF::XmlTag::object) { children.push_back(ReadObject(scene)); } else if( nodeName == D3MF::XmlTag::build) { diff --git a/code/D3MFOpcPackage.cpp b/code/D3MFOpcPackage.cpp index 2f7c8a25e..3bf545813 100644 --- a/code/D3MFOpcPackage.cpp +++ b/code/D3MFOpcPackage.cpp @@ -288,8 +288,8 @@ bool D3MFZipArchive::Exists(const char* pFile) const { return false; } - std::string rFile(pFile); - std::map::const_iterator it = m_ArchiveMap.find(rFile); + std::string filename(pFile); + std::map::const_iterator it = m_ArchiveMap.find(filename); bool exist( false ); if(it != m_ArchiveMap.end()) { exist = true; diff --git a/code/DXFHelper.h b/code/DXFHelper.h index 5aaf4844f..49ca2814d 100644 --- a/code/DXFHelper.h +++ b/code/DXFHelper.h @@ -169,7 +169,6 @@ public: } private: - LineSplitter splitter; int groupcode; std::string value; diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 9bb023b4e..537a4f558 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -1642,9 +1642,8 @@ void Converter::TrySetTextureProperties( aiMaterial* out_mat, const TextureMap& } void Converter::TrySetTextureProperties( aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, - const std::string& propName, - aiTextureType target, const MeshGeometry* const mesh ) -{ + const std::string& propName, + aiTextureType target, const MeshGeometry* const mesh ) { LayeredTextureMap::const_iterator it = layeredTextures.find( propName ); if ( it == layeredTextures.end() ) { return; diff --git a/code/Importer.cpp b/code/Importer.cpp index feddadf58..6422ca6d0 100644 --- a/code/Importer.cpp +++ b/code/Importer.cpp @@ -622,7 +622,6 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) if (s != std::string::npos) { DefaultLogger::get()->info("File extension not known, trying signature-based detection"); for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { - if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) { imp = pimpl->mImporter[a]; break; @@ -947,6 +946,7 @@ BaseImporter* Importer::GetImporter (const char* szExtension) const size_t Importer::GetImporterIndex (const char* szExtension) const { ai_assert(szExtension); + ASSIMP_BEGIN_EXCEPTION_REGION(); // skip over wildcard and dot characters at string head -- diff --git a/code/SceneCombiner.cpp b/code/SceneCombiner.cpp index 8647bd1ba..240dbbb12 100644 --- a/code/SceneCombiner.cpp +++ b/code/SceneCombiner.cpp @@ -806,8 +806,9 @@ void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/, for (std::vector::const_iterator it = begin; it != end;++it) { if ((*it)->mNormals) { ::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D)); + } else { + DefaultLogger::get()->warn( "JoinMeshes: Normals expected but input mesh contains no normals" ); } - else DefaultLogger::get()->warn("JoinMeshes: Normals expected but input mesh contains no normals"); pv2 += (*it)->mNumVertices; } } @@ -817,28 +818,29 @@ void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/, pv2 = out->mTangents = new aiVector3D[out->mNumVertices]; aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices]; - for (std::vector::const_iterator it = begin; it != end;++it) { + for (std::vector::const_iterator it = begin; it != end;++it) { if ((*it)->mTangents) { ::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices*sizeof(aiVector3D)); ::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D)); + } else { + DefaultLogger::get()->warn( "JoinMeshes: Tangents expected but input mesh contains no tangents" ); } - else DefaultLogger::get()->warn("JoinMeshes: Tangents expected but input mesh contains no tangents"); pv2 += (*it)->mNumVertices; pv2b += (*it)->mNumVertices; } } // copy texture coordinates unsigned int n = 0; - while ((**begin).HasTextureCoords(n)) { + while ((**begin).HasTextureCoords(n)) { out->mNumUVComponents[n] = (*begin)->mNumUVComponents[n]; pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices]; for (std::vector::const_iterator it = begin; it != end;++it) { - if ((*it)->mTextureCoords[n]) { ::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D)); + } else { + DefaultLogger::get()->warn( "JoinMeshes: UVs expected but input mesh contains no UVs" ); } - else DefaultLogger::get()->warn("JoinMeshes: UVs expected but input mesh contains no UVs"); pv2 += (*it)->mNumVertices; } ++n; @@ -848,11 +850,11 @@ void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/, while ((**begin).HasVertexColors(n)) { aiColor4D* pv2 = out->mColors[n] = new aiColor4D[out->mNumVertices]; for (std::vector::const_iterator it = begin; it != end;++it) { - if ((*it)->mColors[n]) { ::memcpy(pv2,(*it)->mColors[n],(*it)->mNumVertices*sizeof(aiColor4D)); + } else { + DefaultLogger::get()->warn( "JoinMeshes: VCs expected but input mesh contains no VCs" ); } - else DefaultLogger::get()->warn("JoinMeshes: VCs expected but input mesh contains no VCs"); pv2 += (*it)->mNumVertices; } ++n; diff --git a/include/assimp/DefaultLogger.hpp b/include/assimp/DefaultLogger.hpp index d6a88b0f3..1f0c899be 100644 --- a/include/assimp/DefaultLogger.hpp +++ b/include/assimp/DefaultLogger.hpp @@ -131,9 +131,7 @@ public: bool detatchStream(LogStream *pStream, unsigned int severity); - private: - // ---------------------------------------------------------------------- /** @briefPrivate construction for internal use by create(). * @param severity Logging granularity */ @@ -143,8 +141,6 @@ private: /** @briefDestructor */ ~DefaultLogger(); -private: - /** @brief Logs debug infos, only been written when severity level VERBOSE is set */ void OnDebug(const char* message); From d0edb942087795887e347a60755f1ef8e9be95f7 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Mar 2018 20:38:03 +0100 Subject: [PATCH 136/183] Update D3MFImporter.cpp Use static cast instead of c-cast. --- code/D3MFImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 535652502..c46685b45 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -282,7 +282,7 @@ private: MatIdArray = it->second; } } - MatIdArray.push_back( (unsigned int)newMatIdx ); + MatIdArray.push_back( static_cast( newMatIdx ) ); mMatId2MatArray[ mActiveMatGroup ] = MatIdArray; } From 339e33303e6af97cd986ddffe70ad753076cc990 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Mar 2018 20:39:15 +0100 Subject: [PATCH 137/183] Update DefaultIOSystem.cpp Use static_cast instead of int. --- code/DefaultIOSystem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/DefaultIOSystem.cpp b/code/DefaultIOSystem.cpp index 6e9c0321e..d65681b9b 100644 --- a/code/DefaultIOSystem.cpp +++ b/code/DefaultIOSystem.cpp @@ -76,7 +76,7 @@ bool DefaultIOSystem::Exists( const char* pFile) const #ifdef _WIN32 wchar_t fileName16[PATHLIMIT]; - bool isUnicode = IsTextUnicode(pFile, int(strlen(pFile)), NULL); + bool isUnicode = IsTextUnicode(pFile, static_cast(strlen(pFile)), NULL); if (isUnicode) { MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT); @@ -110,7 +110,7 @@ IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode) FILE* file; #ifdef _WIN32 wchar_t fileName16[PATHLIMIT]; - bool isUnicode = IsTextUnicode(strFile, int(strlen(strFile)), NULL ); + bool isUnicode = IsTextUnicode(strFile, static_cast(strlen(strFile)), NULL ); if (isUnicode) { MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT); std::string mode8(strMode); @@ -158,7 +158,7 @@ inline static void MakeAbsolutePath (const char* in, char* _out) { ai_assert(in && _out); #if defined( _MSC_VER ) || defined( __MINGW32__ ) - bool isUnicode = IsTextUnicode(in, int(strlen(in)), NULL); + bool isUnicode = IsTextUnicode(in, static_cast(strlen(in)), NULL); if (isUnicode) { wchar_t out16[PATHLIMIT]; wchar_t in16[PATHLIMIT]; From b01594f6a82c899fec8dfd4f81c5c4ee852d7129 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Mar 2018 21:52:42 +0100 Subject: [PATCH 138/183] Update EmbedTexturesProcess.cpp Replace c-cast by static_cast. --- code/EmbedTexturesProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/EmbedTexturesProcess.cpp b/code/EmbedTexturesProcess.cpp index b434d79d7..27313869d 100644 --- a/code/EmbedTexturesProcess.cpp +++ b/code/EmbedTexturesProcess.cpp @@ -134,7 +134,7 @@ bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { // Add the new texture auto pTexture = new aiTexture(); pTexture->mHeight = 0; // Means that this is still compressed - pTexture->mWidth = uint32_t(imageSize); + pTexture->mWidth = static_cats(imageSize); pTexture->pcData = imageContent; auto extension = path.substr(path.find_last_of('.') + 1u); From 1a887a64c193abde8b1f64b7981df19704c0d0f3 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Mar 2018 21:56:26 +0100 Subject: [PATCH 139/183] Update EmbedTexturesProcess.cpp Fix typo. --- code/EmbedTexturesProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/EmbedTexturesProcess.cpp b/code/EmbedTexturesProcess.cpp index 27313869d..076339468 100644 --- a/code/EmbedTexturesProcess.cpp +++ b/code/EmbedTexturesProcess.cpp @@ -134,7 +134,7 @@ bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { // Add the new texture auto pTexture = new aiTexture(); pTexture->mHeight = 0; // Means that this is still compressed - pTexture->mWidth = static_cats(imageSize); + pTexture->mWidth = static_cast(imageSize); pTexture->pcData = imageContent; auto extension = path.substr(path.find_last_of('.') + 1u); From 4ae7298489921ead2e3b2e500feec888846eff9f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Mar 2018 21:59:45 +0100 Subject: [PATCH 140/183] Update DefaultIOSystem.cpp fix typo --- code/DefaultIOSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/DefaultIOSystem.cpp b/code/DefaultIOSystem.cpp index d65681b9b..36c14e41d 100644 --- a/code/DefaultIOSystem.cpp +++ b/code/DefaultIOSystem.cpp @@ -76,7 +76,7 @@ bool DefaultIOSystem::Exists( const char* pFile) const #ifdef _WIN32 wchar_t fileName16[PATHLIMIT]; - bool isUnicode = IsTextUnicode(pFile, static_cast(strlen(pFile)), NULL); + bool isUnicode = IsTextUnicode(pFile, static_cast(strlen(pFile)), NULL); if (isUnicode) { MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT); From af3bba15726f586d4b43bfa9bab2b9b7535dd7ce Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 20 Mar 2018 23:38:08 +0100 Subject: [PATCH 141/183] fix review findings. --- code/Importer/IFC/IFCLoader.cpp | 5 ++- code/ObjExporter.cpp | 27 ++++++++------- code/ObjFileImporter.cpp | 30 ++++++----------- code/ObjFileParser.cpp | 7 ++-- code/OgreParsingUtils.h | 55 +++++++++++++------------------ code/OgreXmlSerializer.cpp | 19 +++++------ include/assimp/ParsingUtils.h | 53 ++++++++++++++++------------- include/assimp/StringComparison.h | 41 +++++++++++++---------- include/assimp/StringUtils.h | 12 ++++--- 9 files changed, 122 insertions(+), 127 deletions(-) diff --git a/code/Importer/IFC/IFCLoader.cpp b/code/Importer/IFC/IFCLoader.cpp index 5acc89545..9faf68cbb 100644 --- a/code/Importer/IFC/IFCLoader.cpp +++ b/code/Importer/IFC/IFCLoader.cpp @@ -583,9 +583,8 @@ typedef std::map Metadata; // ------------------------------------------------------------------------------------------------ void ProcessMetadata(const Schema_2x3::ListOf< Schema_2x3::Lazy< Schema_2x3::IfcProperty >, 1, 0 >& set, ConversionData& conv, Metadata& properties, - const std::string& prefix = "", - unsigned int nest = 0) -{ + const std::string& prefix = "", + unsigned int nest = 0) { for(const Schema_2x3::IfcProperty& property : set) { const std::string& key = prefix.length() > 0 ? (prefix + "." + property.Name) : property.Name; if (const Schema_2x3::IfcPropertySingleValue* const singleValue = property.ToPtr()) { diff --git a/code/ObjExporter.cpp b/code/ObjExporter.cpp index 9beb418f3..d08b5f859 100644 --- a/code/ObjExporter.cpp +++ b/code/ObjExporter.cpp @@ -132,18 +132,18 @@ ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene, bool noMt mOutputMat.precision(16); WriteGeometryFile(noMtl); - if (!noMtl) + if ( !noMtl ) { WriteMaterialFile(); + } } // ------------------------------------------------------------------------------------------------ ObjExporter::~ObjExporter() { - + // empty } // ------------------------------------------------------------------------------------------------ -std::string ObjExporter :: GetMaterialLibName() -{ +std::string ObjExporter::GetMaterialLibName() { // within the Obj file, we use just the relative file name with the path stripped const std::string& s = GetMaterialLibFileName(); std::string::size_type il = s.find_last_of("/\\"); @@ -158,8 +158,9 @@ std::string ObjExporter :: GetMaterialLibName() std::string ObjExporter::GetMaterialLibFileName() { // Remove existing .obj file extension so that the final material file name will be fileName.mtl and not fileName.obj.mtl size_t lastdot = filename.find_last_of('.'); - if (lastdot != std::string::npos) - return filename.substr(0, lastdot) + MaterialExt; + if ( lastdot != std::string::npos ) { + return filename.substr( 0, lastdot ) + MaterialExt; + } return filename + MaterialExt; } @@ -172,8 +173,7 @@ void ObjExporter::WriteHeader(std::ostringstream& out) { } // ------------------------------------------------------------------------------------------------ -std::string ObjExporter::GetMaterialName(unsigned int index) -{ +std::string ObjExporter::GetMaterialName(unsigned int index) { const aiMaterial* const mat = pScene->mMaterials[index]; if ( nullptr == mat ) { static const std::string EmptyStr; @@ -191,8 +191,7 @@ std::string ObjExporter::GetMaterialName(unsigned int index) } // ------------------------------------------------------------------------------------------------ -void ObjExporter::WriteMaterialFile() -{ +void ObjExporter::WriteMaterialFile() { WriteHeader(mOutputMat); for(unsigned int i = 0; i < pScene->mNumMaterials; ++i) { @@ -310,8 +309,9 @@ void ObjExporter::WriteGeometryFile(bool noMtl) { if (!m.name.empty()) { mOutput << "g " << m.name << endl; } - if (!noMtl) + if ( !noMtl ) { mOutput << "usemtl " << m.matname << endl; + } for(const Face& f : m.faces) { mOutput << f.kind << ' '; @@ -382,7 +382,7 @@ void ObjExporter::colIndexMap::getColors( std::vector &colors ) { // ------------------------------------------------------------------------------------------------ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat) { - mMeshes.push_back(MeshInstance()); + mMeshes.push_back(MeshInstance() ); MeshInstance& mesh = mMeshes.back(); mesh.name = std::string( name.data, name.length ); @@ -436,8 +436,7 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4 } // ------------------------------------------------------------------------------------------------ -void ObjExporter::AddNode(const aiNode* nd, const aiMatrix4x4& mParent) -{ +void ObjExporter::AddNode(const aiNode* nd, const aiMatrix4x4& mParent) { const aiMatrix4x4& mAbs = mParent * nd->mTransformation; for(unsigned int i = 0; i < nd->mNumMeshes; ++i) { diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp index 02d6ac581..bce94ebbb 100644 --- a/code/ObjFileImporter.cpp +++ b/code/ObjFileImporter.cpp @@ -207,30 +207,24 @@ void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene // Create the root node of the scene pScene->mRootNode = new aiNode; - if ( !pModel->m_ModelName.empty() ) - { + if ( !pModel->m_ModelName.empty() ) { // Set the name of the scene pScene->mRootNode->mName.Set(pModel->m_ModelName); - } - else - { + } else { // This is a fatal error, so break down the application ai_assert(false); } // Create nodes for the whole scene std::vector MeshArray; - for (size_t index = 0; index < pModel->m_Objects.size(); index++) - { + for (size_t index = 0; index < pModel->m_Objects.size(); ++index ) { createNodes(pModel, pModel->m_Objects[ index ], pScene->mRootNode, pScene, MeshArray); } // Create mesh pointer buffer for this scene - if (pScene->mNumMeshes > 0) - { + if (pScene->mNumMeshes > 0) { pScene->mMeshes = new aiMesh*[ MeshArray.size() ]; - for (size_t index =0; index < MeshArray.size(); index++) - { + for (size_t index =0; index < MeshArray.size(); ++index ) { pScene->mMeshes[ index ] = MeshArray[ index ]; } } @@ -261,8 +255,7 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile appendChildToParentNode( pParent, pNode ); } - for ( size_t i=0; i< pObject->m_Meshes.size(); i++ ) - { + for ( size_t i=0; i< pObject->m_Meshes.size(); ++i ) { unsigned int meshId = pObject->m_Meshes[ i ]; aiMesh *pMesh = createTopology( pModel, pObject, meshId ); if( pMesh ) { @@ -275,8 +268,7 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile } // Create all nodes from the sub-objects stored in the current object - if ( !pObject->m_SubObjects.empty() ) - { + if ( !pObject->m_SubObjects.empty() ) { size_t numChilds = pObject->m_SubObjects.size(); pNode->mNumChildren = static_cast( numChilds ); pNode->mChildren = new aiNode*[ numChilds ]; @@ -286,16 +278,14 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile // Set mesh instances into scene- and node-instances const size_t meshSizeDiff = MeshArray.size()- oldMeshSize; - if ( meshSizeDiff > 0 ) - { + if ( meshSizeDiff > 0 ) { pNode->mMeshes = new unsigned int[ meshSizeDiff ]; pNode->mNumMeshes = static_cast( meshSizeDiff ); size_t index = 0; - for (size_t i = oldMeshSize; i < MeshArray.size(); i++) - { + for (size_t i = oldMeshSize; i < MeshArray.size(); ++i ) { pNode->mMeshes[ index ] = pScene->mNumMeshes; pScene->mNumMeshes++; - index++; + ++index; } } diff --git a/code/ObjFileParser.cpp b/code/ObjFileParser.cpp index ae95edbcb..7cd2d36c2 100644 --- a/code/ObjFileParser.cpp +++ b/code/ObjFileParser.cpp @@ -612,13 +612,14 @@ void ObjFileParser::getMaterialLib() { if ( '/' != *path.rbegin() ) { path += '/'; } - absName = path + strMatName; + absName += path; + absName += strMatName; } else { absName = strMatName; } - IOStream *pFile = m_pIO->Open( absName ); - if (!pFile ) { + IOStream *pFile = m_pIO->Open( absName ); + if ( nullptr == pFile ) { DefaultLogger::get()->error("OBJ: Unable to locate material file " + strMatName); std::string strMatFallbackName = m_originalObjFileName.substr(0, m_originalObjFileName.length() - 3) + "mtl"; DefaultLogger::get()->info("OBJ: Opening fallback material file " + strMatFallbackName); diff --git a/code/OgreParsingUtils.h b/code/OgreParsingUtils.h index a77b94121..c6b6e0caf 100644 --- a/code/OgreParsingUtils.h +++ b/code/OgreParsingUtils.h @@ -52,27 +52,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -namespace Assimp -{ -namespace Ogre -{ +namespace Assimp { +namespace Ogre { /// Returns a lower cased copy of @s. -static inline std::string ToLower(std::string s) +static AI_FORCE_INLINE +std::string ToLower(std::string s) { std::transform(s.begin(), s.end(), s.begin(), ::tolower); return s; } /// Returns if @c s ends with @c suffix. If @c caseSensitive is false, both strings will be lower cased before matching. -static inline bool EndsWith(const std::string &s, const std::string &suffix, bool caseSensitive = true) -{ - if (s.empty() || suffix.empty()) - { +static AI_FORCE_INLINE +bool EndsWith(const std::string &s, const std::string &suffix, bool caseSensitive = true) { + if (s.empty() || suffix.empty()) { return false; - } - else if (s.length() < suffix.length()) - { + } else if (s.length() < suffix.length()) { return false; } @@ -82,48 +78,43 @@ static inline bool EndsWith(const std::string &s, const std::string &suffix, boo size_t len = suffix.length(); std::string sSuffix = s.substr(s.length()-len, len); + return (ASSIMP_stricmp(sSuffix, suffix) == 0); } // Below trim functions adapted from http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring /// Trim from start -static inline std::string &TrimLeft(std::string &s, bool newlines = true) -{ - if (!newlines) - { +static AI_FORCE_INLINE +std::string &TrimLeft(std::string &s, bool newlines = true) { + if (!newlines) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](char c) { return !Assimp::IsSpace(c); })); - } - else - { + } else { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](char c) { return !Assimp::IsSpaceOrNewLine(c); })); } return s; } /// Trim from end -static inline std::string &TrimRight(std::string &s, bool newlines = true) -{ - if (!newlines) - { +static AI_FORCE_INLINE +std::string &TrimRight(std::string &s, bool newlines = true) { + if (!newlines) { s.erase(std::find_if(s.rbegin(), s.rend(), [](char c) { return !Assimp::IsSpace(c); }).base(),s.end()); - } - else - { + } else { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](char c) { return !Assimp::IsSpaceOrNewLine(c); })); } return s; } /// Trim from both ends -static inline std::string &Trim(std::string &s, bool newlines = true) -{ +static AI_FORCE_INLINE +std::string &Trim(std::string &s, bool newlines = true) { return TrimLeft(TrimRight(s, newlines), newlines); } /// Skips a line from current @ss position until a newline. Returns the skipped part. -static inline std::string SkipLine(std::stringstream &ss) -{ +static AI_FORCE_INLINE +std::string SkipLine(std::stringstream &ss) { std::string skipped; getline(ss, skipped); return skipped; @@ -131,8 +122,8 @@ static inline std::string SkipLine(std::stringstream &ss) /// Skips a line and reads next element from @c ss to @c nextElement. /** @return Skipped line content until newline. */ -static inline std::string NextAfterNewLine(std::stringstream &ss, std::string &nextElement) -{ +static AI_FORCE_INLINE +std::string NextAfterNewLine(std::stringstream &ss, std::string &nextElement) { std::string skipped = SkipLine(ss); ss >> nextElement; return skipped; diff --git a/code/OgreXmlSerializer.cpp b/code/OgreXmlSerializer.cpp index 78acaa7fd..12b2bcbd9 100644 --- a/code/OgreXmlSerializer.cpp +++ b/code/OgreXmlSerializer.cpp @@ -213,18 +213,18 @@ std::string &OgreXmlSerializer::SkipCurrentNode() DefaultLogger::get()->debug("Skipping node <" + m_currentNodeName + ">"); #endif - for(;;) - { - if (!m_reader->read()) - { + for(;;) { + if (!m_reader->read()) { m_currentNodeName = ""; return m_currentNodeName; } - if (m_reader->getNodeType() != irr::io::EXN_ELEMENT_END) + if ( m_reader->getNodeType() != irr::io::EXN_ELEMENT_END ) { continue; - else if (std::string(m_reader->getNodeName()) == m_currentNodeName) + } else if ( std::string( m_reader->getNodeName() ) == m_currentNodeName ) { break; + } } + return NextNode(); } @@ -303,17 +303,16 @@ static const char *anZ = "z"; // Mesh -MeshXml *OgreXmlSerializer::ImportMesh(XmlReader *reader) -{ +MeshXml *OgreXmlSerializer::ImportMesh(XmlReader *reader) { OgreXmlSerializer serializer(reader); MeshXml *mesh = new MeshXml(); serializer.ReadMesh(mesh); + return mesh; } -void OgreXmlSerializer::ReadMesh(MeshXml *mesh) -{ +void OgreXmlSerializer::ReadMesh(MeshXml *mesh) { if (NextNode() != nnMesh) { throw DeadlyImportError("Root node is <" + m_currentNodeName + "> expecting "); } diff --git a/include/assimp/ParsingUtils.h b/include/assimp/ParsingUtils.h index 40e183a5c..4553072db 100644 --- a/include/assimp/ParsingUtils.h +++ b/include/assimp/ParsingUtils.h @@ -115,8 +115,8 @@ bool IsSpaceOrNewLine( char_t in) { // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool SkipSpaces( const char_t* in, const char_t** out) -{ +AI_FORCE_INLINE +bool SkipSpaces( const char_t* in, const char_t** out) { while( *in == ( char_t )' ' || *in == ( char_t )'\t' ) { ++in; } @@ -126,15 +126,15 @@ AI_FORCE_INLINE bool SkipSpaces( const char_t* in, const char_t** out) // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool SkipSpaces( const char_t** inout) -{ +AI_FORCE_INLINE +bool SkipSpaces( const char_t** inout) { return SkipSpaces(*inout,inout); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool SkipLine( const char_t* in, const char_t** out) -{ +AI_FORCE_INLINE +bool SkipLine( const char_t* in, const char_t** out) { while( *in != ( char_t )'\r' && *in != ( char_t )'\n' && *in != ( char_t )'\0' ) { ++in; } @@ -149,15 +149,15 @@ AI_FORCE_INLINE bool SkipLine( const char_t* in, const char_t** out) // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool SkipLine( const char_t** inout) -{ +AI_FORCE_INLINE +bool SkipLine( const char_t** inout) { return SkipLine(*inout,inout); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool SkipSpacesAndLineEnd( const char_t* in, const char_t** out) -{ +AI_FORCE_INLINE +bool SkipSpacesAndLineEnd( const char_t* in, const char_t** out) { while( *in == ( char_t )' ' || *in == ( char_t )'\t' || *in == ( char_t )'\r' || *in == ( char_t )'\n' ) { ++in; } @@ -167,15 +167,15 @@ AI_FORCE_INLINE bool SkipSpacesAndLineEnd( const char_t* in, const char_t** out) // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool SkipSpacesAndLineEnd( const char_t** inout) -{ +AI_FORCE_INLINE +bool SkipSpacesAndLineEnd( const char_t** inout) { return SkipSpacesAndLineEnd(*inout,inout); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool GetNextLine( const char_t*& buffer, char_t out[ BufferSize ] ) -{ +AI_FORCE_INLINE +bool GetNextLine( const char_t*& buffer, char_t out[ BufferSize ] ) { if( ( char_t )'\0' == *buffer ) { return false; } @@ -203,7 +203,8 @@ AI_FORCE_INLINE bool IsNumeric( char_t in) // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool TokenMatch(char_t*& in, const char* token, unsigned int len) +AI_FORCE_INLINE +bool TokenMatch(char_t*& in, const char* token, unsigned int len) { if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) { if (in[len] != '\0') { @@ -223,26 +224,32 @@ AI_FORCE_INLINE bool TokenMatch(char_t*& in, const char* token, unsigned int len * @param token Token to check for * @param len Number of characters to check */ -AI_FORCE_INLINE bool TokenMatchI(const char*& in, const char* token, unsigned int len) -{ +AI_FORCE_INLINE +bool TokenMatchI(const char*& in, const char* token, unsigned int len) { if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len])) { in += len+1; return true; } return false; } + // --------------------------------------------------------------------------------- -AI_FORCE_INLINE void SkipToken(const char*& in) -{ +AI_FORCE_INLINE +void SkipToken(const char*& in) { SkipSpaces(&in); - while (!IsSpaceOrNewLine(*in))++in; + while ( !IsSpaceOrNewLine( *in ) ) { + ++in; + } } + // --------------------------------------------------------------------------------- -AI_FORCE_INLINE std::string GetNextToken(const char*& in) -{ +AI_FORCE_INLINE +std::string GetNextToken(const char*& in) { SkipSpacesAndLineEnd(&in); const char* cur = in; - while (!IsSpaceOrNewLine(*in))++in; + while ( !IsSpaceOrNewLine( *in ) ) { + ++in; + } return std::string(cur,(size_t)(in-cur)); } diff --git a/include/assimp/StringComparison.h b/include/assimp/StringComparison.h index 1fada49dd..aea7f001a 100644 --- a/include/assimp/StringComparison.h +++ b/include/assimp/StringComparison.h @@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define INCLUDED_AI_STRING_WORKERS_H #include +#include #include "StringComparison.h" #include @@ -72,8 +73,8 @@ namespace Assimp { * @param number Number to be written * @return Length of the output string, excluding the '\0' */ -inline unsigned int ASSIMP_itoa10( char* out, unsigned int max, int32_t number) -{ +AI_FORCE_INLINE +unsigned int ASSIMP_itoa10( char* out, unsigned int max, int32_t number) { ai_assert(NULL != out); // write the unary minus to indicate we have a negative number @@ -91,7 +92,7 @@ inline unsigned int ASSIMP_itoa10( char* out, unsigned int max, int32_t number) const unsigned int digit = number / cur; if (mustPrint || digit > 0 || 1 == cur) { - // print all future zeroes from now + // print all future zeroe's from now mustPrint = true; *out++ = '0'+static_cast(digit); @@ -116,8 +117,8 @@ inline unsigned int ASSIMP_itoa10( char* out, unsigned int max, int32_t number) * size of the array automatically. */ template -inline unsigned int ASSIMP_itoa10( char(& out)[length], int32_t number) -{ +AI_FORCE_INLINE +unsigned int ASSIMP_itoa10( char(& out)[length], int32_t number) { return ASSIMP_itoa10(out,length,number); } @@ -132,9 +133,10 @@ inline unsigned int ASSIMP_itoa10( char(& out)[length], int32_t number) * @param s2 Second input string * @return 0 if the given strings are identical */ -inline int ASSIMP_stricmp(const char *s1, const char *s2) -{ - ai_assert(NULL != s1 && NULL != s2); +AI_FORCE_INLINE +int ASSIMP_stricmp(const char *s1, const char *s2) { + ai_assert( NULL != s1 ); + ai_assert( NULL != s2 ); #if (defined _MSC_VER) @@ -161,8 +163,8 @@ inline int ASSIMP_stricmp(const char *s1, const char *s2) * @param b Second string * @return 0 if a == b */ -inline int ASSIMP_stricmp(const std::string& a, const std::string& b) -{ +AI_FORCE_INLINE +int ASSIMP_stricmp(const std::string& a, const std::string& b) { int i = (int)b.length()-(int)a.length(); return (i ? i : ASSIMP_stricmp(a.c_str(),b.c_str())); } @@ -179,10 +181,13 @@ inline int ASSIMP_stricmp(const std::string& a, const std::string& b) * @param n Macimum number of characters to compare * @return 0 if the given strings are identical */ -inline int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n) -{ - ai_assert(NULL != s1 && NULL != s2); - if (!n)return 0; +AI_FORCE_INLINE +int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n) { + ai_assert( NULL != s1 ); + ai_assert( NULL != s2 ); + if ( !n ) { + return 0; + } #if (defined _MSC_VER) @@ -213,14 +218,16 @@ inline int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n) * * todo: move somewhere where it fits better in than here */ -inline unsigned int integer_pow (unsigned int base, unsigned int power) -{ +AI_FORCE_INLINE +unsigned int integer_pow( unsigned int base, unsigned int power ) { unsigned int res = 1; - for (unsigned int i = 0; i < power;++i) + for ( unsigned int i = 0; i < power; ++i ) { res *= base; + } return res; } + } // end of namespace #endif // ! AI_STRINGCOMPARISON_H_INC diff --git a/include/assimp/StringUtils.h b/include/assimp/StringUtils.h index 157454126..906898b53 100644 --- a/include/assimp/StringUtils.h +++ b/include/assimp/StringUtils.h @@ -42,6 +42,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef INCLUDED_AI_STRINGUTILS_H #define INCLUDED_AI_STRINGUTILS_H +#include + #include #include #include @@ -55,7 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /// @return The number of written characters if the buffer size was big enough. If an encoding error occurs, a negative number is returned. #if defined(_MSC_VER) && _MSC_VER < 1900 - inline + AI_FORCE_INLINE int c99_ai_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) { int count(-1); if (0 != size) { @@ -68,7 +70,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. return count; } - inline + AI_FORCE_INLINE int ai_snprintf(char *outBuf, size_t size, const char *format, ...) { int count; va_list ap; @@ -89,7 +91,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /// @param value The value to write into the std::string. /// @return The value as a std::string template -inline +AI_FORCE_INLINE std::string to_string( T value ) { std::ostringstream os; os << value; @@ -102,7 +104,7 @@ std::string to_string( T value ) { /// @param begin The first character of the string. /// @param end The last character /// @return The float value, 0.0f in cas of an error. -inline +AI_FORCE_INLINE float ai_strtof( const char *begin, const char *end ) { if ( nullptr == begin ) { return 0.0f; @@ -124,7 +126,7 @@ float ai_strtof( const char *begin, const char *end ) { /// @param toConvert Value to convert /// @return The hexadecimal string, is empty in case of an error. template -inline +AI_FORCE_INLINE std::string DecimalToHexa( T toConvert ) { std::string result; std::stringstream ss; From efd4a6c37103ef66210cac53026890f45ec29b9f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 21 Mar 2018 20:31:04 +0100 Subject: [PATCH 142/183] Update Readme.md - Fix broken link - Update list with supported importers + exporters --- Readme.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index a6f2e3d3d..4ee8a0e90 100644 --- a/Readme.md +++ b/Readme.md @@ -32,8 +32,8 @@ Please check our Wiki as well: https://github.com/assimp/assimp/wiki #### Supported file formats #### -A full list [is here](http://assimp.org/main_features_formats.html). __Importers__: + - 3D - 3DS - 3MF @@ -109,6 +109,8 @@ __Exporters__: - STEP - glTF 1.0 (partial) - glTF 2.0 (partial) +- 3MF ( experimental ) +- FBX ( experimental ) ### Building ### Take a look into the `INSTALL` file. Our build system is CMake, if you used CMake before there is a good chance you know what to do. From cad11df0394b6a4f9f5f2e5f945f605ea8cd44df Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Thu, 22 Mar 2018 16:54:12 +0800 Subject: [PATCH 143/183] Fix mingw compilation --- code/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 6e2db91db..dafe017ef 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -485,9 +485,9 @@ ADD_ASSIMP_IMPORTER( IFC ) if (ASSIMP_BUILD_IFC_IMPORTER) if (MSVC) - set_source_files_properties(IFCReaderGen1.cpp IFCReaderGen2.cpp PROPERTIES COMPILE_FLAGS "/bigobj") + set_source_files_properties(Importer/IFC/IFCReaderGen1_2x3.cpp Importer/IFC/IFCReaderGen2_2x3.cpp PROPERTIES COMPILE_FLAGS "/bigobj") elseif(CMAKE_COMPILER_IS_MINGW) - set_source_files_properties(IFCReaderGen1.cpp IFCReaderGen2.cpp PROPERTIES COMPILE_FLAGS "-O2 -Wa,-mbig-obj") + set_source_files_properties(Importer/IFC/IFCReaderGen1_2x3.cpp Importer/IFC/IFCReaderGen2_2x3.cpp PROPERTIES COMPILE_FLAGS "-O2 -Wa,-mbig-obj") endif() endif (ASSIMP_BUILD_IFC_IMPORTER) From 6c21a30958b718e78144e2f492563f07d1ca6bf4 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 22 Mar 2018 20:07:47 +0100 Subject: [PATCH 144/183] closes https://github.com/assimp/assimp/issues/1850: remove buggy setup in cmake. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 15374a3fb..fe4239351 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,7 +110,7 @@ if (WIN32) endif() IF(MSVC) - SET (CMAKE_PREFIX_PATH "D:\\libs\\devil") +# SET (CMAKE_PREFIX_PATH "D:\\libs\\devil") OPTION( ASSIMP_INSTALL_PDB "Install MSVC debug files." ON From 349d877899ea855e68a1baa0a01a1a5ebeb2bd17 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 22 Mar 2018 21:28:52 +0100 Subject: [PATCH 145/183] Update CMakeLists.txt Remove dead code. --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fe4239351..0026dcb7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,7 +110,6 @@ if (WIN32) endif() IF(MSVC) -# SET (CMAKE_PREFIX_PATH "D:\\libs\\devil") OPTION( ASSIMP_INSTALL_PDB "Install MSVC debug files." ON From 13ae0a0ac3a1f464158b24e341fe2a889ce48904 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 22 Mar 2018 22:14:10 +0100 Subject: [PATCH 146/183] FBX: fix parse error for uv-coordinates. --- code/FBXMeshGeometry.cpp | 25 ++++++++++---------- code/FBXMeshGeometry.h | 5 ++-- include/assimp/IOStreamBuffer.h | 41 +++++++++++++++++---------------- 3 files changed, 35 insertions(+), 36 deletions(-) diff --git a/code/FBXMeshGeometry.cpp b/code/FBXMeshGeometry.cpp index 6f7b84d96..12001f76c 100644 --- a/code/FBXMeshGeometry.cpp +++ b/code/FBXMeshGeometry.cpp @@ -79,14 +79,13 @@ Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, // ------------------------------------------------------------------------------------------------ Geometry::~Geometry() { - + // empty } const Skin* Geometry::DeformerSkin() const { return skin; } - // ------------------------------------------------------------------------------------------------ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) : Geometry(id, element,name, doc) @@ -186,9 +185,8 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin } // ------------------------------------------------------------------------------------------------ -MeshGeometry::~MeshGeometry() -{ - +MeshGeometry::~MeshGeometry() { + // empty } // ------------------------------------------------------------------------------------------------ @@ -218,7 +216,7 @@ const std::vector& MeshGeometry::GetFaceIndexCounts() const { // ------------------------------------------------------------------------------------------------ const std::vector& MeshGeometry::GetTextureCoords( unsigned int index ) const { - static const std::vector empty; + const std::vector empty; return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : m_uvs[ index ]; } @@ -227,7 +225,7 @@ std::string MeshGeometry::GetTextureCoordChannelName( unsigned int index ) const } const std::vector& MeshGeometry::GetVertexColors( unsigned int index ) const { - static const std::vector empty; + const std::vector empty; return index >= AI_MAX_NUMBER_OF_COLOR_SETS ? empty : m_colors[ index ]; } @@ -308,7 +306,6 @@ void MeshGeometry::ReadLayerElement(const Scope& layerElement) << type << ", index: " << typedIndex); } - // ------------------------------------------------------------------------------------------------ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scope& source) { @@ -412,7 +409,6 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop } } - // ------------------------------------------------------------------------------------------------ // Lengthy utility function to read and resolve a FBX vertex data array - that is, the // output is in polygon vertex order. This logic is used for reading normals, UVs, colors, @@ -431,7 +427,7 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, bool isDirect = ReferenceInformationType == "Direct"; bool isIndexToDirect = ReferenceInformationType == "IndexToDirect"; - // fallback to direct data if there is no index data element + // fall-back to direct data if there is no index data element if ( isIndexToDirect && !HasElement( source, indexDataElementName ) ) { isDirect = true; isIndexToDirect = false; @@ -499,9 +495,14 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, return; } + const T empty; unsigned int next = 0; for(int i : uvIndices) { - if (static_cast(i) >= tempData.size()) { + if ( -1 == i ) { + data_out[ next++ ] = empty; + continue; + } + if (static_cast(i) >= tempData.size()) { DOMError("index out of range",&GetRequiredElement(source,indexDataElementName)); } @@ -528,7 +529,6 @@ void MeshGeometry::ReadVertexDataNormals(std::vector& normals_out, c m_mappings); } - // ------------------------------------------------------------------------------------------------ void MeshGeometry::ReadVertexDataUV(std::vector& uv_out, const Scope& source, const std::string& MappingInformationType, @@ -543,7 +543,6 @@ void MeshGeometry::ReadVertexDataUV(std::vector& uv_out, const Scope m_mappings); } - // ------------------------------------------------------------------------------------------------ void MeshGeometry::ReadVertexDataColors(std::vector& colors_out, const Scope& source, const std::string& MappingInformationType, diff --git a/code/FBXMeshGeometry.h b/code/FBXMeshGeometry.h index 5dbf6f491..acd44668a 100644 --- a/code/FBXMeshGeometry.h +++ b/code/FBXMeshGeometry.h @@ -68,7 +68,6 @@ private: const Skin* skin; }; - typedef std::vector MatIndexArray; @@ -95,8 +94,8 @@ public: * if no tangents are specified */ const std::vector& GetTangents() const; - /** Get a list of all vertex binormals or an empty array - * if no binormals are specified */ + /** Get a list of all vertex bi-normals or an empty array + * if no bi-normals are specified */ const std::vector& GetBinormals() const; /** Return list of faces - each entry denotes a face and specifies diff --git a/include/assimp/IOStreamBuffer.h b/include/assimp/IOStreamBuffer.h index fdc339062..c93a191df 100644 --- a/include/assimp/IOStreamBuffer.h +++ b/include/assimp/IOStreamBuffer.h @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include + #include "ParsingUtils.h" #include @@ -270,8 +271,8 @@ bool IOStreamBuffer::getNextDataLine( std::vector &buffer, T continuationT } buffer[ i ] = m_cache[ m_cachePos ]; - m_cachePos++; - i++; + ++m_cachePos; + ++i; if ( m_cachePos >= m_cacheSize ) { if ( !readNextBlock() ) { return false; @@ -280,13 +281,12 @@ bool IOStreamBuffer::getNextDataLine( std::vector &buffer, T continuationT } buffer[ i ] = '\n'; - m_cachePos++; + ++m_cachePos; return true; } -static -inline +static inline bool isEndOfCache( size_t pos, size_t cacheSize ) { return ( pos == cacheSize ); } @@ -314,11 +314,11 @@ bool IOStreamBuffer::getNextLine(std::vector &buffer) { } } - size_t i = 0; + size_t i( 0 ); while (!IsLineEnd(m_cache[ m_cachePos ])) { buffer[i] = m_cache[ m_cachePos ]; - m_cachePos++; - i++; + ++m_cachePos; + ++i; if (m_cachePos >= m_cacheSize) { if (!readNextBlock()) { return false; @@ -326,7 +326,7 @@ bool IOStreamBuffer::getNextLine(std::vector &buffer) { } } buffer[i] = '\n'; - m_cachePos++; + ++m_cachePos; return true; } @@ -334,18 +334,19 @@ bool IOStreamBuffer::getNextLine(std::vector &buffer) { template inline bool IOStreamBuffer::getNextBlock( std::vector &buffer) { - //just return the last blockvalue if getNextLine was used before - if ( m_cachePos != 0) { - buffer = std::vector(m_cache.begin() + m_cachePos, m_cache.end()); - m_cachePos = 0; - } - else { - if ( !readNextBlock() ) - return false; + // Return the last block-value if getNextLine was used before + if ( 0 != m_cachePos ) { + buffer = std::vector( m_cache.begin() + m_cachePos, m_cache.end() ); + m_cachePos = 0; + } else { + if ( !readNextBlock() ) { + return false; + } - buffer = std::vector(m_cache.begin(), m_cache.end()); - } - return true; + buffer = std::vector(m_cache.begin(), m_cache.end()); + } + + return true; } } // !ns Assimp From be4c7801152c3b54bb32eb7d4598a140b4efcd5f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 22 Mar 2018 23:08:25 +0100 Subject: [PATCH 147/183] Update FBXMeshGeometry.cpp Fix build. --- code/FBXMeshGeometry.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/FBXMeshGeometry.cpp b/code/FBXMeshGeometry.cpp index 12001f76c..cc1a5a83e 100644 --- a/code/FBXMeshGeometry.cpp +++ b/code/FBXMeshGeometry.cpp @@ -216,7 +216,7 @@ const std::vector& MeshGeometry::GetFaceIndexCounts() const { // ------------------------------------------------------------------------------------------------ const std::vector& MeshGeometry::GetTextureCoords( unsigned int index ) const { - const std::vector empty; + static const std::vector empty; return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : m_uvs[ index ]; } @@ -225,7 +225,7 @@ std::string MeshGeometry::GetTextureCoordChannelName( unsigned int index ) const } const std::vector& MeshGeometry::GetVertexColors( unsigned int index ) const { - const std::vector empty; + static const std::vector empty; return index >= AI_MAX_NUMBER_OF_COLOR_SETS ? empty : m_colors[ index ]; } From 15e9907340f98aa2ad39a56ae072fe0994a45215 Mon Sep 17 00:00:00 2001 From: Tommy Date: Mon, 26 Mar 2018 11:25:32 +0200 Subject: [PATCH 148/183] Address some gcc warnings about signed / unsigned comparison. --- test/unit/utMetadata.cpp | 2 +- test/unit/utPLYImportExport.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/unit/utMetadata.cpp b/test/unit/utMetadata.cpp index 42f632bce..0801ffd3f 100644 --- a/test/unit/utMetadata.cpp +++ b/test/unit/utMetadata.cpp @@ -199,7 +199,7 @@ TEST_F( utMetadata, copy_test ) { m_data->Set( 6, "aiVector3D", vecVal ); aiMetadata copy( *m_data ); - EXPECT_EQ( 7, copy.mNumProperties ); + EXPECT_EQ( 7u, copy.mNumProperties ); // bool test { diff --git a/test/unit/utPLYImportExport.cpp b/test/unit/utPLYImportExport.cpp index e0f96195b..c009bda39 100644 --- a/test/unit/utPLYImportExport.cpp +++ b/test/unit/utPLYImportExport.cpp @@ -132,10 +132,10 @@ TEST_F( utPLYImportExport, vertexColorTest ) { EXPECT_EQ(true, scene->mMeshes[0]->HasVertexColors(0)); auto first_face = scene->mMeshes[0]->mFaces[0]; - EXPECT_EQ(3, first_face.mNumIndices); - EXPECT_EQ(0, first_face.mIndices[0]); - EXPECT_EQ(1, first_face.mIndices[1]); - EXPECT_EQ(2, first_face.mIndices[2]); + EXPECT_EQ(3u, first_face.mNumIndices); + EXPECT_EQ(0u, first_face.mIndices[0]); + EXPECT_EQ(1u, first_face.mIndices[1]); + EXPECT_EQ(2u, first_face.mIndices[2]); } //Test issue #623, PLY importer should not automatically create faces From 2dff6e2d5b434d0d738693fadd189f5dcfc7859f Mon Sep 17 00:00:00 2001 From: Tommy Date: Mon, 26 Mar 2018 18:27:15 +0200 Subject: [PATCH 149/183] FBXExportProperty: implement float and long array properties. --- code/FBXExportProperty.cpp | 36 ++++++++++++++++++++++++++++++++++++ code/FBXExportProperty.h | 2 ++ 2 files changed, 38 insertions(+) diff --git a/code/FBXExportProperty.cpp b/code/FBXExportProperty.cpp index 975e9a09a..e139bb95a 100644 --- a/code/FBXExportProperty.cpp +++ b/code/FBXExportProperty.cpp @@ -116,6 +116,20 @@ FBX::Property::Property(const std::vector& va) for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } } +FBX::Property::Property(const std::vector& va) + : type('l'), data(8*va.size()) +{ + int64_t* d = reinterpret_cast(data.data()); + for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } +} + +FBX::Property::Property(const std::vector& va) + : type('f'), data(4*va.size()) +{ + float* d = reinterpret_cast(data.data()); + for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } +} + FBX::Property::Property(const std::vector& va) : type('d'), data(8*va.size()) { @@ -178,6 +192,28 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s) s.PutI4((reinterpret_cast(d))[i]); } return; + case 'l': + N = data.size() / 8; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + d = data.data(); + for (size_t i = 0; i < N; ++i) { + s.PutI8((reinterpret_cast(d))[i]); + } + return; + case 'f': + N = data.size() / 4; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + d = data.data(); + for (size_t i = 0; i < N; ++i) { + s.PutF4((reinterpret_cast(d))[i]); + } + return; case 'd': N = data.size() / 8; s.PutU4(uint32_t(N)); // number of elements diff --git a/code/FBXExportProperty.h b/code/FBXExportProperty.h index 8920346a3..40a020688 100644 --- a/code/FBXExportProperty.h +++ b/code/FBXExportProperty.h @@ -96,7 +96,9 @@ public: explicit Property(const std::string& s, bool raw=false); explicit Property(const std::vector& r); explicit Property(const std::vector& va); + explicit Property(const std::vector& va); explicit Property(const std::vector& va); + explicit Property(const std::vector& va); explicit Property(const aiMatrix4x4& vm); // this will catch any type not defined above, From 57bd1258394b58ada6ec1295d86c2d774db66fc6 Mon Sep 17 00:00:00 2001 From: Tommy Date: Mon, 26 Mar 2018 18:30:47 +0200 Subject: [PATCH 150/183] FBX Export: implement basic animation export. --- code/FBXExporter.cpp | 439 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 434 insertions(+), 5 deletions(-) diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 9ba5c0333..2cb7e470a 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -525,8 +525,9 @@ void FBXExporter::WriteDefinitions () total_count += count; // AnimationStack / FbxAnimStack - // this seems to always be here in Maya exports - count = 0; + // this seems to always be here in Maya exports, + // but no harm seems to come of leaving it out. + count = mScene->mNumAnimations; if (count) { n = FBX::Node("ObjectType", Property("AnimationStack")); n.AddChild("Count", count); @@ -544,8 +545,11 @@ void FBXExporter::WriteDefinitions () } // AnimationLayer / FbxAnimLayer - // this seems to always be here in Maya exports - count = 0; + // this seems to always be here in Maya exports, + // but no harm seems to come of leaving it out. + // Assimp doesn't support animation layers, + // so there will be one per aiAnimation + count = mScene->mNumAnimations; if (count) { n = FBX::Node("ObjectType", Property("AnimationLayer")); n.AddChild("Count", count); @@ -821,7 +825,7 @@ void FBXExporter::WriteDefinitions () } // AnimationCurveNode / FbxAnimCurveNode - count = 0; + count = mScene->mNumAnimations * 3; if (count) { n = FBX::Node("ObjectType", Property("AnimationCurveNode")); n.AddChild("Count", count); @@ -834,6 +838,15 @@ void FBXExporter::WriteDefinitions () total_count += count; } + // AnimationCurve / FbxAnimCurve + count = mScene->mNumAnimations * 9; + if (count) { + n = FBX::Node("ObjectType", Property("AnimationCurve")); + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + } + // Pose count = 0; for (size_t i = 0; i < mScene->mNumMeshes; ++i) { @@ -911,6 +924,12 @@ aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene) return transform; } +int64_t to_ktime(double ticks, const aiAnimation* anim) { + if (anim->mTicksPerSecond <= 0) { + return ticks * FBX::SECOND; + } + return (ticks / anim->mTicksPerSecond) * FBX::SECOND; +} void FBXExporter::WriteObjects () { @@ -1849,6 +1868,416 @@ void FBXExporter::WriteObjects () outstream, mScene->mRootNode, 0, limbnodes ); + // animations + // + // in FBX there are: + // * AnimationStack - corresponds to an aiAnimation + // * AnimationLayer - a combinable animation component + // * AnimationCurveNode - links the property to be animated + // * AnimationCurve - defines animation data for a single property value + // + // the CurveNode also provides the default value for a property, + // such as the X, Y, Z coordinates for animatable translation. + // + // the Curve only specifies values for one component of the property, + // so there will be a separate AnimationCurve for X, Y, and Z. + // + // Assimp has: + // * aiAnimation - basically corresponds to an AnimationStack + // * aiNodeAnim - defines all animation for one aiNode + // * aiVectorKey/aiQuatKey - define the keyframe data for T/R/S + // + // assimp has no equivalent for AnimationLayer, + // and these are flattened on FBX import. + // we can assume there will be one per AnimationStack. + // + // the aiNodeAnim contains all animation data for a single aiNode, + // which will correspond to three AnimationCurveNode's: + // one each for translation, rotation and scale. + // The data for each of these will be put in 9 AnimationCurve's, + // T.X, T.Y, T.Z, R.X, R.Y, R.Z, etc. + + // AnimationStack / aiAnimation + std::vector animation_stack_uids(mScene->mNumAnimations); + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + int64_t animstack_uid = generate_uid(); + animation_stack_uids[ai] = animstack_uid; + const aiAnimation* anim = mScene->mAnimations[ai]; + + FBX::Node asnode("AnimationStack"); + std::string name = anim->mName.C_Str() + FBX::SEPARATOR + "AnimStack"; + asnode.AddProperties(animstack_uid, name, ""); + FBX::Node p("Properties70"); + p.AddP70time("LocalStart", 0); + p.AddP70time("LocalStop", 0); + p.AddP70time("ReferenceStart", 0); + p.AddP70time("ReferenceStop", 0); + asnode.AddChild(p); + + // this node absurdly always pretends it has children + asnode.Begin(outstream); + asnode.DumpProperties(outstream); + asnode.EndProperties(outstream); + asnode.DumpChildren(outstream); + asnode.End(outstream, true); + + // note: animation stacks are not connected to anything + } + + // AnimationLayer - one per aiAnimation + std::vector animation_layer_uids(mScene->mNumAnimations); + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + int64_t animlayer_uid = generate_uid(); + animation_layer_uids[ai] = animlayer_uid; + FBX::Node alnode("AnimationLayer"); + alnode.AddProperties(animlayer_uid, FBX::SEPARATOR + "AnimLayer", ""); + + // this node absurdly always pretends it has children + alnode.Begin(outstream); + alnode.DumpProperties(outstream); + alnode.EndProperties(outstream); + alnode.DumpChildren(outstream); + alnode.End(outstream, true); + + // connect to the relevant animstack + FBX::Node c("C"); + c.AddProperties("OO", animlayer_uid, animation_stack_uids[ai]); + connections.push_back(c); // TODO: emplace_back + } + + // AnimCurveNode - three per aiNodeAnim + std::vector>> curve_node_uids; + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + const aiAnimation* anim = mScene->mAnimations[ai]; + const int64_t layer_uid = animation_layer_uids[ai]; + std::vector> nodeanim_uids; + for (size_t nai = 0; nai < anim->mNumChannels; ++nai) { + const aiNodeAnim* na = anim->mChannels[nai]; + // get the corresponding aiNode + const aiNode* node = mScene->mRootNode->FindNode(na->mNodeName); + // and its transform + const aiMatrix4x4 node_xfm = get_world_transform(node, mScene); + aiVector3D T, R, S; + node_xfm.Decompose(S, R, T); + // generate uids for all AnimationCurveNode + std::array ids; + ids[0] = generate_uid(); // T + ids[1] = generate_uid(); // R + ids[2] = generate_uid(); // S + + // translation + FBX::Node t("AnimationCurveNode"); + t.AddProperties(ids[0], "T" + FBX::SEPARATOR + "AnimCurveNode", ""); + FBX::Node tp("Properties70"); + tp.AddP70numberA("d|X", T.x); + tp.AddP70numberA("d|Y", T.y); + tp.AddP70numberA("d|Z", T.z); + t.AddChild(tp); + t.Dump(outstream); + // connect to layer + FBX::Node tcl("C"); + tcl.AddProperties("OO", ids[0], layer_uid); + connections.push_back(tcl); // TODO: emplace_back + // connect to bone + FBX::Node tcb("C"); + tcb.AddProperties("OP", ids[0], node_uids[node], "Lcl Translation"); + connections.push_back(tcb); // TODO: emplace_back + + // rotation + FBX::Node r("AnimationCurveNode"); + r.AddProperties(ids[1], "R" + FBX::SEPARATOR + "AnimCurveNode", ""); + FBX::Node rp("Properties70"); + rp.AddP70numberA("d|X", DEG*R.x); + rp.AddP70numberA("d|Y", DEG*R.y); + rp.AddP70numberA("d|Z", DEG*R.z); + r.AddChild(rp); + r.Dump(outstream); + // connect to layer + FBX::Node rcl("C"); + rcl.AddProperties("OO", ids[1], layer_uid); + connections.push_back(rcl); // TODO: emplace_back + // connect to bone + FBX::Node rcb("C"); + rcb.AddProperties("OP", ids[1], node_uids[node], "Lcl Rotation"); + connections.push_back(rcb); // TODO: emplace_back + + // scale + FBX::Node s("AnimationCurveNode"); + s.AddProperties(ids[2], "S" + FBX::SEPARATOR + "AnimCurveNode", ""); + FBX::Node sp("Properties70"); + sp.AddP70numberA("d|X", S.x); + sp.AddP70numberA("d|Y", S.y); + sp.AddP70numberA("d|Z", S.z); + s.AddChild(sp); + s.Dump(outstream); + // connect to layer + FBX::Node scl("C"); + scl.AddProperties("OO", ids[2], layer_uid); + connections.push_back(scl); // TODO: emplace_back + // connect to bone + FBX::Node scb("C"); + scb.AddProperties("OP", ids[2], node_uids[node], "Lcl Scaling"); + connections.push_back(scb); // TODO: emplace_back + + nodeanim_uids.push_back(ids); + } + curve_node_uids.push_back(nodeanim_uids); + } + + // AnimCurve - defines actual keyframe data. + // there's a separate curve for every component of every vector, + // for example a transform curvenode will have separate X/Y/Z AnimCurve's + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + const aiAnimation* anim = mScene->mAnimations[ai]; + for (size_t nai = 0; nai < anim->mNumChannels; ++nai) { + const aiNodeAnim* na = anim->mChannels[nai]; + // get the corresponding aiNode + const aiNode* node = mScene->mRootNode->FindNode(na->mNodeName); + // and its transform + const aiMatrix4x4 node_xfm = get_world_transform(node, mScene); + aiVector3D T, R, S; + node_xfm.Decompose(S, R, T); + const std::array& ids = curve_node_uids[ai][nai]; + + int64_t curve_uid; + std::vector times; + std::vector values; + + // TODO: compress code (it's a bit annoying) + + // translation x + FBX::Node tx("AnimationCurve"); + curve_uid = generate_uid(); + tx.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); + tx.AddChild("Default", double(T.x)); // default value + tx.AddChild("KeyVer", int32_t(4009)); + times.clear(); + values.clear(); + for (size_t pki = 0; pki < na->mNumPositionKeys; ++pki) { + const aiVectorKey& k = na->mPositionKeys[pki]; + times.push_back(to_ktime(k.mTime, anim)); + values.push_back(k.mValue.x); + } + tx.AddChild("KeyTime", times); + tx.AddChild("KeyValueFloat", values); + // TODO: keyframe flags (STUB for now) + tx.AddChild("KeyAttrFlags", std::vector{0}); + tx.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); + tx.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); + tx.Dump(outstream); + FBX::Node txc("C"); + txc.AddProperties("OP", curve_uid, ids[0], "d|X"); + connections.push_back(txc); // TODO: emplace_back + + // translation y + FBX::Node ty("AnimationCurve"); + curve_uid = generate_uid(); + ty.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); + ty.AddChild("Default", double(T.y)); // default value + ty.AddChild("KeyVer", int32_t(4009)); + times.clear(); + values.clear(); + for (size_t pki = 0; pki < na->mNumPositionKeys; ++pki) { + const aiVectorKey& k = na->mPositionKeys[pki]; + times.push_back(to_ktime(k.mTime, anim)); + values.push_back(k.mValue.y); + } + ty.AddChild("KeyTime", times); + ty.AddChild("KeyValueFloat", values); + // TODO: keyframe flags (STUB for now) + ty.AddChild("KeyAttrFlags", std::vector{0}); + ty.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); + ty.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); + ty.Dump(outstream); + FBX::Node tyc("C"); + tyc.AddProperties("OP", curve_uid, ids[0], "d|Y"); + connections.push_back(tyc); // TODO: emplace_back + + // translation z + FBX::Node tz("AnimationCurve"); + curve_uid = generate_uid(); + tz.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); + tz.AddChild("Default", double(T.z)); // default value + tz.AddChild("KeyVer", int32_t(4009)); + times.clear(); + values.clear(); + for (size_t pki = 0; pki < na->mNumPositionKeys; ++pki) { + const aiVectorKey& k = na->mPositionKeys[pki]; + times.push_back(to_ktime(k.mTime, anim)); + values.push_back(k.mValue.z); + } + tz.AddChild("KeyTime", times); + tz.AddChild("KeyValueFloat", values); + // TODO: keyframe flags (STUB for now) + tz.AddChild("KeyAttrFlags", std::vector{0}); + tz.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); + tz.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); + tz.Dump(outstream); + FBX::Node tzc("C"); + tzc.AddProperties("OP", curve_uid, ids[0], "d|Z"); + connections.push_back(tzc); // TODO: emplace_back + + // rotation x + FBX::Node rx("AnimationCurve"); + curve_uid = generate_uid(); + rx.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); + rx.AddChild("Default", double(R.x)); // default value + rx.AddChild("KeyVer", int32_t(4009)); + times.clear(); + values.clear(); + for (size_t pki = 0; pki < na->mNumRotationKeys; ++pki) { + const aiQuatKey& k = na->mRotationKeys[pki]; + times.push_back(to_ktime(k.mTime, anim)); + // there must be a better way... + aiMatrix4x4 m(k.mValue.GetMatrix()); + aiVector3D qs, qr, qt; + m.Decompose(qs, qr, qt); + qr *= DEG; + values.push_back(qr.x); + } + rx.AddChild("KeyTime", times); + rx.AddChild("KeyValueFloat", values); + // TODO: keyframe flags (STUB for now) + rx.AddChild("KeyAttrFlags", std::vector{0}); + rx.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); + rx.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); + rx.Dump(outstream); + FBX::Node rxc("C"); + rxc.AddProperties("OP", curve_uid, ids[1], "d|X"); + connections.push_back(rxc); // TODO: emplace_back + + // rotation y + FBX::Node ry("AnimationCurve"); + curve_uid = generate_uid(); + ry.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); + ry.AddChild("Default", double(R.y)); // default value + ry.AddChild("KeyVer", int32_t(4009)); + times.clear(); + values.clear(); + for (size_t pki = 0; pki < na->mNumRotationKeys; ++pki) { + const aiQuatKey& k = na->mRotationKeys[pki]; + times.push_back(to_ktime(k.mTime, anim)); + // there must be a better way... + aiMatrix4x4 m(k.mValue.GetMatrix()); + aiVector3D qs, qr, qt; + m.Decompose(qs, qr, qt); + qr *= DEG; + values.push_back(qr.y); + } + ry.AddChild("KeyTime", times); + ry.AddChild("KeyValueFloat", values); + // TODO: keyframe flags (STUB for now) + ry.AddChild("KeyAttrFlags", std::vector{0}); + ry.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); + ry.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); + ry.Dump(outstream); + FBX::Node ryc("C"); + ryc.AddProperties("OP", curve_uid, ids[1], "d|Y"); + connections.push_back(ryc); // TODO: emplace_back + + // rotation z + FBX::Node rz("AnimationCurve"); + curve_uid = generate_uid(); + rz.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); + rz.AddChild("Default", double(R.z)); // default value + rz.AddChild("KeyVer", int32_t(4009)); + times.clear(); + values.clear(); + for (size_t pki = 0; pki < na->mNumRotationKeys; ++pki) { + const aiQuatKey& k = na->mRotationKeys[pki]; + times.push_back(to_ktime(k.mTime, anim)); + // there must be a better way... + aiMatrix4x4 m(k.mValue.GetMatrix()); + aiVector3D qs, qr, qt; + m.Decompose(qs, qr, qt); + qr *= DEG; + values.push_back(qr.z); + } + rz.AddChild("KeyTime", times); + rz.AddChild("KeyValueFloat", values); + // TODO: keyframe flags (STUB for now) + rz.AddChild("KeyAttrFlags", std::vector{0}); + rz.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); + rz.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); + rz.Dump(outstream); + FBX::Node rzc("C"); + rzc.AddProperties("OP", curve_uid, ids[1], "d|Z"); + connections.push_back(rzc); // TODO: emplace_back + + // scale x + FBX::Node sx("AnimationCurve"); + curve_uid = generate_uid(); + sx.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); + sx.AddChild("Default", double(S.x)); // default value + sx.AddChild("KeyVer", int32_t(4009)); + times.clear(); + values.clear(); + for (size_t pki = 0; pki < na->mNumScalingKeys; ++pki) { + const aiVectorKey& k = na->mScalingKeys[pki]; + times.push_back(to_ktime(k.mTime, anim)); + values.push_back(k.mValue.x); + } + sx.AddChild("KeyTime", times); + sx.AddChild("KeyValueFloat", values); + // TODO: keyframe flags (STUB for now) + sx.AddChild("KeyAttrFlags", std::vector{0}); + sx.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); + sx.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); + sx.Dump(outstream); + FBX::Node sxc("C"); + sxc.AddProperties("OP", curve_uid, ids[2], "d|X"); + connections.push_back(sxc); // TODO: emplace_back + + // scale y + FBX::Node sy("AnimationCurve"); + curve_uid = generate_uid(); + sy.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); + sy.AddChild("Default", double(S.y)); // default value + sy.AddChild("KeyVer", int32_t(4009)); + times.clear(); + values.clear(); + for (size_t pki = 0; pki < na->mNumScalingKeys; ++pki) { + const aiVectorKey& k = na->mScalingKeys[pki]; + times.push_back(to_ktime(k.mTime, anim)); + values.push_back(k.mValue.y); + } + sy.AddChild("KeyTime", times); + sy.AddChild("KeyValueFloat", values); + // TODO: keyframe flags (STUB for now) + sy.AddChild("KeyAttrFlags", std::vector{0}); + sy.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); + sy.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); + sy.Dump(outstream); + FBX::Node syc("C"); + syc.AddProperties("OP", curve_uid, ids[2], "d|Y"); + connections.push_back(syc); // TODO: emplace_back + + // scale z + FBX::Node sz("AnimationCurve"); + curve_uid = generate_uid(); + sz.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); + sz.AddChild("Default", double(S.z)); // default value + sz.AddChild("KeyVer", int32_t(4009)); + times.clear(); + values.clear(); + for (size_t pki = 0; pki < na->mNumScalingKeys; ++pki) { + const aiVectorKey& k = na->mScalingKeys[pki]; + times.push_back(to_ktime(k.mTime, anim)); + values.push_back(k.mValue.z); + } + sz.AddChild("KeyTime", times); + sz.AddChild("KeyValueFloat", values); + // TODO: keyframe flags (STUB for now) + sz.AddChild("KeyAttrFlags", std::vector{0}); + sz.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); + sz.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); + sz.Dump(outstream); + FBX::Node szc("C"); + szc.AddProperties("OP", curve_uid, ids[2], "d|Z"); + connections.push_back(szc); // TODO: emplace_back + } + } + object_node.End(outstream, true); } From ee0cdb39544ba8459a9052eda526b0a2a0c16a63 Mon Sep 17 00:00:00 2001 From: Tommy Date: Tue, 27 Mar 2018 13:03:10 +0200 Subject: [PATCH 151/183] FBX Export: Tidy animation export code. --- code/FBXExporter.cpp | 388 +++++++++++++------------------------------ code/FBXExporter.h | 17 ++ 2 files changed, 129 insertions(+), 276 deletions(-) diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 2cb7e470a..284076ef6 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -962,7 +962,7 @@ void FBXExporter::WriteObjects () std::vector vertex_indices; // map of vertex value to its index in the data vector std::map index_by_vertex_value; - int32_t index = 0; + int32_t index = 0; for (size_t vi = 0; vi < m->mNumVertices; ++vi) { aiVector3D vtx = m->mVertices[vi]; auto elem = index_by_vertex_value.find(vtx); @@ -1071,7 +1071,7 @@ void FBXExporter::WriteObjects () std::vector uv_data; std::vector uv_indices; std::map index_by_uv; - int32_t index = 0; + int32_t index = 0; for (size_t fi = 0; fi < m->mNumFaces; ++fi) { const aiFace &f = m->mFaces[fi]; for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) { @@ -1908,13 +1908,14 @@ void FBXExporter::WriteObjects () std::string name = anim->mName.C_Str() + FBX::SEPARATOR + "AnimStack"; asnode.AddProperties(animstack_uid, name, ""); FBX::Node p("Properties70"); - p.AddP70time("LocalStart", 0); - p.AddP70time("LocalStop", 0); + p.AddP70time("LocalStart", 0); // assimp doesn't store this + p.AddP70time("LocalStop", to_ktime(anim->mDuration, anim)); p.AddP70time("ReferenceStart", 0); - p.AddP70time("ReferenceStop", 0); + p.AddP70time("ReferenceStop", to_ktime(anim->mDuration, anim)); asnode.AddChild(p); // this node absurdly always pretends it has children + // (in this case it does, but just in case...) asnode.Begin(outstream); asnode.DumpProperties(outstream); asnode.EndProperties(outstream); @@ -1959,66 +1960,32 @@ void FBXExporter::WriteObjects () const aiMatrix4x4 node_xfm = get_world_transform(node, mScene); aiVector3D T, R, S; node_xfm.Decompose(S, R, T); - // generate uids for all AnimationCurveNode + + // AnimationCurveNode uids std::array ids; ids[0] = generate_uid(); // T ids[1] = generate_uid(); // R ids[2] = generate_uid(); // S // translation - FBX::Node t("AnimationCurveNode"); - t.AddProperties(ids[0], "T" + FBX::SEPARATOR + "AnimCurveNode", ""); - FBX::Node tp("Properties70"); - tp.AddP70numberA("d|X", T.x); - tp.AddP70numberA("d|Y", T.y); - tp.AddP70numberA("d|Z", T.z); - t.AddChild(tp); - t.Dump(outstream); - // connect to layer - FBX::Node tcl("C"); - tcl.AddProperties("OO", ids[0], layer_uid); - connections.push_back(tcl); // TODO: emplace_back - // connect to bone - FBX::Node tcb("C"); - tcb.AddProperties("OP", ids[0], node_uids[node], "Lcl Translation"); - connections.push_back(tcb); // TODO: emplace_back + WriteAnimationCurveNode(outstream, + ids[0], "T", T, "Lcl Translation", + layer_uid, node_uids[node] + ); // rotation - FBX::Node r("AnimationCurveNode"); - r.AddProperties(ids[1], "R" + FBX::SEPARATOR + "AnimCurveNode", ""); - FBX::Node rp("Properties70"); - rp.AddP70numberA("d|X", DEG*R.x); - rp.AddP70numberA("d|Y", DEG*R.y); - rp.AddP70numberA("d|Z", DEG*R.z); - r.AddChild(rp); - r.Dump(outstream); - // connect to layer - FBX::Node rcl("C"); - rcl.AddProperties("OO", ids[1], layer_uid); - connections.push_back(rcl); // TODO: emplace_back - // connect to bone - FBX::Node rcb("C"); - rcb.AddProperties("OP", ids[1], node_uids[node], "Lcl Rotation"); - connections.push_back(rcb); // TODO: emplace_back + WriteAnimationCurveNode(outstream, + ids[1], "R", R, "Lcl Rotation", + layer_uid, node_uids[node] + ); // scale - FBX::Node s("AnimationCurveNode"); - s.AddProperties(ids[2], "S" + FBX::SEPARATOR + "AnimCurveNode", ""); - FBX::Node sp("Properties70"); - sp.AddP70numberA("d|X", S.x); - sp.AddP70numberA("d|Y", S.y); - sp.AddP70numberA("d|Z", S.z); - s.AddChild(sp); - s.Dump(outstream); - // connect to layer - FBX::Node scl("C"); - scl.AddProperties("OO", ids[2], layer_uid); - connections.push_back(scl); // TODO: emplace_back - // connect to bone - FBX::Node scb("C"); - scb.AddProperties("OP", ids[2], node_uids[node], "Lcl Scaling"); - connections.push_back(scb); // TODO: emplace_back + WriteAnimationCurveNode(outstream, + ids[2], "S", S, "Lcl Scale", + layer_uid, node_uids[node] + ); + // store the uids for later use nodeanim_uids.push_back(ids); } curve_node_uids.push_back(nodeanim_uids); @@ -2039,242 +2006,52 @@ void FBXExporter::WriteObjects () node_xfm.Decompose(S, R, T); const std::array& ids = curve_node_uids[ai][nai]; - int64_t curve_uid; std::vector times; - std::vector values; + std::vector xval, yval, zval; - // TODO: compress code (it's a bit annoying) - - // translation x - FBX::Node tx("AnimationCurve"); - curve_uid = generate_uid(); - tx.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); - tx.AddChild("Default", double(T.x)); // default value - tx.AddChild("KeyVer", int32_t(4009)); - times.clear(); - values.clear(); - for (size_t pki = 0; pki < na->mNumPositionKeys; ++pki) { - const aiVectorKey& k = na->mPositionKeys[pki]; + // position/translation + for (size_t ki = 0; ki < na->mNumPositionKeys; ++ki) { + const aiVectorKey& k = na->mPositionKeys[ki]; times.push_back(to_ktime(k.mTime, anim)); - values.push_back(k.mValue.x); + xval.push_back(k.mValue.x); + yval.push_back(k.mValue.y); + zval.push_back(k.mValue.z); } - tx.AddChild("KeyTime", times); - tx.AddChild("KeyValueFloat", values); - // TODO: keyframe flags (STUB for now) - tx.AddChild("KeyAttrFlags", std::vector{0}); - tx.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); - tx.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); - tx.Dump(outstream); - FBX::Node txc("C"); - txc.AddProperties("OP", curve_uid, ids[0], "d|X"); - connections.push_back(txc); // TODO: emplace_back + // one curve each for X, Y, Z + WriteAnimationCurve(outstream, T.x, times, xval, ids[0], "d|X"); + WriteAnimationCurve(outstream, T.y, times, yval, ids[0], "d|Y"); + WriteAnimationCurve(outstream, T.z, times, zval, ids[0], "d|Z"); - // translation y - FBX::Node ty("AnimationCurve"); - curve_uid = generate_uid(); - ty.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); - ty.AddChild("Default", double(T.y)); // default value - ty.AddChild("KeyVer", int32_t(4009)); - times.clear(); - values.clear(); - for (size_t pki = 0; pki < na->mNumPositionKeys; ++pki) { - const aiVectorKey& k = na->mPositionKeys[pki]; + // rotation + times.clear(); xval.clear(); yval.clear(); zval.clear(); + for (size_t ki = 0; ki < na->mNumRotationKeys; ++ki) { + const aiQuatKey& k = na->mRotationKeys[ki]; times.push_back(to_ktime(k.mTime, anim)); - values.push_back(k.mValue.y); - } - ty.AddChild("KeyTime", times); - ty.AddChild("KeyValueFloat", values); - // TODO: keyframe flags (STUB for now) - ty.AddChild("KeyAttrFlags", std::vector{0}); - ty.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); - ty.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); - ty.Dump(outstream); - FBX::Node tyc("C"); - tyc.AddProperties("OP", curve_uid, ids[0], "d|Y"); - connections.push_back(tyc); // TODO: emplace_back - - // translation z - FBX::Node tz("AnimationCurve"); - curve_uid = generate_uid(); - tz.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); - tz.AddChild("Default", double(T.z)); // default value - tz.AddChild("KeyVer", int32_t(4009)); - times.clear(); - values.clear(); - for (size_t pki = 0; pki < na->mNumPositionKeys; ++pki) { - const aiVectorKey& k = na->mPositionKeys[pki]; - times.push_back(to_ktime(k.mTime, anim)); - values.push_back(k.mValue.z); - } - tz.AddChild("KeyTime", times); - tz.AddChild("KeyValueFloat", values); - // TODO: keyframe flags (STUB for now) - tz.AddChild("KeyAttrFlags", std::vector{0}); - tz.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); - tz.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); - tz.Dump(outstream); - FBX::Node tzc("C"); - tzc.AddProperties("OP", curve_uid, ids[0], "d|Z"); - connections.push_back(tzc); // TODO: emplace_back - - // rotation x - FBX::Node rx("AnimationCurve"); - curve_uid = generate_uid(); - rx.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); - rx.AddChild("Default", double(R.x)); // default value - rx.AddChild("KeyVer", int32_t(4009)); - times.clear(); - values.clear(); - for (size_t pki = 0; pki < na->mNumRotationKeys; ++pki) { - const aiQuatKey& k = na->mRotationKeys[pki]; - times.push_back(to_ktime(k.mTime, anim)); - // there must be a better way... + // TODO: aiQuaternion method to convert to Euler... aiMatrix4x4 m(k.mValue.GetMatrix()); aiVector3D qs, qr, qt; m.Decompose(qs, qr, qt); qr *= DEG; - values.push_back(qr.x); + xval.push_back(qr.x); + yval.push_back(qr.y); + zval.push_back(qr.z); } - rx.AddChild("KeyTime", times); - rx.AddChild("KeyValueFloat", values); - // TODO: keyframe flags (STUB for now) - rx.AddChild("KeyAttrFlags", std::vector{0}); - rx.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); - rx.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); - rx.Dump(outstream); - FBX::Node rxc("C"); - rxc.AddProperties("OP", curve_uid, ids[1], "d|X"); - connections.push_back(rxc); // TODO: emplace_back + WriteAnimationCurve(outstream, R.x, times, xval, ids[1], "d|X"); + WriteAnimationCurve(outstream, R.y, times, yval, ids[1], "d|Y"); + WriteAnimationCurve(outstream, R.z, times, zval, ids[1], "d|Z"); - // rotation y - FBX::Node ry("AnimationCurve"); - curve_uid = generate_uid(); - ry.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); - ry.AddChild("Default", double(R.y)); // default value - ry.AddChild("KeyVer", int32_t(4009)); - times.clear(); - values.clear(); - for (size_t pki = 0; pki < na->mNumRotationKeys; ++pki) { - const aiQuatKey& k = na->mRotationKeys[pki]; + // scaling/scale + times.clear(); xval.clear(); yval.clear(); zval.clear(); + for (size_t ki = 0; ki < na->mNumScalingKeys; ++ki) { + const aiVectorKey& k = na->mScalingKeys[ki]; times.push_back(to_ktime(k.mTime, anim)); - // there must be a better way... - aiMatrix4x4 m(k.mValue.GetMatrix()); - aiVector3D qs, qr, qt; - m.Decompose(qs, qr, qt); - qr *= DEG; - values.push_back(qr.y); + xval.push_back(k.mValue.x); + yval.push_back(k.mValue.y); + zval.push_back(k.mValue.z); } - ry.AddChild("KeyTime", times); - ry.AddChild("KeyValueFloat", values); - // TODO: keyframe flags (STUB for now) - ry.AddChild("KeyAttrFlags", std::vector{0}); - ry.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); - ry.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); - ry.Dump(outstream); - FBX::Node ryc("C"); - ryc.AddProperties("OP", curve_uid, ids[1], "d|Y"); - connections.push_back(ryc); // TODO: emplace_back - - // rotation z - FBX::Node rz("AnimationCurve"); - curve_uid = generate_uid(); - rz.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); - rz.AddChild("Default", double(R.z)); // default value - rz.AddChild("KeyVer", int32_t(4009)); - times.clear(); - values.clear(); - for (size_t pki = 0; pki < na->mNumRotationKeys; ++pki) { - const aiQuatKey& k = na->mRotationKeys[pki]; - times.push_back(to_ktime(k.mTime, anim)); - // there must be a better way... - aiMatrix4x4 m(k.mValue.GetMatrix()); - aiVector3D qs, qr, qt; - m.Decompose(qs, qr, qt); - qr *= DEG; - values.push_back(qr.z); - } - rz.AddChild("KeyTime", times); - rz.AddChild("KeyValueFloat", values); - // TODO: keyframe flags (STUB for now) - rz.AddChild("KeyAttrFlags", std::vector{0}); - rz.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); - rz.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); - rz.Dump(outstream); - FBX::Node rzc("C"); - rzc.AddProperties("OP", curve_uid, ids[1], "d|Z"); - connections.push_back(rzc); // TODO: emplace_back - - // scale x - FBX::Node sx("AnimationCurve"); - curve_uid = generate_uid(); - sx.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); - sx.AddChild("Default", double(S.x)); // default value - sx.AddChild("KeyVer", int32_t(4009)); - times.clear(); - values.clear(); - for (size_t pki = 0; pki < na->mNumScalingKeys; ++pki) { - const aiVectorKey& k = na->mScalingKeys[pki]; - times.push_back(to_ktime(k.mTime, anim)); - values.push_back(k.mValue.x); - } - sx.AddChild("KeyTime", times); - sx.AddChild("KeyValueFloat", values); - // TODO: keyframe flags (STUB for now) - sx.AddChild("KeyAttrFlags", std::vector{0}); - sx.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); - sx.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); - sx.Dump(outstream); - FBX::Node sxc("C"); - sxc.AddProperties("OP", curve_uid, ids[2], "d|X"); - connections.push_back(sxc); // TODO: emplace_back - - // scale y - FBX::Node sy("AnimationCurve"); - curve_uid = generate_uid(); - sy.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); - sy.AddChild("Default", double(S.y)); // default value - sy.AddChild("KeyVer", int32_t(4009)); - times.clear(); - values.clear(); - for (size_t pki = 0; pki < na->mNumScalingKeys; ++pki) { - const aiVectorKey& k = na->mScalingKeys[pki]; - times.push_back(to_ktime(k.mTime, anim)); - values.push_back(k.mValue.y); - } - sy.AddChild("KeyTime", times); - sy.AddChild("KeyValueFloat", values); - // TODO: keyframe flags (STUB for now) - sy.AddChild("KeyAttrFlags", std::vector{0}); - sy.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); - sy.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); - sy.Dump(outstream); - FBX::Node syc("C"); - syc.AddProperties("OP", curve_uid, ids[2], "d|Y"); - connections.push_back(syc); // TODO: emplace_back - - // scale z - FBX::Node sz("AnimationCurve"); - curve_uid = generate_uid(); - sz.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); - sz.AddChild("Default", double(S.z)); // default value - sz.AddChild("KeyVer", int32_t(4009)); - times.clear(); - values.clear(); - for (size_t pki = 0; pki < na->mNumScalingKeys; ++pki) { - const aiVectorKey& k = na->mScalingKeys[pki]; - times.push_back(to_ktime(k.mTime, anim)); - values.push_back(k.mValue.z); - } - sz.AddChild("KeyTime", times); - sz.AddChild("KeyValueFloat", values); - // TODO: keyframe flags (STUB for now) - sz.AddChild("KeyAttrFlags", std::vector{0}); - sz.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); - sz.AddChild("KeyAttrRefCount", std::vector{static_cast(times.size())}); - sz.Dump(outstream); - FBX::Node szc("C"); - szc.AddProperties("OP", curve_uid, ids[2], "d|Z"); - connections.push_back(szc); // TODO: emplace_back + WriteAnimationCurve(outstream, S.x, times, xval, ids[2], "d|X"); + WriteAnimationCurve(outstream, S.y, times, yval, ids[2], "d|Y"); + WriteAnimationCurve(outstream, S.z, times, zval, ids[2], "d|Z"); } } @@ -2548,6 +2325,65 @@ void FBXExporter::WriteModelNodes( } } + +void FBXExporter::WriteAnimationCurveNode( + StreamWriterLE& outstream, + int64_t uid, + std::string name, // "T", "R", or "S" + aiVector3D default_value, + std::string property_name, // "Lcl Translation" etc + int64_t layer_uid, + int64_t node_uid +) { + FBX::Node n("AnimationCurveNode"); + n.AddProperties(uid, name + FBX::SEPARATOR + "AnimCurveNode", ""); + FBX::Node p("Properties70"); + p.AddP70numberA("d|X", default_value.x); + p.AddP70numberA("d|Y", default_value.y); + p.AddP70numberA("d|Z", default_value.z); + n.AddChild(p); + n.Dump(outstream); + // connect to layer + FBX::Node cl("C"); + cl.AddProperties("OO", uid, layer_uid); + this->connections.push_back(cl); // TODO: emplace_back + // connect to bone + FBX::Node cb("C"); + cb.AddProperties("OP", uid, node_uid, property_name); + this->connections.push_back(cb); // TODO: emplace_back +} + + +void FBXExporter::WriteAnimationCurve( + StreamWriterLE& outstream, + double default_value, + const std::vector& times, + const std::vector& values, + int64_t curvenode_uid, + const std::string& property_link // "d|X", "d|Y", etc +) { + FBX::Node n("AnimationCurve"); + int64_t curve_uid = generate_uid(); + n.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); + n.AddChild("Default", default_value); + n.AddChild("KeyVer", int32_t(4009)); + n.AddChild("KeyTime", times); + n.AddChild("KeyValueFloat", values); + // TODO: keyattr flags and data (STUB for now) + n.AddChild("KeyAttrFlags", std::vector{0}); + n.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); + ai_assert(times.size() <= std::numeric_limits::max()); + n.AddChild( + "KeyAttrRefCount", + std::vector{static_cast(times.size())} + ); + n.Dump(outstream); + FBX::Node c("C"); + c.AddProperties("OP", curve_uid, curvenode_uid, property_link); + this->connections.push_back(c); // TODO: emplace_back +} + + void FBXExporter::WriteConnections () { // we should have completed the connection graph already, diff --git a/code/FBXExporter.h b/code/FBXExporter.h index ce2f67e24..553cf60fe 100644 --- a/code/FBXExporter.h +++ b/code/FBXExporter.h @@ -139,6 +139,23 @@ namespace Assimp const std::unordered_set& limbnodes, std::vector>& transform_chain ); + void WriteAnimationCurveNode( + StreamWriterLE& outstream, + int64_t uid, + std::string name, // "T", "R", or "S" + aiVector3D default_value, + std::string property_name, // "Lcl Translation" etc + int64_t animation_layer_uid, + int64_t node_uid + ); + void WriteAnimationCurve( + StreamWriterLE& outstream, + double default_value, + const std::vector& times, + const std::vector& values, + int64_t curvenode_id, + const std::string& property_link // "d|X", "d|Y", etc + ); }; } From e972b73fc21e3738f5ee928cf07b54f007a5d59b Mon Sep 17 00:00:00 2001 From: Tommy Date: Tue, 27 Mar 2018 13:29:03 +0200 Subject: [PATCH 152/183] FBX Export: tidy includes --- code/FBXExporter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 284076ef6..7dcf752da 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -63,6 +63,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include // localtime, tm_* #include #include +#include +#include #include #include // endl From 00277d93f2a56ace39fc3088b4c62308001ae192 Mon Sep 17 00:00:00 2001 From: Tommy Date: Wed, 28 Mar 2018 15:47:27 +0200 Subject: [PATCH 153/183] Tidier FBXExportNode construction with properties. --- code/FBXExportNode.h | 9 +-- code/FBXExporter.cpp | 136 +++++++++++++++++++------------------------ 2 files changed, 64 insertions(+), 81 deletions(-) diff --git a/code/FBXExportNode.h b/code/FBXExportNode.h index edce8f700..2539a9458 100644 --- a/code/FBXExportNode.h +++ b/code/FBXExportNode.h @@ -69,11 +69,12 @@ public: // public data members public: // constructors Node() = default; Node(const std::string& n) : name(n) {} - Node(const std::string& n, const FBX::Property &p) + + // convenience template to construct with properties directly + template + Node(const std::string& n, const More... more) : name(n) - { properties.push_back(p); } - Node(const std::string& n, const std::vector &pv) - : name(n), properties(pv) {} + { AddProperties(more...); } public: // functions to add properties or children // add a single property to the node diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 7dcf752da..8b633462d 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -520,7 +520,7 @@ void FBXExporter::WriteDefinitions () // GlobalSettings // this seems to always be here in Maya exports - n = FBX::Node("ObjectType", Property("GlobalSettings")); + n = FBX::Node("ObjectType", "GlobalSettings"); count = 1; n.AddChild("Count", count); object_nodes.push_back(n); @@ -531,9 +531,9 @@ void FBXExporter::WriteDefinitions () // but no harm seems to come of leaving it out. count = mScene->mNumAnimations; if (count) { - n = FBX::Node("ObjectType", Property("AnimationStack")); + n = FBX::Node("ObjectType", "AnimationStack"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxAnimStack")); + pt = FBX::Node("PropertyTemplate", "FbxAnimStack"); p = FBX::Node("Properties70"); p.AddP70string("Description", ""); p.AddP70time("LocalStart", 0); @@ -553,9 +553,9 @@ void FBXExporter::WriteDefinitions () // so there will be one per aiAnimation count = mScene->mNumAnimations; if (count) { - n = FBX::Node("ObjectType", Property("AnimationLayer")); + n = FBX::Node("ObjectType", "AnimationLayer"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FBXAnimLayer")); + pt = FBX::Node("PropertyTemplate", "FBXAnimLayer"); p = FBX::Node("Properties70"); p.AddP70("Weight", "Number", "", "A", double(100)); p.AddP70bool("Mute", 0); @@ -583,9 +583,9 @@ void FBXExporter::WriteDefinitions () count = 1; // TODO: select properly if (count) { // FbxSkeleton - n = FBX::Node("ObjectType", Property("NodeAttribute")); + n = FBX::Node("ObjectType", "NodeAttribute"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxSkeleton")); + pt = FBX::Node("PropertyTemplate", "FbxSkeleton"); p = FBX::Node("Properties70"); p.AddP70color("Color", 0.8, 0.8, 0.8); p.AddP70double("Size", 33.333333333333); @@ -601,9 +601,9 @@ void FBXExporter::WriteDefinitions () // <~~ node heirarchy count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node) if (count) { - n = FBX::Node("ObjectType", Property("Model")); + n = FBX::Node("ObjectType", "Model"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxNode")); + pt = FBX::Node("PropertyTemplate", "FbxNode"); p = FBX::Node("Properties70"); p.AddP70enum("QuaternionInterpolate", 0); p.AddP70vector("RotationOffset", 0.0, 0.0, 0.0); @@ -698,9 +698,9 @@ void FBXExporter::WriteDefinitions () // <~~ aiMesh count = mScene->mNumMeshes; if (count) { - n = FBX::Node("ObjectType", Property("Geometry")); + n = FBX::Node("ObjectType", "Geometry"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxMesh")); + pt = FBX::Node("PropertyTemplate", "FbxMesh"); p = FBX::Node("Properties70"); p.AddP70color("Color", 0, 0, 0); p.AddP70vector("BBoxMin", 0, 0, 0); @@ -724,7 +724,7 @@ void FBXExporter::WriteDefinitions () count = mScene->mNumMaterials; if (count) { bool has_phong = has_phong_mat(mScene); - n = FBX::Node("ObjectType", Property("Material")); + n = FBX::Node("ObjectType", "Material"); n.AddChild("Count", count); pt = FBX::Node("PropertyTemplate"); if (has_phong) { @@ -771,9 +771,9 @@ void FBXExporter::WriteDefinitions () // one for each image file. count = int32_t(count_images(mScene)); if (count) { - n = FBX::Node("ObjectType", Property("Video")); + n = FBX::Node("ObjectType", "Video"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxVideo")); + pt = FBX::Node("PropertyTemplate", "FbxVideo"); p = FBX::Node("Properties70"); p.AddP70bool("ImageSequence", 0); p.AddP70int("ImageSequenceOffset", 0); @@ -800,9 +800,9 @@ void FBXExporter::WriteDefinitions () // <~~ aiTexture count = int32_t(count_textures(mScene)); if (count) { - n = FBX::Node("ObjectType", Property("Texture")); + n = FBX::Node("ObjectType", "Texture"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxFileTexture")); + pt = FBX::Node("PropertyTemplate", "FbxFileTexture"); p = FBX::Node("Properties70"); p.AddP70enum("TextureTypeUse", 0); p.AddP70numberA("Texture alpha", 1.0); @@ -829,9 +829,9 @@ void FBXExporter::WriteDefinitions () // AnimationCurveNode / FbxAnimCurveNode count = mScene->mNumAnimations * 3; if (count) { - n = FBX::Node("ObjectType", Property("AnimationCurveNode")); + n = FBX::Node("ObjectType", "AnimationCurveNode"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxAnimCurveNode")); + pt = FBX::Node("PropertyTemplate", "FbxAnimCurveNode"); p = FBX::Node("Properties70"); p.AddP70("d", "Compound", "", ""); pt.AddChild(p); @@ -843,7 +843,7 @@ void FBXExporter::WriteDefinitions () // AnimationCurve / FbxAnimCurve count = mScene->mNumAnimations * 9; if (count) { - n = FBX::Node("ObjectType", Property("AnimationCurve")); + n = FBX::Node("ObjectType", "AnimationCurve"); n.AddChild("Count", count); object_nodes.push_back(n); total_count += count; @@ -856,7 +856,7 @@ void FBXExporter::WriteDefinitions () if (mesh->HasBones()) { ++count; } } if (count) { - n = FBX::Node("ObjectType", Property("Pose")); + n = FBX::Node("ObjectType", "Pose"); n.AddChild("Count", count); object_nodes.push_back(n); total_count += count; @@ -865,7 +865,7 @@ void FBXExporter::WriteDefinitions () // Deformer count = int32_t(count_deformers(mScene)); if (count) { - n = FBX::Node("ObjectType", Property("Deformer")); + n = FBX::Node("ObjectType", "Deformer"); n.AddChild("Count", count); object_nodes.push_back(n); total_count += count; @@ -874,9 +874,9 @@ void FBXExporter::WriteDefinitions () // (template) count = 0; if (count) { - n = FBX::Node("ObjectType", Property("")); + n = FBX::Node("ObjectType", ""); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("")); + pt = FBX::Node("PropertyTemplate", ""); p = FBX::Node("Properties70"); pt.AddChild(p); n.AddChild(pt); @@ -1008,7 +1008,7 @@ void FBXExporter::WriteObjects () // normals, if any if (m->HasNormals()) { - FBX::Node normals("LayerElementNormal", Property(int32_t(0))); + FBX::Node normals("LayerElementNormal", int32_t(0)); normals.Begin(outstream); normals.DumpProperties(outstream); normals.EndProperties(outstream); @@ -1055,7 +1055,7 @@ void FBXExporter::WriteObjects () err << " but may be incorrectly interpreted on load."; DefaultLogger::get()->warn(err.str()); } - FBX::Node uv("LayerElementUV", Property(int32_t(uvi))); + FBX::Node uv("LayerElementUV", int32_t(uvi)); uv.Begin(outstream); uv.DumpProperties(outstream); uv.EndProperties(outstream); @@ -1100,7 +1100,7 @@ void FBXExporter::WriteObjects () // i'm not really sure why this material section exists, // as the material is linked via "Connections". // it seems to always have the same "0" value. - FBX::Node mat("LayerElementMaterial", Property(int32_t(0))); + FBX::Node mat("LayerElementMaterial", int32_t(0)); mat.AddChild("Version", int32_t(101)); mat.AddChild("Name", ""); mat.AddChild("MappingInformationType", "AllSame"); @@ -1112,7 +1112,7 @@ void FBXExporter::WriteObjects () // finally we have the layer specifications, // which select the normals / UV set / etc to use. // TODO: handle multiple uv sets correctly? - FBX::Node layer("Layer", Property(int32_t(0))); + FBX::Node layer("Layer", int32_t(0)); layer.AddChild("Version", int32_t(100)); FBX::Node le("LayerElement"); le.AddChild("Type", "LayerElementNormal"); @@ -1408,14 +1408,12 @@ void FBXExporter::WriteObjects () const int64_t texture_uid = generate_uid(); // link the texture to the material - FBX::Node c("C"); - c.AddProperties("OP", texture_uid, material_uid, prop_name); - connections.push_back(c); + connections.emplace_back( + "C", "OP", texture_uid, material_uid, prop_name + ); // link the image data to the texture - c = FBX::Node("C"); - c.AddProperties("OO", image_uid, texture_uid); - connections.push_back(c); + connections.emplace_back("C", "OO", image_uid, texture_uid); // now write the actual texture node FBX::Node tnode("Texture"); @@ -1598,9 +1596,7 @@ void FBXExporter::WriteObjects () dnode.Dump(outstream); // connect it - FBX::Node c("C"); - c.AddProperties("OO", deformer_uid, mesh_uids[mi]); - connections.push_back(c); // TODO: emplace_back + connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]); // we will be indexing by vertex... // but there might be a different number of "vertices" @@ -1744,14 +1740,14 @@ void FBXExporter::WriteObjects () sdnode.Dump(outstream); // lastly, connect to the parent deformer - c = FBX::Node("C"); - c.AddProperties("OO", subdeformer_uid, deformer_uid); - connections.push_back(c); // TODO: emplace_back + connections.emplace_back( + "C", "OO", subdeformer_uid, deformer_uid + ); // we also need to connect the limb node to the subdeformer. - c = FBX::Node("C"); - c.AddProperties("OO", node_uids[bone_node], subdeformer_uid); - connections.push_back(c); // TODO: emplace_back + connections.emplace_back( + "C", "OO", node_uids[bone_node], subdeformer_uid + ); } // if we cannot create a valid FBX file, simply die. @@ -1943,9 +1939,9 @@ void FBXExporter::WriteObjects () alnode.End(outstream, true); // connect to the relevant animstack - FBX::Node c("C"); - c.AddProperties("OO", animlayer_uid, animation_stack_uids[ai]); - connections.push_back(c); // TODO: emplace_back + connections.emplace_back( + "C", "OO", animlayer_uid, animation_stack_uids[ai] + ); } // AnimCurveNode - three per aiNodeAnim @@ -2240,9 +2236,7 @@ void FBXExporter::WriteModelNodes( node_uid = generate_uid(); node_uids[node] = node_uid; } - FBX::Node c("C"); - c.AddProperties("OO", node_uid, parent_uid); - connections.push_back(c); + connections.emplace_back("C", "OO", node_uid, parent_uid); } // what type of node is this? @@ -2250,17 +2244,15 @@ void FBXExporter::WriteModelNodes( // handled later } else if (node->mNumMeshes == 1) { // connect to child mesh, which should have been written previously - FBX::Node c("C"); - c.AddProperties("OO", mesh_uids[node->mMeshes[0]], node_uid); - connections.push_back(c); + connections.emplace_back( + "C", "OO", mesh_uids[node->mMeshes[0]], node_uid + ); // also connect to the material for the child mesh - c = FBX::Node("C"); - c.AddProperties( - "OO", + connections.emplace_back( + "C", "OO", material_uids[mScene->mMeshes[node->mMeshes[0]]->mMaterialIndex], node_uid ); - connections.push_back(c); // write model node WriteModelNode(outstream, node, node_uid, "Mesh", transform_chain); } else if (limbnodes.count(node)) { @@ -2274,9 +2266,7 @@ void FBXExporter::WriteModelNodes( na.AddChild("TypeFlags", Property("Skeleton")); na.Dump(outstream); // and connect them - FBX::Node c("C"); - c.AddProperties("OO", node_attribute_uid, node_uid); - connections.push_back(c); + connections.emplace_back("C", "OO", node_attribute_uid, node_uid); } else { // generate a null node so we can add children to it WriteModelNode(outstream, node, node_uid, "Null", transform_chain); @@ -2288,23 +2278,19 @@ void FBXExporter::WriteModelNodes( // make a new model node int64_t new_node_uid = generate_uid(); // connect to parent node - FBX::Node c("C"); - c.AddProperties("OO", new_node_uid, node_uid); - connections.push_back(c); + connections.emplace_back("C", "OO", new_node_uid, node_uid); // connect to child mesh, which should have been written previously - c = FBX::Node("C"); - c.AddProperties("OO", mesh_uids[node->mMeshes[i]], new_node_uid); - connections.push_back(c); + connections.emplace_back( + "C", "OO", mesh_uids[node->mMeshes[i]], new_node_uid + ); // also connect to the material for the child mesh - c = FBX::Node("C"); - c.AddProperties( - "OO", + connections.emplace_back( + "C", "OO", material_uids[ mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex ], new_node_uid ); - connections.push_back(c); // write model node FBX::Node m("Model"); // take name from mesh name, if it exists @@ -2346,13 +2332,9 @@ void FBXExporter::WriteAnimationCurveNode( n.AddChild(p); n.Dump(outstream); // connect to layer - FBX::Node cl("C"); - cl.AddProperties("OO", uid, layer_uid); - this->connections.push_back(cl); // TODO: emplace_back + this->connections.emplace_back("C", "OO", uid, layer_uid); // connect to bone - FBX::Node cb("C"); - cb.AddProperties("OP", uid, node_uid, property_name); - this->connections.push_back(cb); // TODO: emplace_back + this->connections.emplace_back("C", "OP", uid, node_uid, property_name); } @@ -2380,9 +2362,9 @@ void FBXExporter::WriteAnimationCurve( std::vector{static_cast(times.size())} ); n.Dump(outstream); - FBX::Node c("C"); - c.AddProperties("OP", curve_uid, curvenode_uid, property_link); - this->connections.push_back(c); // TODO: emplace_back + this->connections.emplace_back( + "C", "OP", curve_uid, curvenode_uid, property_link + ); } From aadb413fc07dccb8fe5b8fee8704b941f5713816 Mon Sep 17 00:00:00 2001 From: Tommy Date: Wed, 28 Mar 2018 16:09:07 +0200 Subject: [PATCH 154/183] Explicit handling of an FBXExportNode edge-case. --- code/FBXExportNode.cpp | 2 +- code/FBXExportNode.h | 3 +++ code/FBXExporter.cpp | 15 +++++---------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/code/FBXExportNode.cpp b/code/FBXExportNode.cpp index 596901989..621e72f09 100644 --- a/code/FBXExportNode.cpp +++ b/code/FBXExportNode.cpp @@ -168,7 +168,7 @@ void FBX::Node::Dump(Assimp::StreamWriterLE &s) DumpChildren(s); // finish, filling in end offset placeholder - End(s, !children.empty()); + End(s, force_has_children || !children.empty()); } void FBX::Node::Begin(Assimp::StreamWriterLE &s) diff --git a/code/FBXExportNode.h b/code/FBXExportNode.h index 2539a9458..58d3d14ee 100644 --- a/code/FBXExportNode.h +++ b/code/FBXExportNode.h @@ -66,6 +66,9 @@ public: // public data members std::vector properties; // node properties std::vector children; // child nodes + // some nodes always pretend they have children... + bool force_has_children = false; + public: // constructors Node() = default; Node(const std::string& n) : name(n) {} diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 8b633462d..46782cc2e 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -419,6 +419,7 @@ void FBXExporter::WriteReferences () // always empty for now. // not really sure what this is for. FBX::Node n("References"); + n.force_has_children = true; n.Dump(outfile); } @@ -1914,11 +1915,8 @@ void FBXExporter::WriteObjects () // this node absurdly always pretends it has children // (in this case it does, but just in case...) - asnode.Begin(outstream); - asnode.DumpProperties(outstream); - asnode.EndProperties(outstream); - asnode.DumpChildren(outstream); - asnode.End(outstream, true); + asnode.force_has_children = true; + asnode.Dump(outstream); // note: animation stacks are not connected to anything } @@ -1932,11 +1930,8 @@ void FBXExporter::WriteObjects () alnode.AddProperties(animlayer_uid, FBX::SEPARATOR + "AnimLayer", ""); // this node absurdly always pretends it has children - alnode.Begin(outstream); - alnode.DumpProperties(outstream); - alnode.EndProperties(outstream); - alnode.DumpChildren(outstream); - alnode.End(outstream, true); + alnode.force_has_children = true; + alnode.Dump(outstream); // connect to the relevant animstack connections.emplace_back( From e549705af0aa53a611a7739aa5567b392e4bea63 Mon Sep 17 00:00:00 2001 From: Tommy Date: Wed, 28 Mar 2018 21:38:22 +0200 Subject: [PATCH 155/183] StreamWriter: add a PutChar method to write a single char. --- include/assimp/StreamWriter.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/assimp/StreamWriter.h b/include/assimp/StreamWriter.h index 6f492df90..5ce3b172b 100644 --- a/include/assimp/StreamWriter.h +++ b/include/assimp/StreamWriter.h @@ -203,6 +203,12 @@ public: Put(n); } + // --------------------------------------------------------------------- + /** Write a single character to the stream */ + void PutChar(char c) { + Put(c); + } + // --------------------------------------------------------------------- /** Write an aiString to the stream */ void PutString(const aiString& s) From 826243f289cb4cdbc1c5e79dc8593ce6805a5f49 Mon Sep 17 00:00:00 2001 From: Tommy Date: Wed, 28 Mar 2018 21:40:26 +0200 Subject: [PATCH 156/183] Implement ascii FBX export. It's available under the 'fbxa' format id. --- code/Exporter.cpp | 4 +- code/FBXExportNode.cpp | 376 ++++++++++++++++++++++++++++++++----- code/FBXExportNode.h | 81 ++++++-- code/FBXExportProperty.cpp | 155 +++++++++++++-- code/FBXExportProperty.h | 6 +- code/FBXExporter.cpp | 275 ++++++++++++++++++--------- code/FBXExporter.h | 14 ++ 7 files changed, 747 insertions(+), 164 deletions(-) diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 49523e658..b7478c0be 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -98,7 +98,7 @@ void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportPrope void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*); -//void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* ); // ------------------------------------------------------------------------------------------------ @@ -173,7 +173,7 @@ Exporter::ExportFormatEntry gExporters[] = #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ), - //Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ), + Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ), #endif #ifndef ASSIMP_BUILD_NO_3MF_EXPORTER diff --git a/code/FBXExportNode.cpp b/code/FBXExportNode.cpp index 621e72f09..514d5f360 100644 --- a/code/FBXExportNode.cpp +++ b/code/FBXExportNode.cpp @@ -45,10 +45,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FBXCommon.h" #include // StreamWriterLE +#include // DeadlyExportError #include #include +#include +#include // ostringstream #include // shared_ptr +#include // snprintf // AddP70 helpers... there's no usable pattern here, // so all are defined as separate functions. @@ -145,33 +149,174 @@ void FBX::Node::AddP70time( } +// public member functions for writing nodes to stream + +void FBX::Node::Dump( + std::shared_ptr outfile, + bool binary, int indent +) { + if (binary) { + Assimp::StreamWriterLE outstream(outfile); + DumpBinary(outstream); + } else { + std::ostringstream ss; + DumpAscii(ss, indent); + std::string s = ss.str(); + outfile->Write(s.c_str(), s.size(), 1); + } +} + +void FBX::Node::Dump( + Assimp::StreamWriterLE &outstream, + bool binary, int indent +) { + if (binary) { + DumpBinary(outstream); + } else { + std::ostringstream ss; + DumpAscii(ss, indent); + outstream.PutString(ss.str()); + } +} + + +// public member functions for low-level writing + +void FBX::Node::Begin( + Assimp::StreamWriterLE &s, + bool binary, int indent +) { + if (binary) { + BeginBinary(s); + } else { + // assume we're at the correct place to start already + (void)indent; + std::ostringstream ss; + BeginAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::DumpProperties( + Assimp::StreamWriterLE& s, + bool binary, int indent +) { + if (binary) { + DumpPropertiesBinary(s); + } else { + std::ostringstream ss; + DumpPropertiesAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::EndProperties( + Assimp::StreamWriterLE &s, + bool binary, int indent +) { + EndProperties(s, binary, indent, properties.size()); +} + +void FBX::Node::EndProperties( + Assimp::StreamWriterLE &s, + bool binary, int indent, + size_t num_properties +) { + if (binary) { + EndPropertiesBinary(s, num_properties); + } else { + // nothing to do + (void)indent; + } +} + +void FBX::Node::BeginChildren( + Assimp::StreamWriterLE &s, + bool binary, int indent +) { + if (binary) { + // nothing to do + } else { + std::ostringstream ss; + BeginChildrenAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::DumpChildren( + Assimp::StreamWriterLE& s, + bool binary, int indent +) { + if (binary) { + DumpChildrenBinary(s); + } else { + std::ostringstream ss; + DumpChildrenAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::End( + Assimp::StreamWriterLE &s, + bool binary, int indent, + bool has_children +) { + if (binary) { + EndBinary(s, has_children); + } else { + std::ostringstream ss; + EndAscii(ss, indent, has_children); + s.PutString(ss.str()); + } +} + + // public member functions for writing to binary fbx -void FBX::Node::Dump(std::shared_ptr outfile) -{ - Assimp::StreamWriterLE outstream(outfile); - Dump(outstream); -} - -void FBX::Node::Dump(Assimp::StreamWriterLE &s) +void FBX::Node::DumpBinary(Assimp::StreamWriterLE &s) { // write header section (with placeholders for some things) - Begin(s); + BeginBinary(s); // write properties - DumpProperties(s); + DumpPropertiesBinary(s); // go back and fill in property related placeholders - EndProperties(s, properties.size()); + EndPropertiesBinary(s, properties.size()); // write children - DumpChildren(s); + DumpChildrenBinary(s); // finish, filling in end offset placeholder - End(s, force_has_children || !children.empty()); + EndBinary(s, force_has_children || !children.empty()); } -void FBX::Node::Begin(Assimp::StreamWriterLE &s) + +// public member functions for writing to ascii fbx + +void FBX::Node::DumpAscii(std::ostream &s, int indent) +{ + // write name + BeginAscii(s, indent); + + // write properties + DumpPropertiesAscii(s, indent); + + if (force_has_children || !children.empty()) { + // begin children (with a '{') + BeginChildrenAscii(s, indent + 1); + // write children + DumpChildrenAscii(s, indent + 1); + } + + // finish (also closing the children bracket '}') + EndAscii(s, indent, force_has_children || !children.empty()); +} + + +// private member functions for low-level writing to fbx + +void FBX::Node::BeginBinary(Assimp::StreamWriterLE &s) { // remember start pos so we can come back and write the end pos this->start_pos = s.Tell(); @@ -189,26 +334,14 @@ void FBX::Node::Begin(Assimp::StreamWriterLE &s) this->property_start = s.Tell(); } -void FBX::Node::DumpProperties(Assimp::StreamWriterLE& s) +void FBX::Node::DumpPropertiesBinary(Assimp::StreamWriterLE& s) { for (auto &p : properties) { - p.Dump(s); + p.DumpBinary(s); } } -void FBX::Node::DumpChildren(Assimp::StreamWriterLE& s) -{ - for (FBX::Node& child : children) { - child.Dump(s); - } -} - -void FBX::Node::EndProperties(Assimp::StreamWriterLE &s) -{ - EndProperties(s, properties.size()); -} - -void FBX::Node::EndProperties( +void FBX::Node::EndPropertiesBinary( Assimp::StreamWriterLE &s, size_t num_properties ) { @@ -222,7 +355,14 @@ void FBX::Node::EndProperties( s.Seek(pos); } -void FBX::Node::End( +void FBX::Node::DumpChildrenBinary(Assimp::StreamWriterLE& s) +{ + for (FBX::Node& child : children) { + child.DumpBinary(s); + } +} + +void FBX::Node::EndBinary( Assimp::StreamWriterLE &s, bool has_children ) { @@ -237,48 +377,192 @@ void FBX::Node::End( } -// static member functions +void FBX::Node::BeginAscii(std::ostream& s, int indent) +{ + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << name << ": "; +} -// convenience function to create and write a property node, -// holding a single property which is an array of values. -// does not copy the data, so is efficient for large arrays. +void FBX::Node::DumpPropertiesAscii(std::ostream &s, int indent) +{ + for (size_t i = 0; i < properties.size(); ++i) { + if (i > 0) { s << ", "; } + properties[i].DumpAscii(s, indent); + } +} + +void FBX::Node::BeginChildrenAscii(std::ostream& s, int indent) +{ + // only call this if there are actually children + s << " {"; + (void)indent; +} + +void FBX::Node::DumpChildrenAscii(std::ostream& s, int indent) +{ + // children will need a lot of padding and corralling + if (children.size() || force_has_children) { + for (size_t i = 0; i < children.size(); ++i) { + // no compression in ascii files, so skip this node if it exists + if (children[i].name == "EncryptionType") { continue; } + // the child can dump itself + children[i].DumpAscii(s, indent); + } + } +} + +void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children) +{ + if (!has_children) { return; } // nothing to do + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "}"; +} + +// private helpers for static member functions + +// ascii property node from vector of doubles +void FBX::Node::WritePropertyNodeAscii( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + int indent +){ + char buffer[32]; + FBX::Node node(name); + node.Begin(s, false, indent); + std::string vsize = std::to_string(v.size()); + // * { + s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n"); + // indent + 1 + for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); } + // a: value,value,value,... + s.PutString("a: "); + int count = 0; + for (size_t i = 0; i < v.size(); ++i) { + if (i > 0) { s.PutChar(','); } + int len = snprintf(buffer, sizeof(buffer), "%f", v[i]); + count += len; + if (count > 2048) { s.PutChar('\n'); count = 0; } + if (len < 0 || len > 31) { + // this should never happen + throw DeadlyExportError("failed to convert double to string"); + } + for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); } + } + // } + s.PutChar('\n'); + for (int i = 0; i < indent; ++i) { s.PutChar('\t'); } + s.PutChar('}'); s.PutChar(' '); + node.End(s, false, indent, false); +} + +// ascii property node from vector of int32_t +void FBX::Node::WritePropertyNodeAscii( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + int indent +){ + char buffer[32]; + FBX::Node node(name); + node.Begin(s, false, indent); + std::string vsize = std::to_string(v.size()); + // * { + s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n"); + // indent + 1 + for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); } + // a: value,value,value,... + s.PutString("a: "); + int count = 0; + for (size_t i = 0; i < v.size(); ++i) { + if (i > 0) { s.PutChar(','); } + int len = snprintf(buffer, sizeof(buffer), "%d", v[i]); + count += len; + if (count > 2048) { s.PutChar('\n'); count = 0; } + if (len < 0 || len > 31) { + // this should never happen + throw DeadlyExportError("failed to convert double to string"); + } + for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); } + } + // } + s.PutChar('\n'); + for (int i = 0; i < indent; ++i) { s.PutChar('\t'); } + s.PutChar('}'); s.PutChar(' '); + node.End(s, false, indent, false); +} + +// binary property node from vector of doubles // TODO: optional zip compression! -void FBX::Node::WritePropertyNode( +void FBX::Node::WritePropertyNodeBinary( const std::string& name, const std::vector& v, Assimp::StreamWriterLE& s ){ - Node node(name); - node.Begin(s); + FBX::Node node(name); + node.BeginBinary(s); s.PutU1('d'); s.PutU4(uint32_t(v.size())); // number of elements s.PutU4(0); // no encoding (1 would be zip-compressed) s.PutU4(uint32_t(v.size()) * 8); // data size for (auto it = v.begin(); it != v.end(); ++it) { s.PutF8(*it); } - node.EndProperties(s, 1); - node.End(s, false); + node.EndPropertiesBinary(s, 1); + node.EndBinary(s, false); } -// convenience function to create and write a property node, -// holding a single property which is an array of values. -// does not copy the data, so is efficient for large arrays. +// binary property node from vector of int32_t // TODO: optional zip compression! -void FBX::Node::WritePropertyNode( +void FBX::Node::WritePropertyNodeBinary( const std::string& name, const std::vector& v, Assimp::StreamWriterLE& s ){ - Node node(name); - node.Begin(s); + FBX::Node node(name); + node.BeginBinary(s); s.PutU1('i'); s.PutU4(uint32_t(v.size())); // number of elements s.PutU4(0); // no encoding (1 would be zip-compressed) s.PutU4(uint32_t(v.size()) * 4); // data size for (auto it = v.begin(); it != v.end(); ++it) { s.PutI4(*it); } - node.EndProperties(s, 1); - node.End(s, false); + node.EndPropertiesBinary(s, 1); + node.EndBinary(s, false); } +// public static member functions + +// convenience function to create and write a property node, +// holding a single property which is an array of values. +// does not copy the data, so is efficient for large arrays. +void FBX::Node::WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + bool binary, int indent +){ + if (binary) { + FBX::Node::WritePropertyNodeBinary(name, v, s); + } else { + FBX::Node::WritePropertyNodeAscii(name, v, s, indent); + } +} + +// convenience function to create and write a property node, +// holding a single property which is an array of values. +// does not copy the data, so is efficient for large arrays. +void FBX::Node::WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + bool binary, int indent +){ + if (binary) { + FBX::Node::WritePropertyNodeBinary(name, v, s); + } else { + FBX::Node::WritePropertyNodeAscii(name, v, s, indent); + } +} #endif // ASSIMP_BUILD_NO_FBX_EXPORTER #endif // ASSIMP_BUILD_NO_EXPORT diff --git a/code/FBXExportNode.h b/code/FBXExportNode.h index 58d3d14ee..5ddd8c77b 100644 --- a/code/FBXExportNode.h +++ b/code/FBXExportNode.h @@ -142,19 +142,48 @@ public: // support specifically for dealing with Properties70 nodes public: // member functions for writing data to a file or stream - // write the full node as binary data to the given file or stream - void Dump(std::shared_ptr outfile); - void Dump(Assimp::StreamWriterLE &s); + // write the full node to the given file or stream + void Dump( + std::shared_ptr outfile, + bool binary, int indent + ); + void Dump(Assimp::StreamWriterLE &s, bool binary, int indent); // these other functions are for writing data piece by piece. // they must be used carefully. // for usage examples see FBXExporter.cpp. - void Begin(Assimp::StreamWriterLE &s); - void DumpProperties(Assimp::StreamWriterLE& s); - void EndProperties(Assimp::StreamWriterLE &s); - void EndProperties(Assimp::StreamWriterLE &s, size_t num_properties); - void DumpChildren(Assimp::StreamWriterLE& s); - void End(Assimp::StreamWriterLE &s, bool has_children); + void Begin(Assimp::StreamWriterLE &s, bool binary, int indent); + void DumpProperties(Assimp::StreamWriterLE& s, bool binary, int indent); + void EndProperties(Assimp::StreamWriterLE &s, bool binary, int indent); + void EndProperties( + Assimp::StreamWriterLE &s, bool binary, int indent, + size_t num_properties + ); + void BeginChildren(Assimp::StreamWriterLE &s, bool binary, int indent); + void DumpChildren(Assimp::StreamWriterLE& s, bool binary, int indent); + void End( + Assimp::StreamWriterLE &s, bool binary, int indent, + bool has_children + ); + +private: // internal functions used for writing + + void DumpBinary(Assimp::StreamWriterLE &s); + void DumpAscii(Assimp::StreamWriterLE &s, int indent); + void DumpAscii(std::ostream &s, int indent); + + void BeginBinary(Assimp::StreamWriterLE &s); + void DumpPropertiesBinary(Assimp::StreamWriterLE& s); + void EndPropertiesBinary(Assimp::StreamWriterLE &s); + void EndPropertiesBinary(Assimp::StreamWriterLE &s, size_t num_properties); + void DumpChildrenBinary(Assimp::StreamWriterLE& s); + void EndBinary(Assimp::StreamWriterLE &s, bool has_children); + + void BeginAscii(std::ostream &s, int indent); + void DumpPropertiesAscii(std::ostream &s, int indent); + void BeginChildrenAscii(std::ostream &s, int indent); + void DumpChildrenAscii(std::ostream &s, int indent); + void EndAscii(std::ostream &s, int indent, bool has_children); private: // data used for binary dumps size_t start_pos; // starting position in stream @@ -169,11 +198,12 @@ public: // static member functions static void WritePropertyNode( const std::string& name, const T value, - Assimp::StreamWriterLE& s + Assimp::StreamWriterLE& s, + bool binary, int indent ) { FBX::Property p(value); FBX::Node node(name, p); - node.Dump(s); + node.Dump(s, binary, indent); } // convenience function to create and write a property node, @@ -182,7 +212,8 @@ public: // static member functions static void WritePropertyNode( const std::string& name, const std::vector& v, - Assimp::StreamWriterLE& s + Assimp::StreamWriterLE& s, + bool binary, int indent ); // convenience function to create and write a property node, @@ -191,8 +222,34 @@ public: // static member functions static void WritePropertyNode( const std::string& name, const std::vector& v, + Assimp::StreamWriterLE& s, + bool binary, int indent + ); + +private: // static helper functions + static void WritePropertyNodeAscii( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + int indent + ); + static void WritePropertyNodeAscii( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + int indent + ); + static void WritePropertyNodeBinary( + const std::string& name, + const std::vector& v, Assimp::StreamWriterLE& s ); + static void WritePropertyNodeBinary( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s + ); + }; diff --git a/code/FBXExportProperty.cpp b/code/FBXExportProperty.cpp index e139bb95a..431750274 100644 --- a/code/FBXExportProperty.cpp +++ b/code/FBXExportProperty.cpp @@ -48,7 +48,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include // stringstream +#include +#include +#include // ostringstream // constructors for single element properties @@ -164,18 +166,18 @@ size_t FBX::Property::size() } } -void FBX::Property::Dump(Assimp::StreamWriterLE &s) +void FBX::Property::DumpBinary(Assimp::StreamWriterLE &s) { s.PutU1(type); - uint8_t* d; + uint8_t* d = data.data(); size_t N; switch (type) { - case 'C': s.PutU1(*(reinterpret_cast(data.data()))); return; - case 'Y': s.PutI2(*(reinterpret_cast(data.data()))); return; - case 'I': s.PutI4(*(reinterpret_cast(data.data()))); return; - case 'F': s.PutF4(*(reinterpret_cast(data.data()))); return; - case 'D': s.PutF8(*(reinterpret_cast(data.data()))); return; - case 'L': s.PutI8(*(reinterpret_cast(data.data()))); return; + case 'C': s.PutU1(*(reinterpret_cast(d))); return; + case 'Y': s.PutI2(*(reinterpret_cast(d))); return; + case 'I': s.PutI4(*(reinterpret_cast(d))); return; + case 'F': s.PutF4(*(reinterpret_cast(d))); return; + case 'D': s.PutF8(*(reinterpret_cast(d))); return; + case 'L': s.PutI8(*(reinterpret_cast(d))); return; case 'S': case 'R': s.PutU4(uint32_t(data.size())); @@ -187,7 +189,6 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s) s.PutU4(0); // no encoding (1 would be zip-compressed) // TODO: compress if large? s.PutU4(uint32_t(data.size())); // data size - d = data.data(); for (size_t i = 0; i < N; ++i) { s.PutI4((reinterpret_cast(d))[i]); } @@ -198,7 +199,6 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s) s.PutU4(0); // no encoding (1 would be zip-compressed) // TODO: compress if large? s.PutU4(uint32_t(data.size())); // data size - d = data.data(); for (size_t i = 0; i < N; ++i) { s.PutI8((reinterpret_cast(d))[i]); } @@ -209,7 +209,6 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s) s.PutU4(0); // no encoding (1 would be zip-compressed) // TODO: compress if large? s.PutU4(uint32_t(data.size())); // data size - d = data.data(); for (size_t i = 0; i < N; ++i) { s.PutF4((reinterpret_cast(d))[i]); } @@ -220,18 +219,146 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s) s.PutU4(0); // no encoding (1 would be zip-compressed) // TODO: compress if large? s.PutU4(uint32_t(data.size())); // data size - d = data.data(); for (size_t i = 0; i < N; ++i) { s.PutF8((reinterpret_cast(d))[i]); } return; default: - std::stringstream err; + std::ostringstream err; err << "Tried to dump property with invalid type '"; err << type << "'!"; throw DeadlyExportError(err.str()); } } +void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent) +{ + std::ostringstream ss; + ss.imbue(std::locale::classic()); + ss.precision(15); // this seems to match official FBX SDK exports + DumpAscii(ss, indent); + outstream.PutString(ss.str()); +} + +void FBX::Property::DumpAscii(std::ostream& s, int indent) +{ + // no writing type... or anything. just shove it into the stream. + uint8_t* d = data.data(); + size_t N; + size_t swap = data.size(); + size_t count = 0; + switch (type) { + case 'C': + if (*(reinterpret_cast(d))) { s << 'T'; } + else { s << 'F'; } + return; + case 'Y': s << *(reinterpret_cast(d)); return; + case 'I': s << *(reinterpret_cast(d)); return; + case 'F': s << *(reinterpret_cast(d)); return; + case 'D': s << *(reinterpret_cast(d)); return; + case 'L': s << *(reinterpret_cast(d)); return; + case 'S': + // first search to see if it has "\x00\x01" in it - + // which separates fields which are reversed in the ascii version. + // yeah. + // FBX, yeah. + for (size_t i = 0; i < data.size(); ++i) { + if (data[i] == '\0') { + swap = i; + break; + } + } + case 'R': + s << '"'; + // we might as well check this now, + // probably it will never happen + for (size_t i = 0; i < data.size(); ++i) { + char c = data[i]; + if (c == '"') { + throw runtime_error("can't handle quotes in property string"); + } + } + // first write the SWAPPED member (if any) + for (size_t i = swap + 2; i < data.size(); ++i) { + char c = data[i]; + s << c; + } + // then a separator + if (swap != data.size()) { + s << "::"; + } + // then the initial member + for (size_t i = 0; i < swap; ++i) { + char c = data[i]; + s << c; + } + s << '"'; + return; + case 'i': + N = data.size() / 4; // number of elements + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + case 'l': + N = data.size() / 8; + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + case 'f': + N = data.size() / 4; + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + case 'd': + N = data.size() / 8; + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + // set precision to something that can handle doubles + s.precision(15); + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + default: + std::ostringstream err; + err << "Tried to dump property with invalid type '"; + err << type << "'!"; + throw runtime_error(err.str()); + } +} + #endif // ASSIMP_BUILD_NO_FBX_EXPORTER #endif // ASSIMP_BUILD_NO_EXPORT diff --git a/code/FBXExportProperty.h b/code/FBXExportProperty.h index 40a020688..cb3b0113f 100644 --- a/code/FBXExportProperty.h +++ b/code/FBXExportProperty.h @@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include // is_void namespace FBX { @@ -113,7 +114,10 @@ public: size_t size(); // write this property node as binary data to the given stream - void Dump(Assimp::StreamWriterLE &s); + void DumpBinary(Assimp::StreamWriterLE &s); + void DumpAscii(Assimp::StreamWriterLE &s, int indent=0); + void DumpAscii(std::ostream &s, int indent=0); + // note: make sure the ostream is in classic "C" locale private: char type; diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 46782cc2e..e8846be08 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -66,7 +66,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include // endl // RESOURCES: // https://code.blender.org/2013/08/fbx-binary-file-format-specification/ @@ -89,6 +88,8 @@ namespace FBX { "\xfa\xbc\xab\x09\xd0\xc8\xd4\x66\xb1\x76\xfb\x83\x1c\xf7\x26\x7e"; const std::string FOOT_MAGIC = "\xf8\x5a\x8c\x6a\xde\xf5\xd9\x7e\xec\xe9\x0c\xe3\x75\x8f\x29\x0b"; + const std::string COMMENT_UNDERLINE = + ";------------------------------------------------------------------"; } using namespace Assimp; @@ -115,7 +116,7 @@ namespace Assimp { // --------------------------------------------------------------------- // Worker function for exporting a scene to ASCII FBX. // Prototyped and registered in Exporter.cpp - /*void ExportSceneFBXA ( + void ExportSceneFBXA ( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, @@ -126,7 +127,7 @@ namespace Assimp { // perform ascii export exporter.ExportAscii(pFile, pIOSystem); - }*/ // TODO + } } // end of namespace Assimp @@ -194,27 +195,43 @@ void FBXExporter::ExportAscii ( ); } - // this isn't really necessary, - // but the Autodesk FBX SDK puts a similar comment at the top of the file. - // Theirs declares that the file copyright is owned by Autodesk... - std::stringstream head; - using std::endl; - head << "; FBX " << EXPORT_VERSION_STR << " project file" << endl; - head << "; Created by the Open Asset Import Library (Assimp)" << endl; - head << "; http://assimp.org" << endl; - head << "; -------------------------------------------------" << endl; - head << endl; - const std::string ascii_header = head.str(); - outfile->Write(ascii_header.c_str(), ascii_header.size(), 1); + // write the ascii header + WriteAsciiHeader(); // write all the sections WriteAllNodes(); + // make sure the file ends with a newline. + // note: if the file is opened in text mode, + // this should do the right cross-platform thing. + outfile->Write("\n", 1, 1); + // explicitly release file pointer, // so we don't have to rely on class destruction. outfile.reset(); } +void FBXExporter::WriteAsciiHeader() +{ + // basically just a comment at the top of the file + std::stringstream head; + head << "; FBX " << EXPORT_VERSION_STR << " project file\n"; + head << "; Created by the Open Asset Import Library (Assimp)\n"; + head << "; http://assimp.org\n"; + head << "; -------------------------------------------------\n"; + const std::string ascii_header = head.str(); + outfile->Write(ascii_header.c_str(), ascii_header.size(), 1); +} + +void FBXExporter::WriteAsciiSectionHeader(const std::string& title) +{ + StreamWriterLE outstream(outfile); + std::stringstream s; + s << "\n\n; " << title << '\n'; + s << FBX::COMMENT_UNDERLINE << "\n"; + outstream.PutString(s.str()); +} + void FBXExporter::WriteBinaryHeader() { // first a specific sequence of 23 bytes, always the same @@ -294,28 +311,39 @@ void FBXExporter::WriteAllNodes () //FBXHeaderExtension top-level node void FBXExporter::WriteHeaderExtension () { + if (!binary) { + // no title, follows directly from the top comment + } FBX::Node n("FBXHeaderExtension"); StreamWriterLE outstream(outfile); + int indent = 0; // begin node - n.Begin(outstream); + n.Begin(outstream, binary, indent); // write properties // (none) // finish properties - n.EndProperties(outstream, 0); + n.EndProperties(outstream, binary, indent, 0); + + // begin children + n.BeginChildren(outstream, binary, indent); + + indent = 1; // write child nodes FBX::Node::WritePropertyNode( - "FBXHeaderVersion", int32_t(1003), outstream + "FBXHeaderVersion", int32_t(1003), outstream, binary, indent ); FBX::Node::WritePropertyNode( - "FBXVersion", int32_t(EXPORT_VERSION_INT), outstream - ); - FBX::Node::WritePropertyNode( - "EncryptionType", int32_t(0), outstream + "FBXVersion", int32_t(EXPORT_VERSION_INT), outstream, binary, indent ); + if (binary) { + FBX::Node::WritePropertyNode( + "EncryptionType", int32_t(0), outstream, binary, indent + ); + } FBX::Node CreationTimeStamp("CreationTimeStamp"); time_t rawtime; @@ -329,36 +357,50 @@ void FBXExporter::WriteHeaderExtension () CreationTimeStamp.AddChild("Minute", int32_t(now->tm_min)); CreationTimeStamp.AddChild("Second", int32_t(now->tm_sec)); CreationTimeStamp.AddChild("Millisecond", int32_t(0)); - CreationTimeStamp.Dump(outstream); + CreationTimeStamp.Dump(outstream, binary, indent); std::stringstream creator; creator << "Open Asset Import Library (Assimp) " << aiGetVersionMajor() << "." << aiGetVersionMinor() << "." << aiGetVersionRevision(); - FBX::Node::WritePropertyNode("Creator", creator.str(), outstream); + FBX::Node::WritePropertyNode( + "Creator", creator.str(), outstream, binary, indent + ); - FBX::Node sceneinfo("SceneInfo"); + //FBX::Node sceneinfo("SceneInfo"); //sceneinfo.AddProperty("GlobalInfo" + FBX::SEPARATOR + "SceneInfo"); // not sure if any of this is actually needed, // so just write an empty node for now. - sceneinfo.Dump(outstream); + //sceneinfo.Dump(outstream, binary, indent); + + indent = 0; // finish node - n.End(outstream, true); + n.End(outstream, binary, indent, true); // that's it for FBXHeaderExtension... + if (!binary) { return; } // but binary files also need top-level FileID, CreationTime, Creator: std::vector raw(GENERIC_FILEID.size()); for (size_t i = 0; i < GENERIC_FILEID.size(); ++i) { raw[i] = uint8_t(GENERIC_FILEID[i]); } - FBX::Node::WritePropertyNode("FileId", raw, outstream); - FBX::Node::WritePropertyNode("CreationTime", GENERIC_CTIME, outstream); - FBX::Node::WritePropertyNode("Creator", creator.str(), outstream); + FBX::Node::WritePropertyNode( + "FileId", raw, outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "CreationTime", GENERIC_CTIME, outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "Creator", creator.str(), outstream, binary, indent + ); } void FBXExporter::WriteGlobalSettings () { + if (!binary) { + // no title, follows directly from the header extension + } FBX::Node gs("GlobalSettings"); gs.AddChild("Version", int32_t(1000)); @@ -385,11 +427,15 @@ void FBXExporter::WriteGlobalSettings () p.AddP70int("CurrentTimeMarker", -1); gs.AddChild(p); - gs.Dump(outfile); + gs.Dump(outfile, binary, 0); } void FBXExporter::WriteDocuments () { + if (!binary) { + WriteAsciiSectionHeader("Documents Description"); + } + // not sure what the use of multiple documents would be, // or whether any end-application supports it FBX::Node docs("Documents"); @@ -411,16 +457,19 @@ void FBXExporter::WriteDocuments () doc.AddChild("RootNode", int64_t(0)); docs.AddChild(doc); - docs.Dump(outfile); + docs.Dump(outfile, binary, 0); } void FBXExporter::WriteReferences () { + if (!binary) { + WriteAsciiSectionHeader("Document References"); + } // always empty for now. // not really sure what this is for. FBX::Node n("References"); n.force_has_children = true; - n.Dump(outfile); + n.Dump(outfile, binary, 0); } @@ -469,9 +518,6 @@ size_t count_images(const aiScene* scene) { } } } - //for (auto &s : images) { - // std::cout << "found image: " << s << std::endl; - //} return images.size(); } @@ -511,6 +557,11 @@ void FBXExporter::WriteDefinitions () // determining how many of each type of object there are // and specifying the base properties to use when otherwise unspecified. + // ascii section header + if (!binary) { + WriteAsciiSectionHeader("Object definitions"); + } + // we need to count the objects int32_t count; int32_t total_count = 0; @@ -890,7 +941,7 @@ void FBXExporter::WriteDefinitions () defs.AddChild("Version", int32_t(100)); defs.AddChild("Count", int32_t(total_count)); for (auto &n : object_nodes) { defs.AddChild(n); } - defs.Dump(outfile); + defs.Dump(outfile, binary, 0); } @@ -936,14 +987,20 @@ int64_t to_ktime(double ticks, const aiAnimation* anim) { void FBXExporter::WriteObjects () { + if (!binary) { + WriteAsciiSectionHeader("Object properties"); + } // numbers should match those given in definitions! make sure to check StreamWriterLE outstream(outfile); FBX::Node object_node("Objects"); - object_node.Begin(outstream); - object_node.EndProperties(outstream); + int indent = 0; + object_node.Begin(outstream, binary, indent); + object_node.EndProperties(outstream, binary, indent); + object_node.BeginChildren(outstream, binary, indent); // geometry (aiMesh) mesh_uids.clear(); + indent = 1; for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { // it's all about this mesh aiMesh* m = mScene->mMeshes[mi]; @@ -955,9 +1012,11 @@ void FBXExporter::WriteObjects () n.AddProperty(uid); n.AddProperty(FBX::SEPARATOR + "Geometry"); n.AddProperty("Mesh"); - n.Begin(outstream); - n.DumpProperties(outstream); - n.EndProperties(outstream); + n.Begin(outstream, binary, indent); + n.DumpProperties(outstream, binary, indent); + n.EndProperties(outstream, binary, indent); + n.BeginChildren(outstream, binary, indent); + indent = 2; // output vertex data - each vertex should be unique (probably) std::vector flattened_vertices; @@ -981,7 +1040,7 @@ void FBXExporter::WriteObjects () } } FBX::Node::WritePropertyNode( - "Vertices", flattened_vertices, outstream + "Vertices", flattened_vertices, outstream, binary, indent ); // output polygon data as a flattened array of vertex indices. @@ -997,30 +1056,38 @@ void FBXExporter::WriteObjects () ); } FBX::Node::WritePropertyNode( - "PolygonVertexIndex", polygon_data, outstream + "PolygonVertexIndex", polygon_data, outstream, binary, indent ); // here could be edges but they're insane. // it's optional anyway, so let's ignore it. FBX::Node::WritePropertyNode( - "GeometryVersion", int32_t(124), outstream + "GeometryVersion", int32_t(124), outstream, binary, indent ); // normals, if any if (m->HasNormals()) { FBX::Node normals("LayerElementNormal", int32_t(0)); - normals.Begin(outstream); - normals.DumpProperties(outstream); - normals.EndProperties(outstream); - FBX::Node::WritePropertyNode("Version", int32_t(101), outstream); - FBX::Node::WritePropertyNode("Name", "", outstream); + normals.Begin(outstream, binary, indent); + normals.DumpProperties(outstream, binary, indent); + normals.EndProperties(outstream, binary, indent); + normals.BeginChildren(outstream, binary, indent); + indent = 3; FBX::Node::WritePropertyNode( - "MappingInformationType", "ByPolygonVertex", outstream + "Version", int32_t(101), outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "Name", "", outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "MappingInformationType", "ByPolygonVertex", + outstream, binary, indent ); // TODO: vertex-normals or indexed normals when appropriate FBX::Node::WritePropertyNode( - "ReferenceInformationType", "Direct", outstream + "ReferenceInformationType", "Direct", + outstream, binary, indent ); std::vector normal_data; normal_data.reserve(3 * polygon_data.size()); @@ -1033,10 +1100,13 @@ void FBXExporter::WriteObjects () normal_data.push_back(n.z); } } - FBX::Node::WritePropertyNode("Normals", normal_data, outstream); + FBX::Node::WritePropertyNode( + "Normals", normal_data, outstream, binary, indent + ); // note: version 102 has a NormalsW also... not sure what it is, // so we can stick with version 101 for now. - normals.End(outstream, true); + indent = 2; + normals.End(outstream, binary, indent, true); } // uvs, if any @@ -1057,18 +1127,26 @@ void FBXExporter::WriteObjects () DefaultLogger::get()->warn(err.str()); } FBX::Node uv("LayerElementUV", int32_t(uvi)); - uv.Begin(outstream); - uv.DumpProperties(outstream); - uv.EndProperties(outstream); - FBX::Node::WritePropertyNode("Version", int32_t(101), outstream); + uv.Begin(outstream, binary, indent); + uv.DumpProperties(outstream, binary, indent); + uv.EndProperties(outstream, binary, indent); + uv.BeginChildren(outstream, binary, indent); + indent = 3; + FBX::Node::WritePropertyNode( + "Version", int32_t(101), outstream, binary, indent + ); // it doesn't seem like assimp keeps the uv map name, // so just leave it blank. - FBX::Node::WritePropertyNode("Name", "", outstream); FBX::Node::WritePropertyNode( - "MappingInformationType", "ByPolygonVertex", outstream + "Name", "", outstream, binary, indent ); FBX::Node::WritePropertyNode( - "ReferenceInformationType", "IndexToDirect", outstream + "MappingInformationType", "ByPolygonVertex", + outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "ReferenceInformationType", "IndexToDirect", + outstream, binary, indent ); std::vector uv_data; @@ -1093,9 +1171,14 @@ void FBXExporter::WriteObjects () } } } - FBX::Node::WritePropertyNode("UV", uv_data, outstream); - FBX::Node::WritePropertyNode("UVIndex", uv_indices, outstream); - uv.End(outstream, true); + FBX::Node::WritePropertyNode( + "UV", uv_data, outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "UVIndex", uv_indices, outstream, binary, indent + ); + indent = 2; + uv.End(outstream, binary, indent, true); } // i'm not really sure why this material section exists, @@ -1108,7 +1191,7 @@ void FBXExporter::WriteObjects () mat.AddChild("ReferenceInformationType", "IndexToDirect"); std::vector mat_indices = {0}; mat.AddChild("Materials", mat_indices); - mat.Dump(outstream); + mat.Dump(outstream, binary, indent); // finally we have the layer specifications, // which select the normals / UV set / etc to use. @@ -1127,10 +1210,11 @@ void FBXExporter::WriteObjects () le.AddChild("Type", "LayerElementUV"); le.AddChild("TypedIndex", int32_t(0)); layer.AddChild(le); - layer.Dump(outstream); + layer.Dump(outstream, binary, indent); // finish the node record - n.End(outstream, true); + indent = 1; + n.End(outstream, binary, indent, true); } // aiMaterial @@ -1274,7 +1358,7 @@ void FBXExporter::WriteObjects () n.AddChild(p); - n.Dump(outstream); + n.Dump(outstream, binary, indent); } // we need to look up all the images we're using, @@ -1322,7 +1406,7 @@ void FBXExporter::WriteObjects () n.AddChild("UseMipMap", int32_t(0)); n.AddChild("Filename", path); n.AddChild("RelativeFilename", path); - n.Dump(outstream); + n.Dump(outstream, binary, indent); } // Textures @@ -1441,7 +1525,7 @@ void FBXExporter::WriteObjects () tnode.AddChild( "Cropping", int32_t(0), int32_t(0), int32_t(0), int32_t(0) ); - tnode.Dump(outstream); + tnode.Dump(outstream, binary, indent); } } @@ -1594,7 +1678,7 @@ void FBXExporter::WriteObjects () // "acuracy"... this is not a typo.... dnode.AddChild("Link_DeformAcuracy", double(50)); dnode.AddChild("SkinningType", "Linear"); // TODO: other modes? - dnode.Dump(outstream); + dnode.Dump(outstream, binary, indent); // connect it connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]); @@ -1738,7 +1822,7 @@ void FBXExporter::WriteObjects () // there's not really any way around this at the moment. // done - sdnode.Dump(outstream); + sdnode.Dump(outstream, binary, indent); // lastly, connect to the parent deformer connections.emplace_back( @@ -1856,7 +1940,7 @@ void FBXExporter::WriteObjects () } // now write it - bpnode.Dump(outstream); + bpnode.Dump(outstream, binary, indent); }*/ // TODO: cameras, lights @@ -1916,7 +2000,7 @@ void FBXExporter::WriteObjects () // this node absurdly always pretends it has children // (in this case it does, but just in case...) asnode.force_has_children = true; - asnode.Dump(outstream); + asnode.Dump(outstream, binary, indent); // note: animation stacks are not connected to anything } @@ -1931,7 +2015,7 @@ void FBXExporter::WriteObjects () // this node absurdly always pretends it has children alnode.force_has_children = true; - alnode.Dump(outstream); + alnode.Dump(outstream, binary, indent); // connect to the relevant animstack connections.emplace_back( @@ -2048,7 +2132,8 @@ void FBXExporter::WriteObjects () } } - object_node.End(outstream, true); + indent = 0; + object_node.End(outstream, binary, indent, true); } // convenience map of magic node name strings to FBX properties, @@ -2074,13 +2159,14 @@ const std::map> transform_types = { }; // write a single model node to the stream -void WriteModelNode( +void FBXExporter::WriteModelNode( StreamWriterLE& outstream, + bool binary, const aiNode* node, int64_t node_uid, const std::string& type, const std::vector>& transform_chain, - TransformInheritance inherit_type=TransformInheritance_RSrs + TransformInheritance inherit_type ){ const aiVector3D zero = {0, 0, 0}; const aiVector3D one = {1, 1, 1}; @@ -2148,7 +2234,7 @@ void WriteModelNode( m.AddChild("Shading", Property(true)); m.AddChild("Culling", Property("CullingOff")); - m.Dump(outstream); + m.Dump(outstream, binary, 1); } // wrapper for WriteModelNodes to create and pass a blank transform chain @@ -2249,9 +2335,13 @@ void FBXExporter::WriteModelNodes( node_uid ); // write model node - WriteModelNode(outstream, node, node_uid, "Mesh", transform_chain); + WriteModelNode( + outstream, binary, node, node_uid, "Mesh", transform_chain + ); } else if (limbnodes.count(node)) { - WriteModelNode(outstream, node, node_uid, "LimbNode", transform_chain); + WriteModelNode( + outstream, binary, node, node_uid, "LimbNode", transform_chain + ); // we also need to write a nodeattribute to mark it as a skeleton int64_t node_attribute_uid = generate_uid(); FBX::Node na("NodeAttribute"); @@ -2259,12 +2349,14 @@ void FBXExporter::WriteModelNodes( node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode" ); na.AddChild("TypeFlags", Property("Skeleton")); - na.Dump(outstream); + na.Dump(outstream, binary, 1); // and connect them connections.emplace_back("C", "OO", node_attribute_uid, node_uid); } else { // generate a null node so we can add children to it - WriteModelNode(outstream, node, node_uid, "Null", transform_chain); + WriteModelNode( + outstream, binary, node, node_uid, "Null", transform_chain + ); } // if more than one child mesh, make nodes for each mesh @@ -2296,7 +2388,7 @@ void FBXExporter::WriteModelNodes( FBX::Node p("Properties70"); p.AddP70enum("InheritType", 1); m.AddChild(p); - m.Dump(outstream); + m.Dump(outstream, binary, 1); } } @@ -2325,7 +2417,7 @@ void FBXExporter::WriteAnimationCurveNode( p.AddP70numberA("d|Y", default_value.y); p.AddP70numberA("d|Z", default_value.z); n.AddChild(p); - n.Dump(outstream); + n.Dump(outstream, binary, 1); // connect to layer this->connections.emplace_back("C", "OO", uid, layer_uid); // connect to bone @@ -2356,7 +2448,7 @@ void FBXExporter::WriteAnimationCurve( "KeyAttrRefCount", std::vector{static_cast(times.size())} ); - n.Dump(outstream); + n.Dump(outstream, binary, 1); this->connections.emplace_back( "C", "OP", curve_uid, curvenode_uid, property_link ); @@ -2367,13 +2459,18 @@ void FBXExporter::WriteConnections () { // we should have completed the connection graph already, // so basically just dump it here + if (!binary) { + WriteAsciiSectionHeader("Object connections"); + } + // TODO: comments with names in the ascii version FBX::Node conn("Connections"); StreamWriterLE outstream(outfile); - conn.Begin(outstream); + conn.Begin(outstream, binary, 0); + conn.BeginChildren(outstream, binary, 0); for (auto &n : connections) { - n.Dump(outstream); + n.Dump(outstream, binary, 1); } - conn.End(outstream, !connections.empty()); + conn.End(outstream, binary, 0, !connections.empty()); connections.clear(); } diff --git a/code/FBXExporter.h b/code/FBXExporter.h index 553cf60fe..3b9de8acb 100644 --- a/code/FBXExporter.h +++ b/code/FBXExporter.h @@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER #include "FBXExportNode.h" // FBX::Node +#include "FBXCommon.h" // FBX::TransformInheritance #include //#include @@ -104,6 +105,9 @@ namespace Assimp void WriteBinaryHeader(); void WriteBinaryFooter(); + // ascii files have a comment at the top + void WriteAsciiHeader(); + // WriteAllNodes does the actual export. // It just calls all the Write
methods below in order. void WriteAllNodes(); @@ -126,6 +130,7 @@ namespace Assimp // WriteTakes(); // deprecated since at least 2015 (fbx 7.4) // helpers + void WriteAsciiSectionHeader(const std::string& title); void WriteModelNodes( Assimp::StreamWriterLE& s, const aiNode* node, @@ -139,6 +144,15 @@ namespace Assimp const std::unordered_set& limbnodes, std::vector>& transform_chain ); + void WriteModelNode( // nor this + StreamWriterLE& s, + bool binary, + const aiNode* node, + int64_t node_uid, + const std::string& type, + const std::vector>& xfm_chain, + FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs + ); void WriteAnimationCurveNode( StreamWriterLE& outstream, int64_t uid, From 0dad1d0c04fdf4ee04bbb81c56a6cc1a5e45873f Mon Sep 17 00:00:00 2001 From: Tommy Date: Thu, 29 Mar 2018 01:46:59 +0200 Subject: [PATCH 157/183] FBX Export: fix Texture_Alpha_soutce typo. --- code/FBXExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index e8846be08..4db9fc4d9 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -1521,7 +1521,7 @@ void FBXExporter::WriteObjects () tnode.AddChild("RelativeFilename", texture_path); tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0)); tnode.AddChild("ModelUVScaling", double(1.0), double(1.0)); - tnode.AddChild("Texture_Alpha_Soutce", "None"); + tnode.AddChild("Texture_Alpha_Source", "None"); tnode.AddChild( "Cropping", int32_t(0), int32_t(0), int32_t(0), int32_t(0) ); From 4b2120ab5642b8341e350e1d68de86c328b500b0 Mon Sep 17 00:00:00 2001 From: Tommy Date: Thu, 29 Mar 2018 10:45:23 +0200 Subject: [PATCH 158/183] FBX Export: fix for geometric transform nodes with multiple children. --- code/FBXExporter.cpp | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 4db9fc4d9..f31a98604 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -2258,15 +2258,6 @@ void FBXExporter::WriteModelNodes( // first collapse any expanded transformation chains created by FBX import. std::string node_name(node->mName.C_Str()); if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) { - if (node->mNumChildren != 1) { - // this should never happen - std::stringstream err; - err << "FBX transformation node should have exactly 1 child,"; - err << " but " << node->mNumChildren << " found"; - err << " on node \"" << node_name << "\"!"; - throw DeadlyExportError(err.str()); - } - aiNode* next_node = node->mChildren[0]; auto pos = node_name.find(MAGIC_NODE_TAG) + MAGIC_NODE_TAG.size() + 1; std::string type_name = node_name.substr(pos); auto elem = transform_types.find(type_name); @@ -2300,10 +2291,16 @@ void FBXExporter::WriteModelNodes( err << elem->second.second; throw DeadlyExportError(err.str()); } - // now just continue to the next node - WriteModelNodes( - outstream, next_node, parent_uid, limbnodes, transform_chain - ); + // now continue on to any child nodes + for (unsigned i = 0; i < node->mNumChildren; ++i) { + WriteModelNodes( + outstream, + node->mChildren[i], + parent_uid, + limbnodes, + transform_chain + ); + } return; } From 4a4b9cd31bea4a64a85592cb8b8fa5d07863a50d Mon Sep 17 00:00:00 2001 From: Tommy Date: Thu, 29 Mar 2018 12:38:56 +0200 Subject: [PATCH 159/183] assimp_cmd: prettier and better-compressed node hierarchy. Also removed line and nesting limits. It's better to show all the info than to silently discard some. With the new compressed display, it is far less obtrusive anyway. --- tools/assimp_cmd/Info.cpp | 107 +++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 29 deletions(-) diff --git a/tools/assimp_cmd/Info.cpp b/tools/assimp_cmd/Info.cpp index c4d5fe189..ae858bfe8 100644 --- a/tools/assimp_cmd/Info.cpp +++ b/tools/assimp_cmd/Info.cpp @@ -46,12 +46,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Main.h" +#include +#include +#include + const char* AICMD_MSG_INFO_HELP_E = "assimp info [-r] [-v]\n" "\tPrint basic structure of a 3D model\n" "\t-r,--raw: No postprocessing, do a raw import\n" "\t-v,--verbose: Print verbose info such as node transform data\n"; +const std::string TREE_BRANCH_ASCII = "|-"; +const std::string TREE_BRANCH_UTF8 = "\xe2\x94\x9c\xe2\x95\xb4"; +const std::string TREE_STOP_ASCII = "'-"; +const std::string TREE_STOP_UTF8 = "\xe2\x94\x94\xe2\x95\xb4"; +const std::string TREE_CONTINUE_ASCII = "| "; +const std::string TREE_CONTINUE_UTF8 = "\xe2\x94\x82 "; + +// note: by default this is outputing utf-8 text. +// this is well supported on pretty much any linux terminal. +// if this causes problems on some platform, +// put an #ifdef to use the ascii version for that platform. +const std::string TREE_BRANCH = TREE_BRANCH_UTF8; +const std::string TREE_STOP = TREE_STOP_UTF8; +const std::string TREE_CONTINUE = TREE_CONTINUE_UTF8; + // ----------------------------------------------------------------------------------- unsigned int CountNodes(const aiNode* root) @@ -184,46 +203,77 @@ std::string FindPTypes(const aiScene* scene) } // ----------------------------------------------------------------------------------- -void PrintHierarchy(const aiNode* root, unsigned int maxnest, unsigned int maxline, - unsigned int cline, bool verbose, unsigned int cnest=0) -{ - if (cline++ >= maxline || cnest >= maxnest) { - return; +// Prettily print the node graph to stdout +void PrintHierarchy( + const aiNode* node, + const std::string &indent, + bool verbose, + bool last = false, + bool first = true +){ + // tree visualization + std::string branchchar; + if (first) { branchchar = ""; } + else if (last) { branchchar = TREE_STOP; } // "'-" + else { branchchar = TREE_BRANCH; } // "|-" + + // print the indent and the branch character and the name + std::cout << indent << branchchar << node->mName.C_Str(); + + // if there are meshes attached, indicate this + if (node->mNumMeshes) { + std::cout << " (mesh "; + bool sep = false; + for (size_t i=0; i < node->mNumMeshes; ++i) { + unsigned int mesh_index = node->mMeshes[i]; + if (sep) { std::cout << ", "; } + std::cout << mesh_index; + sep = true; + } + std::cout << ")"; } - for(unsigned int i = 0; i < cnest; ++i) { - printf("-- "); - } - printf("\'%s\', meshes: %u\n",root->mName.data,root->mNumMeshes); + // finish the line + std::cout << std::endl; + // in verbose mode, print the transform data as well if (verbose) { - // print the actual transform - //printf(","); + // indent to use + std::string indentadd; + if (last) { indentadd += " "; } + else { indentadd += TREE_CONTINUE; } // "| ".. + if (node->mNumChildren == 0) { indentadd += " "; } + else { indentadd += TREE_CONTINUE; } // .."| " aiVector3D s, r, t; - root->mTransformation.Decompose(s, r, t); + node->mTransformation.Decompose(s, r, t); if (s.x != 1.0 || s.y != 1.0 || s.z != 1.0) { - for(unsigned int i = 0; i < cnest; ++i) { printf(" "); } - printf(" S:[%f %f %f]\n", s.x, s.y, s.z); + std::cout << indent << indentadd; + printf(" S:[%f %f %f]\n", s.x, s.y, s.z); } if (r.x || r.y || r.z) { - for(unsigned int i = 0; i < cnest; ++i) { printf(" "); } - printf(" R:[%f %f %f]\n", r.x, r.y, r.z); + std::cout << indent << indentadd; + printf(" R:[%f %f %f]\n", r.x, r.y, r.z); } if (t.x || t.y || t.z) { - for(unsigned int i = 0; i < cnest; ++i) { printf(" "); } - printf(" T:[%f %f %f]\n", t.x, t.y, t.z); + std::cout << indent << indentadd; + printf(" T:[%f %f %f]\n", t.x, t.y, t.z); } } - //printf("\n"); - for (unsigned int i = 0; i < root->mNumChildren; ++i ) { - PrintHierarchy(root->mChildren[i],maxnest,maxline,cline,verbose,cnest+1); - if(i == root->mNumChildren-1) { - for(unsigned int i = 0; i < cnest; ++i) { - printf(" "); - } - printf("<--\n"); - } + // and recurse + std::string nextIndent; + if (first) { nextIndent = indent; } + else if (last) { nextIndent = indent + " "; } + else { nextIndent = indent + TREE_CONTINUE; } // "| " + for (size_t i = 0; i < node->mNumChildren; ++i) { + bool lastone = (i == node->mNumChildren - 1); + PrintHierarchy( + node->mChildren[i], + nextIndent, + verbose, + lastone, + false + ); } } @@ -406,8 +456,7 @@ int Assimp_Info (const char* const* params, unsigned int num) // node hierarchy printf("\nNode hierarchy:\n"); - unsigned int cline=0; - PrintHierarchy(scene->mRootNode,20,1000,cline,verbose); + PrintHierarchy(scene->mRootNode,"",verbose); printf("\n"); return 0; From 39d98784294ed7bfc5473cd5000b4d7cd7760ed4 Mon Sep 17 00:00:00 2001 From: Tommy Date: Thu, 29 Mar 2018 13:10:41 +0200 Subject: [PATCH 160/183] FBX Export: use ai_snprintf. --- code/FBXExportNode.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/FBXExportNode.cpp b/code/FBXExportNode.cpp index 514d5f360..a1171eca8 100644 --- a/code/FBXExportNode.cpp +++ b/code/FBXExportNode.cpp @@ -47,12 +47,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include // StreamWriterLE #include // DeadlyExportError #include +#include // ai_snprintf #include #include #include // ostringstream #include // shared_ptr -#include // snprintf // AddP70 helpers... there's no usable pattern here, // so all are defined as separate functions. @@ -442,7 +442,7 @@ void FBX::Node::WritePropertyNodeAscii( int count = 0; for (size_t i = 0; i < v.size(); ++i) { if (i > 0) { s.PutChar(','); } - int len = snprintf(buffer, sizeof(buffer), "%f", v[i]); + int len = ai_snprintf(buffer, sizeof(buffer), "%f", v[i]); count += len; if (count > 2048) { s.PutChar('\n'); count = 0; } if (len < 0 || len > 31) { @@ -478,7 +478,7 @@ void FBX::Node::WritePropertyNodeAscii( int count = 0; for (size_t i = 0; i < v.size(); ++i) { if (i > 0) { s.PutChar(','); } - int len = snprintf(buffer, sizeof(buffer), "%d", v[i]); + int len = ai_snprintf(buffer, sizeof(buffer), "%d", v[i]); count += len; if (count > 2048) { s.PutChar('\n'); count = 0; } if (len < 0 || len > 31) { From 070acfbaddc99db506b90667dc3026644561dcd8 Mon Sep 17 00:00:00 2001 From: Tommy Date: Thu, 29 Mar 2018 14:29:02 +0200 Subject: [PATCH 161/183] FBX Export: relax bindpose matrix check slightly. Apparently +/- 1e-5 is too strict. --- code/FBXExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index f31a98604..c524e3911 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -1784,7 +1784,7 @@ void FBXExporter::WriteObjects () // 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-5f; // some error is to be expected + 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); From 26ac2ffb6c4cb321c3431db57d5ba0958c5f6fa0 Mon Sep 17 00:00:00 2001 From: Alexis Breust Date: Thu, 29 Mar 2018 15:52:05 +0200 Subject: [PATCH 162/183] Not duplicating textures for embedded ones --- code/glTF2Exporter.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index b873d9d22..e146327ea 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -316,11 +316,9 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe std::string path = tex.C_Str(); if (path.size() > 0) { - if (path[0] != '*') { - std::map::iterator it = mTexturesByPath.find(path); - if (it != mTexturesByPath.end()) { - texture = mAsset->textures.Get(it->second); - } + std::map::iterator it = mTexturesByPath.find(path); + if (it != mTexturesByPath.end()) { + texture = mAsset->textures.Get(it->second); } if (!texture) { From 4919d3da2f772bfe6bff487e8345fcb2215ddbdb Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 30 Mar 2018 16:58:11 +0200 Subject: [PATCH 163/183] closes https://github.com/assimp/assimp/issues/1836: make documentation much more clear how to apply global scaling. --- code/CMakeLists.txt | 4 ++-- code/FBXConverter.h | 2 ++ code/ScaleProcess.cpp | 7 ------- include/assimp/postprocess.h | 6 ++++-- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index dafe017ef..7ae1a7e63 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -177,8 +177,6 @@ SET( Common_SRCS SkeletonMeshBuilder.cpp SplitByBoneCountProcess.cpp SplitByBoneCountProcess.h - ScaleProcess.cpp - ScaleProcess.h StandardShapes.cpp TargetAnimation.cpp TargetAnimation.h @@ -586,6 +584,8 @@ SET( PostProcessing_SRCS PolyTools.h MakeVerboseFormat.cpp MakeVerboseFormat.h + ScaleProcess.cpp + ScaleProcess.h ) SOURCE_GROUP( PostProcessing FILES ${PostProcessing_SRCS}) diff --git a/code/FBXConverter.h b/code/FBXConverter.h index 06a6f07a7..26ba3cb4e 100644 --- a/code/FBXConverter.h +++ b/code/FBXConverter.h @@ -443,6 +443,8 @@ private: aiScene* const out; const FBX::Document& doc; + + std::vector mLightNames; }; } diff --git a/code/ScaleProcess.cpp b/code/ScaleProcess.cpp index 0f60fbdb4..facc39d7d 100644 --- a/code/ScaleProcess.cpp +++ b/code/ScaleProcess.cpp @@ -88,13 +88,6 @@ void ScaleProcess::Execute( aiScene* pScene ) { void ScaleProcess::traverseNodes( aiNode *node ) { applyScaling( node ); - - /*for ( unsigned int i = 0; i < node->mNumChildren; ++i ) { - aiNode *currentNode = currentNode->mChildren[ i ]; - if ( nullptr != currentNode ) { - traverseNodes( currentNode ); - } - }*/ } void ScaleProcess::applyScaling( aiNode *currentNode ) { diff --git a/include/assimp/postprocess.h b/include/assimp/postprocess.h index ebb6728f3..f6c0833ee 100644 --- a/include/assimp/postprocess.h +++ b/include/assimp/postprocess.h @@ -537,9 +537,11 @@ enum aiPostProcessSteps /**
This step will perform a global scale of the model. * * Some importers are providing a mechanism to define a scaling unit for the - * model. This post processing step can be used to do so. + * model. This post processing step can be used to do so. You need to get the + * global scaling from your importer settings like in FBX. Use the flag + * AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY from the global property table to configure this. * - * Use #AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY to control this. + * Use #AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY to setup the global scaing factor. */ aiProcess_GlobalScale = 0x8000000, From c97fb99435b80da87cae138fb026f764cc672cf7 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 31 Mar 2018 16:37:39 +0200 Subject: [PATCH 164/183] closes https://github.com/assimp/assimp/issues/1855: fix correction of node names. --- code/FBXConverter.cpp | 118 +++++++++++++++++++----------------------- code/FBXConverter.h | 29 ++++------- 2 files changed, 63 insertions(+), 84 deletions(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index f3a908dd4..cf5d42de2 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -61,6 +61,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include +#include namespace Assimp { namespace FBX { @@ -133,9 +135,7 @@ void Converter::ConvertRootNode() { ConvertNodes( 0L, *out->mRootNode ); } - -void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform ) -{ +void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform ) { const std::vector& conns = doc.GetConnectionsByDestinationSequenced( id, "Model" ); std::vector nodes; @@ -153,14 +153,14 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa } const Object* const object = con->SourceObject(); - if ( !object ) { + if ( nullptr == object ) { FBXImporter::LogWarn( "failed to convert source object for Model link" ); continue; } const Model* const model = dynamic_cast( object ); - if ( model ) { + if ( nullptr != model ) { nodes_chain.clear(); post_nodes_chain.clear(); @@ -174,7 +174,7 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa ai_assert( nodes_chain.size() ); - const std::string& original_name = FixNodeName( model->Name() ); + 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 @@ -189,7 +189,15 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa } if ( !name_carrier ) { + NodeNameCache::const_iterator it( std::find( mNodeNames.begin(), mNodeNames.end(), original_name ) ); + if ( it != mNodeNames.end() ) { + original_name = original_name + std::string( "001" ); + } + + mNodeNames.push_back( original_name ); nodes_chain.push_back( new aiNode( original_name ) ); + } else { + original_name = nodes_chain.back()->mName.C_Str(); } //setup metadata on newest node @@ -250,11 +258,11 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa ConvertNodes( model->ID(), *last_parent, new_abs_transform ); if ( doc.Settings().readLights ) { - ConvertLights( *model ); + ConvertLights( *model, original_name ); } if ( doc.Settings().readCameras ) { - ConvertCameras( *model ); + ConvertCameras( *model, original_name ); } nodes.push_back( nodes_chain.front() ); @@ -278,34 +286,31 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa } -void Converter::ConvertLights( const Model& model ) -{ +void Converter::ConvertLights( const Model& model, const std::string &orig_name ) { const std::vector& node_attrs = model.GetAttributes(); for( const NodeAttribute* attr : node_attrs ) { const Light* const light = dynamic_cast( attr ); if ( light ) { - ConvertLight( model, *light ); + ConvertLight( *light, orig_name ); } } } -void Converter::ConvertCameras( const Model& model ) -{ +void Converter::ConvertCameras( const Model& model, const std::string &orig_name ) { const std::vector& node_attrs = model.GetAttributes(); for( const NodeAttribute* attr : node_attrs ) { const Camera* const cam = dynamic_cast( attr ); if ( cam ) { - ConvertCamera( model, *cam ); + ConvertCamera( *cam, orig_name ); } } } -void Converter::ConvertLight( const Model& model, const Light& light ) -{ +void Converter::ConvertLight( const Light& light, const std::string &orig_name ) { lights.push_back( new aiLight() ); aiLight* const out_light = lights.back(); - out_light->mName.Set( FixNodeName( model.Name() ) ); + out_light->mName.Set( orig_name ); const float intensity = light.Intensity() / 100.0f; const aiVector3D& col = light.Color(); @@ -378,12 +383,12 @@ void Converter::ConvertLight( const Model& model, const Light& light ) } } -void Converter::ConvertCamera( const Model& model, const Camera& cam ) +void Converter::ConvertCamera( const Camera& cam, const std::string &orig_name ) { cameras.push_back( new aiCamera() ); aiCamera* const out_camera = cameras.back(); - out_camera->mName.Set( FixNodeName( model.Name() ) ); + out_camera->mName.Set( orig_name ); out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight(); @@ -397,6 +402,31 @@ void Converter::ConvertCamera( const Model& model, const Camera& cam ) out_camera->mClipPlaneFar = cam.FarPlane(); } +static bool HasName( NodeNameCache &cache, const std::string &name ) { + NodeNameCache::const_iterator it( std::find( cache.begin(), cache.end(), name ) ); + return it != cache.end(); + +} +void Converter::GetUniqueName( const std::string &name, std::string uniqueName ) { + if ( !HasName( mNodeNames, name ) ) { + uniqueName = name; + return; + } + + int i( 0 ); + std::string newName; + while ( HasName( mNodeNames, newName ) ) { + ++i; + newName.clear(); + newName += name; + std::stringstream ext; + ext << std::setfill( '0' ) << std::setw( 3 ) << i; + newName += ext.str(); + } + uniqueName = newName; + mNodeNames.push_back( uniqueName ); +} + const char* Converter::NameTransformationComp( TransformationComp comp ) { @@ -738,7 +768,7 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector // not be guaranteed. ai_assert( NeedsComplexTransformationChain( model ) == is_complex ); - const std::string& name = FixNodeName( model.Name() ); + std::string name = FixNodeName( model.Name() ); // now, if we have more than just Translation, Scaling and Rotation, // we need to generate a full node chain to accommodate for assimp's @@ -786,8 +816,10 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector // 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( name ); + nd->mName.Set( uniqueName ); for (const auto &transform : chain) { nd->mTransformation = nd->mTransformation * transform; @@ -2005,50 +2037,6 @@ void Converter::ConvertAnimations() } } -void Converter::RenameNode( const std::string& fixed_name, const std::string& new_name ) { - if ( node_names.find( fixed_name ) == node_names.end() ) { - FBXImporter::LogError( "Cannot rename node " + fixed_name + ", not existing."); - return; - } - - if ( node_names.find( new_name ) != node_names.end() ) { - FBXImporter::LogError( "Cannot rename node " + fixed_name + " to " + new_name +", name already existing." ); - return; - } - - ai_assert( node_names.find( fixed_name ) != node_names.end() ); - ai_assert( node_names.find( new_name ) == node_names.end() ); - - renamed_nodes[ fixed_name ] = new_name; - - const aiString fn( fixed_name ); - - for( aiCamera* cam : cameras ) { - if ( cam->mName == fn ) { - cam->mName.Set( new_name ); - break; - } - } - - for( aiLight* light : lights ) { - if ( light->mName == fn ) { - light->mName.Set( new_name ); - break; - } - } - - for( aiAnimation* anim : animations ) { - for ( unsigned int i = 0; i < anim->mNumChannels; ++i ) { - aiNodeAnim* const na = anim->mChannels[ i ]; - if ( na->mNodeName == fn ) { - na->mNodeName.Set( new_name ); - break; - } - } - } -} - - std::string Converter::FixNodeName( const std::string& name ) { // strip Model:: prefix, avoiding ambiguities (i.e. don't strip if diff --git a/code/FBXConverter.h b/code/FBXConverter.h index 26ba3cb4e..7ae68c5e5 100644 --- a/code/FBXConverter.h +++ b/code/FBXConverter.h @@ -68,6 +68,8 @@ namespace FBX { class Document; +using NodeNameCache = std::vector; + /** * Convert a FBX #Document to #aiScene * @param out Empty scene to be populated @@ -117,16 +119,19 @@ private: void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4()); // ------------------------------------------------------------------------------------------------ - void ConvertLights(const Model& model); + void ConvertLights(const Model& model, const std::string &orig_name ); // ------------------------------------------------------------------------------------------------ - void ConvertCameras(const Model& model); + void ConvertCameras(const Model& model, const std::string &orig_name ); // ------------------------------------------------------------------------------------------------ - void ConvertLight(const Model& model, const Light& light); + void ConvertLight( const Light& light, const std::string &orig_name ); // ------------------------------------------------------------------------------------------------ - void ConvertCamera(const Model& model, const Camera& cam); + void ConvertCamera( const Camera& cam, const std::string &orig_name ); + + // ------------------------------------------------------------------------------------------------ + void GetUniqueName( const std::string &name, std::string uniqueName ); // ------------------------------------------------------------------------------------------------ // this returns unified names usable within assimp identifiers (i.e. no space characters - @@ -258,18 +263,6 @@ private: // convert animation data to aiAnimation et al void ConvertAnimations(); - // ------------------------------------------------------------------------------------------------ - // rename a node already partially converted. fixed_name is a string previously returned by - // FixNodeName, new_name specifies the string FixNodeName should return on all further invocations - // which would previously have returned the old value. - // - // this also updates names in node animations, cameras and light sources and is thus slow. - // - // NOTE: the caller is responsible for ensuring that the new name is unique and does - // not collide with any other identifiers. The best way to ensure this is to only - // append to the old name, which is guaranteed to match these requirements. - void RenameNode(const std::string& fixed_name, const std::string& new_name); - // ------------------------------------------------------------------------------------------------ // takes a fbx node name and returns the identifier to be used in the assimp output scene. // the function is guaranteed to provide consistent results over multiple invocations @@ -281,7 +274,6 @@ private: // XXX: better use multi_map .. typedef std::map > NodeMap; - // ------------------------------------------------------------------------------------------------ void ConvertAnimationStack(const AnimationStack& st); @@ -439,12 +431,11 @@ private: typedef std::map NameNameMap; NameNameMap renamed_nodes; + NodeNameCache mNodeNames; double anim_fps; aiScene* const out; const FBX::Document& doc; - - std::vector mLightNames; }; } From 2a5728b4c3149feee04a7becda1704e17cd01f50 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 31 Mar 2018 17:25:29 +0200 Subject: [PATCH 165/183] FBX-Importer: fix usage of deprecated include. --- code/FBXConverter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index cf5d42de2..89e0d3edc 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -61,7 +61,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include +#include #include namespace Assimp { From f3cc2f79fccd490c6a2a47e0938d49fb87768fd0 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 31 Mar 2018 17:30:31 +0200 Subject: [PATCH 166/183] FBX-Importer: remove unused lookup table. --- code/FBXConverter.cpp | 6 ++---- code/FBXConverter.h | 3 --- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 89e0d3edc..9f275b7cb 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -2054,8 +2054,7 @@ std::string Converter::FixNodeName( const std::string& name ) } node_names[ temp ] = true; - const NameNameMap::const_iterator rit = renamed_nodes.find( temp ); - return rit == renamed_nodes.end() ? temp : ( *rit ).second; + return temp; } const NodeNameMap::const_iterator it = node_names.find( name ); @@ -2066,8 +2065,7 @@ std::string Converter::FixNodeName( const std::string& name ) } node_names[ name ] = false; - const NameNameMap::const_iterator rit = renamed_nodes.find( name ); - return rit == renamed_nodes.end() ? name : ( *rit ).second; + return name; } void Converter::ConvertAnimationStack( const AnimationStack& st ) diff --git a/code/FBXConverter.h b/code/FBXConverter.h index 7ae68c5e5..83e469e3c 100644 --- a/code/FBXConverter.h +++ b/code/FBXConverter.h @@ -428,9 +428,6 @@ private: typedef std::map NodeNameMap; NodeNameMap node_names; - typedef std::map NameNameMap; - NameNameMap renamed_nodes; - NodeNameCache mNodeNames; double anim_fps; From c9bb3592ff8c3866ac0dcc8f2b2089caba06879f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 31 Mar 2018 18:10:29 +0200 Subject: [PATCH 167/183] FBX-Importer: remove unused lookup tables for nodes. --- code/FBXConverter.cpp | 20 +------------------- code/FBXConverter.h | 4 ---- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 9f275b7cb..2c9818a16 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -2037,34 +2037,16 @@ void Converter::ConvertAnimations() } } -std::string Converter::FixNodeName( const std::string& name ) -{ +std::string Converter::FixNodeName( const std::string& name ) { // strip Model:: prefix, avoiding ambiguities (i.e. don't strip if // this causes ambiguities, well possible between empty identifiers, // such as "Model::" and ""). Make sure the behaviour is consistent // across multiple calls to FixNodeName(). if ( name.substr( 0, 7 ) == "Model::" ) { std::string temp = name.substr( 7 ); - - const NodeNameMap::const_iterator it = node_names.find( temp ); - if ( it != node_names.end() ) { - if ( !( *it ).second ) { - return FixNodeName( name + "_" ); - } - } - node_names[ temp ] = true; - return temp; } - const NodeNameMap::const_iterator it = node_names.find( name ); - if ( it != node_names.end() ) { - if ( ( *it ).second ) { - return FixNodeName( name + "_" ); - } - } - node_names[ name ] = false; - return name; } diff --git a/code/FBXConverter.h b/code/FBXConverter.h index 83e469e3c..60fd04e2c 100644 --- a/code/FBXConverter.h +++ b/code/FBXConverter.h @@ -424,10 +424,6 @@ private: typedef std::map NodeAnimBitMap; NodeAnimBitMap node_anim_chain_bits; - // name -> has had its prefix_stripped? - typedef std::map NodeNameMap; - NodeNameMap node_names; - NodeNameCache mNodeNames; double anim_fps; From dbf32abfb2c3e2d4eff327d4e7dd806ce6b2d043 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 31 Mar 2018 18:51:31 +0200 Subject: [PATCH 168/183] Build: use c++11 in openddl-parser as well. --- CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0026dcb7f..ffd978562 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,8 +138,7 @@ SET (PROJECT_VERSION "${ASSIMP_VERSION}") SET( ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources" ) -# Needed for openddl_parser config, no use of c++11 at this moment -ADD_DEFINITIONS( -DOPENDDL_NO_USE_CPP11 ) +# Enable C++1 globally set_property( GLOBAL PROPERTY CXX_STANDARD 11 ) # Get the current working branch From 417c4fcb457ab8282e8698596cba2de99c604e92 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 31 Mar 2018 18:53:00 +0200 Subject: [PATCH 169/183] Cmake: remove dead code. --- CMakeLists.txt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ffd978562..f48fcd773 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,14 +191,6 @@ SET(CPACK_COMPONENTS_ALL assimp-bin ${LIBASSIMP_COMPONENT} ${LIBASSIMP-DEV_COMPO SET(ASSIMP_LIBRARY_SUFFIX "" CACHE STRING "Suffix to append to library names") IF( UNIX ) - # Ensure that we do not run into issues like http://www.tcm.phy.cam.ac.uk/sw/inodes64.html on 32 bit linux - IF( ${OPERATING_SYSTEM} MATCHES "Android") - ELSE() - IF ( CMAKE_SIZEOF_VOID_P EQUAL 4) # only necessary for 32-bit linux - #ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 ) - ENDIF() - ENDIF() - # Use GNUInstallDirs for Unix predefined directories INCLUDE(GNUInstallDirs) ENDIF( UNIX ) From 676e1c956561273986a74029915cfdea8ede30a9 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 31 Mar 2018 19:12:53 +0200 Subject: [PATCH 170/183] Cmake: adapt cmake script for VS2017 --- CMakeLists.txt | 108 +++++++++++++++++++++++++------------------------ 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f48fcd773..83e1be081 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,7 +160,7 @@ EXECUTE_PROCESS( ) IF(NOT GIT_COMMIT_HASH) - SET(GIT_COMMIT_HASH 0) + SET(GIT_COMMIT_HASH 0) ENDIF(NOT GIT_COMMIT_HASH) IF(ASSIMP_DOUBLE_PRECISION) @@ -178,10 +178,10 @@ CONFIGURE_FILE( ) INCLUDE_DIRECTORIES( - ./ - include - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_BINARY_DIR}/include + ./ + include + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/include ) LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules" ) @@ -203,13 +203,13 @@ IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT CMAKE_COMPILER_IS_MINGW) SET(LIBSTDC++_LIBRARIES -lstdc++) ELSEIF(MSVC) # enable multi-core compilation with MSVC - add_compile_options(/MP) - if("${CMAKE_GENERATOR}" MATCHES "(Win64|IA64)") - add_compile_options( /bigobj ) - endif() + ADD_COMPILE_OPTIONS(/MP) + IF("${CMAKE_GENERATOR}" MATCHES "(Win64|IA64)") + ADD_COMPILE_OPTIONS( /bigobj ) + ENDIF() # disable "elements of array '' will be default initialized" warning on MSVC2013 IF(MSVC12) - add_compile_options(/wd4351) + ADD_COMPILE_OPTIONS(/wd4351) ENDIF() ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" ) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fvisibility=hidden -fPIC -Wall -Wno-long-long -std=c++11" ) @@ -220,39 +220,39 @@ ELSEIF( CMAKE_COMPILER_IS_MINGW ) ADD_DEFINITIONS( -U__STRICT_ANSI__ ) ENDIF() -if (IOS) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3") -endif() +IF (IOS) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3") +ENDIF() -if (ASSIMP_COVERALLS) - MESSAGE(STATUS "Coveralls enabled") - INCLUDE(Coveralls) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") -endif() +IF (ASSIMP_COVERALLS) + MESSAGE(STATUS "Coveralls enabled") + INCLUDE(Coveralls) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") +ENDIF() -if (ASSIMP_WERROR) +IF (ASSIMP_WERROR) MESSAGE(STATUS "Treating warnings as errors") IF (MSVC) - add_compile_options(/WX) + ADD_COMPILE_OPTIONS(/WX) ELSE() SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") ENDIF() -endif() +ENDIF() -if (ASSIMP_ASAN) - MESSAGE(STATUS "AddressSanitizer enabled") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") -endif() +IF (ASSIMP_ASAN) + MESSAGE(STATUS "AddressSanitizer enabled") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") +ENDIF() -if (ASSIMP_UBSAN) - MESSAGE(STATUS "Undefined Behavior sanitizer enabled") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") -endif() +IF (ASSIMP_UBSAN) + MESSAGE(STATUS "Undefined Behavior sanitizer enabled") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") +ENDIF() INCLUDE (FindPkgMacros) INCLUDE (PrecompiledHeader) @@ -285,42 +285,42 @@ ENDIF() IF (NOT TARGET uninstall) # add make uninstall capability CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) - add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") + ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") ENDIF() # cmake configuration files CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" @ONLY IMMEDIATE) CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config-version.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake" @ONLY IMMEDIATE) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake" DESTINATION "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}" COMPONENT ${LIBASSIMP-DEV_COMPONENT}) +INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake" DESTINATION "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}" COMPONENT ${LIBASSIMP-DEV_COMPONENT}) FIND_PACKAGE( DirectX ) IF( BUILD_DOCS ) - add_subdirectory(doc) + ADD_SUBDIRECTORY(doc) ENDIF( BUILD_DOCS ) # Look for system installed irrXML IF ( SYSTEM_IRRXML ) - find_package( IrrXML REQUIRED ) + FIND_PACKAGE( IrrXML REQUIRED ) ENDIF( SYSTEM_IRRXML ) # Search for external dependencies, and build them from source if not found # Search for zlib IF ( NOT ASSIMP_BUILD_ZLIB ) - find_package(ZLIB) + FIND_PACKAGE(ZLIB) ENDIF( NOT ASSIMP_BUILD_ZLIB ) IF( NOT ZLIB_FOUND ) - message(STATUS "compiling zlib from souces") + MESSAGE(STATUS "compiling zlib from souces") INCLUDE(CheckIncludeFile) INCLUDE(CheckTypeSize) INCLUDE(CheckFunctionExists) # compile from sources - add_subdirectory(contrib/zlib) + ADD_SUBDIRECTORY(contrib/zlib) SET(ZLIB_FOUND 1) SET(ZLIB_LIBRARIES zlibstatic) SET(ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/contrib/zlib ${CMAKE_CURRENT_BINARY_DIR}/contrib/zlib) -else(NOT ZLIB_FOUND) +ELSE(NOT ZLIB_FOUND) ADD_DEFINITIONS(-DASSIMP_BUILD_NO_OWN_ZLIB) SET(ZLIB_LIBRARIES_LINKED -lz) ENDIF(NOT ZLIB_FOUND) @@ -362,7 +362,9 @@ IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) SET(C4D_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/includes") # pick the correct prebuilt library - IF(MSVC14) + IF(MSVC15) + SET(C4D_LIB_POSTFIX "_2017") + ELSEIF(MSVC14) SET(C4D_LIB_POSTFIX "_2015") ELSEIF(MSVC12) SET(C4D_LIB_POSTFIX "_2013") @@ -403,7 +405,7 @@ ADD_SUBDIRECTORY(contrib) ADD_SUBDIRECTORY( code/ ) IF ( ASSIMP_BUILD_ASSIMP_TOOLS ) IF ( WIN32 AND DirectX_D3DX9_LIBRARY ) - option ( ASSIMP_BUILD_ASSIMP_VIEW "If the Assimp view tool is built. (requires DirectX)" ${DirectX_FOUND} ) + OPTION ( ASSIMP_BUILD_ASSIMP_VIEW "If the Assimp view tool is built. (requires DirectX)" ${DirectX_FOUND} ) IF ( ASSIMP_BUILD_ASSIMP_VIEW ) ADD_SUBDIRECTORY( tools/assimp_view/ ) ENDIF ( ASSIMP_BUILD_ASSIMP_VIEW ) @@ -467,8 +469,8 @@ IF(CMAKE_CPACK_COMMAND AND UNIX AND ASSIMP_OPT_BUILD_PACKAGES) SET(CPACK_PACKAGE_INSTALL_DIRECTORY "assimp${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}") SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") - string(TOUPPER ${LIBASSIMP_COMPONENT} "LIBASSIMP_COMPONENT_UPPER") - string(TOUPPER ${LIBASSIMP-DEV_COMPONENT} "LIBASSIMP-DEV_COMPONENT_UPPER") + STRING(TOUPPER ${LIBASSIMP_COMPONENT} "LIBASSIMP_COMPONENT_UPPER") + STRING(TOUPPER ${LIBASSIMP-DEV_COMPONENT} "LIBASSIMP-DEV_COMPONENT_UPPER") SET(CPACK_COMPONENT_ASSIMP-BIN_DISPLAY_NAME "tools") SET(CPACK_COMPONENT_ASSIMP-BIN_DEPENDS "${LIBASSIMP_COMPONENT}" ) @@ -498,8 +500,8 @@ IF(CMAKE_CPACK_COMMAND AND UNIX AND ASSIMP_OPT_BUILD_PACKAGES) SET(CPACK_DEBIAN_DISTRIBUTION_RELEASES lucid maverick natty oneiric precise CACHE STRING "Release code-names of the distrubiton release") ENDIF() SET(DPUT_HOST "" CACHE STRING "PPA repository to upload the debian sources") - include(CPack) - include(DebSourcePPA) + INCLUDE(CPack) + INCLUDE(DebSourcePPA) ENDIF() if(WIN32) @@ -511,14 +513,16 @@ if(WIN32) SET(LIB_DIR "${PROJECT_SOURCE_DIR}/lib32/") ENDIF() - if(MSVC12) + IF(MSVC12) SET(ASSIMP_MSVC_VERSION "vc120") - elseif(MSVC14) + ELSEIF(MSVC14) SET(ASSIMP_MSVC_VERSION "vc140") + ELSEIF(MSVC15) + SET(ASSIMP_MSVC_VERSION "vc141") ENDIF(MSVC12) - if(MSVC12 OR MSVC14) - add_custom_target(UpdateAssimpLibsDebugSymbolsAndDLLs COMMENT "Copying Assimp Libraries ..." VERBATIM) + IF(MSVC12 OR MSVC14 OR MSVC15 ) + ADD_CUSTOM_TARGET(UpdateAssimpLibsDebugSymbolsAndDLLs COMMENT "Copying Assimp Libraries ..." VERBATIM) IF(CMAKE_GENERATOR MATCHES "^Visual Studio") ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.dll ${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.dll VERBATIM) ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.exp ${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.exp VERBATIM) @@ -539,5 +543,5 @@ if(WIN32) ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb ${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM) ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb ${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM) ENDIF() - ENDIF(MSVC12 OR MSVC14) + ENDIF(MSVC12 OR MSVC14 OR MSVC15 ) ENDIF (WIN32) From 04c65ccdcbd6b3f7d55920351648715aedb3557f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 1 Apr 2018 17:16:14 +0200 Subject: [PATCH 171/183] closes https://github.com/assimp/assimp/issues/1831: make config CMAKE_LIBRARY_CONFIG overridable. --- assimp-config.cmake.in | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/assimp-config.cmake.in b/assimp-config.cmake.in index e06cc10ee..5031c8f8d 100644 --- a/assimp-config.cmake.in +++ b/assimp-config.cmake.in @@ -34,36 +34,18 @@ if( MSVC ) else() set(MSVC_PREFIX "vc150") endif() - set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" FORCE) + set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" ) else() - set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the openrave libraries" FORCE) + set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the openrave libraries" ) endif() set( ASSIMP_CXX_FLAGS ) # dynamically linked library -if( WIN32 ) - # for visual studio linking, most of the time boost dlls will be used - set( ASSIMP_CXX_FLAGS " -DBOOST_ALL_DYN_LINK -DBOOST_ALL_NO_LIB") -endif() set( ASSIMP_LINK_FLAGS "" ) set( ASSIMP_LIBRARY_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_LIB_INSTALL_DIR@") set( ASSIMP_INCLUDE_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_INCLUDE_INSTALL_DIR@") set( ASSIMP_LIBRARIES assimp${ASSIMP_LIBRARY_SUFFIX}) set( ASSIMP_LIBRARIES ${ASSIMP_LIBRARIES}@CMAKE_DEBUG_POSTFIX@) -# search for the boost version assimp was compiled with -#set(Boost_USE_MULTITHREAD ON) -#set(Boost_USE_STATIC_LIBS OFF) -#set(Boost_USE_STATIC_RUNTIME OFF) -#find_package(Boost ${ASSIMP_Boost_VERSION} EXACT COMPONENTS thread date_time) -#if(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") -# set( ASSIMP_INCLUDE_DIRS "${ASSIMP_INCLUDE_DIRS}" ${Boost_INCLUDE_DIRS}) -#else(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") -# message(WARNING "Failed to find Boost ${ASSIMP_Boost_VERSION} necessary for assimp") -#endif(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") - -# the boost version assimp was compiled with -set( ASSIMP_Boost_VERSION "@Boost_MAJOR_VERSION@.@Boost_MINOR_VERSION@") - # for compatibility with pkg-config set(ASSIMP_CFLAGS_OTHER "${ASSIMP_CXX_FLAGS}") set(ASSIMP_LDFLAGS_OTHER "${ASSIMP_LINK_FLAGS}") @@ -74,7 +56,6 @@ MARK_AS_ADVANCED( ASSIMP_LINK_FLAGS ASSIMP_INCLUDE_DIRS ASSIMP_LIBRARIES - ASSIMP_Boost_VERSION ASSIMP_CFLAGS_OTHER ASSIMP_LDFLAGS_OTHER ASSIMP_LIBRARY_SUFFIX From 35f4e7101a2ac88e3478156be77dd083fc64257b Mon Sep 17 00:00:00 2001 From: Edward Andrew <=ed.andrew95@gmail.com> Date: Mon, 2 Apr 2018 01:46:14 +0100 Subject: [PATCH 172/183] Fixed issue#1219. CalcTangentsProcess::ProcessMesh. --- code/CalcTangentsProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/CalcTangentsProcess.cpp b/code/CalcTangentsProcess.cpp index cb0117aef..d77d70a5b 100644 --- a/code/CalcTangentsProcess.cpp +++ b/code/CalcTangentsProcess.cpp @@ -190,7 +190,7 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y; float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f; // when t1, t2, t3 in same position in UV space, just use default UV direction. - if ( 0 == sx && 0 ==sy && 0 == tx && 0 == ty ) { + if ( sx * ty == sy * tx ) { sx = 0.0; sy = 1.0; tx = 1.0; ty = 0.0; } From 4d662c42b3f984b68b4fc38c8807abff6ec7622b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 2 Apr 2018 10:27:05 +0200 Subject: [PATCH 173/183] Add check for SSE2-support. --- code/CMakeLists.txt | 2 ++ code/simd.cpp | 34 ++++++++++++++++++++++++++++++++++ code/simd.h | 5 +++++ test/CMakeLists.txt | 1 + test/unit/utAnim.cpp | 4 +--- 5 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 code/simd.cpp create mode 100644 code/simd.h diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 7ae1a7e63..d5d05db18 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -186,6 +186,8 @@ SET( Common_SRCS Bitmap.cpp Version.cpp CreateAnimMesh.cpp + simd.h + simd.cpp ) SOURCE_GROUP(Common FILES ${Common_SRCS}) diff --git a/code/simd.cpp b/code/simd.cpp new file mode 100644 index 000000000..e07398156 --- /dev/null +++ b/code/simd.cpp @@ -0,0 +1,34 @@ +#include "simd.h" + +namespace Assimp { + bool CPUSupportsSSE2() { +#if defined(__x86_64__) || defined(_M_X64) + //* x86_64 always has SSE2 instructions */ + return true; +#elif defined(__GNUC__) && defined(i386) + // for GCC x86 we check cpuid + unsigned int d; + __asm__( + "pushl %%ebx\n\t" + "cpuid\n\t" + "popl %%ebx\n\t" + : "=d" ( d ) + :"a" ( 1 ) ); + return ( d & 0x04000000 ) != 0; +#elif (defined(_MSC_VER) && defined(_M_IX86)) + // also check cpuid for MSVC x86 + unsigned int d; + __asm { + xor eax, eax + inc eax + push ebx + cpuid + pop ebx + mov d, edx + } + return ( d & 0x04000000 ) != 0; +#else + return false; +#endif + } +} diff --git a/code/simd.h b/code/simd.h new file mode 100644 index 000000000..d490cbe85 --- /dev/null +++ b/code/simd.h @@ -0,0 +1,5 @@ +#pragma once + +namespace Assimp { + bool CPUSupportsSSE2(); +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 049dbcdd2..7c5ff3706 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -53,6 +53,7 @@ endif() LINK_DIRECTORIES( ${Assimp_BINARY_DIR} ${AssetImporter_BINARY_DIR}/lib ) SET( COMMON + unit/utSimd.cpp unit/utIOSystem.cpp unit/utIOStreamBuffer.cpp unit/utIssues.cpp diff --git a/test/unit/utAnim.cpp b/test/unit/utAnim.cpp index 04ee8c96d..40c840d7e 100644 --- a/test/unit/utAnim.cpp +++ b/test/unit/utAnim.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2018, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -106,4 +104,4 @@ TEST_F( utAnim, aiAnimationTest ) { ok = false; } EXPECT_TRUE( ok ); -} \ No newline at end of file +} From 7faf638cca1ea5f05680df812dd0445a828460fa Mon Sep 17 00:00:00 2001 From: Andy Maloney Date: Mon, 2 Apr 2018 14:41:02 -0400 Subject: [PATCH 174/183] Add links to some file format info in readme --- Readme.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Readme.md b/Readme.md index 4ee8a0e90..c78a0c1c8 100644 --- a/Readme.md +++ b/Readme.md @@ -35,29 +35,29 @@ Please check our Wiki as well: https://github.com/assimp/assimp/wiki __Importers__: - 3D -- 3DS -- 3MF +- [3DS](https://en.wikipedia.org/wiki/.3ds) +- [3MF](https://en.wikipedia.org/wiki/3D_Manufacturing_Format) - AC -- AC3D +- [AC3D](https://en.wikipedia.org/wiki/AC3D) - ACC - AMJ - ASE - ASK - B3D -- BLEND (Blender) -- BVH -- COB +- [BLEND](https://en.wikipedia.org/wiki/.blend_(file_format)) +- [BVH](https://en.wikipedia.org/wiki/Biovision_Hierarchy) - CMS -- DAE/Collada -- DXF +- COB +- [DAE/Collada](https://en.wikipedia.org/wiki/COLLADA) +- [DXF](https://en.wikipedia.org/wiki/AutoCAD_DXF) - ENFF -- FBX -- glTF 1.0 + GLB -- glTF 2.0 +- [FBX](https://en.wikipedia.org/wiki/FBX) +- [glTF 1.0](https://en.wikipedia.org/wiki/GlTF#glTF_1.0) + GLB +- [glTF 2.0](https://en.wikipedia.org/wiki/GlTF#glTF_2.0) - HMB - IFC-STEP - IRR / IRRMESH -- LWO +- [LWO](https://en.wikipedia.org/wiki/LightWave_3D) - LWS - LXO - MD2 @@ -70,10 +70,10 @@ __Importers__: - MS3D - NDO - NFF -- OBJ -- OFF -- OGEX -- PLY +- [OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file) +- [OFF](https://en.wikipedia.org/wiki/OFF_(file_format)) +- [OGEX](https://en.wikipedia.org/wiki/Open_Game_Engine_Exchange) +- [PLY](https://en.wikipedia.org/wiki/PLY_(file_format)) - PMX - PRJ - Q3O @@ -82,19 +82,19 @@ __Importers__: - SCN - SIB - SMD -- STL -- STP +- [STP](https://en.wikipedia.org/wiki/ISO_10303-21) +- [STL](https://en.wikipedia.org/wiki/STL_(file_format)) - TER - UC - VTA - X -- X3D +- [X3D](https://en.wikipedia.org/wiki/X3D) - XGL - ZGL Additionally, some formats are supported by dependency on non-free code or external SDKs (not built by default): -- C4D (https://github.com/assimp/assimp/wiki/Cinema4D-&-Melange) +- [C4D](https://en.wikipedia.org/wiki/Cinema_4D) (https://github.com/assimp/assimp/wiki/Cinema4D-&-Melange) __Exporters__: From 6147a4f50d7dd746b7955b97c00d1f1564342c59 Mon Sep 17 00:00:00 2001 From: James Carthew Date: Wed, 4 Apr 2018 08:08:28 +1000 Subject: [PATCH 175/183] Updated COB importer plugin to flip normals. --- code/COBLoader.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/code/COBLoader.cpp b/code/COBLoader.cpp index 793cbfe75..222088cbd 100644 --- a/code/COBLoader.cpp +++ b/code/COBLoader.cpp @@ -47,11 +47,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_COB_IMPORTER #include "COBLoader.h" #include "COBScene.h" - +#include "ConvertToLHProcess.h" #include #include #include - #include #include #include @@ -105,7 +104,7 @@ COBImporter::~COBImporter() bool COBImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { const std::string& extension = GetExtension(pFile); - if (extension == "cob" || extension == "scn") { + if (extension == "cob" || extension == "scn" || extension == "COB" || extension == "SCN") { return true; } @@ -225,6 +224,9 @@ void COBImporter::InternReadFile( const std::string& pFile, } pScene->mRootNode = BuildNodes(*root.get(),scene,pScene); + //flip normals after import + FlipWindingOrderProcess flip; + flip.Execute( pScene ); } // ------------------------------------------------------------------------------------------------ @@ -1299,3 +1301,4 @@ void COBImporter::ReadUnit_Binary(COB::Scene& out, StreamReaderLE& reader, const #endif + From c232c4bfb328fa6762aecbd258b4d8fd795a1349 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 4 Apr 2018 16:32:18 +0200 Subject: [PATCH 176/183] Add missing file to fix the build. --- test/unit/utSimd.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 test/unit/utSimd.cpp diff --git a/test/unit/utSimd.cpp b/test/unit/utSimd.cpp new file mode 100644 index 000000000..da1b19fce --- /dev/null +++ b/test/unit/utSimd.cpp @@ -0,0 +1,57 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#include "UnitTestPCH.h" + +#include "simd.h" + +using namespace ::Assimp; + +class utSimd : public ::testing::Test { +protected: + // empty +}; + +TEST_F( utSimd, SSE2SupportedTest ) { + bool isSupported; + + isSupported = CPUSupportsSSE2(); +} From 4254142575019cb567306ce58c310847bd6d5a56 Mon Sep 17 00:00:00 2001 From: James Carthew Date: Thu, 5 Apr 2018 06:37:18 +1000 Subject: [PATCH 177/183] Updated GLView to draw with white lighting after drawCoordSystem(). --- tools/assimp_qt_viewer/glview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/assimp_qt_viewer/glview.cpp b/tools/assimp_qt_viewer/glview.cpp index 5b755d456..d30d0b5de 100644 --- a/tools/assimp_qt_viewer/glview.cpp +++ b/tools/assimp_qt_viewer/glview.cpp @@ -609,6 +609,7 @@ void CGLView::drawCoordSystem() { // Z, -Z qglColor(QColor(Qt::blue)), glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, 0.0, 100000.0); qglColor(QColor(Qt::yellow)), glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, 0.0, -100000.0); + qglColor(QColor(Qt::white)); glEnd(); } From 8cb25328cb4381d4d1d11627b5c6c9444e021cc3 Mon Sep 17 00:00:00 2001 From: James Carthew Date: Thu, 5 Apr 2018 17:50:30 +1000 Subject: [PATCH 178/183] committing all changes. --- code/3DSLoader.cpp | 4 ++-- tools/assimp_qt_viewer/glview.cpp | 29 ++++++++++++++++++++++++++- tools/assimp_qt_viewer/glview.hpp | 11 ++++++++-- tools/assimp_qt_viewer/mainwindow.cpp | 20 ++++++++++++++++++ tools/assimp_qt_viewer/mainwindow.hpp | 4 +++- tools/assimp_qt_viewer/mainwindow.ui | 22 +++++++++++++++++++- 6 files changed, 83 insertions(+), 7 deletions(-) diff --git a/code/3DSLoader.cpp b/code/3DSLoader.cpp index 92a64e3d8..bfe2bf6e5 100644 --- a/code/3DSLoader.cpp +++ b/code/3DSLoader.cpp @@ -71,7 +71,7 @@ static const aiImporterDesc desc = { 0, 0, 0, - "3ds prj" + "3ds prj 3DS PRJ" }; @@ -127,7 +127,7 @@ Discreet3DSImporter::~Discreet3DSImporter() { // Returns whether the class can handle the format of the given file. bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { std::string extension = GetExtension(pFile); - if(extension == "3ds" || extension == "prj" ) { + if(extension == "3ds" || extension == "3DS" || extension == "prj"|| extension == "PRJ" ) { return true; } diff --git a/tools/assimp_qt_viewer/glview.cpp b/tools/assimp_qt_viewer/glview.cpp index d30d0b5de..a9a60a6fc 100644 --- a/tools/assimp_qt_viewer/glview.cpp +++ b/tools/assimp_qt_viewer/glview.cpp @@ -560,6 +560,30 @@ void CGLView::Enable_Textures(const bool pEnable) } } +void CGLView::Enable_Axes(const bool pEnable){ + if(pEnable) + { + this->mAxesEnabled = true; + } + else + { + this->mAxesEnabled = false; + } +} + +void CGLView::Enable_Reload_Textures(const bool pEnable) +{ + if(pEnable) + { + this->mReloadTexturesEnabled = true; +// this->mScene->ImportTextures(this->mScene->pScenePath); + } + else + { + this->mReloadTexturesEnabled = false; + } +} + /********************************************************************/ /*********************** Override functions ************************/ /********************************************************************/ @@ -630,7 +654,10 @@ void CGLView::paintGL() if ( mLightingEnabled ) { glDisable( GL_LIGHTING );///TODO: display list } - drawCoordSystem(); + if (this->mAxesEnabled == true) + { + drawCoordSystem(); + } glDisable(GL_COLOR_MATERIAL); if(mLightingEnabled) glEnable(GL_LIGHTING); diff --git a/tools/assimp_qt_viewer/glview.hpp b/tools/assimp_qt_viewer/glview.hpp index ecd7c6b0f..1c397f13f 100644 --- a/tools/assimp_qt_viewer/glview.hpp +++ b/tools/assimp_qt_viewer/glview.hpp @@ -75,7 +75,9 @@ private: }; public: - + bool mAxesEnabled = true; + // Textures + bool mReloadTexturesEnabled = false; // If true then textures will reload when the window is activated. /// \enum ELightType /// Type of light source. enum class ELightType { Directional, Point, Spot }; @@ -155,7 +157,6 @@ private: GLdouble mCamera_Viewport_AspectRatio;///< Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). // Lighting bool mLightingEnabled = false;///< If true then OpenGL lighting is enabled (glEnable(GL_LIGHTING)), if false - disabled. - // Textures ///TODO: map is goooood, but not for case when one image can be used in different materials with difference in: texture transformation, targeting of the /// texture (ambient or emission, or even height map), texture properties. QMap mTexture_IDMap;///< Map image filenames to textures ID's. @@ -306,6 +307,12 @@ public: /// \param [in] pEnable - if true then enable textures, false - disable textures. void Enable_Textures(const bool pEnable); + void Enable_Axes(const bool pEnable); + /// \fn void Enable_Textures(const bool pEnable) + /// Control textures drawing. + /// \param [in] pEnable - if true then enable textures, false - disable textures. + void Enable_Reload_Textures(const bool pEnable); + /********************************************************************/ /******************** Lighting control functions ********************/ /********************************************************************/ diff --git a/tools/assimp_qt_viewer/mainwindow.cpp b/tools/assimp_qt_viewer/mainwindow.cpp index acfc8e589..d7d809086 100644 --- a/tools/assimp_qt_viewer/mainwindow.cpp +++ b/tools/assimp_qt_viewer/mainwindow.cpp @@ -48,6 +48,7 @@ QTime time_begin = QTime::currentTime(); ui->cbxLighting->setChecked(true); mGLView->Lighting_Enable(); ui->cbxBBox->setChecked(false); mGLView->Enable_SceneBBox(false); ui->cbxTextures->setChecked(true); mGLView->Enable_Textures(true); + ui->cbxReloadTextures->setChecked(true); mGLView->Enable_Reload_Textures(false); // // Fill info labels // @@ -194,6 +195,13 @@ GLfloat step; /********************************************************************/ /********************** Constructor/Destructor **********************/ /********************************************************************/ +bool MainWindow::event(QEvent *e) +{ + if (e->type() == QEvent::WindowActivate && this->mGLView->mReloadTexturesEnabled == true) { + qInfo() << "Window Activated"; + } + return QWidget::event(e); +} MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), @@ -364,6 +372,18 @@ void MainWindow::on_cbxBBox_clicked(bool checked) mGLView->updateGL(); } +void MainWindow::on_cbxDrawAxes_clicked(bool checked) +{ + mGLView->Enable_Axes(checked); + mGLView->updateGL(); +} + +void MainWindow::on_cbxReloadTextures_clicked(bool checked) +{ + mGLView->Enable_Reload_Textures(checked); + mGLView->updateGL(); +} + void MainWindow::on_cbxTextures_clicked(bool checked) { mGLView->Enable_Textures(checked); diff --git a/tools/assimp_qt_viewer/mainwindow.hpp b/tools/assimp_qt_viewer/mainwindow.hpp index 350b37abf..da8a852ac 100644 --- a/tools/assimp_qt_viewer/mainwindow.hpp +++ b/tools/assimp_qt_viewer/mainwindow.hpp @@ -90,7 +90,7 @@ protected: /// \param [in] pEvent - pointer to event data. void keyPressEvent(QKeyEvent* pEvent) override; - + bool event(QEvent*); public: /********************************************************************/ @@ -133,4 +133,6 @@ private slots: void on_lstCamera_clicked(const QModelIndex &index); void on_cbxBBox_clicked(bool checked); void on_cbxTextures_clicked(bool checked); + void on_cbxDrawAxes_clicked(bool checked); + void on_cbxReloadTextures_clicked(bool checked); }; diff --git a/tools/assimp_qt_viewer/mainwindow.ui b/tools/assimp_qt_viewer/mainwindow.ui index 105a470e0..9b139bafd 100644 --- a/tools/assimp_qt_viewer/mainwindow.ui +++ b/tools/assimp_qt_viewer/mainwindow.ui @@ -7,7 +7,7 @@ 0 0 641 - 734 + 778 @@ -501,6 +501,23 @@ + + + + Show Axes + + + true + + + + + + + Live Reload Textures + + + @@ -513,4 +530,7 @@ + + installEventFilter() + From 08ddd5c296589fe398eb851a7aa9f34025f6c5c5 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 6 Apr 2018 10:53:32 +0200 Subject: [PATCH 179/183] Export missig symbols. --- code/simd.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ code/simd.h | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/code/simd.cpp b/code/simd.cpp index e07398156..9c86cb970 100644 --- a/code/simd.cpp +++ b/code/simd.cpp @@ -1,3 +1,45 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ #include "simd.h" namespace Assimp { diff --git a/code/simd.h b/code/simd.h index d490cbe85..8fe2bb851 100644 --- a/code/simd.h +++ b/code/simd.h @@ -1,5 +1,51 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ #pragma once +#include + namespace Assimp { - bool CPUSupportsSSE2(); -} + +bool ASSIMP_API CPUSupportsSSE2(); + +} // Namespace Assimp From d17cf76c8576592572f9a6093648c176df840fa2 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 6 Apr 2018 11:16:52 +0200 Subject: [PATCH 180/183] Fix compiler warning in unit test: unused variable. --- test/unit/utSimd.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/unit/utSimd.cpp b/test/unit/utSimd.cpp index da1b19fce..33358c0da 100644 --- a/test/unit/utSimd.cpp +++ b/test/unit/utSimd.cpp @@ -54,4 +54,9 @@ TEST_F( utSimd, SSE2SupportedTest ) { bool isSupported; isSupported = CPUSupportsSSE2(); + if ( isSupported ) { + std::cout << "Supported" << std::endl; + } else { + std::cout << "Not supported" << std::endl; + } } From 32b466941beb1d041eaad84cd2be0b697d31fcfb Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 6 Apr 2018 12:01:22 +0200 Subject: [PATCH 181/183] Add missig docs and fix some small review findings. --- code/ScenePrivate.h | 32 ++++++++++----- code/simd.cpp | 54 +++++++++++++------------- code/simd.h | 2 + test/unit/AbstractImportExportBase.cpp | 2 +- 4 files changed, 53 insertions(+), 37 deletions(-) diff --git a/code/ScenePrivate.h b/code/ScenePrivate.h index a09d8784b..6a44def2c 100644 --- a/code/ScenePrivate.h +++ b/code/ScenePrivate.h @@ -42,22 +42,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file Stuff to deal with aiScene::mPrivate */ +#pragma once #ifndef AI_SCENEPRIVATE_H_INCLUDED #define AI_SCENEPRIVATE_H_INCLUDED #include -namespace Assimp { +namespace Assimp { +// Forward declarations class Importer; struct ScenePrivateData { - ScenePrivateData() - : mOrigImporter() - , mPPStepsApplied() - , mIsCopy() - {} + : mOrigImporter( nullptr ) + , mPPStepsApplied( 0 ) + , mIsCopy( false ) { + // empty + } // Importer that originally loaded the scene though the C-API // If set, this object is owned by this private data instance. @@ -75,14 +77,24 @@ struct ScenePrivateData { }; // Access private data stored in the scene -inline ScenePrivateData* ScenePriv(aiScene* in) { +inline +ScenePrivateData* ScenePriv(aiScene* in) { + ai_assert( nullptr != in ); + if ( nullptr == in ) { + return nullptr; + } return static_cast(in->mPrivate); } -inline const ScenePrivateData* ScenePriv(const aiScene* in) { +inline +const ScenePrivateData* ScenePriv(const aiScene* in) { + ai_assert( nullptr != in ); + if ( nullptr == in ) { + return nullptr; + } return static_cast(in->mPrivate); } -} +} // Namespace Assimp -#endif +#endif // AI_SCENEPRIVATE_H_INCLUDED diff --git a/code/simd.cpp b/code/simd.cpp index 9c86cb970..bd951bffa 100644 --- a/code/simd.cpp +++ b/code/simd.cpp @@ -43,34 +43,36 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "simd.h" namespace Assimp { - bool CPUSupportsSSE2() { + +bool CPUSupportsSSE2() { #if defined(__x86_64__) || defined(_M_X64) - //* x86_64 always has SSE2 instructions */ - return true; + //* x86_64 always has SSE2 instructions */ + return true; #elif defined(__GNUC__) && defined(i386) - // for GCC x86 we check cpuid - unsigned int d; - __asm__( - "pushl %%ebx\n\t" - "cpuid\n\t" - "popl %%ebx\n\t" - : "=d" ( d ) - :"a" ( 1 ) ); - return ( d & 0x04000000 ) != 0; + // for GCC x86 we check cpuid + unsigned int d; + __asm__( + "pushl %%ebx\n\t" + "cpuid\n\t" + "popl %%ebx\n\t" + : "=d" ( d ) + :"a" ( 1 ) ); + return ( d & 0x04000000 ) != 0; #elif (defined(_MSC_VER) && defined(_M_IX86)) - // also check cpuid for MSVC x86 - unsigned int d; - __asm { - xor eax, eax - inc eax - push ebx - cpuid - pop ebx - mov d, edx - } - return ( d & 0x04000000 ) != 0; -#else - return false; -#endif + // also check cpuid for MSVC x86 + unsigned int d; + __asm { + xor eax, eax + inc eax + push ebx + cpuid + pop ebx + mov d, edx } + return ( d & 0x04000000 ) != 0; +#else + return false; +#endif } + +} // Namespace Assimp diff --git a/code/simd.h b/code/simd.h index 8fe2bb851..19117569d 100644 --- a/code/simd.h +++ b/code/simd.h @@ -46,6 +46,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { +/// @brief Checks if the platform supports SSE2 optimization +/// @return true, if SSE2 is supported. false if SSE2 is not supported. bool ASSIMP_API CPUSupportsSSE2(); } // Namespace Assimp diff --git a/test/unit/AbstractImportExportBase.cpp b/test/unit/AbstractImportExportBase.cpp index f75ba4ea2..c09ec0fd7 100644 --- a/test/unit/AbstractImportExportBase.cpp +++ b/test/unit/AbstractImportExportBase.cpp @@ -45,5 +45,5 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace ::Assimp; AbstractImportExportBase::~AbstractImportExportBase() { - // empty + // empty } From 3bd80a2537b6cfeecec7852b578ede1234e5eb38 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 6 Apr 2018 12:05:30 +0200 Subject: [PATCH 182/183] closes https://github.com/assimp/assimp/issues/1881: make template-based get and put in streamreader/writer public. --- include/assimp/StreamReader.h | 2 +- include/assimp/StreamWriter.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/include/assimp/StreamReader.h b/include/assimp/StreamReader.h index 90cc65fee..3fab00ad2 100644 --- a/include/assimp/StreamReader.h +++ b/include/assimp/StreamReader.h @@ -283,7 +283,6 @@ public: return *this; } -private: // --------------------------------------------------------------------- /** Generic read method. ByteSwap::Swap(T*) *must* be defined */ template @@ -300,6 +299,7 @@ private: return f; } +private: // --------------------------------------------------------------------- void InternBegin() { if (!stream) { diff --git a/include/assimp/StreamWriter.h b/include/assimp/StreamWriter.h index 5ce3b172b..deb35fb4d 100644 --- a/include/assimp/StreamWriter.h +++ b/include/assimp/StreamWriter.h @@ -255,8 +255,6 @@ public: cursor = new_cursor; } -private: - // --------------------------------------------------------------------- /** Generic write method. ByteSwap::Swap(T*) *must* be defined */ template From 0a3fb04b041ba5723a74792959cdd06d6ee05ea5 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 6 Apr 2018 16:23:59 +0200 Subject: [PATCH 183/183] Update ScenePrivate.h add missing header --- code/ScenePrivate.h | 1 + 1 file changed, 1 insertion(+) diff --git a/code/ScenePrivate.h b/code/ScenePrivate.h index 6a44def2c..50959f455 100644 --- a/code/ScenePrivate.h +++ b/code/ScenePrivate.h @@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_SCENEPRIVATE_H_INCLUDED #define AI_SCENEPRIVATE_H_INCLUDED +#include #include namespace Assimp {