CelticCraft/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelConvertLandscapeMaterial.cpp

154 lines
No EOL
4.9 KiB
C++

// Copyright 2020 Phyronnaz
#include "VoxelConvertLandscapeMaterial.h"
#include "VoxelMinimal.h"
#include "VoxelUtilities/VoxelConfigUtilities.h"
#include "VoxelRender/VoxelMaterialExpressions.h"
#include "Editor.h"
#include "Materials/Material.h"
#include "Containers/Ticker.h"
#include "ContentBrowserModule.h"
#include "ScopedTransaction.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "Framework/Notifications/NotificationManager.h"
void FVoxelConvertLandscapeMaterial::Init()
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
ContentBrowserModule.GetAllAssetViewContextMenuExtenders().Add(FContentBrowserMenuExtender_SelectedAssets::CreateLambda([=](const TArray<FAssetData>& SelectedAssets)
{
const auto Extender = MakeShared<FExtender>();
for (auto& Asset : SelectedAssets)
{
if (Asset.GetClass() != UMaterial::StaticClass())
{
return Extender;
}
}
Extender->AddMenuExtension(
"CommonAssetActions",
EExtensionHook::After,
nullptr,
FMenuExtensionDelegate::CreateLambda([=](FMenuBuilder& MenuBuilder)
{
MenuBuilder.AddMenuEntry(
VOXEL_LOCTEXT("Convert landscape material to voxel"),
VOXEL_LOCTEXT("Will replace all landscape layer nodes with nodes compatible with both voxels and landscapes"),
FSlateIcon(NAME_None, NAME_None),
FUIAction(FExecuteAction::CreateLambda([=]()
{
for (auto& Asset : SelectedAssets)
{
auto* Material = Cast<UMaterial>(Asset.GetAsset());
if (ensure(Material))
{
ConvertMaterial(Material);
}
}
})));
}));
return Extender;
}));
}
void FVoxelConvertLandscapeMaterial::ConvertMaterial(UMaterial* Material)
{
FScopedTransaction Transaction(TEXT("ConvertMaterial"), VOXEL_LOCTEXT("Convert landscape material to voxel"), Material);
TSet<UMaterialFunction*> VisitedFunctions;
const int32 NumReplaced = ConvertExpressions(Material, Material->GetEditorOnlyData()->ExpressionCollection.Expressions, VisitedFunctions);
const FText Text = FText::Format(VOXEL_LOCTEXT("{0} expressions replaced in {1}"), NumReplaced, FText::FromName(Material->GetFName()));
LOG_VOXEL(Log, TEXT("%s"), *Text.ToString());
FNotificationInfo Info(Text);
Info.ExpireDuration = 10.f;
Info.CheckBoxState = ECheckBoxState::Checked;
FSlateNotificationManager::Get().AddNotification(Info);
}
int32 FVoxelConvertLandscapeMaterial::ConvertExpressions(UObject* Owner, const TArray<UMaterialExpression*>& Expressions, TSet<UMaterialFunction*>& VisitedFunctions)
{
int32 NumReplaced = 0;
const auto ExpressionsCopy = Expressions;
for (auto* Expression : ExpressionsCopy)
{
auto* VoxelClass = FVoxelMaterialExpressionUtilities::GetVoxelExpression(Expression->GetClass());
if (VoxelClass)
{
ConvertExpression(Owner, Expression, VoxelClass);
NumReplaced++;
}
if (auto* FunctionCall = Cast<UMaterialExpressionMaterialFunctionCall>(Expression))
{
auto* Function = Cast<UMaterialFunction>(FunctionCall->MaterialFunction);
if (Function && !VisitedFunctions.Contains(Function))
{
VisitedFunctions.Add(Function);
NumReplaced += ConvertExpressions(Function, Function->GetEditorOnlyData()->ExpressionCollection.Expressions, VisitedFunctions);
}
}
}
return NumReplaced;
}
void FVoxelConvertLandscapeMaterial::ConvertExpression(UObject* Owner, UMaterialExpression* Expression, UClass* NewClass)
{
Owner->Modify();
auto* NewExpression = NewObject<UMaterialExpression>(Owner, NewClass, NAME_None, RF_Transactional);
check(NewClass->IsChildOf(Expression->GetClass()));
Expression->Modify();
NewExpression->Modify();
auto& Expressions = Owner->IsA<UMaterial>() ? CastChecked<UMaterial>(Owner)->GetEditorOnlyData()->ExpressionCollection.Expressions : CastChecked<UMaterialFunction>(Owner)->GetEditorOnlyData()->ExpressionCollection.Expressions;
ensure(Expressions.Remove(Expression) == 1);
Expressions.Add(NewExpression);
// Copy data
for (TFieldIterator<FProperty> It(Expression->GetClass()); It; ++It)
{
auto* Property = *It;
if (!Property->HasAnyPropertyFlags(CPF_Transient))
{
Property->CopyCompleteValue(Property->ContainerPtrToValuePtr<void>(NewExpression), Property->ContainerPtrToValuePtr<void>(Expression));
}
}
// Fixup other links
for (UMaterialExpression* OtherExpression : Expressions)
{
if (OtherExpression != Expression)
{
for (FExpressionInput* Input : OtherExpression->GetInputs())
{
if (Input->Expression == Expression)
{
OtherExpression->Modify();
Input->Expression = NewExpression;
}
}
}
}
// Check material parameter inputs
if (auto* Material = Cast<UMaterial>(Owner))
{
for (int32 InputIndex = 0; InputIndex < MP_MAX; InputIndex++)
{
FExpressionInput* Input = Material->GetExpressionInputForProperty((EMaterialProperty)InputIndex);
if (Input && Input->Expression == Expression)
{
Input->Expression = NewExpression;
}
}
}
}