// Copyright Epic Games, Inc. All Rights Reserved. #include "CADKernelRetessellateAction.h" #include "LegacyParametricSurfaceData.h" #include "CADKernelEngine.h" #include "ParametricSurfaceData.h" #include "Algo/AnyOf.h" #include "Async/ParallelFor.h" #include "Components/StaticMeshComponent.h" #include "Engine/StaticMesh.h" #include "Engine/StaticMeshActor.h" #include "Logging/LogMacros.h" #if WITH_EDITOR #include "ContentBrowserMenuContexts.h" #include "IStaticMeshEditor.h" #include "Toolkits/IToolkit.h" #include "Toolkits/ToolkitManager.h" #include "ToolMenus.h" #endif DEFINE_LOG_CATEGORY_STATIC(LogCADKernelRetessellation, Log, All) #define LOCTEXT_NAMESPACE "CADKernelRetessellateAction" static const FName CADKernelTessellationTag(TEXT("CADKernel")); static bool GCADKernelRetessellation = false; FAutoConsoleVariableRef GCADKernelDebugUseEngine( TEXT("CADKernel.Debug.Retessellation"), GCADKernelRetessellation, TEXT(""), ECVF_Default); namespace UE::CADKernel { class FRetessellationUtils { public: static bool ConfirmAndUpdateTessellation(FCADKernelRetessellationSettings& Settings) { return false; } static UParametricSurfaceData* GetParametricSurfaceData(UStaticMesh* StaticMesh) { UParametricSurfaceData* ParametricSurfaceData = nullptr; if (IInterface_AssetUserData* AssetUserData = Cast(StaticMesh)) { ParametricSurfaceData = AssetUserData->GetAssetUserData(); } return ParametricSurfaceData; } static bool Retessellate(UStaticMesh& StaticMesh, const FLegacyParametricSurfaceData* LegacyData = nullptr) { IInterface_AssetUserData* AssetUserData = Cast(&StaticMesh); if (!ensure(AssetUserData)) { return false; } UParametricSurfaceData* ParametricSurfaceData = LegacyData ? LegacyData->ToCADKernel() : GetParametricSurfaceData(&StaticMesh); if (!ensure(ParametricSurfaceData)) { return false; } if (GCADKernelRetessellation) { // If StaticMesh has been generated using the Datasmith importer, add ParametricSurfaceData to StaticMesh's AssetUserData if (LegacyData) { ParametricSurfaceData->Rename(nullptr, &StaticMesh, REN_DontCreateRedirectors | REN_NonTransactional); AssetUserData->AddAssetUserData(ParametricSurfaceData); } } return GCADKernelRetessellation; } #if WITH_EDITOR static void FinalizeChanges(UStaticMesh* StaticMesh) { StaticMesh->PostEditChange(); StaticMesh->MarkPackageDirty(); // Refresh associated editor TSharedPtr EditingToolkit = FToolkitManager::Get().FindEditorForAsset(StaticMesh); if (IStaticMeshEditor* StaticMeshEditorInUse = StaticCastSharedPtr(EditingToolkit).Get()) { StaticMeshEditorInUse->RefreshTool(); } } #endif }; } bool FCADKernelRetessellateAction::CanRetessellate(UStaticMesh* StaticMesh) { using namespace UE::CADKernel; return FRetessellationUtils::GetParametricSurfaceData(StaticMesh) != nullptr; } bool FCADKernelRetessellateAction::RetessellateArray(const TArray& StaticMeshes) { using namespace UE::CADKernel; return false; } bool FCADKernelRetessellateAction::RetessellateArray(const TArray& Assets) { TArray StaticMeshes; StaticMeshes.Reserve(Assets.Num()); for (const FAssetData& AssetData : Assets) { if (UStaticMesh* StaticMesh = Cast(AssetData.GetAsset())) { StaticMeshes.Add(StaticMesh); } } return RetessellateArray(StaticMeshes); } bool FCADKernelRetessellateAction::RetessellateArray(const TArray& StaticMeshActors) { TSet StaticMeshes; StaticMeshes.Reserve(StaticMeshActors.Num()); for (AStaticMeshActor* StaticMeshActor : StaticMeshActors) { if (StaticMeshActor) { if (UStaticMeshComponent* MeshComponent = StaticMeshActor->GetStaticMeshComponent()) { if (UStaticMesh* StaticMesh = MeshComponent->GetStaticMesh()) { StaticMeshes.Add(StaticMesh); } } } } return RetessellateArray(StaticMeshes.Array()); } bool FCADKernelRetessellateAction::RetessellateLegacy(UStaticMesh& StaticMesh, FArchive& Ar) { using namespace UE::CADKernel; ensure(Ar.IsLoading()); FLegacyParametricSurfaceData LegacyParametricSurfaceData; LegacyParametricSurfaceData.Serialize(Ar); return FRetessellationUtils::Retessellate(StaticMesh, &LegacyParametricSurfaceData); } #if WITH_EDITOR namespace MenuExtension_CADKernel_StaticMesh { static const TAttribute Label = LOCTEXT("CADKernelRetessellateAction_Label", "Retessellate (New)"); static const TAttribute ToolTip = LOCTEXT("CADKernelRetessellateAction_Tooltip", "This will execute the action for your type."); static const FSlateIcon Icon = FSlateIcon(); static void ExecuteRetessellation(const FToolMenuContext& MenuContext) { const UContentBrowserAssetContextMenuContext* CBContext = UContentBrowserAssetContextMenuContext::FindContextWithAssets(MenuContext); FCADKernelRetessellateAction::RetessellateArray(CBContext->LoadSelectedObjects()); } static bool DisplayMenuEntry(const FToolMenuContext& MenuContext) { const UContentBrowserAssetContextMenuContext* CBContext = UContentBrowserAssetContextMenuContext::FindContextWithAssets(MenuContext); for (UStaticMesh* StaticMesh : CBContext->LoadSelectedObjects()) { if (FCADKernelRetessellateAction::CanRetessellate(StaticMesh)) { return GCADKernelRetessellation; } } return false; } static FDelayedAutoRegisterHelper DelayedAutoRegister(EDelayedRegisterRunPhase::EndOfEngineInit, [] { UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateLambda([]() { FToolMenuOwnerScoped OwnerScoped(UE_MODULE_NAME); UToolMenu* Menu = UE::ContentBrowser::ExtendToolMenu_AssetContextMenu(UStaticMesh::StaticClass()); FToolMenuSection& Section = Menu->FindOrAddSection("GetAssetActions"); Section.AddDynamicEntry(NAME_None, FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { if (const UContentBrowserAssetContextMenuContext* CBContext = UContentBrowserAssetContextMenuContext::FindContextWithAssets(InSection)) { bool bCanRetessellate = Algo::AnyOf(CBContext->SelectedAssets, [](const FAssetData& InAssetData) { int32 Value; return InAssetData.GetTagValue(CADKernelTessellationTag, Value); } ); FToolUIAction UIAction; UIAction.IsActionVisibleDelegate = FToolMenuIsActionButtonVisible::CreateStatic(&DisplayMenuEntry); UIAction.ExecuteAction = FToolMenuExecuteAction::CreateStatic(&ExecuteRetessellation); InSection.AddMenuEntry("CADKernelRetessellateAction_Entry", Label, ToolTip, Icon, UIAction); } }) ); }) ); } ); } #endif #undef LOCTEXT_NAMESPACE