// Copyright Epic Games, Inc. All Rights Reserved. #include "DatasmithSketchUpUtils.h" #include "DatasmithSketchUpCommon.h" #include "DatasmithSketchUpString.h" #include "DatasmithSketchUpSummary.h" #include "IDatasmithSceneElements.h" // SketchUp SDK. #include "DatasmithSketchUpSDKBegins.h" #include "SketchUpAPI/model/camera.h" #include "SketchUpAPI/model/component_definition.h" #include "SketchUpAPI/model/component_instance.h" #include "SketchUpAPI/model/drawing_element.h" #include "SketchUpAPI/model/edge.h" #include "SketchUpAPI/model/entities.h" #include "SketchUpAPI/model/entity.h" #include "SketchUpAPI/model/face.h" #include "SketchUpAPI/model/geometry.h" #include "SketchUpAPI/model/group.h" #include "SketchUpAPI/model/layer.h" #if !defined(SKP_SDK_2019) && !defined(SKP_SDK_2020) #include "SketchUpAPI/model/layer_folder.h" #endif #include "SketchUpAPI/model/material.h" #include "SketchUpAPI/model/mesh_helper.h" #include "SketchUpAPI/model/model.h" #include "SketchUpAPI/model/scene.h" #include "SketchUpAPI/model/texture.h" #include "SketchUpAPI/model/uv_helper.h" #include "DatasmithSketchUpSDKCeases.h" // Imath third party library. #include "Algo/Compare.h" #include "Algo/Copy.h" #include "Imath/ImathMatrixAlgo.h" using namespace DatasmithSketchUp; DatasmithSketchUp::FEntityIDType DatasmithSketchUpUtils::GetEntityID(SUEntityRef InEntityRef) { int32 Id = 0; SUEntityGetID(InEntityRef, &Id); // we can ignore the returned SU_RESULT return FEntityIDType{ Id }; } SULayerRef DatasmithSketchUpUtils::GetEffectiveLayer( SUComponentInstanceRef InComponentInstanceRef, // valid SketckUp component instance SULayerRef InInheritedLayerRef ) { SUDrawingElementRef DrawingElementRef = SUComponentInstanceToDrawingElement(InComponentInstanceRef); return GetEffectiveLayer(DrawingElementRef, InInheritedLayerRef); } SULayerRef DatasmithSketchUpUtils::GetEffectiveLayer(SUDrawingElementRef DrawingElementRef, SULayerRef InInheritedLayerRef) { // Retrieve the SketckUp component instance layer. SULayerRef SComponentInstanceLayerRef = SU_INVALID; SUDrawingElementGetLayer(DrawingElementRef, &SComponentInstanceLayerRef); // we can ignore the returned SU_RESULT // Retrieve the SketchUp component instance layer name. FString SComponentInstanceLayerName; SComponentInstanceLayerName = SuGetString(SULayerGetName, SComponentInstanceLayerRef); // Return the effective layer. return SComponentInstanceLayerName.Equals(TEXT("Layer0")) ? InInheritedLayerRef : SComponentInstanceLayerRef; } FComponentDefinitionIDType DatasmithSketchUpUtils::GetComponentID( SUComponentDefinitionRef InComponentDefinitionRef ) { return GetEntityID(SUComponentDefinitionToEntity(InComponentDefinitionRef)); } FComponentInstanceIDType DatasmithSketchUpUtils::GetComponentInstanceID( SUComponentInstanceRef InComponentInstanceRef ) { return GetEntityID(SUComponentInstanceToEntity(InComponentInstanceRef)); } FComponentInstanceIDType DatasmithSketchUpUtils::GetGroupID( SUGroupRef InGroupRef ) { return GetEntityID(SUGroupToEntity(InGroupRef)); } int64 DatasmithSketchUpUtils::GetComponentPID( SUComponentInstanceRef InComponentInstanceRef ) { // Get the SketckUp component instance persistent ID. int64 SPersistentID = 0; SUEntityGetPersistentID(SUComponentInstanceToEntity(InComponentInstanceRef), &SPersistentID); // we can ignore the returned SU_RESULT return SPersistentID; } FEntityIDType DatasmithSketchUpUtils::GetSceneID( SUSceneRef InSceneRef ) { return GetEntityID(SUSceneToEntity(InSceneRef));; } FEntityIDType DatasmithSketchUpUtils::GetMaterialID( SUMaterialRef InMaterialRef ) { return GetEntityID(SUMaterialToEntity(InMaterialRef)); } int32 DatasmithSketchUpUtils::GetFaceID( SUFaceRef InFaceRef ) { // Get the SketckUp face ID. int32 SFaceID = 0; SUEntityGetID(SUFaceToEntity(InFaceRef), &SFaceID); // we can ignore the returned SU_RESULT return SFaceID; } int32 DatasmithSketchUpUtils::GetEdgeID( SUEdgeRef InEdgeRef ) { // Get the SketckUp edge ID. int32 SEdgeID = 0; SUEntityGetID(SUEdgeToEntity(InEdgeRef), &SEdgeID); // we can ignore the returned SU_RESULT return SEdgeID; } bool DatasmithSketchUpUtils::IsVisible( SUComponentInstanceRef InSComponentInstanceRef, SULayerRef InSEffectiveLayerRef ) { // Get the flag indicating whether or not the SketchUp component instance is hidden. bool bSComponentInstanceHidden = false; SUDrawingElementGetHidden(SUComponentInstanceToDrawingElement(InSComponentInstanceRef), &bSComponentInstanceHidden); // we can ignore the returned SU_RESULT // Get the flag indicating whether or not the SketchUp component instance effective layer is visible. bool bSEffectiveLayerVisible = true; SULayerGetVisibility(InSEffectiveLayerRef, &bSEffectiveLayerVisible); // we can ignore the returned SU_RESULT return (!bSComponentInstanceHidden && bSEffectiveLayerVisible); } bool DatasmithSketchUpUtils::IsLayerVisible( SULayerRef LayerRef ) { bool bVisible = true; SULayerGetVisibility(LayerRef, &bVisible); // Search for invisible ancestor folder (parent invisibility overrides child's visibility) // LayerFolder introduced in SketchUp 2021 #if !defined(SKP_SDK_2019) && !defined(SKP_SDK_2020) SULayerFolderRef LayerFolderRef = SU_INVALID; SULayerGetParentLayerFolder(LayerRef, &LayerFolderRef); while (bVisible && SUIsValid(LayerFolderRef)) { bool bLayerFolderVisible = true; SULayerFolderGetVisibility(LayerFolderRef, &bLayerFolderVisible); bVisible = bVisible && bLayerFolderVisible; SULayerFolderRef ParentLayerFolderRef = SU_INVALID; SULayerFolderGetParentLayerFolder(LayerFolderRef, &ParentLayerFolderRef); LayerFolderRef = ParentLayerFolderRef; } #endif return bVisible; } SUMaterialRef DatasmithSketchUpUtils::GetMaterial( SUComponentInstanceRef InComponentInstanceRef ) { // Retrieve the SketckUp drawing element material. SUMaterialRef SMaterialRef = SU_INVALID; SUDrawingElementGetMaterial(SUComponentInstanceToDrawingElement(InComponentInstanceRef), &SMaterialRef); // we can ignore the returned SU_RESULT return SMaterialRef; } void DatasmithSketchUpUtils::SetActorTransform( const TSharedPtr& InActorElement, SUTransformation const& InWorldTransform ) { FVector Translation{}; FQuat Rotation{}; FVector Scale{}; FVector Shear{}; if (DecomposeTransform(InWorldTransform, Translation, Rotation, Scale, Shear)) { if (Rotation.IsNormalized()) { InActorElement->SetTranslation(Translation.X, Translation.Y, Translation.Z, false); InActorElement->SetRotation(Rotation.X, Rotation.Y, Rotation.Z, Rotation.W, false); InActorElement->SetScale(Scale.X, Scale.Y, Scale.Z, false); } else { ToRuby::LogWarn( FString::Printf(TEXT("WARNING: Actor %ls (%ls) has unnormalized rotation"), InActorElement->GetName(), InActorElement->GetLabel())); } } else { ToRuby::LogWarn( FString::Printf(TEXT("WARNING: Actor %ls (%ls) has some zero scaling or rotation can't be converted"), InActorElement->GetName(), InActorElement->GetLabel())); } } bool DatasmithSketchUpUtils::DecomposeTransform( SUTransformation const& InWorldTransform, // SketchUp world transform to convert FVector& OutTranslation, FQuat& OutRotation, FVector& OutScale, FVector& OutShear) { // We use Imath::extractAndRemoveScalingAndShear() because FMatrix::ExtractScaling() is deemed unreliable. // Set up a scaling and rotation matrix. Imath::Matrix44 Matrix ( InWorldTransform.values[0], InWorldTransform.values[1], InWorldTransform.values[2], 0.0, InWorldTransform.values[4], InWorldTransform.values[5], InWorldTransform.values[6], 0.0, InWorldTransform.values[8], InWorldTransform.values[9], InWorldTransform.values[10], 0.0, 0.0, 0.0, 0.0, 1.0 ); // Remove any scaling from the matrix and get the scale vector that was initially present. Imath::Vec3 Scale; Imath::Vec3 Shear; if (!Imath::extractAndRemoveScalingAndShear(Matrix, Scale, Shear, false)) { // Scaling is zero return false; } if (InWorldTransform.values[15] != 1.0) { // Apply the extra SketchUp uniform scaling factor. Scale *= InWorldTransform.values[15]; } OutScale = FVector(Scale.x, Scale.y, Scale.z); OutShear = FVector(Shear.x, Shear.y, Shear.z); // Initialize a rotation quaternion with the rotation matrix. Imath::Quat Quaternion = Imath::extractQuat(Matrix); // Convert the SketchUp right-handed Z-up coordinate rotation into an Unreal left-handed Z-up coordinate rotation. // This is done by inverting the X and Z components of the quaternion to mirror the quaternion on the XZ-plane. Quaternion.v.x = -Quaternion.v.x; Quaternion.v.z = -Quaternion.v.z; Quaternion.normalize(); // Make sure Unreal will be able to handle the rotation quaternion. double Angle = Quaternion.angle(); Imath::Vec3 Axis = Quaternion.axis(); OutRotation = FQuat(FVector(Axis.x, Axis.y, Axis.z), Angle); // Convert the SketchUp right-handed Z-up coordinate translation into an Unreal left-handed Z-up coordinate translation. // To avoid perturbating X, which is forward in Unreal, the handedness conversion is done by flipping the side vector Y. // SketchUp uses inches as internal system unit for all 3D coordinates in the model while Unreal uses centimeters. OutTranslation = FromSketchUp::ConvertPosition(InWorldTransform.values[12], InWorldTransform.values[13], InWorldTransform.values[14]); return true; } bool DatasmithSketchUpUtils::SplitTransform( SUTransformation const& InWorldTransform, // SketchUp world transform to convert SUTransformation& OutWorldTransform, SUTransformation& OutMeshActorWorldTransform, SUTransformation& OutBakeTransform ) { // Set up a scaling and rotation matrix. auto& SMatrix = InWorldTransform.values; Imath::Matrix44 Matrix( SMatrix[0], SMatrix[1], SMatrix[2], 0.0, SMatrix[4], SMatrix[5], SMatrix[6], 0.0, SMatrix[8], SMatrix[9], SMatrix[10], 0.0, 0.0, 0.0, 0.0, 1.0); // Remove any scaling from the matrix and get the scale vector that was initially present. Imath::Vec3 Scale; Imath::Vec3 Shear; if (!Imath::extractAndRemoveScalingAndShear(Matrix, Scale, Shear, false)) { // Scaling is zero return false; } // Scale/Shear removed, now it's just Rotation Imath::Matrix44 RotationMatrix(Matrix); Imath::Matrix44 TranslationMatrix; TranslationMatrix.translate(Imath::Vec3(SMatrix[12], SMatrix[13], SMatrix[14])); if (SMatrix[15] > UE_SMALL_NUMBER) { // Apply the extra SketchUp uniform (inverse)scaling factor. Scale /= SMatrix[15]; } Imath::Matrix44 ScaleMatrix; ScaleMatrix.scale(Scale); Imath::Matrix44 ShearMatrix; ShearMatrix.shear(Shear); Imath::Matrix44 ActorMatrix = RotationMatrix*TranslationMatrix; Imath::Matrix44 MeshActorMatrix = RotationMatrix*TranslationMatrix; auto CopyImathMatrixToSU = [](const Imath::Matrix44& Source, SUTransformation& Dest) { for (int32 Index=0; Index<16; ++Index) { Dest.values[Index] = Source.getValue()[Index]; } }; CopyImathMatrixToSU(ActorMatrix, OutWorldTransform); CopyImathMatrixToSU(MeshActorMatrix, OutMeshActorWorldTransform); CopyImathMatrixToSU(ScaleMatrix*ShearMatrix, OutBakeTransform); return true; } SUTransformation DatasmithSketchUpUtils::GetComponentInstanceTransform(SUComponentInstanceRef InComponentInstanceRef, SUTransformation const& InWorldTransform) { // Get the SketchUp component instance transform. SUTransformation SComponentInstanceTransform; SUComponentInstanceGetTransform(InComponentInstanceRef, &SComponentInstanceTransform); // we can ignore the returned SU_RESULT // Compute the world transform of the SketchUp component instance. SUTransformation SComponentInstanceWorldTransform; SUTransformationMultiply(&InWorldTransform, &SComponentInstanceTransform, &SComponentInstanceWorldTransform); // we can ignore the returned SU_RESULT return SComponentInstanceWorldTransform; } bool DatasmithSketchUpUtils::CompareSUTransformations(const SUTransformation& A, const SUTransformation& B) { return Algo::Compare( MakeArrayView(A.values, std::size(A.values)), MakeArrayView(B.values, std::size(B.values)), [](const double& A, const double& B) { return FMath::Abs(A-B) < UE_KINDA_SMALL_NUMBER; }); }