Files
2025-05-18 13:04:45 +08:00

372 lines
12 KiB
C++

// 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<IDatasmithActorElement>& 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<double> 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<double> Scale;
Imath::Vec3<double> Shear;
if (!Imath::extractAndRemoveScalingAndShear<double>(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<double> Quaternion = Imath::extractQuat<double>(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<double> 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<double> 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<double> Scale;
Imath::Vec3<double> Shear;
if (!Imath::extractAndRemoveScalingAndShear<double>(Matrix, Scale, Shear, false))
{
// Scaling is zero
return false;
}
// Scale/Shear removed, now it's just Rotation
Imath::Matrix44<double> RotationMatrix(Matrix);
Imath::Matrix44<double> TranslationMatrix;
TranslationMatrix.translate(Imath::Vec3<double>(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<double> ScaleMatrix;
ScaleMatrix.scale(Scale);
Imath::Matrix44<double> ShearMatrix;
ShearMatrix.shear(Shear);
Imath::Matrix44<double> ActorMatrix = RotationMatrix*TranslationMatrix;
Imath::Matrix44<double> MeshActorMatrix = RotationMatrix*TranslationMatrix;
auto CopyImathMatrixToSU = [](const Imath::Matrix44<double>& 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; });
}