// Copyright Epic Games, Inc. All Rights Reserved. #include "ModelingToolTargetUtil.h" #include "ToolTargets/ToolTarget.h" #include "TargetInterfaces/DynamicMeshCommitter.h" #include "TargetInterfaces/DynamicMeshProvider.h" #include "TargetInterfaces/MaterialProvider.h" #include "TargetInterfaces/MeshDescriptionCommitter.h" #include "TargetInterfaces/MeshDescriptionProvider.h" #include "TargetInterfaces/PrimitiveComponentBackedTarget.h" #include "TargetInterfaces/AssetBackedTarget.h" #include "TargetInterfaces/SkeletalMeshBackedTarget.h" #include "TargetInterfaces/StaticMeshBackedTarget.h" #include "TargetInterfaces/DynamicMeshSource.h" #include "TargetInterfaces/PhysicsDataSource.h" #include "ModelingObjectsCreationAPI.h" #include "Components/PrimitiveComponent.h" #include "Components/StaticMeshComponent.h" #include "GameFramework/Volume.h" #include "Components/DynamicMeshComponent.h" #include "MeshConversionOptions.h" #include "MeshDescriptionToDynamicMesh.h" #include "DynamicMeshToMeshDescription.h" #include "StaticMeshAttributes.h" #if WITH_EDITOR #include "StaticMeshCompiler.h" #include "SkinnedAssetCompiler.h" #endif #include "DynamicMesh/NonManifoldMappingSupport.h" #define LOCTEXT_NAMESPACE "ModelingToolTargetUtil" using namespace UE::Geometry; namespace UE::ToolTarget::Internal { static TAutoConsoleVariable CVarCapturePostEditChangeInTransactions( TEXT("modeling.CapturePostEditChangeInTransactions"), true, TEXT("When true, PostEditChange will be included in tool-target based tool transactions.")); } AActor* UE::ToolTarget::GetTargetActor(UToolTarget* Target) { ISceneComponentBackedTarget* TargetComponent = Cast(Target); if (TargetComponent) { return TargetComponent->GetOwnerActor(); } ensure(false); return nullptr; } UPrimitiveComponent* UE::ToolTarget::GetTargetComponent(UToolTarget* Target) { IPrimitiveComponentBackedTarget* TargetComponent = Cast(Target); if (TargetComponent) { return TargetComponent->GetOwnerComponent(); } return nullptr; } USceneComponent* UE::ToolTarget::GetTargetSceneComponent(UToolTarget* Target) { ISceneComponentBackedTarget* TargetComponent = Cast(Target); if (TargetComponent) { return TargetComponent->GetOwnerSceneComponent(); } return nullptr; } FString UE::ToolTarget::GetHumanReadableName(UToolTarget* Target) { IAssetBackedTarget* TargetAsset = Cast(Target); if (TargetAsset) { return TargetAsset->GetSourceData()->GetName(); } ISceneComponentBackedTarget* TargetComponent = Cast(Target); if (TargetComponent) { return TargetComponent->GetOwnerSceneComponent()->GetFullGroupName(false); } return FString(""); } bool UE::ToolTarget::HideSourceObject(UToolTarget* Target) { ISceneComponentBackedTarget* TargetComponent = Cast(Target); if (TargetComponent) { TargetComponent->SetOwnerVisibility(false); return true; } ensure(false); return false; } bool UE::ToolTarget::ShowSourceObject(UToolTarget* Target) { ISceneComponentBackedTarget* TargetComponent = Cast(Target); if (TargetComponent) { TargetComponent->SetOwnerVisibility(true); return true; } ensure(false); return false; } bool UE::ToolTarget::SetSourceObjectVisible(UToolTarget* Target, bool bVisible) { if (bVisible) { return ShowSourceObject(Target); } else { return HideSourceObject(Target); } } FTransform3d UE::ToolTarget::GetLocalToWorldTransform(UToolTarget* Target) { ISceneComponentBackedTarget* TargetComponent = Cast(Target); if (TargetComponent) { return (FTransform3d)TargetComponent->GetWorldTransform(); } return FTransform3d(); } FComponentMaterialSet UE::ToolTarget::GetMaterialSet(UToolTarget* Target, bool bPreferAssetMaterials) { FComponentMaterialSet MaterialSet; IMaterialProvider* MaterialProvider = Cast(Target); if (ensure(MaterialProvider)) { MaterialProvider->GetMaterialSet(MaterialSet, bPreferAssetMaterials); } return MaterialSet; } bool UE::ToolTarget::CommitMaterialSetUpdate( UToolTarget* Target, const FComponentMaterialSet& UpdatedMaterials, bool bApplyToAsset) { IMaterialProvider* MaterialProvider = Cast(Target); if (MaterialProvider) { return MaterialProvider->CommitMaterialSetUpdate(UpdatedMaterials, bApplyToAsset); } return false; } TArray UE::ToolTarget::GetMeshDescriptionLODs(UToolTarget* Target, bool& bOutTargetSupportsLODs, bool bOnlyReturnDefaultLOD, bool bExcludeAutoGeneratedLODs) { if (!bOnlyReturnDefaultLOD) { if (IMeshDescriptionProvider* MeshDescriptionProvider = Cast(Target)) { bOutTargetSupportsLODs = MeshDescriptionProvider->SupportsLODs(); return MeshDescriptionProvider->GetAvailableLODs(bExcludeAutoGeneratedLODs); } } bOutTargetSupportsLODs = false; TArray DefaultArray{ EMeshLODIdentifier::Default }; return DefaultArray; } EMeshLODIdentifier UE::ToolTarget::GetTargetMeshDescriptionLOD( UToolTarget* Target, bool& bOutTargetSupportsLODs) { if (IMeshDescriptionProvider* MeshDescriptionProvider = Cast(Target)) { bOutTargetSupportsLODs = MeshDescriptionProvider->SupportsLODs(); return MeshDescriptionProvider->GetMeshDescriptionLOD(); } bOutTargetSupportsLODs = false; return EMeshLODIdentifier::Default; } const FMeshDescription* UE::ToolTarget::GetMeshDescription(UToolTarget* Target, const FGetMeshParameters& GetMeshParams) { static FMeshDescription EmptyMeshDescription; IMeshDescriptionProvider* MeshDescriptionProvider = Cast(Target); if (MeshDescriptionProvider) { return MeshDescriptionProvider->GetMeshDescription(GetMeshParams); } ensure(false); return &EmptyMeshDescription; } FMeshDescription UE::ToolTarget::GetEmptyMeshDescription(UToolTarget* Target) { static FMeshDescription EmptyMeshDescription; IMeshDescriptionProvider* MeshDescriptionProvider = Cast(Target); if (MeshDescriptionProvider) { return MeshDescriptionProvider->GetEmptyMeshDescription(); } ensure(false); return EmptyMeshDescription; } FMeshDescription UE::ToolTarget::GetMeshDescriptionCopy(UToolTarget* Target, const FGetMeshParameters& GetMeshParams) { IMeshDescriptionProvider* MeshDescriptionProvider = Cast(Target); if (MeshDescriptionProvider) { return MeshDescriptionProvider->GetMeshDescriptionCopy(GetMeshParams); } ensure(false); return FMeshDescription(); } FDynamicMesh3 UE::ToolTarget::GetDynamicMeshCopy(UToolTarget* Target, bool bWantMeshTangents) { FGetMeshParameters GetMeshParams; GetMeshParams.bWantMeshTangents = bWantMeshTangents; return GetDynamicMeshCopy(Target, GetMeshParams); } int32 UE::ToolTarget::GetTriangleCount(UToolTarget* Target) { IPersistentDynamicMeshSource* DynamicMeshSource = Cast(Target); if (DynamicMeshSource) { UDynamicMesh* DynamicMesh = DynamicMeshSource->GetDynamicMeshContainer(); return DynamicMesh->GetTriangleCount(); } IMeshDescriptionProvider* MeshDescriptionProvider = Cast(Target); if (MeshDescriptionProvider) { if (const FMeshDescription* MeshDescription = MeshDescriptionProvider->GetMeshDescription()) { return MeshDescription->Triangles().Num(); } } return 0; } FDynamicMesh3 UE::ToolTarget::GetDynamicMeshCopy(UToolTarget* Target, const FGetMeshParameters& InGetMeshParams) { IPersistentDynamicMeshSource* DynamicMeshSource = Cast(Target); if (DynamicMeshSource) { UDynamicMesh* DynamicMesh = DynamicMeshSource->GetDynamicMeshContainer(); FDynamicMesh3 Mesh; DynamicMesh->ProcessMesh([&](const FDynamicMesh3& ReadMesh) { Mesh = ReadMesh; }); return Mesh; } IDynamicMeshProvider* DynamicMeshProvider = Cast(Target); if (DynamicMeshProvider) { return DynamicMeshProvider->GetDynamicMesh(InGetMeshParams); } IMeshDescriptionProvider* MeshDescriptionProvider = Cast(Target); FDynamicMesh3 Mesh(EMeshComponents::FaceGroups); Mesh.EnableAttributes(); if (MeshDescriptionProvider) { FMeshDescriptionToDynamicMesh Converter; Converter.bVIDsFromNonManifoldMeshDescriptionAttr = true; Converter.SetPolygonGroupToMaterialIndexMap(MeshDescriptionProvider->GetPolygonGroupToMaterialIndexMap()); if (InGetMeshParams.bWantMeshTangents) { const FMeshDescription MeshDescriptionCopy = MeshDescriptionProvider->GetMeshDescriptionCopy(InGetMeshParams); Converter.Convert(&MeshDescriptionCopy, Mesh, InGetMeshParams.bWantMeshTangents); } else { Converter.Convert(MeshDescriptionProvider->GetMeshDescription(InGetMeshParams), Mesh); } return Mesh; } ensure(false); return Mesh; } UE::ToolTarget::EDynamicMeshUpdateResult UE::ToolTarget::CommitMeshDescriptionUpdate(UToolTarget* Target, const FMeshDescription* UpdatedMesh, const FComponentMaterialSet* UpdatedMaterials, const FCommitMeshParameters& CommitParams) { if (!ensure(UpdatedMesh != nullptr)) { return EDynamicMeshUpdateResult::Failed; } IMeshDescriptionCommitter* MeshDescriptionCommitter = Cast(Target); if (!ensure(MeshDescriptionCommitter)) { return EDynamicMeshUpdateResult::Failed; } if (UpdatedMaterials != nullptr) { CommitMaterialSetUpdate(Target, *UpdatedMaterials, true); } bool bOK = MeshDescriptionCommitter->CommitMeshDescription(*UpdatedMesh, CommitParams); return (bOK) ? EDynamicMeshUpdateResult::Ok : EDynamicMeshUpdateResult::Failed; } UE::ToolTarget::EDynamicMeshUpdateResult UE::ToolTarget::CommitMeshDescriptionUpdate(UToolTarget* Target, FMeshDescription&& UpdatedMesh, const FCommitMeshParameters& CommitParams) { if (IMeshDescriptionCommitter* MeshDescriptionCommitter = Cast(Target)) { bool bOK = MeshDescriptionCommitter->CommitMeshDescription(MoveTemp(UpdatedMesh), CommitParams); return (bOK) ? EDynamicMeshUpdateResult::Ok : EDynamicMeshUpdateResult::Failed; } return EDynamicMeshUpdateResult::Failed; } UE::ToolTarget::EDynamicMeshUpdateResult UE::ToolTarget::CommitMeshDescriptionUpdateViaDynamicMesh( UToolTarget* Target, const UE::Geometry::FDynamicMesh3& UpdatedMesh, bool bHaveModifiedTopology, const FCommitMeshParameters& CommitParams) { IMeshDescriptionCommitter* MeshDescriptionCommitter = Cast(Target); if (!ensure(MeshDescriptionCommitter)) { return EDynamicMeshUpdateResult::Failed; } FDynamicMeshToMeshDescription Converter; Converter.SetMaterialIDMapFromInverseMap(MeshDescriptionCommitter->GetPolygonGroupToMaterialIndexMap()); FMeshDescription ConvertedMesh; if (bHaveModifiedTopology) { ConvertedMesh = UE::ToolTarget::GetEmptyMeshDescription(Target); Converter.Convert(&UpdatedMesh, ConvertedMesh); } else { // FGetMeshParameters should never need to be initialized to anything other than the // default because we are either (1) ignoring the tangents, in which case, we don't // want to update them anyway (2) replacing them, in which case, we don't need to // compute them ConvertedMesh = UE::ToolTarget::GetMeshDescriptionCopy(Target, FGetMeshParameters()); Converter.Update(&UpdatedMesh, ConvertedMesh); } bool bOK = MeshDescriptionCommitter->CommitMeshDescription(MoveTemp(ConvertedMesh), CommitParams); return (bOK) ? EDynamicMeshUpdateResult::Ok : EDynamicMeshUpdateResult::Failed; } void UE::ToolTarget::Internal::CommitDynamicMeshViaIPersistentDynamicMeshSource( IPersistentDynamicMeshSource& DynamicMeshSource, const FDynamicMesh3& UpdatedMesh, bool bHaveModifiedTopology) { UDynamicMesh* DynamicMesh = DynamicMeshSource.GetDynamicMeshContainer(); TUniquePtr CurrentMesh = DynamicMesh->ExtractMesh(); TSharedPtr CurrentMeshShared(CurrentMesh.Release()); DynamicMesh->EditMesh([&](FDynamicMesh3& EditMesh) { EditMesh.CompactCopy(UpdatedMesh); FNonManifoldMappingSupport::RemoveAllNonManifoldMappingData(EditMesh); }); TSharedPtr NewMeshShared = MakeShared(); DynamicMesh->ProcessMesh([&](const FDynamicMesh3& ReadMesh) { *NewMeshShared = ReadMesh; }); TUniquePtr ReplaceChange = MakeUnique(CurrentMeshShared, NewMeshShared); DynamicMeshSource.CommitDynamicMeshChange(MoveTemp(ReplaceChange), LOCTEXT("CommitDynamicMeshUpdate_MeshSource", "Update Mesh")); // todo support bModifiedTopology flag? } UE::ToolTarget::EDynamicMeshUpdateResult UE::ToolTarget::CommitDynamicMeshUpdate( UToolTarget* Target, const FDynamicMesh3& UpdatedMesh, bool bHaveModifiedTopology, const FConversionToMeshDescriptionOptions& ConversionOptions, const FComponentMaterialSet* UpdatedMaterials) { if (UpdatedMaterials != nullptr) { CommitMaterialSetUpdate(Target, *UpdatedMaterials, true); } IPersistentDynamicMeshSource* DynamicMeshSource = Cast(Target); if (DynamicMeshSource) { Internal::CommitDynamicMeshViaIPersistentDynamicMeshSource( *DynamicMeshSource, UpdatedMesh, bHaveModifiedTopology); return EDynamicMeshUpdateResult::Ok; } IDynamicMeshCommitter* DynamicMeshCommitter = Cast(Target); if (DynamicMeshCommitter) { IDynamicMeshCommitter::FDynamicMeshCommitInfo CommitInfo; CommitInfo.bTopologyChanged = bHaveModifiedTopology; CommitInfo.bPolygroupsChanged = ConversionOptions.bSetPolyGroups; CommitInfo.bPositionsChanged = ConversionOptions.bUpdatePositions; CommitInfo.bNormalsChanged = ConversionOptions.bUpdateNormals; CommitInfo.bTangentsChanged = ConversionOptions.bUpdateTangents; CommitInfo.bUVsChanged = ConversionOptions.bUpdateUVs; CommitInfo.bVertexColorsChanged = ConversionOptions.bUpdateVtxColors; CommitInfo.bTransformVertexColorsSRGBToLinear = ConversionOptions.bTransformVtxColorsSRGBToLinear; DynamicMeshCommitter->CommitDynamicMesh(UpdatedMesh, CommitInfo); return EDynamicMeshUpdateResult::Ok; } IMeshDescriptionCommitter* MeshDescriptionCommitter = Cast(Target); if (MeshDescriptionCommitter) { FMeshDescription ConvertedMesh; FDynamicMeshToMeshDescription Converter(ConversionOptions); Converter.SetMaterialIDMapFromInverseMap(MeshDescriptionCommitter->GetPolygonGroupToMaterialIndexMap()); if (!bHaveModifiedTopology) { Converter.UpdateUsingConversionOptions(&UpdatedMesh, ConvertedMesh); } else { Converter.Convert(&UpdatedMesh, ConvertedMesh); } bool bOK = MeshDescriptionCommitter->CommitMeshDescription(MoveTemp(ConvertedMesh)); return (bOK) ? EDynamicMeshUpdateResult::Ok : EDynamicMeshUpdateResult::Failed; } ensure(false); return EDynamicMeshUpdateResult::Failed; } UE::ToolTarget::EDynamicMeshUpdateResult UE::ToolTarget::CommitDynamicMeshUVUpdate(UToolTarget* Target, const UE::Geometry::FDynamicMesh3* UpdatedMesh) { IPersistentDynamicMeshSource* DynamicMeshSource = Cast(Target); if (DynamicMeshSource) { // just do a full mesh update for now // todo actually only update UVs? return CommitDynamicMeshUpdate(Target, *UpdatedMesh, true); } IDynamicMeshCommitter* DynamicMeshCommitter = Cast(Target); if (DynamicMeshCommitter) { IDynamicMeshCommitter::FDynamicMeshCommitInfo CommitInfo(false); CommitInfo.bUVsChanged = true; DynamicMeshCommitter->CommitDynamicMesh(*UpdatedMesh, CommitInfo); return EDynamicMeshUpdateResult::Ok; } IMeshDescriptionCommitter* MeshDescriptionCommitter = Cast(Target); if (!ensure(MeshDescriptionCommitter)) { return EDynamicMeshUpdateResult::Failed; } FMeshDescription NewMeshDescription = UE::ToolTarget::GetMeshDescriptionCopy(Target); bool bVerticesOnly = false; bool bAttributesOnly = true; FDynamicMeshToMeshDescription Converter; Converter.SetMaterialIDMapFromInverseMap(MeshDescriptionCommitter->GetPolygonGroupToMaterialIndexMap()); if (FDynamicMeshToMeshDescription::HaveMatchingElementCounts(UpdatedMesh, &NewMeshDescription, bVerticesOnly, bAttributesOnly)) { Converter.UpdateAttributes(UpdatedMesh, NewMeshDescription, false, false, true/*update uvs*/); } else { // must have been duplicate tris in the mesh description; we can't count on 1-to-1 mapping of TriangleIDs. Just convert Converter.Convert(UpdatedMesh, NewMeshDescription); } bool bOK = MeshDescriptionCommitter->CommitMeshDescription(MoveTemp(NewMeshDescription)); return (bOK) ? EDynamicMeshUpdateResult::Ok : EDynamicMeshUpdateResult::Failed; } UE::ToolTarget::EDynamicMeshUpdateResult UE::ToolTarget::CommitDynamicMeshNormalsUpdate( UToolTarget* Target, const UE::Geometry::FDynamicMesh3* UpdatedMesh, bool bUpdateTangents) { IPersistentDynamicMeshSource* DynamicMeshSource = Cast(Target); if (DynamicMeshSource) { // just do a full mesh update for now return CommitDynamicMeshUpdate(Target, *UpdatedMesh, true); } IDynamicMeshCommitter* DynamicMeshCommitter = Cast(Target); if (DynamicMeshCommitter) { IDynamicMeshCommitter::FDynamicMeshCommitInfo CommitInfo(false); CommitInfo.bNormalsChanged = true; CommitInfo.bTangentsChanged = bUpdateTangents; DynamicMeshCommitter->CommitDynamicMesh(*UpdatedMesh, CommitInfo); return EDynamicMeshUpdateResult::Ok; } IMeshDescriptionCommitter* MeshDescriptionCommitter = Cast(Target); if (!ensure(MeshDescriptionCommitter)) { return EDynamicMeshUpdateResult::Failed; } FMeshDescription NewMeshDescription = UE::ToolTarget::GetMeshDescriptionCopy(Target); bool bVerticesOnly = false; bool bAttributesOnly = true; FDynamicMeshToMeshDescription Converter; Converter.SetMaterialIDMapFromInverseMap(MeshDescriptionCommitter->GetPolygonGroupToMaterialIndexMap()); if (FDynamicMeshToMeshDescription::HaveMatchingElementCounts(UpdatedMesh, &NewMeshDescription, bVerticesOnly, bAttributesOnly)) { Converter.UpdateAttributes(UpdatedMesh, NewMeshDescription, true, bUpdateTangents, false); } else { // must have been duplicate tris in the mesh description; we can't count on 1-to-1 mapping of TriangleIDs. Just convert Converter.Convert(UpdatedMesh, NewMeshDescription); } bool bOK = MeshDescriptionCommitter->CommitMeshDescription(MoveTemp(NewMeshDescription)); return (bOK) ? EDynamicMeshUpdateResult::Ok : EDynamicMeshUpdateResult::Failed; } bool UE::ToolTarget::SupportsIncrementalMeshChanges(UToolTarget* Target) { IPersistentDynamicMeshSource* DynamicMeshSource = Cast(Target); if (DynamicMeshSource) { return DynamicMeshSource->HasDynamicMeshComponent(); } return false; } bool UE::ToolTarget::ApplyIncrementalMeshEditChange( UToolTarget* Target, TFunctionRef MeshEditChangeFunc ) { IPersistentDynamicMeshSource* DynamicMeshSource = Cast(Target); if (DynamicMeshSource && DynamicMeshSource->HasDynamicMeshComponent()) { bool bOK; UDynamicMeshComponent* Component = DynamicMeshSource->GetDynamicMeshComponent(); Component->EditMesh([&](FDynamicMesh3& EditMesh) { bOK = MeshEditChangeFunc(EditMesh, Component); }); return bOK; } return false; } bool UE::ToolTarget::ConfigureCreateMeshObjectParams(UToolTarget* SourceTarget, FCreateMeshObjectParams& DerivedParamsOut) { ISceneComponentBackedTarget* ComponentTarget = Cast(SourceTarget); if (ComponentTarget) { if (Cast(ComponentTarget->GetOwnerSceneComponent()) != nullptr) { DerivedParamsOut.TypeHint = ECreateObjectTypeHint::StaticMesh; return true; } if (Cast(ComponentTarget->GetOwnerSceneComponent()) != nullptr) { DerivedParamsOut.TypeHint = ECreateObjectTypeHint::DynamicMeshActor; return true; } AVolume* VolumeActor = Cast(ComponentTarget->GetOwnerActor()); if (VolumeActor != nullptr) { DerivedParamsOut.TypeHint = ECreateObjectTypeHint::Volume; DerivedParamsOut.TypeHintClass = VolumeActor->GetClass(); return true; } } return false; } UBodySetup* UE::ToolTarget::GetPhysicsBodySetup(UToolTarget* Target) { if (IPhysicsDataSource* PhysicsSource = Cast(Target)) { return PhysicsSource->GetBodySetup(); } return nullptr; } IInterface_CollisionDataProvider* UE::ToolTarget::GetPhysicsCollisionDataProvider(UToolTarget* Target) { if (IPhysicsDataSource* PhysicsSource = Cast(Target)) { return PhysicsSource->GetComplexCollisionProvider(); } return nullptr; } UStaticMesh* UE::ToolTarget::GetStaticMeshFromTargetIfAvailable(UToolTarget* Target) { IStaticMeshBackedTarget* TargetStaticMeshTarget = Cast(Target); UStaticMesh* TargetStaticMesh = TargetStaticMeshTarget ? TargetStaticMeshTarget->GetStaticMesh() : nullptr; return TargetStaticMesh; } USkeletalMesh* UE::ToolTarget::GetSkeletalMeshFromTargetIfAvailable(UToolTarget* Target) { ISkeletalMeshBackedTarget* TargetSkeletalMeshTarget = Cast(Target); USkeletalMesh* TargetSkeletalMesh = TargetSkeletalMeshTarget ? TargetSkeletalMeshTarget->GetSkeletalMesh() : nullptr; return TargetSkeletalMesh; } #if WITH_EDITOR void UE::ToolTarget::Internal::PostEditChangeWithConditionalUndo(UObject* Object) { if (CVarCapturePostEditChangeInTransactions->GetBool()) { Object->PostEditChange(); } else { TGuardValue SuppressTransaction(GUndo, nullptr); Object->PostEditChange(); // For StaticMesh & SkeletalMesh, we additionally block on the StaticMesh/SkeletalMesh build // to ensure it completes while transactions are disabled. This is necessary because // closing out the transaction scope will force the compile to complete outside of this scope // where transactions are still captured. if (UStaticMesh* StaticMesh = Cast(Object)) { FStaticMeshCompilingManager::Get().FinishCompilation({StaticMesh}); } else if (USkinnedAsset* SkinnedAsset = Cast(Object)) { FSkinnedAssetCompilingManager::Get().FinishCompilation({SkinnedAsset}); } } } #endif #undef LOCTEXT_NAMESPACE