CelticCraft/Plugins/VoxelFree/Source/VoxelEditor/Private/K2Nodes/K2Node_AddDataItem.cpp

250 lines
No EOL
9.9 KiB
C++

// Copyright 2020 Phyronnaz
#include "K2Node_AddDataItem.h"
#include "VoxelGenerators/VoxelGeneratorInstanceWrapper.h"
#include "VoxelPlaceableItems/VoxelPlaceableItemManager.h"
#include "AssetRegistryModule.h"
#include "KismetCompiler.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "BlueprintNodeSpawner.h"
#include "K2Node_CallFunction.h"
#include "K2Node_MakeArray.h"
#include "K2Node_MakeStruct.h"
const FName UK2Node_AddDataItem::PC_Generator = "Generator";
const FName UK2Node_AddDataItem::PC_Bounds = "Bounds";
const FName UK2Node_AddDataItem::PC_Mask = "Mask";
UEdGraphPin* FindOutputStructPinChecked(UEdGraphNode* Node)
{
check(NULL != Node);
UEdGraphPin* OutputPin = NULL;
for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
{
UEdGraphPin* Pin = Node->Pins[PinIndex];
if (Pin && (EGPD_Output == Pin->Direction))
{
OutputPin = Pin;
break;
}
}
check(NULL != OutputPin);
return OutputPin;
}
void UK2Node_AddDataItem::AllocateDefaultPins()
{
Super::AllocateDefaultPins();
// Execution pins
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
if (!ensure(DataItemConfig))
{
return;
}
if (DataItemConfig->HasAnyFlags(RF_NeedLoad))
{
// Preload to get correct pins
PreloadObject(const_cast<UVoxelGraphDataItemConfig*>(DataItemConfig.Get()));
}
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, UVoxelPlaceableItemManager::StaticClass(), UEdGraphSchema_K2::PSC_Self);
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, UVoxelGeneratorInstanceWrapper::StaticClass(), PC_Generator);
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Struct, FVoxelIntBox::StaticStruct(), PC_Bounds);
auto* MaskPin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Int, UEdGraphSchema_K2::PSC_Bitmask, StaticEnum<EVoxel32BitMask>(), PC_Mask);
MaskPin->DefaultValue = "1";
for (auto& Parameter : DataItemConfig->Parameters)
{
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Real, Parameter);
}
}
FText UK2Node_AddDataItem::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
return GetTitle(DataItemConfig ? DataItemConfig->GetName() : FString());
}
FText UK2Node_AddDataItem::GetTooltipText() const
{
return GetTooltip(DataItemConfig ? DataItemConfig->GetName() : FString());
}
FText UK2Node_AddDataItem::GetMenuCategory() const
{
return VOXEL_LOCTEXT("Voxel");
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void UK2Node_AddDataItem::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
Super::ExpandNode(CompilerContext, SourceGraph);
if (!DataItemConfig)
{
CompilerContext.MessageLog.Error(TEXT("DataItemConfig is not set!"), this);
return;
}
TArray<UEdGraphPin*> ParameterInputPins;
{
TArray<FName> InputPinsName;
for (auto& Pin : Pins)
{
if (Pin->Direction == EGPD_Input && Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Float)
{
ParameterInputPins.Add(Pin);
InputPinsName.Add(Pin->GetFName());
}
}
if (DataItemConfig->Parameters != InputPinsName)
{
CompilerContext.MessageLog.Error(TEXT("Outdated node @@! Right click it and click Refresh Node"), this);
return;
}
}
UK2Node_MakeArray* MakeArrayNode = CompilerContext.SpawnIntermediateNode<UK2Node_MakeArray>(this, SourceGraph);
MakeArrayNode->AllocateDefaultPins();
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(MakeArrayNode, this);
UK2Node_MakeStruct* MakeStruct = CompilerContext.SpawnIntermediateNode<UK2Node_MakeStruct>(this, SourceGraph);
MakeStruct->StructType = FVoxelDataItemConstructionInfo::StaticStruct();
MakeStruct->AllocateDefaultPins();
MakeStruct->bMadeAfterOverridePinRemoval = true;
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(MakeStruct, this);
UEdGraphPin* ArrayOut = MakeArrayNode->GetOutputPin();
// Connect the output of the "Make Array" pin to the function's "Parameters" pin
ArrayOut->MakeLinkTo(MakeStruct->FindPinChecked(GET_MEMBER_NAME_STRING_CHECKED(FVoxelDataItemConstructionInfo, Parameters)));
// This will set the "Make Array" node's type, only works if one pin is connected.
MakeArrayNode->PinConnectionListChanged(ArrayOut);
for (int32 Index = 0; Index < ParameterInputPins.Num(); Index++)
{
// The "Make Array" node already has one pin available, so don't create one for ArgIdx == 0
if (Index > 0)
{
MakeArrayNode->AddInputPin();
}
// Find the input pin on the "Make Array" node by index.
const FString PinName = FString::Printf(TEXT("[%d]"), Index);
UEdGraphPin* ArrayPin = MakeArrayNode->FindPinChecked(PinName);
// Move our input pin to the array one
CompilerContext.MovePinLinksToIntermediate(*ParameterInputPins[Index], *ArrayPin);
}
UFunction* BlueprintFunction = UVoxelPlaceableItemManager::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UVoxelPlaceableItemManager, AddDataItem));
UK2Node_CallFunction* CallFunction = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
CallFunction->SetFromFunction(BlueprintFunction);
CallFunction->AllocateDefaultPins();
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(CallFunction, this);
FindOutputStructPinChecked(MakeStruct)->MakeLinkTo(CallFunction->FindPinChecked(TEXT("Info")));
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(UEdGraphSchema_K2::PSC_Self), *CallFunction->FindPinChecked(UEdGraphSchema_K2::PSC_Self));
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(PC_Generator), *MakeStruct->FindPinChecked(GET_FUNCTION_NAME_STRING_CHECKED(FVoxelDataItemConstructionInfo, Generator)));
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(PC_Bounds), *MakeStruct->FindPinChecked(GET_FUNCTION_NAME_STRING_CHECKED(FVoxelDataItemConstructionInfo, Bounds)));
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(PC_Mask), *MakeStruct->FindPinChecked(GET_FUNCTION_NAME_STRING_CHECKED(FVoxelDataItemConstructionInfo, Mask)));
CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *CallFunction->GetExecPin());
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(UEdGraphSchema_K2::PN_Then), *CallFunction->GetThenPin());
BreakAllNodeLinks();
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void UK2Node_AddDataItem::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
if (!ActionRegistrar.GetActionKeyFilter())
{
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
FARFilter Filter;
Filter.ClassNames.Add(UVoxelGraphDataItemConfig::StaticClass()->GetFName());
Filter.bRecursiveClasses = true;
TArray<FAssetData> Assets;
AssetRegistryModule.Get().GetAssets(Filter, Assets);
for (auto& Asset : Assets)
{
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
NodeSpawner->DefaultMenuSignature.MenuName = GetTitle(Asset.AssetName.ToString());
NodeSpawner->DefaultMenuSignature.Tooltip = GetTooltip(Asset.AssetName.ToString());
// Force load
// This sucks, but else we get weird errors when assets are loaded as AddDataItem nodes dependencies
// The item configs cannot have any dependencies, and are relatively small assets, so it should be fine
Asset.GetAsset();
if (Asset.IsAssetLoaded())
{
UVoxelGraphDataItemConfig* NewDataItemConfig = CastChecked<UVoxelGraphDataItemConfig>(Asset.GetAsset());
NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateLambda(
[NewDataItemConfig = MakeWeakObjectPtr(NewDataItemConfig)](UEdGraphNode* NewNode, bool /*bIsTemplateNode*/)
{
CastChecked<UK2Node_AddDataItem>(NewNode)->DataItemConfig = NewDataItemConfig.Get();
});
}
else
{
NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateLambda(
[Asset](UEdGraphNode* NewNode, bool /*bIsTemplateNode*/)
{
CastChecked<UK2Node_AddDataItem>(NewNode)->DataItemConfig = CastChecked<UVoxelGraphDataItemConfig>(Asset.GetAsset());
});
}
ActionRegistrar.AddBlueprintAction(Asset, NodeSpawner);
}
}
else
{
auto* NewDataItemConfig = Cast<UVoxelGraphDataItemConfig>(ActionRegistrar.GetActionKeyFilter());
if (NewDataItemConfig)
{
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateLambda(
[NewDataItemConfig = MakeWeakObjectPtr(NewDataItemConfig)](UEdGraphNode* NewNode, bool /*bIsTemplateNode*/)
{
CastChecked<UK2Node_AddDataItem>(NewNode)->DataItemConfig = NewDataItemConfig.Get();
});
NodeSpawner->DefaultMenuSignature.MenuName = GetTitle(NewDataItemConfig->GetName());
NodeSpawner->DefaultMenuSignature.Tooltip = GetTooltip(NewDataItemConfig->GetName());
ActionRegistrar.AddBlueprintAction(NewDataItemConfig, NodeSpawner);
}
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
FText UK2Node_AddDataItem::GetTitle(const FString& AssetName)
{
return FText::Format(VOXEL_LOCTEXT("Add Data Item: {0}"), FText::FromString(FName::NameToDisplayString(AssetName, false)));
}
FText UK2Node_AddDataItem::GetTooltip(const FString& AssetName)
{
return FText::Format(VOXEL_LOCTEXT("Use this to add a new voxel data item with the parameters defined in {0} to your voxel world"), FText::FromString(FName::NameToDisplayString(AssetName, false)));
}